Skip to main content
🌉 ros1_bridgeROS 2 · June 2026

ROS 1 to ROS 2 Bridge Guide 2026

Migrate gradually from ROS 1 (Noetic) to ROS 2 (Humble/Jazzy) using ros1_bridge: set up a dual-source environment, use dynamic_bridge for auto-discovery or parameter_bridge for explicit topic mapping, bridge /tf, and port custom message types.

Migration Strategy

Start by bridging stable ROS 1 nodes (sensor drivers, hardware interfaces) into ROS 2, while writing new features natively in ROS 2. Port the most critical nodes first, then remove the bridge when the system is fully migrated. Avoid keeping ros1_bridge in production long-term — it adds latency and complexity.

1

ros1_bridge — When and Why

ros1_bridge lets ROS 1 (Noetic) and ROS 2 (Humble/Jazzy) nodes talk to each other. It's the standard migration path: keep stable ROS 1 nodes running while you port others to ROS 2.

text
ros1_bridge Architecture
═══════════════════════════════════════════════════════
ROS 1 nodes ←→ ros1_bridge ←→ ROS 2 nodes
(XMLRPC/TCPROS)              (DDS/FastDDS)

What it bridges:
  Topics   — pub/sub with automatic type mapping
  Services — request/response cross-version
  Actions  — NOT directly; use topic bridge for actionlib topics

Requirements:
  1. ROS 1 (Noetic) installed alongside ROS 2 (Humble/Jazzy)
  2. Both environments must be sourced in the SAME shell
  3. Message types must have equivalent definitions in both distros
  4. ROS 1 roscore must be running

Installation:
  sudo apt install ros-humble-ros1-bridge

Source order (critical):
  source /opt/ros/noetic/setup.bash    # ROS 1 FIRST
  source /opt/ros/humble/setup.bash    # ROS 2 SECOND
  # (ROS 2 setup must come after ROS 1)

Limitations:
  - Only bridges topics/services with matching type pairs
  - Custom message types need source on BOTH sides and recompilation
  - Not available in containers without dual ROS install
  - Adds latency (~1-5ms per hop)
  - Not a long-term solution — plan to port fully to ROS 2

For new robots, avoid ros1_bridge entirely and write directly in ROS 2. Use the bridge only for large existing codebases where full porting would take months. Prioritize porting the most critical nodes first.

2

Environment Setup — Dual-Source Shell

Both ROS 1 and ROS 2 must be sourced in the same terminal session. The roscore must be running before the bridge starts.

bash
# ── Terminal 1: source both, start roscore ───────────────────
source /opt/ros/noetic/setup.bash
source /opt/ros/humble/setup.bash

# Verify both are sourced
echo $ROS_DISTRO         # should show: humble (last sourced wins)
roscore &                # start ROS 1 master in background

# Alternatively (systemd or separate terminal):
# ROS_MASTER_URI=http://localhost:11311 roscore &

# ── Verify roscore is up ─────────────────────────────────────
rosnode list             # should show /rosout (ROS 1 nodes)
ros2 node list           # should show nothing yet

# ── Check bridge package is installed ────────────────────────
ros2 pkg list | grep ros1_bridge
# → ros1_bridge

# ── Environment variables ─────────────────────────────────────
# Tells bridge where to find the ROS 1 master
export ROS_MASTER_URI=http://localhost:11311
export ROS_HOSTNAME=localhost    # for multi-machine setups
3

dynamic_bridge — Auto-Bridge All Topics

dynamic_bridge monitors ROS 1 and ROS 2 topic registrations and automatically creates bridges for topics where both sides have active publishers and subscribers.

bash
# ── Terminal 1: source + roscore ─────────────────────────────
source /opt/ros/noetic/setup.bash
source /opt/ros/humble/setup.bash
roscore &

# ── Terminal 2: start dynamic_bridge ─────────────────────────
source /opt/ros/noetic/setup.bash
source /opt/ros/humble/setup.bash

ros2 run ros1_bridge dynamic_bridge

# dynamic_bridge output:
# [INFO] [ros1_bridge]: Waiting for ROS 1 master...
# [INFO] [ros1_bridge]: ROS 1 master at http://localhost:11311
# created 1 bridges for topics [/scan] ...
# created 1 bridges for topics [/cmd_vel] ...

# ── Terminal 3: ROS 1 publisher ───────────────────────────────
source /opt/ros/noetic/setup.bash
rostopic pub /chatter std_msgs/String "data: 'Hello from ROS 1'" -r 1

# ── Terminal 4: ROS 2 subscriber ─────────────────────────────
source /opt/ros/humble/setup.bash
ros2 topic echo /chatter    # receives ROS 1 messages!

# ── Bridge direction ──────────────────────────────────────────
# By default, bridges BOTH directions:
# ROS 1 pub → ROS 2 sub   (and vice versa)
# Bridge only activates when BOTH ends have a pub and a sub

# Bridge only ROS 1 → ROS 2:
ros2 run ros1_bridge dynamic_bridge --bridge-all-1to2-topics

dynamic_bridge only creates a bridge when there's at least one publisher AND one subscriber on both sides. If you publish from ROS 1 but have no ROS 2 subscriber yet, no bridge is created. This is by design to avoid unnecessary traffic.

4

static_bridge — Explicit Topic/Service Mapping

static_bridge lets you explicitly specify which topics and services to bridge, with their types. This is more predictable and avoids the auto-discovery overhead of dynamic_bridge.

bash
# static_bridge with explicit topic and type mapping

# Bridge a specific topic (ROS 1 → ROS 2)
ros2 run ros1_bridge static_bridge   --ros1-topic /scan   --ros2-topic /scan   --ros1-type sensor_msgs/LaserScan   --ros2-type sensor_msgs/msg/LaserScan

# Bridge multiple topics (using parameter YAML)
# bridge_params.yaml:
# ---
# topics:
#   - topic: /scan
#     type: sensor_msgs/msg/LaserScan
#     queue_size: 10
#   - topic: /odom
#     type: nav_msgs/msg/Odometry
#     queue_size: 10
#   - topic: /cmd_vel
#     type: geometry_msgs/msg/Twist
#     queue_size: 1
#   - topic: /tf
#     type: tf2_msgs/msg/TFMessage
#     queue_size: 10
# services:
#   - service: /add_two_ints
#     type: example_interfaces/srv/AddTwoInts

# Run with YAML config
ros2 run ros1_bridge parameter_bridge   /scan@sensor_msgs/msg/LaserScan[sensor_msgs/LaserScan   /odom@nav_msgs/msg/Odometry[nav_msgs/Odometry   /cmd_vel@geometry_msgs/msg/Twist]geometry_msgs/Twist   /tf@tf2_msgs/msg/TFMessage[tf2_msgs/TFMessage

# Syntax: topic@ros2_type[ros1_type    (ROS 1 → ROS 2)
#         topic@ros2_type]ros1_type    (ROS 2 → ROS 1)
#         topic@ros2_type[ros1_type]   (bidirectional)
5

Custom Message Type Bridging

Bridging custom message types requires the package source on both the ROS 1 and ROS 2 sides, plus a bridge mapping package.

bash
# For custom messages, you need source packages on both sides.
# Example: my_custom_msgs package

# ── Step 1: Create the ROS 1 package ─────────────────────────
# ~/ros1_ws/src/my_custom_msgs/msg/RobotStatus.msg:
# float32 battery_level
# bool is_moving
# string error_message

# Build in ROS 1 workspace
cd ~/ros1_ws && catkin_make

# ── Step 2: Create the ROS 2 package ─────────────────────────
# ~/ros2_ws/src/my_custom_msgs/msg/RobotStatus.msg (identical)
# float32 battery_level
# bool is_moving
# string error_message

# Build in ROS 2 workspace
cd ~/ros2_ws && colcon build

# ── Step 3: Rebuild ros1_bridge with both workspaces ─────────
source /opt/ros/noetic/setup.bash
source ~/ros1_ws/devel/setup.bash      # ROS 1 custom msgs
source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash    # ROS 2 custom msgs

colcon build --packages-select ros1_bridge --cmake-force-configure
# ros1_bridge auto-detects the paired message types and generates bridges

# ── Verify custom type bridge was generated ──────────────────
ros2 run ros1_bridge ros1_bridge_generate_factories --print
# Should list: my_custom_msgs/msg/RobotStatus

ros1_bridge finds matching type pairs at compile time — if you add a custom message after building the bridge, you must rebuild ros1_bridge from source. This is the most common pain point with custom types.

6

Troubleshooting Common Bridge Issues

The most frequent ros1_bridge problems: no bridge created, type mismatch, QoS incompatibility, and performance issues.

bash
# ── Issue 1: No bridge created for my topic ──────────────────
# Cause: dynamic_bridge requires pub AND sub on both sides
# Fix: start a subscriber on the receiving side first

# Debug: list available bridge pairs
ros2 run ros1_bridge dynamic_bridge --print-pairs
# Shows all type pairs it knows how to bridge

# ── Issue 2: "No factory for type" error ─────────────────────
# Cause: custom message type not in bridge's factory
# Fix: rebuild ros1_bridge with both workspaces sourced
#   (see custom messages section above)

# ── Issue 3: QoS incompatibility ─────────────────────────────
# ros1_bridge publishes with RELIABLE QoS by default
# If your ROS 2 node uses BEST_EFFORT, add QoS override:
ros2 topic echo /scan --qos-reliability best_effort

# ── Issue 4: TF transforms not bridging ──────────────────────
# /tf and /tf_static need explicit bridges in parameter_bridge
ros2 run ros1_bridge parameter_bridge   /tf@tf2_msgs/msg/TFMessage[tf2_msgs/TFMessage   /tf_static@tf2_msgs/msg/TFMessage[tf2_msgs/TFMessage

# ── Issue 5: High latency or dropped messages ─────────────────
# Cause: bridge is single-threaded by default
# Fix: use separate bridge instances per topic for parallelism
# Or: reduce queue_size for low-latency topics like /cmd_vel

# ── Verify bridge is working ──────────────────────────────────
# ROS 2 side: check message rate
ros2 topic hz /scan          # should match ROS 1 publish rate

# ROS 1 side:
rostopic hz /scan            # confirm ROS 1 publisher rate

Quick Reference

Command / ConceptDetails
Installsudo apt install ros-humble-ros1-bridge
Source ordersource noetic/setup.bash THEN humble/setup.bash (ROS 2 last)
dynamic_bridgeros2 run ros1_bridge dynamic_bridge (auto, needs pub+sub on both)
parameter_bridgeros2 run ros1_bridge parameter_bridge /topic@ros2_type[ros1_type
Direction syntax[ros1_type = ROS1→ROS2, ]ros1_type = ROS2→ROS1, both = bidirectional
Bridge /tf/tf@tf2_msgs/msg/TFMessage[tf2_msgs/TFMessage (explicit)
Custom msgsRebuild ros1_bridge from source with both workspaces sourced
List bridge pairsros2 run ros1_bridge dynamic_bridge --print-pairs
No bridge createddynamic_bridge needs both publisher AND subscriber active
Verifyros2 topic hz /topic — should match ROS 1 publish rate