Skip to main content

Lesson 12.1: The ros_gz Bridge

The Connection Problem

Gazebo runs a physics simulation. ROS 2 is a middleware for robotics. They speak different languages:

Gazebo thinks in terms of entities, models, and physics properties. It publishes sensor data in Gazebo-native message types (gz.msgs.Image, gz.msgs.LaserScan).

ROS 2 thinks in terms of nodes and topics. It publishes data in ROS message types (sensor_msgs/msg/Image, sensor_msgs/msg/LaserScan).

For a ROS 2 application to control a robot in Gazebo, something must translate between them. That something is ros_gz_bridge—a tool that bidirectionally maps ROS 2 topics to Gazebo topics, converting messages as needed.

Without the bridge: Your ROS 2 node publishes geometry_msgs/msg/Twist to /cmd_vel, but Gazebo has no idea what this message is. Your robot doesn't move.

With the bridge: The bridge translates /cmd_vel (Twist) to Gazebo's /cmd_vel (gz.msgs.Twist), and your robot responds.


Understanding Bridge Architecture

Think of ros_gz_bridge as a translator in a telephone conversation:

ROS 2 Node (English)

"Set velocity to 1.0 m/s forward"

ros_gz_bridge (Translator)

[Interprets intent, converts to Gazebo format]

Gazebo (Gazebo language)
"Apply velocity vector [1.0, 0, 0]"

Physics Simulation
Robot moves

The bridge handles three operations:

1. ROS 2 → Gazebo (ROS_TO_GZ)

  • ROS node publishes message on topic /cmd_vel
  • Bridge receives, converts to Gazebo format
  • Bridge writes to Gazebo topic /cmd_vel
  • Gazebo entity executes command

2. Gazebo → ROS 2 (GZ_TO_ROS)

  • Gazebo publishes sensor data on internal topic (e.g., camera image)
  • Bridge receives, converts to ROS message format
  • Bridge publishes on ROS 2 topic (e.g., /camera/image_raw)
  • ROS 2 nodes subscribe and process

3. Bidirectional (BIDIRECTIONAL)

  • Same topic, messages flow both directions
  • Less common, mainly for synchronized state

Bridge Syntax: The Three Parts

A bridge configuration has three key components:

/topic_name @ ROS_MESSAGE_TYPE @ GAZEBO_MESSAGE_TYPE

Example:

/cmd_vel @ geometry_msgs/msg/Twist @ gz.msgs.Twist

Breaking this down:

Part 1: Topic Name

/cmd_vel

The topic both sides use (or Gazebo topic if different). In practice, usually the same.

Part 2: ROS Message Type

geometry_msgs/msg/Twist

The message type ROS 2 uses. Full qualified name: package/msg/MessageName.

Common ROS types:

  • geometry_msgs/msg/Twist — velocity commands (linear + angular)
  • sensor_msgs/msg/Image — camera images
  • sensor_msgs/msg/LaserScan — LIDAR data (2D)
  • sensor_msgs/msg/PointCloud2 — point clouds (3D)
  • std_msgs/msg/Float64 — scalar numeric values

Part 3: Gazebo Message Type

gz.msgs.Twist

The message type Gazebo uses. Namespace is always gz.msgs.

Common Gazebo types:

  • gz.msgs.Twist — velocity commands
  • gz.msgs.Image — camera images
  • gz.msgs.LaserScan — LIDAR data
  • gz.msgs.PointCloudPacked — point clouds (packed binary)
  • gz.msgs.Double — scalar values
  • gz.msgs.Boolean — true/false values

Command-Line Bridge: Quick Testing

The simplest way to test a bridge is via command line:

ros2 run ros_gz_bridge parameter_bridge /cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist

This launches a single bridge for the /cmd_vel topic, converting between ROS Twist and Gazebo Twist.

To add more topics, run additional bridge commands in separate terminals:

# Terminal 1: Velocity commands
ros2 run ros_gz_bridge parameter_bridge /cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist

# Terminal 2: Camera image
ros2 run ros_gz_bridge parameter_bridge /camera/image_raw@sensor_msgs/msg/Image@gz.msgs.Image

# Terminal 3: LIDAR scan
ros2 run ros_gz_bridge parameter_bridge /lidar/scan@sensor_msgs/msg/LaserScan@gz.msgs.LaserScan

Testing the bridge:

In one terminal, echo a topic to verify data flows:

ros2 topic echo /cmd_vel

In another terminal, publish a test message:

ros2 topic pub /cmd_vel geometry_msgs/msg/Twist '{"linear": {"x": 0.5}, "angular": {"z": 0.0}}'

If the robot moves, the bridge is working.


YAML Configuration: Production Setup

Command-line works for testing. For real applications, use YAML configuration to define all bridges at once:

File: bridge.yaml

# Single topic bridge (ROS to Gazebo)
- ros_topic_name: "/cmd_vel"
gz_topic_name: "/cmd_vel"
ros_type_name: "geometry_msgs/msg/Twist"
gz_type_name: "gz.msgs.Twist"
direction: ROS_TO_GZ

# Single topic bridge (Gazebo to ROS)
- ros_topic_name: "/camera/image_raw"
gz_topic_name: "/camera/image"
ros_type_name: "sensor_msgs/msg/Image"
gz_type_name: "gz.msgs.Image"
direction: GZ_TO_ROS

# Bidirectional bridge
- ros_topic_name: "/odom"
gz_topic_name: "/odom"
ros_type_name: "nav_msgs/msg/Odometry"
gz_type_name: "gz.msgs.Odometry"
direction: BIDIRECTIONAL

Launch the bridge with YAML:

ros2 run ros_gz_bridge parameter_bridge --ros-args -p config_file:=bridge.yaml

Advantages of YAML:

  • All bridges in one place
  • Version control friendly (commit to git)
  • Reusable across multiple robots
  • Clear documentation of message mappings

Common Message Type Mappings

Here are the most useful ROS 2 ↔ Gazebo message mappings:

Velocity and Motion

ROS 2                           Gazebo
geometry_msgs/msg/Twist ↔ gz.msgs.Twist
geometry_msgs/msg/Pose ↔ gz.msgs.Pose
geometry_msgs/msg/Transform ↔ gz.msgs.Transform

Use for: Robot velocity commands, position commands, coordinate transforms

Sensors

ROS 2                           Gazebo
sensor_msgs/msg/Image ↔ gz.msgs.Image
sensor_msgs/msg/LaserScan ↔ gz.msgs.LaserScan
sensor_msgs/msg/PointCloud2 ↔ gz.msgs.PointCloudPacked
sensor_msgs/msg/Imu ↔ gz.msgs.IMU

Use for: Camera output, LIDAR scans, 3D point clouds, inertial data

Simple Values

ROS 2                         Gazebo
std_msgs/msg/Float64 ↔ gz.msgs.Double
std_msgs/msg/Int32 ↔ gz.msgs.Int32
std_msgs/msg/Bool ↔ gz.msgs.Boolean
std_msgs/msg/String ↔ gz.msgs.StringMsg

Use for: Scalar sensor readings, configuration values, simple signals


Verifying Bridge Connectivity

Once your bridge is running, verify it's actually connected:

Step 1: Check ROS 2 topics

ros2 topic list

You should see your bridge topics listed.

Step 2: Check Gazebo topics

gz topic --list

You should see Gazebo's internal topics.

Step 3: Echo topic data

# Check if data is flowing
ros2 topic echo /cmd_vel

If you see messages, the bridge is publishing.

Step 4: Test with a published message

# Send a test command
ros2 topic pub /cmd_vel geometry_msgs/msg/Twist '{"linear": {"x": 1.0, "y": 0, "z": 0}, "angular": {"x": 0, "y": 0, "z": 0}}'

Watch the simulated robot. If it moves, the bridge works end-to-end.


Debugging Bridge Problems

Problem 1: Bridge won't start

[ERROR] ros_gz_bridge: Invalid bridge syntax: /cmd_vel@...

Check:

  • Syntax is /topic@ROS_TYPE@GZ_TYPE
  • No extra spaces
  • ROS type includes package (geometry_msgs/msg/Twist, not just Twist)
  • Gazebo type starts with gz.msgs

Problem 2: Data not flowing (bridge runs but no messages)

# Check if bridge is listening
ros2 topic info /cmd_vel
# Should show "1 publisher, 0 subscribers" or similar

# Manually publish a test message
ros2 topic pub /cmd_vel geometry_msgs/msg/Twist '{"linear": {"x": 1.0}}'

If manual publishing works but nothing happens automatically:

  • Check that your ROS 2 node is actually publishing (use ros2 topic echo)
  • Verify message format matches (wrong fields = message rejected)

Problem 3: Type mismatch errors

[WARN] Cannot find ROS type: geometry_msgs/msg/Twist

The type doesn't exist or isn't installed. Install the message package:

sudo apt install ros-humble-geometry-msgs

Exercise: Configure Your First Bridge

Goal: Bridge velocity commands from ROS 2 to a simulated robot in Gazebo.

Setup:

  1. Launch Gazebo with a robot model (from Chapter 10/11)
  2. In a terminal, start the bridge:
    ros2 run ros_gz_bridge parameter_bridge /cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist
  3. In another terminal, publish a velocity command:
    ros2 topic pub /cmd_vel geometry_msgs/msg/Twist '{"linear": {"x": 0.5, "y": 0, "z": 0}, "angular": {"x": 0, "y": 0, "z": 0}}'

Success: The robot moves forward in Gazebo.

Verify:

  • ros2 topic list includes /cmd_vel
  • ros2 topic echo /cmd_vel shows your velocity messages
  • Robot in Gazebo responds to commands

Try With AI

Setup: Open ChatGPT or Claude and ask about ros_gz_bridge configuration for your specific robot.

Prompt Set:

Prompt 1 (Understanding message types):

I'm working with ros_gz_bridge and my robot has these sensors:
- A forward-facing camera
- A 2D LIDAR scanner
- An IMU sensor

Show me the YAML configuration for bridging these three sensors
from Gazebo to ROS 2. Include topic names and message types.

Prompt 2 (Troubleshooting):

My ros_gz_bridge is running but ros2 topic echo /cmd_vel shows no messages
even though my node is publishing. What could be wrong? Walk me through
the debugging checklist.

Prompt 3 (Advanced configuration):

I want to bridge the robot's odometry (position estimate) bidirectionally
between Gazebo and ROS 2. What message type should I use, and should
the direction be BIDIRECTIONAL or ROS_TO_GZ? Explain the tradeoff.

Expected Outcomes:

  • AI provides working YAML configuration you can adapt
  • AI suggests debugging steps you might not have considered
  • AI explains why certain message types match better than others

Safety Note: Test all bridge configurations in simulation first. A misconfigured bridge might command unexpected motion once deployed.


Next Steps

You've configured the bridge. In the next lesson, you'll use this bridge to spawn robots dynamically into Gazebo worlds, enabling modular testing and multi-robot scenarios.

What emerged from this lesson: Understanding how ROS 2 and Gazebo communicate enables debugging when things go wrong. The bridge is the foundation for all ROS 2-Gazebo integration work.


Key Concepts Checkpoint

Before moving on, verify you understand:

  • Bridge purpose: Translate between ROS 2 and Gazebo message formats
  • Bridge syntax: /topic@ROS_TYPE@GZ_TYPE with direction
  • Configuration methods: Command-line for testing, YAML for production
  • Message mappings: Knowing which ROS type corresponds to which Gazebo type
  • Verification: Using ros2 topic list/echo to confirm bridge functionality

If these are clear, you're ready for Lesson 12.2.