Simulation
Gazebo and ROS
These notes discuss practical usage of (Modern) Gazebo in ROS 2.
Installation
- Install gazebo and its ROS components with
sudo apt install ros-kilted-ros-gz-sim - Tutorials for Gazebo Ionic (Ionic is the version name)
- Each version of ROS 2 has a corresponding recommended Gazebo version: ROS Kilted goes with Gazebo Ionic
- As of ROS 2 Jazzy, specific Gazebo vendor packages are provided with ROS and can be installed with
sudo apt install ros-<my_ros_distro>-ros-gz-sim
- Tutorial on Integrating Gazebo with ROS 2
- The python launchfile examples in the tutorial above uses several poor practices that you should not adopt:
- Using
get_package_share_directory: instead use theFindPackageSharesubstitution- There are other scattered instances of using a non-declarative style in Python launchfiles.
SetEnvironmentVariable: with the package layout we use, setting the environment variable is not necessary because it is set when theinstall/setup.bashis sourced.IncludeLaunchDescripition(PythonLaunchDescriptionSource)ThePythonLaunchDescriptionSourceis unnecessary and therefore should not be used;IncludeLaunchDescriptioncan directly handle launchfiles of any time.
- Using
- The python launchfile examples in the tutorial above uses several poor practices that you should not adopt:
Running
From the Command Line
- Run gazebo with
ros2 launch ros_gz_sim gz_sim.launch.py- This launchfile provides a method to pass options to gazebo by specifying
gz_args:=<args to gazebo>. - The arguments are the same as those taken by
gz sim - The
gzcommand can also launchgazebodirectly (this is the non-ros method of starting gazebo).- The launchfile sets up the environment for Gazebo to better integrate with ROS so it is generally recommended
- This launchfile provides a method to pass options to gazebo by specifying
- ros_gz provides the primary method for interacting with Gazebo via ROS 2
- The
gzcommand is the primary way of controlling Gazebo independently of ROS- Using the
gzcommand is a valuable debugging tool, because it can help narrow down if the problem is with Gazebo or with ROS
- Using the
From a Launch File
- The server can be started with the
<gz_server>action in an XML launchfile - The GzServer action is used in a Python launchfile
- The python
GzServerclass (the python Action) provides the code that parses the<gz_server>xml tag, so these two actions are equivalent
- The python
- Prior to the introduction of the
<gz_server>tag, thegz_sim.launch.pylaunchfile needed to be included in your launchfile.- This old method is no longer recommended.
Package Layout
- An example of this package layout can be found at https://github.com/m-elwin/me495_gazebo.
- SDF files for
worldsgo in aworlds/directory and are installed toshare/<pkg_name>/worlds- Use
worldsto provide any custom world simulation assets created in your package
- Use
- SDF files for
modelsgo in amodels/directory and are installed toshare/<pkg_name>/models- Each model has all of its assets under a subdirectory of models (e.g.,
models/mymodel) - Each model has a
model.sdffile that specifies the model name. - To find the model, the name of the subdirectory must match the model name.
- Store any customized models created with your package
- Each model has all of its assets under a subdirectory of models (e.g.,
The
GZ_SIM_<SOMETHING>_PATHenvironment variables tellsgazebowhere to find SDF files and other resources- A ROS package that provides gazebo resources needs to set these variables appropriately.
Create a
.dsvfile in theenv-hooksdirectory of your package with the following content (be sure to update the<pkg_name>to be the name of the package):prepend-non-duplicate;GZ_SIM_RESOURCE_PATH;share/<pkg_name>/models prepend-non-duplicate;GZ_SIM_RESOURCE_PATH;share/<pkg_name>/worlds prepend-non-duplicate;GZ_SIM_PLUGIN_PATH;share/<pkg_name>/plugins prepend-non-duplicate;GZ_SYSTEM_PLUGIN_PATH;share/<pkg_name>/plugins
- Follow the colcon.pkg instructions to install the hook and have it update the environment whenever you source
install/setup.bash- When the hook is installed properly, after
source install/setup.bash,echo $GZ_SIM_RESOURCE_PATHshould contain the paths to thegazeboresources - The
PLUGINpaths are there for reference: plugins must be written in C++ and are beyond the scope of these notes.
- When the hook is installed properly, after
- In ROS 1, these paths were handled in the
package.xmlwith special Gazebo tags. It seems like similar functionality is being introduced into ROS 2, though we are not currently adopting this practice: see Transitional Documentation for more information.
SDF Files
Format
- Gazebo uses SDF files to represent robots, worlds, and everything that appears in the simulation.
- An SDF is an XML-based file format that is "human readable"
- SDF Tutorials provides help with writing SDF files, including building worlds
- Most Gazebo models also include additional assets (3D meshes, textures, etc) that the SF file refers to.
- There are two types of entity that can be described by SDF: models and worlds
- Models are individual objects in an environment
- Worlds describe a whole entire simulation scene and typically contain or refer to many models
Model Management
- Gazebo allows anybody to share simulation assets with the world via Gazebo Fuel
- The website contains functionality to preview, download, and link-to assets for simulation
- You can obtain a URI to refer to the model in your SDF file: this will cause Gazebo to download the model when required
- It is also possible to dowload a zip file containing all assets and the SDF file describing a world/model
- Assets that are referred to by URI will be downloaded and cached by Gazebo
- Thus loading a world for the first time can take a while due to the data being downloaded but it will load much quicker a second time.
- There are trade-offs between providing modles/worlds directly versus referring to them by URL
- By referring to the item by URL, the user only downloads the data when needed
- However, the server must be available when the user runs the simulation for the first time or else the asset cannot be used.
- Storing models/assets in the git repository ensures they are always available, but can enlarge the size of the repository and complicate version control
Creating Worlds
- In the Gazebo GUI, you can add the Resource Spawner to add models directly from Gazebo Fuel or your local computer, enabling you to create SDF world files interactively
- When saving the world file, Gazebo will include a URI that contains the absolute local path to each model in the world
- This means that, even if you spawned a resource from Fuel, the saved URI will refer to it's cached location
- Thus, by default, worlds saved in the Gazebo GUI cannot be used across computers
- After saving a world file, you must manually edit the URIs to make them portable
- Search for the
<uri>tag - Replace the
<uri>as follows:- If you are distributing the model with your repository (e.g., in the
modelsdirectory), it can be replaced withmodel:///model_name, because the template package we use installs those models and puts the install directory on theGZ_SIM_RESOURCE_PATH.- Models can be downloaded from Fuel and added to your repository
- If you wish for the end user to download the model from Fuel the first time the simulation is run, replace the
URIwith theURLfrom the Fuel
- If you are distributing the model with your repository (e.g., in the
- Search for the
Loading URDF/SDF files
URDF to SDF
- ROS can be used to load both URDF and SDF models into the simulator.
- URDF files are automatically converted to SDF prior to being loaded.
- For a URDF model to be loaded successfully, all links must have visual, collision, and inertia specified.
Additional
sdftags can be added to a URDF by using the<gazebo>tag, which provide additional flexibility when converting a URDF to an SDF<robot xmlns:xacro="http://www.ros.org/wiki/xacro> <gazebo> <!-- Resulting SDF file will have all the SDF tags here and will be associated with the full robot model at the top-level--> </gazebo> <gazebo reference="link_name"> <!-- resulting SDF file will have these SDF tags under the link named "link_name" --> </gazebo> <gazebo reference="joint_name"> <!-- resulting SDF file will have these SDF tags under the joint named "joint_name" --> </gazebo> </robot>
- A typical project structure is for a robot to provide:
- A
robot.urdf.xacrofile that contains robot definition without any gazebo tags. - A
robot.gazebo.xacrofile that includes only the tags necessary for gazebo robot.urdf.xacrotakes an argument (e.g.,gazebo) that conditionally includesrobot.gazebo.xacro.- If the argument is true, the
robot.gazebo.xacrofile is included - If the argument is false, the
robot.gazebo.xacrofile is NOT included, and the resulting URDF file is free of any Gazebo dependencies
- If the argument is true, the
- A
The SDF Gazebo extension to URDF provides more details.
SDF to URDF
- It is also possible to maintain a robot as an SDF file and convert it to a URDF file automatically using the sdformat_urdf package
- The SDF format is much broader than the URDF format, so maintaining an SDF file that can be reliably converted into a URDF file imposes additional restrictions on the SDF file
- Currently, most ROS packages that support Gazebo provide URDF files that can be converted into SDF files.
Debugging
It can be helpful to inspect urdf files at different stages of conversion, especially if there are errors
when trying to load your model.
- Make sure the files are installed via
setup.py - Source the workspace
- Run
xacro ./install/path_to_xacro_file > robot.urdfto produce theURDFfile - Run
gz sdf -p robot.urdf > robot.sdfto produce theSDFfile
Spawning Robots
There are several ways to spawn a URDF model in gazebo, provided by the ros_gz_sim package
From Command Line
- The
ros2 run ros_gz_sim createnode is the most straightforward method to spawning a model from the command-line (use--helpto see all the options- It gives full control of the model
- SDF and URDF files are supported (with URDF files being automatically converted)
- For a
xacrofile, you should load the model either using atopic(i.e., if therobot_state_publisher) is already running or from amodel_stringby using command substitution:$(xacro /path/to/myrobot.urdf.xacro)
- From the command line,
ros2 launch ros_gz_sim gz_spawn_model.launch.pycan be used.- This launch file is recommended in the tutorial, but seems to do a bunch of unnecessary/legacy environmental setup
From a Launch File
- The
<gz_spawn_model>tag is used to spawn a robot from an XML launch file - There is also GzSpawnModel for python launchfiles
- If you inspect the above file, you can see that the Launch Action (coded in Python) is responsible for parsing the
XMLaction, so the options and capabilities should be the same.
- If you inspect the above file, you can see that the Launch Action (coded in Python) is responsible for parsing the
- The
<gz_spawn_model>action was introduced relatively recently: prior to it's existence, you had to include thegz_spawn_model.launch.pylaunchfile in your launchfile or run thecreatenode directly.- Going forward the
<gz_spawn_model>tag is the preferred method.
- Going forward the
- As of 10/2025, the
gz_spawn_modelhas several optional arguments that (perhaps due to a bug) need to be present in thexmleven if they are set to the empty string""- The
x,y, andzcoordinates must be explicitly set to a floating-point value
- The
Bridging
- The ROS GZ Bridge is a node that is used to connect ROS topics and services to Gazebo topics and services.
- Once the bridge is running you can publish/subscribe to the topics like any other ROS topic
- You should generally run only one
ros_gz_bridgeand specify all topics/services that must be bridged - The Gazebo ROS 2 Bridge Documentation provides more information.
From the Command Line
ros2 run ros_gz_bridge parameter_bridge topic@ros_type@gz_type- Provide the topic name, the ROS type, and the corresponding gazebo type
- Use
--helpfor options/syntax - The second
@in the above syntax starts a bi-directional bridge (e.g. topics from gazebo are published to ROS and topics from ROS are published to Gazebo)- Replacing the second
@with a[creates a bridge from Gazebo to ROS and using a]creates a bridge from ROS to Gazebo - Sometimes uni-directional bridges are necessary. For example, we may want to receive
tfframes from Gazebo without having all of ROS's Tf frames existing in the simulator. - In general, use a uni-directional bridge whenever you do not explicitly need bi-directional communication; to avoid subtle unforeseen problems
- Replacing the second
- Be careful about topic types: for example the ROS type of the
/tftopic isTFMessagenotTransformStamped - Mappings between ROS and gz topic types are found in the API documentation
- The
parameter_bridgenode also accepts configuration in YAML Format
From a Launch File
- The
<ros_gz_bridge>tag is used to spawn a robot from an XML launch file- Using this tag is the preferred method. It also provides options to
use_composition, enabling it to be automatically run in the same container asgazebo, which reduces communication overhead.
- Using this tag is the preferred method. It also provides options to
- There is also RosGzBridge for python launchfiles
- If you inspect the above file, you can see that the Launch Action (coded in Python) is responsible for parsing the
XMLaction, so the options and capabilities should be the same.
- If you inspect the above file, you can see that the Launch Action (coded in Python) is responsible for parsing the
- The
<ros_gz_bridge>action was introduced relatively recently: prior to it's existence, you had to start the bridge node manually and provide all parameters as command line arguments
Simulation Time
- Time in simulation can run at a different speed than time in the real world
- ROS 2 Nodes can use time from a simulator by setting the
use_sim_timeparameter totrue - When
use_sim_timeistruethe node will get it's time from the/clocktopic rather than the system clock.- For example, if the simulation is running at
50%real time, then a100Hztimer in a ROS 2 node using simulation time will have that timer go off at100Hzin simulation time, which corresponds to50Hzin realtime.
- For example, if the simulation is running at
- To get
Gazeboto generateclocktopic, it must be bridged torosusing theparameter_bridge: by passing/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock - For many simulation applications, nodes do not need to use simulation time. However, if code is checking timestamps (such as the Nav2 stack), then ensuring that all nodes are using simulated time becomes important.
- Information about Timing issues with ROS 2 control
Plugins
- Gazebo uses plugins to enhance/modify functionality
- Some plugins can be associated with specific models and used to control them
- Useful Plugins can be found in the API documentation, especially the Systems.
- The documentation includes what tags can be used in the
SDFto include the plugin - Assume the C++ namespace of the plugin is given as
xx::yy::zz::systems::PluginNameetc. Then in the SDF file the plugin is included as - Use
<plugin name="xx::yy::zz::systems::PluginName" filename="xx-yy-zz-plugin-name-system">- For example, to use the
gz::sim::systems::DiffDriveplugin:- Set
nametogz::sim::systems::DiffDrive - set
filenametogz-sim-diff-drive-system - Options for this plugin go inside the
<plugin></plugin>tag (see API Documentation)
- Set
- For example, to use the
- The documentation includes what tags can be used in the
- The JointStatePublisher plugin publishes joint states
Common Problems
General Issues
- Gazebo uses a client-server architecture. Often the server does not exit when the client does and is running in the background
- If a server is already running in the background run
gz sim -gto launch the client.
- If a server is already running in the background run
- Make sure there aren't extraneous Gazebo processes running by looking through your process list for processes containing "gz" and killing them.
Software in Transition
As of 10/2025: There are three simultaneous transitions happening that have resulted in rapid churn in how ROS 2 and Gazebo works, which can make navigating online sources difficult:
- Transition from ROS to ROS 2
- Although the transition is complete there are still many references to "using Gazebo with ROS" which refer to ROS 1 and "Gazebo Classic" (the simulator formerly known as Gazebo)
- Transition from Gazebo Classic (i.e., the original Gazebo Simulator ) to Gazebo (e.g., the new Gazebo Simulator)
- Many google searches for "Gazebo" in fact return results for "Gazebo Classic": these simulators are completely different
- Modern gazebo documentation and resources are hosted on https://gazebosim.org. Gazebo Classic is hosted on https://classic.gazebosim.org
- The renaming of Ignition Gazebo (former name of the modern Gazebo simulator) to Gazebo and the renaming of Gazebo (the original Gazebo simulator) to Gazebo Classic
- There are still many references to
ignitionand commands toign - Here is a guide in case you only see the old documentation: Ignition to Gazebo.
- There are still many references to
- ROS 2 now distributes it's own "vendored" version of Gazebo with each ROS 2 release (before, gazebo was distributed with Ubuntu)
- These packages appear to be installed under
/opt/ros/<rosdistro>/opt: maybe it's to avoid name conflicts but it ends up polluting the path quite a bit.
- These packages appear to be installed under
- A new method of Gazebo ROS 2 Integration, which looks promising but does not seem
to be fully implemented or feature complete.
- It allows starting the
gazebo_serverusing a<gz_server>tag in a Launchfile (excellent!) - The server can run in a container as a composable node (great!)
- The ROS 2 Gazebo Bridge can also run in the composable Node and has it's own
xmllaunchfile tag (wonderful!) - The
create_own_containerattribute does not seem to be implemented in the currently released Jazzy version ofros_gz_simpackage (though it is in the code…??) - There does not seem to be a straightforward way (or at least I have not found one) to start the Gazebo Gui client for use with the
<gz_server>gz sim -gsometimes connects to the server and sometimes does not: simply starting and restarting it a few times seems to work but it is unreliable on my system (might need to just do a bit more debugging here).- It would be, in my opinion, a useful contribution to add the capability to run the Gazebo Gui as a component node and launch it with a
<gz_client>tag from a launchfile.
- It would be, in my opinion, a useful contribution to add the capability to run the Gazebo Gui as a component node and launch it with a
- The migration to the ROS 1 way of handling Gazebo Paths (custom tags in
package.xml) versus the ROS 2 way (using colconenv-hooks) may be better long-term, but it's implementation relies on using certain launchfiles which we cannot fully use currently. - The demonstrations for
ros_gz_simdo not seem updated for the new method. - Overall, I would like to adopt the new methods, but their still seems to be a mix of documentation promoting the new methods, examples with the old methods. Hopefully by next year this will be a bit more mature.
- It allows starting the
- There is also talk of maintaining robots as SDF files rather than URDF files. I have not yet seen this in practice, and am hesitant to adopt this practice given that SDF files are more general than URDF files (so URDF can always be converted to SDF but not vice-versa).
Documentation
- Gazebo Tutorials
- SDF Format
- ROS 2 Gazebo Interface (See the
README.mdfiles in each package in the repository) - Fuel Model Library