Using Gazebo#

Added in version Jazzy: This section.

Warning

This topic is under construction and this might not even be its final form. Please feel free to open an issue if you spot any typos or other problems.

Hint

You can start the simulator with the following command.

gz sim

In this section we will see the basic operations related to Gazebo and create our own sdf file.

Basic functionality#

These two official tutorials cover the basic functionality of Gazebo. They are very well made with up-to-date images and videos. Please go through them to familiarise yourself with the basic functionality.

Fun with plugins#

Check the guides below for basic functionality.

World files .sdf#

See also

Official documentation: https://gazebosim.org/docs/harmonic/sdf_worlds/

Let us start with the sample scene shapes.sdf. This example is available in the official repository. It is shown below to save you a click. The format is xml, which should be a familiar file format by now.

Contents of shapes.sdf
<?xml version="1.0" ?>
<sdf version="1.11">
  <!--
    Try moving a model using the command in the following CDATA block::
  -->
  <![CDATA[
    gz service -s /world/shapes/set_pose \
               --reqtype gz.msgs.Pose --reptype gz.msgs.Boolean \
               --timeout 300 --req 'name: "box", position: {z: 5.0}'
  ]]>
  <world name="shapes">
    <scene>
      <ambient>1.0 1.0 1.0</ambient>
      <background>0.8 0.8 0.8</background>
    </scene>

    <light type="directional" name="sun">
      <cast_shadows>true</cast_shadows>
      <pose>0 0 10 0 0 0</pose>
      <diffuse>0.8 0.8 0.8 1</diffuse>
      <specular>0.2 0.2 0.2 1</specular>
      <attenuation>
        <range>1000</range>
        <constant>0.9</constant>
        <linear>0.01</linear>
        <quadratic>0.001</quadratic>
      </attenuation>
      <direction>-0.5 0.1 -0.9</direction>
    </light>

    <model name="ground_plane">
      <static>true</static>
      <link name="link">
        <collision name="collision">
          <geometry>
            <plane>
              <normal>0 0 1</normal>
              <size>100 100</size>
            </plane>
          </geometry>
        </collision>
        <visual name="visual">
          <geometry>
            <plane>
              <normal>0 0 1</normal>
              <size>100 100</size>
            </plane>
          </geometry>
          <material>
            <ambient>0.8 0.8 0.8 1</ambient>
            <diffuse>0.8 0.8 0.8 1</diffuse>
            <specular>0.8 0.8 0.8 1</specular>
          </material>
        </visual>
      </link>
    </model>

    <model name="box">
      <pose>0 0 0.5 0 0 0</pose>
      <link name="box_link">
        <inertial>
          <inertia>
            <ixx>0.16666</ixx>
            <ixy>0</ixy>
            <ixz>0</ixz>
            <iyy>0.16666</iyy>
            <iyz>0</iyz>
            <izz>0.16666</izz>
          </inertia>
          <mass>1.0</mass>
        </inertial>
        <collision name="box_collision">
          <geometry>
            <box>
              <size>1 1 1</size>
            </box>
          </geometry>
        </collision>

        <visual name="box_visual">
          <geometry>
            <box>
              <size>1 1 1</size>
            </box>
          </geometry>
          <material>
            <ambient>1 0 0 1</ambient>
            <diffuse>1 0 0 1</diffuse>
            <specular>1 0 0 1</specular>
          </material>
        </visual>
      </link>
    </model>

    <model name="cylinder">
      <pose>0 -1.5 0.5 0 0 0</pose>
      <link name="cylinder_link">
        <inertial>
          <inertia>
            <ixx>0.1458</ixx>
            <ixy>0</ixy>
            <ixz>0</ixz>
            <iyy>0.1458</iyy>
            <iyz>0</iyz>
            <izz>0.125</izz>
          </inertia>
          <mass>1.0</mass>
        </inertial>
        <collision name="cylinder_collision">
          <geometry>
            <cylinder>
              <radius>0.5</radius>
              <length>1.0</length>
            </cylinder>
          </geometry>
        </collision>

        <visual name="cylinder_visual">
          <geometry>
            <cylinder>
              <radius>0.5</radius>
              <length>1.0</length>
            </cylinder>
          </geometry>
          <material>
            <ambient>0 1 0 1</ambient>
            <diffuse>0 1 0 1</diffuse>
            <specular>0 1 0 1</specular>
          </material>
        </visual>
      </link>
    </model>

    <model name="sphere">
      <pose>0 1.5 0.5 0 0 0</pose>
      <link name="sphere_link">
        <inertial>
          <inertia>
            <ixx>0.1</ixx>
            <ixy>0</ixy>
            <ixz>0</ixz>
            <iyy>0.1</iyy>
            <iyz>0</iyz>
            <izz>0.1</izz>
          </inertia>
          <mass>1.0</mass>
        </inertial>
        <collision name="sphere_collision">
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </collision>

        <visual name="sphere_visual">
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
          <material>
            <ambient>0 0 1 1</ambient>
            <diffuse>0 0 1 1</diffuse>
            <specular>0 0 1 1</specular>
          </material>
        </visual>
      </link>
    </model>

    <model name="capsule">
      <pose>0 -3.0 0.5 0 0 0</pose>
      <link name="capsule_link">
        <inertial>
          <inertia>
            <ixx>0.074154</ixx>
            <ixy>0</ixy>
            <ixz>0</ixz>
            <iyy>0.074154</iyy>
            <iyz>0</iyz>
            <izz>0.018769</izz>
          </inertia>
          <mass>1.0</mass>
        </inertial>
        <collision name="capsule_collision">
          <geometry>
            <capsule>
              <radius>0.2</radius>
              <length>0.6</length>
            </capsule>
          </geometry>
        </collision>
        <visual name="capsule_visual">
          <geometry>
            <capsule>
              <radius>0.2</radius>
              <length>0.6</length>
            </capsule>
          </geometry>
          <material>
            <ambient>1 1 0 1</ambient>
            <diffuse>1 1 0 1</diffuse>
            <specular>1 1 0 1</specular>
          </material>
        </visual>
      </link>
    </model>

    <model name="ellipsoid">
      <pose>0 3.0 0.5 0 0 0</pose>
      <link name="ellipsoid_link">
        <inertial>
          <inertia>
            <ixx>0.068</ixx>
            <ixy>0</ixy>
            <ixz>0</ixz>
            <iyy>0.058</iyy>
            <iyz>0</iyz>
            <izz>0.026</izz>
          </inertia>
          <mass>1.0</mass>
        </inertial>
        <collision name="ellipsoid_collision">
          <geometry>
            <ellipsoid>
              <radii>0.2 0.3 0.5</radii>
            </ellipsoid>
          </geometry>
        </collision>
        <visual name="ellipsoid_visual">
          <geometry>
            <ellipsoid>
              <radii>0.2 0.3 0.5</radii>
            </ellipsoid>
          </geometry>
          <material>
            <ambient>1 0 1 1</ambient>
            <diffuse>1 0 1 1</diffuse>
            <specular>1 0 1 1</specular>
          </material>
        </visual>
      </link>
    </model>

    <model name="cone">
      <pose>0 4.5 0.5 0 0 0</pose>
      <link name="cone_link">
        <inertial auto="true">
          <density>1</density>
        </inertial>
        <collision name="cone_collision">
          <geometry>
            <cone>
              <radius>0.5</radius>
              <length>1.0</length>
            </cone>
          </geometry>
        </collision>

        <visual name="cone_visual">
          <geometry>
            <cone>
              <radius>0.5</radius>
              <length>1.0</length>
            </cone>
          </geometry>
          <material>
            <ambient>1 0.47 0 1</ambient>
            <diffuse>1 0.47 0 1</diffuse>
            <specular>1 0.47 0 1</specular>
          </material>
        </visual>
      </link>
    </model>
  </world>
</sdf>

As you could have noticed, even simple world files can have a relatively complex format. The example shapes.sdf, that has only a few simple objects, is difficult to parse visually. For most things, it is easier to use Gazebo itself and limit and direct changes in .sdf files.

Adding or removing things directly on Gazebo is sometimes not repeatable with possible GUI changes across versions. In addition, it is not as natural for a text-based tutorial such as this one. In those scenarios, I will show how to edit the file directly.

This tutorial will limit itself on using default .sdf files and making small modifications. Our interest is to be able obtain and send useful information from Gazebo to ROS.

You will notice in this file some of the most common elements of .sdf. These are not the only elements and this list is not meant to be comprehensive.

  • <sdf> is the highest-level element, defining the entire .sdf.

  • <world name="world_name"> encloses the elements in this world and gives it a name. This name, rather than the name of the file, will be used when exposing topics and services.

  • <scene> has broad elements of the scene, such as background color.

  • Entities can include:
    • <light> a source of light

    • <model> a model in the simulation, such as simple shapes or a robot, that can include other information such as <link>, <visual>, and <collision>.

Note

If your application demands it, you will need specialised programs to create 3D models, such as Blender. It won’t be usually manageable to create complicated models directly on Gazebo.

In this example, because we’re using simple shapes supported by Gazebo, they are defined with <geometry> elements.

Gazebo topics and services#

Caution

Internal Gazebo topics and services are not the same as ROS2 topics and services.

A Gazebo scene will have a number of internal topics and services. We can interact with them using gz commands. The messages that flow through Gazebo are based on protobuf, not ROS2 IDL.

Let us use an example with sensors. This example is available in the official repository.

Contents of sensors_demo.sdf
  1<?xml version="1.0" ?>
  2<!--
  3  Load several rendering sensors.
  4-->
  5<sdf version="1.6">
  6  <world name="lidar_sensor">
  7    <physics name="1ms" type="ignored">
  8      <max_step_size>0.001</max_step_size>
  9      <real_time_factor>1.0</real_time_factor>
 10    </physics>
 11    <plugin
 12      filename="gz-sim-physics-system"
 13      name="gz::sim::systems::Physics">
 14    </plugin>
 15    <plugin
 16      filename="gz-sim-sensors-system"
 17      name="gz::sim::systems::Sensors">
 18      <render_engine>ogre2</render_engine>
 19    </plugin>
 20    <plugin
 21      filename="gz-sim-user-commands-system"
 22      name="gz::sim::systems::UserCommands">
 23    </plugin>
 24    <plugin
 25      filename="gz-sim-scene-broadcaster-system"
 26      name="gz::sim::systems::SceneBroadcaster">
 27    </plugin>
 28
 29
 30    <gui fullscreen="0">
 31
 32      <!-- 3D scene -->
 33      <plugin filename="MinimalScene" name="3D View">
 34        <gz-gui>
 35          <title>3D View</title>
 36          <property type="bool" key="showTitleBar">false</property>
 37          <property type="string" key="state">docked</property>
 38        </gz-gui>
 39
 40        <engine>ogre2</engine>
 41        <scene>scene</scene>
 42        <ambient_light>0.4 0.4 0.4</ambient_light>
 43        <background_color>0.8 0.8 0.8</background_color>
 44        <camera_pose>-6 0 6 0 0.5 0</camera_pose>
 45      </plugin>
 46
 47      <!-- Plugins that add functionality to the scene -->
 48      <plugin filename="EntityContextMenuPlugin" name="Entity context menu">
 49        <gz-gui>
 50          <property key="state" type="string">floating</property>
 51          <property key="width" type="double">5</property>
 52          <property key="height" type="double">5</property>
 53          <property key="showTitleBar" type="bool">false</property>
 54        </gz-gui>
 55      </plugin>
 56      <plugin filename="GzSceneManager" name="Scene Manager">
 57        <gz-gui>
 58          <property key="resizable" type="bool">false</property>
 59          <property key="width" type="double">5</property>
 60          <property key="height" type="double">5</property>
 61          <property key="state" type="string">floating</property>
 62          <property key="showTitleBar" type="bool">false</property>
 63        </gz-gui>
 64      </plugin>
 65      <plugin filename="InteractiveViewControl" name="Interactive view control">
 66        <gz-gui>
 67          <property key="resizable" type="bool">false</property>
 68          <property key="width" type="double">5</property>
 69          <property key="height" type="double">5</property>
 70          <property key="state" type="string">floating</property>
 71          <property key="showTitleBar" type="bool">false</property>
 72        </gz-gui>
 73      </plugin>
 74      <plugin filename="CameraTracking" name="Camera Tracking">
 75        <gz-gui>
 76          <property key="resizable" type="bool">false</property>
 77          <property key="width" type="double">5</property>
 78          <property key="height" type="double">5</property>
 79          <property key="state" type="string">floating</property>
 80          <property key="showTitleBar" type="bool">false</property>
 81        </gz-gui>
 82      </plugin>
 83      <!-- World control -->
 84      <plugin filename="WorldControl" name="World control">
 85        <gz-gui>
 86          <title>World control</title>
 87          <property type="bool" key="showTitleBar">false</property>
 88          <property type="bool" key="resizable">false</property>
 89          <property type="double" key="height">72</property>
 90          <property type="double" key="z">1</property>
 91
 92          <property type="string" key="state">floating</property>
 93          <anchors target="3D View">
 94            <line own="left" target="left"/>
 95            <line own="bottom" target="bottom"/>
 96          </anchors>
 97        </gz-gui>
 98
 99        <play_pause>true</play_pause>
100        <step>true</step>
101        <start_paused>true</start_paused>
102        <use_event>true</use_event>
103
104      </plugin>
105
106      <!-- World statistics -->
107      <plugin filename="WorldStats" name="World stats">
108        <gz-gui>
109          <title>World stats</title>
110          <property type="bool" key="showTitleBar">false</property>
111          <property type="bool" key="resizable">false</property>
112          <property type="double" key="height">110</property>
113          <property type="double" key="width">290</property>
114          <property type="double" key="z">1</property>
115
116          <property type="string" key="state">floating</property>
117          <anchors target="3D View">
118            <line own="right" target="right"/>
119            <line own="bottom" target="bottom"/>
120          </anchors>
121        </gz-gui>
122
123        <sim_time>true</sim_time>
124        <real_time>true</real_time>
125        <real_time_factor>true</real_time_factor>
126        <iterations>true</iterations>
127      </plugin>
128
129      <!-- Inspector -->
130      <plugin filename="ComponentInspector" name="Component inspector">
131        <gz-gui>
132          <property type="string" key="state">docked</property>
133        </gz-gui>
134      </plugin>
135
136      <!-- Entity tree -->
137      <plugin filename="EntityTree" name="Entity tree">
138        <gz-gui>
139          <property type="string" key="state">docked</property>
140        </gz-gui>
141      </plugin>
142
143      <plugin filename="ImageDisplay" name="Image Display">
144        <gz-gui>
145          <title>RGB camera</title>
146          <property key="state" type="string">floating</property>
147          <property type="double" key="width">350</property>
148          <property type="double" key="height">315</property>
149        </gz-gui>
150        <topic>camera</topic>
151        <topic_picker>false</topic_picker>
152      </plugin>
153      <plugin filename="ImageDisplay" name="Image Display 2">
154        <gz-gui>
155          <title>Depth camera</title>
156          <property key="state" type="string">floating</property>
157          <property type="double" key="width">350</property>
158          <property type="double" key="height">315</property>
159          <property type="double" key="x">500</property>
160        </gz-gui>
161        <topic>depth_camera</topic>
162        <topic_picker>false</topic_picker>
163      </plugin>
164      <plugin filename="ImageDisplay" name="Image Display 3">
165        <gz-gui>
166          <title>RGBD: image</title>
167          <property key="state" type="string">floating</property>
168          <property type="double" key="width">350</property>
169          <property type="double" key="height">315</property>
170          <property type="double" key="y">320</property>
171        </gz-gui>
172        <topic>rgbd_camera/image</topic>
173        <topic_picker>false</topic_picker>
174      </plugin>
175      <plugin filename="ImageDisplay" name="Image Display 3">
176        <gz-gui>
177          <title>RGBD: depth</title>
178          <property key="state" type="string">floating</property>
179          <property type="double" key="width">350</property>
180          <property type="double" key="height">315</property>
181          <property type="double" key="x">500</property>
182          <property type="double" key="y">320</property>
183        </gz-gui>
184        <topic>rgbd_camera/depth_image</topic>
185        <topic_picker>false</topic_picker>
186      </plugin>
187      <plugin filename="ImageDisplay" name="Image Display 5">
188        <gz-gui>
189          <title>Thermal camera</title>
190          <property key="state" type="string">floating</property>
191          <property type="double" key="width">350</property>
192          <property type="double" key="height">315</property>
193          <property type="double" key="x">500</property>
194          <property type="double" key="y">640</property>
195        </gz-gui>
196        <topic>thermal_camera</topic>
197        <topic_picker>false</topic_picker>
198      </plugin>
199    </gui>
200
201    <light type="directional" name="sun">
202      <cast_shadows>true</cast_shadows>
203      <pose>0 0 10 0 0 0</pose>
204      <diffuse>0.8 0.8 0.8 1</diffuse>
205      <specular>0.2 0.2 0.2 1</specular>
206      <attenuation>
207        <range>1000</range>
208        <constant>0.9</constant>
209        <linear>0.01</linear>
210        <quadratic>0.001</quadratic>
211      </attenuation>
212      <direction>-0.5 0.1 -0.9</direction>
213    </light>
214
215    <model name="ground_plane">
216      <static>true</static>
217      <link name="link">
218        <collision name="collision">
219          <geometry>
220            <!--plane>
221              <normal>0 0 1</normal>
222              <size>100 100</size>
223            </plane-->
224            <box>
225              <size>20 20 0.1</size>
226            </box>
227          </geometry>
228        </collision>
229        <visual name="visual">
230          <geometry>
231            <!--plane>
232              <normal>0 0 1</normal>
233              <size>100 100</size>
234            </plane-->
235            <box>
236              <size>20 20 0.1</size>
237            </box>
238          </geometry>
239          <material>
240            <ambient>0.8 0.8 0.8 1</ambient>
241            <diffuse>0.8 0.8 0.8 1</diffuse>
242            <specular>0.8 0.8 0.8 1</specular>
243          </material>
244        </visual>
245      </link>
246    </model>
247
248    <model name="box">
249      <pose>0 -1 0.5 0 0 0</pose>
250      <link name="box_link">
251        <inertial>
252          <inertia>
253            <ixx>1</ixx>
254            <ixy>0</ixy>
255            <ixz>0</ixz>
256            <iyy>1</iyy>
257            <iyz>0</iyz>
258            <izz>1</izz>
259          </inertia>
260          <mass>1.0</mass>
261        </inertial>
262        <collision name="box_collision">
263          <geometry>
264            <box>
265              <size>1 1 1</size>
266            </box>
267          </geometry>
268        </collision>
269
270        <visual name="box_visual">
271          <geometry>
272            <box>
273              <size>1 1 1</size>
274            </box>
275          </geometry>
276          <material>
277            <ambient>1 0 0 1</ambient>
278            <diffuse>1 0 0 1</diffuse>
279            <specular>1 0 0 1</specular>
280          </material>
281        </visual>
282      </link>
283    </model>
284
285    <model name="cameras_alone">
286      <pose>2.5 0 1.5 0 0.0 3.14</pose>
287      <link name="link">
288        <pose>0.05 0.05 0.05 0 0 0</pose>
289        <inertial>
290          <mass>0.1</mass>
291          <inertia>
292            <ixx>0.000166667</ixx>
293            <iyy>0.000166667</iyy>
294            <izz>0.000166667</izz>
295          </inertia>
296        </inertial>
297        <collision name="collision">
298          <geometry>
299            <box>
300              <size>0.1 0.1 0.1</size>
301            </box>
302          </geometry>
303        </collision>
304        <visual name="visual">
305          <geometry>
306            <box>
307              <size>0.1 0.1 0.1</size>
308            </box>
309          </geometry>
310        </visual>
311        <sensor name="cameras_alone" type="camera">
312          <camera>
313            <horizontal_fov>1.047</horizontal_fov>
314            <image>
315              <width>320</width>
316              <height>240</height>
317            </image>
318            <clip>
319              <near>0.1</near>
320              <far>100</far>
321            </clip>
322          </camera>
323          <always_on>1</always_on>
324          <update_rate>30</update_rate>
325          <visualize>true</visualize>
326          <topic>camera_alone</topic>
327          <enable_metrics>true</enable_metrics>
328        </sensor>
329        <sensor name="depth_camera1" type="depth_camera">
330          <update_rate>10</update_rate>
331          <topic>depth_camera</topic>
332          <enable_metrics>true</enable_metrics>
333          <camera>
334            <horizontal_fov>1.05</horizontal_fov>
335            <image>
336              <width>320</width>
337              <height>240</height>
338              <format>R_FLOAT32</format>
339            </image>
340            <clip>
341              <near>0.1</near>
342              <far>10.0</far>
343            </clip>
344          </camera>
345          </sensor>
346      </link>
347      <static>true</static>
348    </model>
349
350    <model name="camera_with_lidar">
351      <pose>4 0 0.5 0 0.0 3.14</pose>
352      <link name="link">
353        <pose>0.05 0.05 0.05 0 0 0</pose>
354        <inertial>
355          <mass>0.1</mass>
356          <inertia>
357            <ixx>0.000166667</ixx>
358            <iyy>0.000166667</iyy>
359            <izz>0.000166667</izz>
360          </inertia>
361        </inertial>
362        <collision name="collision">
363          <geometry>
364            <box>
365              <size>0.1 0.1 0.1</size>
366            </box>
367          </geometry>
368        </collision>
369        <visual name="visual">
370          <geometry>
371            <box>
372              <size>0.1 0.1 0.1</size>
373            </box>
374          </geometry>
375        </visual>
376        <sensor name="camera" type="camera">
377          <camera>
378            <horizontal_fov>1.047</horizontal_fov>
379            <image>
380              <width>320</width>
381              <height>240</height>
382            </image>
383            <clip>
384              <near>0.1</near>
385              <far>100</far>
386            </clip>
387          </camera>
388          <always_on>1</always_on>
389          <update_rate>30</update_rate>
390          <visualize>true</visualize>
391          <topic>camera</topic>
392        </sensor>
393
394        <sensor name='gpu_lidar' type='gpu_lidar'>"
395          <topic>lidar</topic>
396          <update_rate>10</update_rate>
397          <enable_metrics>true</enable_metrics>
398          <ray>
399            <scan>
400              <horizontal>
401                <samples>640</samples>
402                <resolution>1</resolution>
403                <min_angle>-1.396263</min_angle>
404                <max_angle>1.396263</max_angle>
405              </horizontal>
406              <vertical>
407                <samples>1</samples>
408                <resolution>0.01</resolution>
409                <min_angle>0</min_angle>
410                <max_angle>0</max_angle>
411              </vertical>
412            </scan>
413            <range>
414              <min>0.08</min>
415              <max>10.0</max>
416              <resolution>0.01</resolution>
417            </range>
418          </ray>
419          <visualize>true</visualize>
420        </sensor>
421      </link>
422
423      <static>true</static>
424    </model>
425
426    <model name="rgbd_camera">
427      <pose>5 0 0.5 0 0.0 3.14</pose>
428      <link name="link">
429        <pose>0.05 0.05 0.05 0 0 0</pose>
430        <inertial>
431          <mass>0.1</mass>
432          <inertia>
433            <ixx>0.000166667</ixx>
434            <iyy>0.000166667</iyy>
435            <izz>0.000166667</izz>
436          </inertia>
437        </inertial>
438        <collision name="collision">
439          <geometry>
440            <box>
441              <size>0.1 0.1 0.1</size>
442            </box>
443          </geometry>
444        </collision>
445        <visual name="visual">
446          <geometry>
447            <box>
448              <size>0.1 0.1 0.1</size>
449            </box>
450          </geometry>
451        </visual>
452        <sensor name="rgbd_camera" type="rgbd_camera">
453          <camera>
454            <horizontal_fov>1.047</horizontal_fov>
455            <image>
456              <width>320</width>
457              <height>240</height>
458            </image>
459            <clip>
460              <near>0.1</near>
461              <far>100</far>
462            </clip>
463          </camera>
464          <always_on>1</always_on>
465          <update_rate>30</update_rate>
466          <visualize>true</visualize>
467          <topic>rgbd_camera</topic>
468          <enable_metrics>true</enable_metrics>
469        </sensor>
470      </link>
471    </model>
472
473    <model name="thermal_camera">
474      <pose>3 0 0.5 0 0.0 3.14</pose>
475      <link name="link">
476        <pose>0.05 0.05 0.05 0 0 0</pose>
477        <inertial>
478          <mass>0.1</mass>
479          <inertia>
480            <ixx>0.000166667</ixx>
481            <iyy>0.000166667</iyy>
482            <izz>0.000166667</izz>
483          </inertia>
484        </inertial>
485        <collision name="collision">
486          <geometry>
487            <box>
488              <size>0.1 0.1 0.1</size>
489            </box>
490          </geometry>
491        </collision>
492        <visual name="visual">
493          <geometry>
494            <box>
495              <size>0.1 0.1 0.1</size>
496            </box>
497          </geometry>
498        </visual>
499        <sensor name="thermal_camera" type="thermal_camera">
500          <camera>
501            <horizontal_fov>1.047</horizontal_fov>
502            <image>
503              <width>320</width>
504              <height>240</height>
505            </image>
506            <clip>
507              <near>0.1</near>
508              <far>100</far>
509            </clip>
510          </camera>
511          <always_on>1</always_on>
512          <update_rate>30</update_rate>
513          <visualize>true</visualize>
514          <topic>thermal_camera</topic>
515        </sensor>
516      </link>
517    </model>
518
519    <include>
520      <pose>0 1 3 0.0 0.0 1.57</pose>
521      <uri>
522      https://fuel.gazebosim.org/1.0/OpenRobotics/models/Construction Cone
523      </uri>
524    </include>
525
526  </world>
527</sdf>

To enable physical simulation, including sensor, we can see that a new tag, <plugins> is added. These are paramount to guarantee that sensors will work. Further, they create important topics and services in Gazebo. Here are the plugins active on this file.

    <physics name="1ms" type="ignored">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
    </physics>
    <plugin
      filename="gz-sim-physics-system"
      name="gz::sim::systems::Physics">
    </plugin>
    <plugin
      filename="gz-sim-sensors-system"
      name="gz::sim::systems::Sensors">
      <render_engine>ogre2</render_engine>
    </plugin>
    <plugin
      filename="gz-sim-user-commands-system"
      name="gz::sim::systems::UserCommands">
    </plugin>
    <plugin
      filename="gz-sim-scene-broadcaster-system"
      name="gz::sim::systems::SceneBroadcaster">
    </plugin>

We will now start to interact with Gazebo. For this section, suppose that we have the scene always open with the following command.

Tip

You will see online, very frequently, the command using -v4. For instance, as follows. This allows Gazebo to output many messages to the terminal in which it was opened to help you understand what is happening.

gz sim -v4 sensors_demo.sdf
gz sim sensors_demo.sdf

A frequently used tool to allow us to inspect internal Gazebo topics will be, in another terminal, to run the following command. The -l flag allows us to list Gazebo topics.

gz topic -l

If the correct scene is running on Gazebo, the output should be similar to the following output.

/camera
/camera_alone
/camera_info
/clock
/depth_camera
/depth_camera/performance_metrics
/depth_camera/points
/gazebo/resource_paths
/gui/camera/pose
/gui/currently_tracked
/gui/track
/lidar
/lidar/points
/rgbd_camera/camera_info
/rgbd_camera/depth_image
/rgbd_camera/image
/rgbd_camera/performance_metrics
/rgbd_camera/points
/sensors/marker
/stats
/thermal_camera
/world/lidar_sensor/clock
/world/lidar_sensor/dynamic_pose/info
/world/lidar_sensor/pose/info
/world/lidar_sensor/scene/deletion
/world/lidar_sensor/scene/info
/world/lidar_sensor/state
/world/lidar_sensor/stats
/world/lidar_sensor/light_config
/world/lidar_sensor/material_color

Further information can be obtained about topics, for instance, the type of message that flows through them. We can check what is the message type used in one of the sensors as follows.

gz topic -i --topic /rgbd_camera/image

The output of that command should be similar to the following.

Publishers [Address, Message Type]:
  tcp://172.16.191.128:40679, gz.msgs.Image
Subscribers [Address, Message Type]:
  tcp://172.16.191.128:40229, gz.msgs.Image

Information about services, similarly, can be obtained with the following command.

gz service -l

Resulting in the following output.

Services in sensors_demo.sdf
/camera/set_rate
/camera_alone/set_rate
/depth_camera/set_rate
/gazebo/resource_paths/add
/gazebo/resource_paths/get
/gazebo/resource_paths/resolve
/gazebo/worlds
/gui/camera/view_control
/gui/camera/view_control/reference_visual
/gui/camera/view_control/sensitivity
/gui/follow
/gui/follow/offset
/gui/move_to
/gui/move_to/pose
/lidar/set_rate
/rgbd_camera/set_rate
/sensors/marker
/sensors/marker/list
/sensors/marker_array
/server_control
/thermal_camera/set_rate
/world/lidar_sensor/control
/world/lidar_sensor/control/state
/world/lidar_sensor/create
/world/lidar_sensor/create/blocking
/world/lidar_sensor/create_multiple
/world/lidar_sensor/create_multiple/blocking
/world/lidar_sensor/declare_parameter
/world/lidar_sensor/disable_collision
/world/lidar_sensor/disable_collision/blocking
/world/lidar_sensor/enable_collision
/world/lidar_sensor/enable_collision/blocking
/world/lidar_sensor/entity/system/add
/world/lidar_sensor/generate_world_sdf
/world/lidar_sensor/get_parameter
/world/lidar_sensor/gui/info
/world/lidar_sensor/level/set_performer
/world/lidar_sensor/light_config
/world/lidar_sensor/light_config/blocking
/world/lidar_sensor/list_parameters
/world/lidar_sensor/playback/control
/world/lidar_sensor/remove
/world/lidar_sensor/remove/blocking
/world/lidar_sensor/scene/graph
/world/lidar_sensor/scene/info
/world/lidar_sensor/set_parameter
/world/lidar_sensor/set_physics
/world/lidar_sensor/set_physics/blocking
/world/lidar_sensor/set_pose
/world/lidar_sensor/set_pose/blocking
/world/lidar_sensor/set_pose_vector
/world/lidar_sensor/set_pose_vector/blocking
/world/lidar_sensor/set_spherical_coordinates
/world/lidar_sensor/set_spherical_coordinates/blocking
/world/lidar_sensor/state
/world/lidar_sensor/state_async
/world/lidar_sensor/system/info
/world/lidar_sensor/visual_config
/world/lidar_sensor/visual_config/blocking
/world/lidar_sensor/wheel_slip
/world/lidar_sensor/wheel_slip/blocking

We can also obtain information about each service. For instance, with the command.

gz service -i --service /rgbd_camera/set_rate

Resulting in the following output.

Service providers [Address, Request Message Type, Response Message Type]:
  tcp://172.16.191.128:36013, gz.msgs.Double, gz.msgs.Empty

There are multiple ways to interact with the Gazebo internal topics. This can be, for instance, done through the commandline using similar commands to the ones we have shown so far. Nonetheless, if you have spent so much time learning ROS2, it would be more convenient to find a way to bridge the topics and services from Gazebo and ROS2.

Nonetheless, when some piece of information is not flowing as expected, remember these commands to help troubleshoot.

The package ros_gz_sim#

See also

The official repository: gazebosim/ros_gz

The package ros_gz_sim has a few tools allowing us to control Gazebo over ROS2. Namely, the ability to start Gazebo and spawn (add) objects. These might depend on Gazebo topics and services, therefore make sure that the necessary plugins are enabled.

Launching Gazebo#

For example, we can launch Gazebo directly from a launch file.

ros2 launch ros_gz_sim gz_sim.launch.py gz_args:=shapes.sdf

In this command, gz_args we add a representation file known to Gazebo. In this case, shapes.sdf. You can specify other scenes.

Spawn models#

You can use the following command to spawn models in Gazebo.

ros2 launch ros_gz_sim gz_sim.launch.py gz_args:=shapes.sdf
ros2 launch ros_gz_sim gz_spawn_model.launch.py world:=shapes file:=$(ros2 pkg prefix --share ros_gz_sim_demos)/models/vehicle/model.sdf entity_name:=my_vehicle x:=5.0 y:=5.0 z:=0.5

As arguments we have the

  • model filename

  • name for the entity inside Gazebo

  • position coordinates of the model

Note

The scene specified in the argument world must be active in Gazebo for the command above to work.

This will add the model, in this case, model.sdf to the scene shapes.sdf. The output on the terminal will be as follows.

[INFO] [launch]: All log files can be found below /home/murilo/.ros/log/2025-11-06-11-19-14-321361-murilo-VMware20-1-14158
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [create-1]: process started with pid [14161]
[create-1] [WARN] [1762427959.367373569] [ros_gz_sim]: Waiting for service [/world/shapes/create] to become available ...
[create-1] [INFO] [1762427959.598035759] [ros_gz_sim]: Entity creation successful.
[INFO] [create-1]: process has finished cleanly [pid 14161]

The package ros_gz_sim_demos#

See also

The official repository: gazebosim/ros_gz

This official package has many examples integrating Gazebo and ROS2. It showcases many functionalities that are useful for robotics applications.

You can verify all files usable for Gazebo demos in the following folder.

cd $(ros2 pkg prefix --share ros_gz_sim_demos)
nautilus .

The current file structure is shown in the dropdown below. There are many launch files, rviz configuration files, and a few .sdf models.

The structure of ros_gz_sim_demos.
/opt/ros/jazzy/share/ros_gz_sim_demos
├── cmake
│   ├── ros_gz_sim_demosConfig.cmake
│   └── ros_gz_sim_demosConfig-version.cmake
├── environment
│   ├── ament_prefix_path.dsv
│   ├── ament_prefix_path.sh
│   ├── path.dsv
│   ├── path.sh
│   └── ros_gz_sim_demos.dsv
├── launch
│   ├── air_pressure.launch.py
│   ├── battery.launch.py
│   ├── camera.launch.py
│   ├── depth_camera.launch.py
│   ├── diff_drive.launch.py
│   ├── gpu_lidar_bridge.launch.py
│   ├── gpu_lidar.launch.py
│   ├── image_bridge.launch.py
│   ├── imu.launch.py
│   ├── joint_states.launch.py
│   ├── magnetometer.launch.py
│   ├── navsat_gpsfix.launch.py
│   ├── navsat.launch.py
│   ├── rgbd_camera_bridge.launch.py
│   ├── rgbd_camera.launch.py
│   ├── robot_description_publisher.launch.py
│   ├── sdf_parser.launch.py
│   ├── tf_bridge.launch.py
│   └── triggered_camera.launch.py
├── local_setup.bash
├── local_setup.dsv
├── local_setup.sh
├── local_setup.zsh
├── models
│   ├── cardboard_box
│   │   ├── materials
│   │   │   └── textures
│   │   │       └── cardboard_box.png
│   │   ├── meshes
│   │   │   └── cardboard_box.dae
│   │   ├── model.config
│   │   ├── model.sdf
│   │   └── thumbnails
│   │       ├── 1.png
│   │       ├── 2.png
│   │       ├── 3.png
│   │       ├── 4.png
│   │       └── 5.png
│   ├── double_pendulum_model.sdf
│   ├── rrbot.xacro
│   └── vehicle
│       ├── model.config
│       └── model.sdf
├── package.dsv
├── package.xml
├── rviz
│   ├── camera.rviz
│   ├── depth_camera.rviz
│   ├── diff_drive.rviz
│   ├── gpu_lidar_bridge.rviz
│   ├── gpu_lidar.rviz
│   ├── imu.rviz
│   ├── joint_states.rviz
│   ├── rgbd_camera_bridge.rviz
│   ├── rgbd_camera.rviz
│   ├── robot_description_publisher.rviz
│   ├── tf_bridge.rviz
│   └── vehicle.rviz
└── worlds
    ├── default.sdf
    └── vehicle.sdf

Note

The contents above are not retrieved automatically so they might not represent the latest version of the repository. See gazebosim/ros_gz .

The camera.launch.py demo#

We can run this example with the following command.

ros2 launch ros_gz_sim_demos camera.launch.py

This will show Gazebo, with a couple of objects and a camera. The camera view is being rendered by Gazebo. The simulation is started. At the same time, rviz2 is executed. This demo is important because cameras are difficult to simulate otherwise and being able to access them via ROS2 allows you to create powerful image-based robot controllers and planners.

We can take a look at the topics created, while those programs are running, with the following command.

ros2 topic list

This will output the following.

/camera
/camera_info
/clicked_point
/initialpose
/move_base_simple/goal
/parameter_events
/rosout
/tf
/tf_static

The launch file, camera.launch.py, is currently performing the following steps.

  • Starting Gazebo with the built-in scene camera_sensor.sdf.

  • Running rviz2 with the configuration file camera.rviz.

  • Running ros_gz_bridge with the correct parameters to expose the camera sensor from Gazebo to ROS2.

This demo shows the camera simulation results on rviz2 because it’s possibly the most convenient way of showing that the information is correctly flowing through ROS2.

Note

Some unused topics such as /tf are defined in camera.rviz, but don’t worry about those.

Below are the contents of the launch file.

The contents of launch/camera.launch.py
 1# Copyright 2019 Open Source Robotics Foundation, Inc.
 2#
 3# Licensed under the Apache License, Version 2.0 (the "License");
 4# you may not use this file except in compliance with the License.
 5# You may obtain a copy of the License at
 6#
 7#     http://www.apache.org/licenses/LICENSE-2.0
 8#
 9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import os
16
17from ament_index_python.packages import get_package_share_directory
18
19from launch import LaunchDescription
20from launch.actions import DeclareLaunchArgument
21from launch.actions import IncludeLaunchDescription
22from launch.conditions import IfCondition
23from launch.launch_description_sources import PythonLaunchDescriptionSource
24from launch.substitutions import LaunchConfiguration
25
26from launch_ros.actions import Node
27
28
29def generate_launch_description():
30
31    pkg_ros_gz_sim_demos = get_package_share_directory('ros_gz_sim_demos')
32    pkg_ros_gz_sim = get_package_share_directory('ros_gz_sim')
33
34    gz_sim = IncludeLaunchDescription(
35        PythonLaunchDescriptionSource(
36            os.path.join(pkg_ros_gz_sim, 'launch', 'gz_sim.launch.py')),
37        launch_arguments={'gz_args': '-r camera_sensor.sdf'}.items(),
38    )
39
40    # RViz
41    rviz = Node(
42        package='rviz2',
43        executable='rviz2',
44        arguments=['-d', os.path.join(pkg_ros_gz_sim_demos, 'rviz', 'camera.rviz')],
45        condition=IfCondition(LaunchConfiguration('rviz'))
46    )
47
48    # Bridge
49    bridge = Node(
50        package='ros_gz_bridge',
51        executable='parameter_bridge',
52        arguments=['/camera@sensor_msgs/msg/Image@gz.msgs.Image',
53                   '/camera_info@sensor_msgs/msg/CameraInfo@gz.msgs.CameraInfo'],
54        output='screen'
55    )
56
57    return LaunchDescription([
58        DeclareLaunchArgument('rviz', default_value='true',
59                              description='Open RViz.'),
60        gz_sim,
61        bridge,
62        rviz
63    ])

Below are the contents of the rviz file.

The contents of rviz/camera.rviz
  1Panels:
  2  - Class: rviz_common/Displays
  3    Help Height: 0
  4    Name: Displays
  5    Property Tree Widget:
  6      Expanded:
  7        - /Global Options1
  8        - /Camera1
  9        - /Camera1/Status1
 10        - /Image1
 11      Splitter Ratio: 0.5
 12    Tree Height: 557
 13  - Class: rviz_common/Selection
 14    Name: Selection
 15  - Class: rviz_common/Tool Properties
 16    Expanded:
 17      - /2D Nav Goal1
 18      - /Publish Point1
 19    Name: Tool Properties
 20    Splitter Ratio: 0.5886790156364441
 21  - Class: rviz_common/Views
 22    Expanded:
 23      - /Current View1
 24    Name: Views
 25    Splitter Ratio: 0.5
 26Visualization Manager:
 27  Class: ""
 28  Displays:
 29    - Alpha: 0.5
 30      Cell Size: 1
 31      Class: rviz_default_plugins/Grid
 32      Color: 160; 160; 164
 33      Enabled: true
 34      Line Style:
 35        Line Width: 0.029999999329447746
 36        Value: Lines
 37      Name: Grid
 38      Normal Cell Count: 0
 39      Offset:
 40        X: 0
 41        Y: 0
 42        Z: 0
 43      Plane: XY
 44      Plane Cell Count: 10
 45      Reference Frame: <Fixed Frame>
 46      Value: true
 47    - Class: rviz_default_plugins/Camera
 48      Enabled: true
 49      Image Rendering: background and overlay
 50      Name: Camera
 51      Overlay Alpha: 0.5
 52      Queue Size: 10
 53      Topic: /camera
 54      Unreliable: false
 55      Value: true
 56      Visibility:
 57        Grid: true
 58        Image: true
 59        Value: true
 60      Zoom Factor: 1
 61    - Class: rviz_default_plugins/Image
 62      Enabled: true
 63      Max Value: 1
 64      Median window: 5
 65      Min Value: 0
 66      Name: Image
 67      Normalize Range: true
 68      Queue Size: 10
 69      Topic: /camera
 70      Unreliable: false
 71      Value: true
 72  Enabled: true
 73  Global Options:
 74    Background Color: 48; 48; 48
 75    Fixed Frame: camera/link/camera
 76    Frame Rate: 30
 77  Name: root
 78  Tools:
 79    - Class: rviz_default_plugins/MoveCamera
 80    - Class: rviz_default_plugins/Select
 81    - Class: rviz_default_plugins/FocusCamera
 82    - Class: rviz_default_plugins/Measure
 83      Line color: 128; 128; 0
 84    - Class: rviz_default_plugins/SetInitialPose
 85      Topic: /initialpose
 86    - Class: rviz_default_plugins/SetGoal
 87      Topic: /move_base_simple/goal
 88    - Class: rviz_default_plugins/PublishPoint
 89      Single click: true
 90      Topic: /clicked_point
 91  Transformation:
 92    Current:
 93      Class: rviz_default_plugins/TF
 94  Value: true
 95  Views:
 96    Current:
 97      Class: rviz_default_plugins/Orbit
 98      Distance: 19.73822784423828
 99      Enable Stereo Rendering:
100        Stereo Eye Separation: 0.05999999865889549
101        Stereo Focal Distance: 1
102        Swap Stereo Eyes: false
103        Value: false
104      Focal Point:
105        X: 0
106        Y: 0
107        Z: 0
108      Focal Shape Fixed Size: true
109      Focal Shape Size: 0.05000000074505806
110      Invert Z Axis: false
111      Name: Current View
112      Near Clip Distance: 0.009999999776482582
113      Pitch: 0.7903980016708374
114      Target Frame: <Fixed Frame>
115      Value: Orbit (rviz)
116      Yaw: 0.785398006439209
117    Saved: ~
118Window Geometry:
119  Camera:
120    collapsed: false
121  Displays:
122    collapsed: false
123  Height: 702
124  Hide Left Dock: false
125  Hide Right Dock: false
126  Image:
127    collapsed: false
128  QMainWindow State: 000000ff00000000fd00000004000000000000015600000268fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003b00000268000000c700fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c0000026100000001000001c700000268fc0200000005fb0000000c00430061006d006500720061010000003b0000011f0000002800fffffffb0000000a0049006d0061006700650100000160000001430000002800fffffffb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003b000002f8000000a000fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d00650100000000000004500000000000000000000000320000026800000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
129  Selection:
130    collapsed: false
131  Tool Properties:
132    collapsed: false
133  Views:
134    collapsed: false
135  Width: 859
136  X: 517
137  Y: 361

The launch file uses the package ros_gz_bridge, which is used to create the interfaces, for instance topics, between Gazebo and ROS2.

We will see this package in more detail in the following section.

References#