Chapter 2: Gazebo Setup & Configuration
Learning Objectives
By the end of this chapter, you will be able to:
- Install and configure Gazebo for ROS 2
- Understand Gazebo architecture and components
- Create and modify world files
- Use Gazebo plugins for sensor and actuator simulation
- Navigate the Gazebo GUI effectively
Introduction to Gazebo
Gazebo is an open-source 3D robotics simulator that integrates with ROS 2. It provides physics simulation, sensor simulation, and visualization capabilities essential for robotics development.
Why Gazebo?
Advantages:
- Open Source: Free and community-supported
- ROS 2 Integration: Native support through gazebo_ros2_control
- Realistic Physics: Uses ODE, Bullet, Simbody, or DART physics engines
- Sensor Suite: Cameras, LiDAR, IMU, force-torque, contact sensors
- Plugin System: Extensible with custom sensors and behaviors
Use Cases:
- Algorithm testing before hardware deployment
- Multi-robot system simulation
- Sensor fusion development
- Machine learning training environments
Gazebo Architecture
Components
- Gazebo Server (gzserver): Runs physics simulation and sensor generation
- Gazebo Client (gzclient): Provides 3D visualization and user interface
- Physics Engine: Computes rigid body dynamics and collisions
- Sensor Manager: Simulates cameras, LiDAR, IMU, etc.
- Plugin System: Custom functionality through shared libraries
Installation
Ubuntu 22.04 with ROS 2 Humble
# Install Gazebo Garden (recommended for ROS 2 Humble)
sudo apt update
sudo apt install ros-humble-gazebo-ros-pkgs
# Verify installation
gazebo --version
Alternative: Gazebo Fortress
# Add OSRF repository
sudo sh -c 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list'
wget https://packages.osrfoundation.org/gazebo.key -O - | sudo apt-key add -
# Install Gazebo Fortress
sudo apt update
sudo apt install gz-fortress
# Install ROS 2 integration
sudo apt install ros-humble-ros-gz
Verification
# Launch Gazebo with ROS 2 integration
ros2 launch gazebo_ros gazebo.launch.py
# In another terminal, check topics
ros2 topic list
# Expected topics:
# /clock
# /parameter_events
# /rosout
World Files
World files define the simulation environment using SDF (Simulation Description Format).
Basic World File Structure
<?xml version="1.0"?>
<sdf version="1.8">
<world name="my_world">
<!-- Physics settings -->
<physics type="ode">
<max_step_size>0.001</max_step_size>
<real_time_factor>1</real_time_factor>
<real_time_update_rate>1000</real_time_update_rate>
</physics>
<!-- Lighting -->
<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>
<!-- Ground plane -->
<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>
<surface>
<friction>
<ode>
<mu>100</mu>
<mu2>50</mu2>
</ode>
</friction>
</surface>
</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>
</material>
</visual>
</link>
</model>
<!-- Include models from Gazebo model database -->
<include>
<uri>model://sun</uri>
</include>
</world>
</sdf>
Physics Configuration
<physics type="ode">
<max_step_size>0.001</max_step_size> <!-- 1ms timestep -->
<real_time_factor>1</real_time_factor> <!-- 1x real-time -->
<real_time_update_rate>1000</real_time_update_rate> <!-- 1000 Hz -->
<!-- Solver settings -->
<ode>
<solver>
<type>quick</type> <!-- quick, world -->
<iters>50</iters> <!-- Constraint solver iterations -->
<sor>1.3</sor> <!-- Successive over-relaxation -->
</solver>
<constraints>
<cfm>0.0</cfm> <!-- Constraint force mixing -->
<erp>0.2</erp> <!-- Error reduction parameter -->
<contact_max_correcting_vel>100</contact_max_correcting_vel>
<contact_surface_layer>0.001</contact_surface_layer>
</constraints>
</ode>
</physics>
Creating Custom Models
<model name="simple_box">
<pose>0 0 0.5 0 0 0</pose> <!-- x y z roll pitch yaw -->
<static>false</static>
<link name="box_link">
<inertial>
<mass>1.0</mass>
<inertia>
<ixx>0.166667</ixx>
<ixy>0.0</ixy>
<ixz>0.0</ixz>
<iyy>0.166667</iyy>
<iyz>0.0</iyz>
<izz>0.166667</izz>
</inertia>
</inertial>
<collision name="collision">
<geometry>
<box>
<size>1 1 1</size>
</box>
</geometry>
</collision>
<visual name="visual">
<geometry>
<box>
<size>1 1 1</size>
</box>
</geometry>
<material>
<ambient>1 0 0 1</ambient> <!-- Red box -->
<diffuse>1 0 0 1</diffuse>
</material>
</visual>
</link>
</model>
Gazebo Plugins
Plugins extend Gazebo's functionality by adding custom sensors, actuators, and behaviors.
Common Plugin Types
- World Plugins: Modify world behavior
- Model Plugins: Add functionality to models
- Sensor Plugins: Custom sensor types
- System Plugins: Low-level Gazebo modifications
- Visual Plugins: Custom rendering
- GUI Plugins: User interface extensions
ROS 2 Integration Plugins
gazebo_ros_diff_drive
Simulates differential drive robot:
<plugin name="differential_drive_controller" filename="libgazebo_ros_diff_drive.so">
<ros>
<namespace>/demo</namespace>
<remapping>cmd_vel:=cmd_vel</remapping>
<remapping>odom:=odom</remapping>
</ros>
<update_rate>50</update_rate>
<!-- Wheels -->
<left_joint>left_wheel_joint</left_joint>
<right_joint>right_wheel_joint</right_joint>
<!-- Kinematics -->
<wheel_separation>0.5</wheel_separation>
<wheel_diameter>0.2</wheel_diameter>
<!-- Limits -->
<max_wheel_torque>20</max_wheel_torque>
<max_wheel_acceleration>1.0</max_wheel_acceleration>
<!-- Output -->
<publish_odom>true</publish_odom>
<publish_odom_tf>true</publish_odom_tf>
<publish_wheel_tf>false</publish_wheel_tf>
<odometry_frame>odom</odometry_frame>
<robot_base_frame>base_footprint</robot_base_frame>
</plugin>
gazebo_ros_camera
Simulates RGB camera:
<sensor name="camera" type="camera">
<update_rate>30</update_rate>
<camera>
<horizontal_fov>1.047</horizontal_fov> <!-- 60 degrees -->
<image>
<width>640</width>
<height>480</height>
<format>R8G8B8</format>
</image>
<clip>
<near>0.1</near>
<far>100</far>
</clip>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.007</stddev>
</noise>
</camera>
<plugin name="camera_controller" filename="libgazebo_ros_camera.so">
<ros>
<namespace>/robot</namespace>
<remapping>image_raw:=camera/image_raw</remapping>
<remapping>camera_info:=camera/camera_info</remapping>
</ros>
<camera_name>camera</camera_name>
<frame_name>camera_link</frame_name>
<hack_baseline>0.07</hack_baseline>
</plugin>
</sensor>
gazebo_ros_ray_sensor (LiDAR)
<sensor name="lidar" type="ray">
<update_rate>10</update_rate>
<ray>
<scan>
<horizontal>
<samples>360</samples>
<resolution>1</resolution>
<min_angle>-3.14159</min_angle>
<max_angle>3.14159</max_angle>
</horizontal>
</scan>
<range>
<min>0.12</min>
<max>30.0</max>
<resolution>0.01</resolution>
</range>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>
<plugin name="lidar_controller" filename="libgazebo_ros_ray_sensor.so">
<ros>
<namespace>/robot</namespace>
<remapping>~/out:=scan</remapping>
</ros>
<output_type>sensor_msgs/LaserScan</output_type>
<frame_name>lidar_link</frame_name>
</plugin>
</sensor>
gazebo_ros_imu_sensor
<sensor name="imu_sensor" type="imu">
<update_rate>100</update_rate>
<imu>
<angular_velocity>
<x>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>2e-4</stddev>
</noise>
</x>
<y>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>2e-4</stddev>
</noise>
</y>
<z>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>2e-4</stddev>
</noise>
</z>
</angular_velocity>
<linear_acceleration>
<x>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>1.7e-2</stddev>
</noise>
</x>
<y>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>1.7e-2</stddev>
</noise>
</y>
<z>
<noise type="gaussian">
<mean>0.0</mean>
<stddev>1.7e-2</stddev>
</noise>
</z>
</linear_acceleration>
</imu>
<plugin name="imu_plugin" filename="libgazebo_ros_imu_sensor.so">
<ros>
<namespace>/robot</namespace>
<remapping>~/out:=imu</remapping>
</ros>
<initial_orientation_as_reference>false</initial_orientation_as_reference>
</plugin>
</sensor>
Gazebo GUI Overview
Main Interface Components
- Scene View: 3D visualization of the simulated world
- Timeline: Play, pause, step simulation
- Insert Tab: Add models from model database
- World Tab: Configure world properties
- Model Editor: Create and modify models
- Building Editor: Design indoor environments
Camera Controls
- Orbit: Left-click + drag
- Pan: Shift + left-click + drag
- Zoom: Scroll wheel
- Look: Ctrl + left-click + drag
Useful Shortcuts
- Ctrl + R: Reset simulation
- Spacebar: Play/pause
- Ctrl + T: Show/hide timeline
- T: Toggle transparency mode
- G: Show/hide grid
- V: View wireframe mode
Inspecting Models
- Right-click model → View → Joints: Visualize joint axes
- Right-click model → View → Inertia: Show inertia visualizations
- Right-click model → View → Center of Mass: Display COM
- Right-click model → View → Collisions: Show collision geometry
Spawning Models Programmatically
Using ROS 2 Service
import rclpy
from rclpy.node import Node
from gazebo_msgs.srv import SpawnEntity
class ModelSpawner(Node):
def __init__(self):
super().__init__('model_spawner')
self.client = self.create_client(SpawnEntity, '/spawn_entity')
while not self.client.wait_for_service(timeout_sec=1.0):
self.get_logger().info('Waiting for spawn_entity service...')
def spawn_model(self, model_name, model_xml, pose):
request = SpawnEntity.Request()
request.name = model_name
request.xml = model_xml
request.initial_pose = pose
future = self.client.call_async(request)
rclpy.spin_until_future_complete(self, future)
if future.result() is not None:
self.get_logger().info(f'Successfully spawned {model_name}')
return True
else:
self.get_logger().error('Failed to spawn model')
return False
def main():
rclpy.init()
spawner = ModelSpawner()
# Define pose
from geometry_msgs.msg import Pose
pose = Pose()
pose.position.x = 0.0
pose.position.y = 0.0
pose.position.z = 0.5
# Load model from file
with open('path/to/model.sdf', 'r') as f:
model_xml = f.read()
spawner.spawn_model('my_robot', model_xml, pose)
spawner.destroy_node()
rclpy.shutdown()
Best Practices
Performance Optimization
- Use simplified collision meshes: Replace complex meshes with primitive shapes
- Limit sensor update rates: 30 Hz cameras, 10 Hz LiDAR often sufficient
- Run headless: Use gzserver only (no gzclient) for faster simulation
- Adjust physics parameters: Reduce solver iterations if real-time performance needed
Debugging
-
Enable verbose output:
gazebo --verbose -
Check ROS 2 topics:
ros2 topic list
ros2 topic echo /topic_name -
Monitor performance:
gz stats -
Validate SDF files:
gz sdf -k model.sdf
Assessment Questions
- What are the two main components of Gazebo and their roles?
- Explain the difference between collision and visual geometry in a model.
- What is the purpose of the
gazebo_ros_diff_driveplugin? - How do you spawn a model programmatically using ROS 2?
- Name three methods to improve Gazebo simulation performance.
Knowledge Check
-
Multiple Choice: Which physics engine does Gazebo use by default?
- A) Bullet
- B) ODE
- C) DART
- D) PhysX
Answer: B) ODE (Open Dynamics Engine)
-
True/False: The Gazebo client (gzclient) is required to run physics simulation.
- A) True
- B) False
Answer: B) False - Only gzserver is needed for physics; gzclient is just for visualization
-
Multiple Choice: What file format does Gazebo use for world and model definitions?
- A) URDF
- B) XML
- C) SDF
- D) JSON
Answer: C) SDF (Simulation Description Format)
-
Short Answer: Why might you want to run Gazebo in headless mode (without gzclient)?
Answer: Running headless improves performance by eliminating rendering overhead, which is useful for large-scale testing, CI/CD pipelines, or when visualization isn't needed
-
Scenario: Your robot model is falling through the ground plane. What are two possible causes?
Answer: (1) Ground plane collision is not properly defined or disabled; (2) Robot's initial pose has z-coordinate too low, causing it to spawn below the ground
References
Next Steps
In the next chapter, we'll explore URDF and SDF formats in detail, learning when to use each format and how to convert between them.