ROS 2 Learning Path  ยท  Illustrated Educational Book

Chapter 7: Gazebo โ€” ARCHO's Virtual Laboratory

Where gravity, friction, and collisions become real for the first time
Prerequisite: Chapters 4 through 6
Ongoing project: ARCHO robot
Tool: Gazebo (gz_ros2_control)
Reading time: 110 to 140 minutes
What we'll cover in this chapter 7.1From seeing to living 7.2What Gazebo actually simulates 7.3World: the virtual warehouse 7.4Connecting ARCHO to Gazebo 7.5Spawning the robot 7.6Simulated sensors and noise 7.7Closing the loop: from cmd_vel to real motion 7.8Summary, glossary, and exercises

7.1From seeing to living

In the previous chapter, the golden sentence was: "RViz is the robot's information window." Now it's time to complete the second sentence: Gazebo is the robot's virtual laboratory. The difference between the two becomes clear with a simple experiment: if you place ARCHO one meter above the ground, in RViz it will just stay suspended in the air โ€” because RViz only displays what it receives over the network. But in Gazebo, gravity is applied, ARCHO falls, and collides with the ground.

๐Ÿง  Simple Analogy

If you've designed a car, CAD software only shows what it looks like. But a driving simulator shows whether it moves, whether it tips over, whether it collides with a wall, whether the brakes work. Gazebo does exactly this for ARCHO โ€” before even a single real part is purchased or assembled.

flowchart LR A["URDF/Xacro
Robot model"] --> B["Gazebo
Physics engine"] B --> C["Motion, collision,
friction, gravity"] C --> D["Simulated sensors"] D --> E["ROS 2 Topics"] E --> F["RViz / Nav2 / SLAM / ros2_control"] style B fill:#eef0ff,stroke:#3d4bf5,color:#211f1a,font-weight:bold style E fill:#eafaf3,stroke:#0e9e6e,color:#211f1a

7.2What Gazebo actually simulates

Gazebo has a full physics engine that computes the following:

Physical conceptEffect on ARCHO
GravityIf Collision or Inertia is wrong, the robot falls or sinks into the ground
Mass and inertiaThe same values we calculated in Chapter 4 for the body and wheels
FrictionIf it's too low, the wheel spins but the robot doesn't move (like driving on ice)
Contact (Collision)Wheel-to-ground contact, body-to-warehouse-wall contact
๐Ÿ“– Real-Time Factor (RTF)

The ratio of simulation speed to real time. RTF = 1.0 means one second of simulation equals exactly one real second. RTF = 0.5 means the simulation runs at half real-time speed โ€” usually due to a heavy mesh, too many sensors, or weak hardware. For everyday testing, an RTF close to 1 is ideal.

๐Ÿ”ง Simulation Step

Gazebo computes physics in small time steps (e.g., 0.001 seconds). A smaller step means higher accuracy but lower speed; a larger step means higher speed but lower accuracy. This is the same trade-off we saw in Chapter 4 between a detailed Visual mesh and a simple Collision mesh โ€” again, we're dealing with the trade-off between accuracy and performance.

7.3World: the virtual warehouse

In Gazebo there isn't just a robot โ€” we have a complete World: floor, walls, shelves, lighting, and obstacles. The World file (extension .sdf, or the older .world) describes this environment.

archo_ws/src/archo_gazebo/
โ”œโ”€โ”€ worlds/
โ”‚   โ””โ”€โ”€ warehouse.world
โ”œโ”€โ”€ models/
โ”œโ”€โ”€ launch/
โ””โ”€โ”€ config/
Gazebo โ€” warehouse.world Warehouse floor with realistic friction, two metal shelving units, ARCHO scanning the environment with LiDAR
Figure 7.1 โ€” Layout of the Gazebo scene: ARCHO inside a simulated warehouse with shelving units, while LiDAR rays scan the environment.

A simple warehouse.world can include a floor, a few walls, several metal shelving units, and ambient lighting โ€” exactly the scene in which ARCHO will need to build a map and find a path in later chapters.

๐ŸŒ Where the model comes from

The typical model-building path: design in a CAD program โ†’ export a mesh โ†’ define it in URDF/Xacro (Chapter 4) โ†’ load it into Gazebo. But here's the important point: URDF doesn't just define appearance; for Gazebo to show correct physical behavior, the mass, inertia, collision, and joints must also be valid โ€” the very same ones we wrote carefully in Chapter 4.

7.4Connecting ARCHO to Gazebo

For ROS 2 Jazzy, the standard path is gz_ros2_control (the new generation of Gazebo, not the old Gazebo Classic). First, we install the required packages:

sudo apt update
sudo apt install \
  ros-jazzy-ros-gz \
  ros-jazzy-gz-ros2-control \
  ros-jazzy-ros2-control \
  ros-jazzy-ros2-controllers

Now inside archo.urdf.xacro, we tell each wheel joint what interface it has with Gazebo:

<ros2_control name="ArchoGazeboSystem" type="system">
  <hardware>
    <plugin>gz_ros2_control/GazeboSimSystem</plugin>
  </hardware>
  <joint name="left_wheel_joint">
    <command_interface name="velocity"/>
    <state_interface name="position"/>
    <state_interface name="velocity"/>
  </joint>
  <joint name="right_wheel_joint">
    <command_interface name="velocity"/>
    <state_interface name="position"/>
    <state_interface name="velocity"/>
  </joint>
</ros2_control>

<gazebo>
  <plugin
    filename="gz_ros2_control-system"
    name="gz_ros2_control::GazeboSimROS2ControlPlugin">
    <parameters>$(find archo_bringup)/config/controllers.yaml</parameters>
  </plugin>
</gazebo>
ConceptMeaning
command_interfaceWhat we command this joint from the outside (here: velocity)
state_interfaceWhat we read from this joint (position, actual velocity)

This block only defines the interface; exactly how we distribute the velocity command between the two wheels โ€” meaning the Controller itself โ€” is the topic of the next chapter (ros2_control).

7.5Spawning the robot

The complete launch architecture follows these four stages:

flowchart TB A["Launch Gazebo with warehouse.world"] --> B["Publish robot_description"] B --> C["Spawn ARCHO inside the World"] C --> D["Activate Controller Manager and Controllers"] style A fill:#eef0ff,stroke:#3d4bf5,color:#211f1a style D fill:#eafaf3,stroke:#0e9e6e,color:#211f1a,font-weight:bold
ros2 launch ros_gz_sim gz_sim.launch.py gz_args:=warehouse.world
ros2 run ros_gz_sim create -topic robot_description -name archo -z 0.2

After the robot appears, check that the controllers have activated:

dev@archo:~$ ros2 control list_controllers joint_state_broadcaster active diff_drive_controller active

If they didn't activate automatically:

ros2 run controller_manager spawner joint_state_broadcaster
ros2 run controller_manager spawner diff_drive_controller

7.6Simulated sensors and noise

One of Gazebo's best capabilities is sensor simulation โ€” LiDAR, camera, IMU, and encoders. We define ARCHO's LiDAR on the same laser_link we built in Chapter 4:

<gazebo reference="laser_link">
  <sensor name="lidar_sensor" type="gpu_lidar">
    <topic>scan</topic>
    <update_rate>10</update_rate>
    <lidar>
      <scan>
        <horizontal>
          <samples>720</samples>
          <min_angle>-3.14159</min_angle>
          <max_angle>3.14159</max_angle>
        </horizontal>
      </scan>
      <range>
        <min>0.10</min>
        <max>12.0</max>
        <resolution>0.01</resolution>
      </range>
    </lidar>
    <always_on>true</always_on>
  </sensor>
</gazebo>
ros2 topic list | grep scan
ros2 topic hz /scan
๐Ÿ“– Why we deliberately add noise

In the real world, no sensor is perfect โ€” a LiDAR might measure a distance of 2.000 meters as 1.994 or 2.006. If the simulation has no noise at all, algorithms that are tested and tuned against it may fail when transferred to the real robot. Gazebo lets you add controlled noise to LiDAR, IMU, and camera sensors so the simulation stays closer to reality.

๐ŸŒ Nav2 and SLAM don't care

Important point: algorithms like SLAM Toolbox or Nav2, which we'll see in later chapters, have no idea whether the LiDAR is real or virtual. They just listen to the /scan topic with message type sensor_msgs/LaserScan. This is exactly what makes Gazebo such a powerful tool for development without real hardware.

7.7Closing the loop: from cmd_vel to real motion

Now everything is ready to see the complete loop of ARCHO's motion:

flowchart TB A["/cmd_vel
Velocity command (manual or Nav2)"] --> B["diff_drive_controller"] B --> C["ros2_control"] C --> D["Gazebo wheels"] D --> E["Real robot motion in the World"] E --> F["/joint_states"] F --> G["robot_state_publisher"] G --> H["TF Tree"] H --> I["RViz"] style A fill:#eef0ff,stroke:#3d4bf5,color:#211f1a style I fill:#eafaf3,stroke:#0e9e6e,color:#211f1a,font-weight:bold

For a manual test, send a straight-line motion command:

ros2 topic pub /diff_drive_controller/cmd_vel geometry_msgs/msg/TwistStamped \
  "{twist: {linear: {x: 0.3}, angular: {z: 0.0}}}"

And for a turn:

ros2 topic pub /diff_drive_controller/cmd_vel geometry_msgs/msg/TwistStamped \
  "{twist: {linear: {x: 0.0}, angular: {z: 0.7}}}"
๐Ÿ”ง Behind the scenes of Differential Drive

When you tell the robot "move forward at a linear speed of 0.4 meters per second," in reality the speed of each of the two wheels must be computed separately. If v is the linear speed, ฯ‰ the angular speed, L the distance between the wheels, and r the wheel radius:

ฯ‰_left  = (v โˆ’ ฯ‰L/2) / r
ฯ‰_right = (v + ฯ‰L/2) / r

For example, with v=0.4, ฯ‰=0, L=0.4, r=0.08, both wheels must rotate at 5 radians per second. We don't write this calculation ourselves โ€” diff_drive_controller performs this conversion for us in the next chapter.

โœ… Chapter 7 Checkpoint
  • ARCHO stands on the ground in Gazebo; it neither falls through, nor explodes, nor floats.
  • ros2 control list_controllers shows both controllers as active.
  • A manual cmd_vel command causes the robot to actually move in the World.
  • /scan is publishing, and LiDAR points are visible in RViz.

7.8Chapter 7 summary

ARCHO is no longer just a static model โ€” it now lives in a physical world, feels its own weight, collides with the warehouse floor, and for the first time actually moves with a cmd_vel command. Its LiDAR publishes realistic (noisy) data on /scan โ€” the very topic and concept that SLAM and Nav2 will build on in later chapters.

Connection to the main project

ARCHO Project now has a new package โ€” archo_gazebo โ€” with a warehouse world and a full connection to physics. We're still sending velocity commands manually; the next chapter shows how ros2_control correctly distributes these commands between the two wheels.

What the next chapter adds

In Chapter 8, we'll take a close look at the ros2_control layer: the Controller Manager, the types of controllers, and the detailed configuration of the diff_drive_controller that we saw briefly today.

Chapter 7 glossary

Gazebo
A physics simulator for the robot and its environment; it computes gravity, mass, friction, collisions, and sensors.
World
The file describing the simulation environment โ€” floor, walls, objects, lighting.
Real-Time Factor (RTF)
The ratio of simulation speed to real time.
gz_ros2_control
The official plugin that connects ros2_control to Gazebo's physics engine.
command_interface / state_interface
The input (command) and output (actual state) interfaces of each joint in ros2_control.
Spawn
The act of placing a robot model (from robot_description) inside a running World.

Chapter 7 common mistakes โ€” recap