Create a Custom Gazebo Simulation World
In this chapter we will look at different approaches to design a Gazebo world. Before you get started make sure you are familiar with the content of Build a Custom Robot Simulation. In that page you learned how to use URDF and xacro to define your robot. URDF is an XML description format which is used in ROS to describe robots. SDF ia also an XML description format but is native to Gazebo. In general the standard recommendation is to use URDF to describe any elements of your simulation that are controlled through ROS (like your robot) and use SDF to describe the rest of the world that is simulated purely by Gazebo. Remember that xacro just adds extra functionality in XML file and therefore can be used in both URDF and SDF descriptions.
Including Worlds in launch file
In order to change how the design of the world we need to add our own world file that we can modify to our package. This has already been covered on the Simulation (Gazebo) page. It involves the following steps:
- Create a world file inside a folder called worlds within you package.
- Make sure the setup.py file includes the following line:
# ADD AFTER: ('share/' + package_name, ['package.xml']), (os.path.join('share', package_name, 'worlds'), glob(os.path.join('worlds', '*.sdf'))), - In the launch file, which launches your robot change the launch description of gazebo to include the world file as an argument. Note: you should replace
WORLD_FILE_NAMEandWORLD_FILEwith other names.WORLD_FILE = os.path.join(package_path, 'worlds', 'WORLD_FILE_NAME.sdf') gazebo_sim = IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join(get_package_share_directory('ros_gz_sim'), 'launch', 'gz_sim.launch.py') ), launch_arguments={'gz_args': '-r ' + WORLD_FILE +' --physics-engine gz-physics-bullet-featherstone-plugin'}.items() ) - Build your workspace:
cd ~/ros2_ws colcon build --symlink-install
Using xacro with sdf files
If you want to use xacro functionality in your world description you need to pre-compile it to a pure sdf file using the following in your launch file:
world_xacro_file_path = os.path.join(get_package_share_directory('custom_robot_sim'), 'worlds', 'WORLD_FILE_NAME.xacro.sdf')
target_world_file_path = os.path.join(get_package_share_directory('custom_robot_sim'), 'worlds', 'TARGET_WORLD_FILE_NAME.sdf')
world_sdf_xacro = ExecuteProcess(
cmd=[
"xacro",
"-o",
target_world_file_path,
world_xacro_file_path,
]
)
We can then include the path to the pure sdf file as a launch argument when we launch gazebo, the same way we would usually do:
gazebo_sim = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(get_package_share_directory('ros_gz_sim'), 'launch', 'gz_sim.launch.py')
),
launch_arguments={'gz_args': '-r ' + target_world_file_path +' --physics-engine gz-physics-bullet-featherstone-plugin'}.items()
)
Note: since xacro world files need precompiling it is common to use .xacro.sdf for the filename to easily distinguish them from pure SDF files.
Using URDF description
While it is generally not recommended you can use the URDF description to also describe other objects in your world and spawn them dynamically. If you choose to do this you can define the object using URDf like it is explained on the Build a Custom Robot Simulation page. To spawn a model defined this way use the following code in you launch file:
OBJECT_DESCRIPTION = Command(
[
PathJoinSubstitution([FindExecutable(name='xacro')]),
' ',
PathJoinSubstitution(
[FindPackageShare('PACKAGE_NAME'),
'urdf', 'FILE_NAME.urdf.xacro']
),
]
)
SPAWN_OBJECT = Node(
package='ros_gz_sim',
executable='create',
output='screen',
parameters=[{
'string': OBJECT_DESCRIPTION,
'name': 'OBJECT_NAME',
'allow_renaming': True,
'x': 0.0,
'y': 0.0,
'z': 0.0,
}],
)
Using SDF description (the proper way)
In this section we will look at the proper way of defining a Gazebo world using SDF. The official guid to creating worlds using SDF can be found here. The basic structure of an sdf world looks like this:
<?xml version="1.0" ?>
<sdf version="1.6">
<world name="WORLD_NAME">
<model name="MODEL_NAME">
<pose>0.0 0.0 0.0 0.0 0.0 0.0</pose>
<static>false</static>
<link name="LINK_NAME">
</link>
<joint name="JOINT_NAME" type="fixed">
</joint>
<link name="LINK_NAME">
</link>
</model>
<model name="MODEL_NAME">
<pose>0.0 0.0 0.0 0.0 0.0 0.0</pose>
<static>false</static>
<link name="LINK_NAME">
</link>
<joint name="JOINT_NAME" type="fixed">
</joint>
<link name="LINK_NAME">
</link>
</model>
</world>
</sdf>
Inside the <world> tags you can define the different objects that build the environment using the <model> tags. You can also describe other things like the physics or the lighting. If we want to use xacro functionality in your sdf file the basic the sdf definition changes to:
<?xml version="1.0" ?>
<sdf version="1.6" xmlns:xacro="http://www.ros.org/wiki/xacro">
</sdf>
Defining models separately
Normally the model of each object is defined separately and then just referenced insider the world file. This allows you to reuse a model multiple times insider the same world without needing multiple definitions. The general setup includes the following setups:
- Create a folder called models inside in your package. Any sdf models that you create should be placed there. Any model file that you create should either end with
.xacro.sdfif you are using xacro inside or otherwise just.sdfif it’s a pure sdf model. - Maker sure the setup.py file includes the following line:
# ADD AFTER: ('share/' + package_name, ['package.xml']), (os.path.join('share', package_name, 'models'), glob(os.path.join('models', '*.sdf'))), - If you are using xacro in your world file you can reference the model using:
<model name="MODEL_NAME"> <pose>0.0 0.0 0.0 0.0 0.0 0.0</pose> <static>false</static> <xacro:include filename="$(find PACKAGE_NAME)/models/MODEL_FILE_NAME" /> </model>- Don’t forget to build and source your workspace!
Link
This is the basic structure of defining a link with SDF:
<link name="LINK_NAME">
<visual name="VISUAL_LINK_NAME">
<pose>0.0 0.0 0.0 0.0 0.0 0.0</pose>
<geometry>
</geometry>
<material>
</material>
</visual>
<collision name="COLLISION_LINK_NAME">
<pose>0.0 0.0 0.0 0.0 0.0 0.0</pose>
<geometry>
</geometry>
</collision>
<inertial>
</inertial>
</link>
Each link has, similarly to URDF, <visual>, <collision> and <inertial> tags.
Geometries
The official documentation of defining geometries in SDF can be found here.
Box
<box>
<!-- x-axis y-axis z-axis -->
<size>0.1 0.1 0.1</size>
</box>
Cylinder
<cylinder>
<length>0.1</length>
<radius>0.1</radius>
</cylinder>
Sphere
<sphere>
<radius>0.1</radius>
</sphere>
Inertial
This is the basic structure of the inertial tags:
<inertial>
<pose>0 0 0 0 0 0</pose>
<mass>1.0</mass>
<inertia>
<ixx>1.0</ixx>
<ixy>0.0</ixy>
<ixz>0.0</ixz>
<iyy>1.0</iyy>
<iyz>0.0</iyz>
<izz>1.0</izz>
</inertia>
</inertial>
Similarly to URDF you also have access to macros for defining the inertial tags. In order to be able to use them make sure you include the file in you model description using:
<xacro:include filename="$(find custom_robot_sim)/models/inertial_macros.xacro.sdf" />
Box macro
<xacro:inertial_box length="0.1" width="0.1" height="0.1" density="1">
<pose>0 0 0 0 0 0</pose>
</xacro:inertial_box>
Cylinder macro
<xacro:inertial_cylinder length="0.1" radius="0.1" density="1">
<pose>0 0 0 0 0 0</pose>
</xacro:inertial_cylinder>
Sphere macro
<xacro:inertial_sphere radius="0.1" density="1">
<pose>0 0 0 0 0 0</pose>
</xacro:inertial_sphere>
Materials
The color of materials in Gazebo is not defined in RGB but rather using the Blinn-Phong shading model, which uses Ambient, Diffuse, Specular and Emissive. The official Gazebo guide to how these parameters work can be found here. Here is an example material definition:
<material>
<ambient>0.8 0.8 0.8 1.0</ambient>
<diffuse>0.75 0.75 0.75 1.0</diffuse>
<specular>0.5 0.5 0.5 1.0</specular>
<emissive>0.0 0.0 0.0 1.0</emissive>
</material>
Joints
Same as in URDF we use joints to connect links. The official documentation to defining joints can be found here.
Fixed
<joint name="JOINT_NAME" type="fixed">
<parent>LINK_NAME</parent>
<child>LINK_NAME</child>
</joint>
Revolute
<joint name="JOINT_NAME" type="revolute">
<parent>LINK_NAME</parent>
<child>LINK_NAME</child>
<axis>
<xyz>0 0 1</xyz>
<limit>
<lower>-1.57</lower>
<upper>1.57</upper>
</limit>
</axis>
</joint>
Prismatic
<joint name="JOINT_NAME" type="prismatic">
<parent>LINK_NAME</parent>
<child>LINK_NAME</child>
<axis>
<xyz>0 0 1</xyz>
<limit>
<lower>-1.0</lower>
<upper>1.0</upper>
</limit>
</axis>
</joint>
Exercise
Create a model of a cube and a box and place them in the center of the two tables. The tables have a tabletop that is [0.7 0.7 0.02] meters and four legs that are cylinders with radius = 0.02 meters and length = 0.68 meters. They are located at +/- 0.6 meters on the y-axis.
Setup Process
- Add the controller_action.py file from the ros2_students_25 repository in the custom_robot_sim/custom_robot_sim folder in your ROS workspace.
- Replace the following folders in the custom_robot_sim package with the ones from the ros2_students_25 repository: worlds, models, launch, config and urdf.
- Ensure your setup.py includes the following lines:
# AFTER: ('share/' + package_name, ['package.xml']), (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*.launch.py'))), (os.path.join('share', package_name, 'urdf'), glob(os.path.join('urdf', '*.xacro'))), (os.path.join('share', package_name, 'meshes'), glob(os.path.join('meshes', '*.stl'))), (os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.yaml'))), (os.path.join('share', package_name, 'worlds'), glob(os.path.join('worlds', '*.sdf'))), (os.path.join('share', package_name, 'models'), glob(os.path.join('models', '*.sdf'))), # AFTER: 'console_scripts': [ 'joint_trajectory_client = custom_robot_sim.controller_action:main' - Don’t forget to build and source your workspace!
- To launch the world use:
ros2 launch custom_robot_sim custom_manipulator_world_sdf.launch.py - To run the robot movements use:
ros2 run custom_robot_sim joint_trajectory_client
Create Cube Model
- The model of the cube is simply a box of size
[0.05 0.05 0.05]meters. - Place the cube in the center of the left table.
Create a Model of a Box
- Create a a box which is open at the top. The inner dimensions of the box should be
[0.1 0.1 0.075]meters. - Place the box in the center of the right table.