How to Integrate ROS2 Topics into DimOS Using the ROSTransport Bridge
You can integrate ROS2 topics into DimOS by creating a transport map that binds DimOS typed streams to ROSTransport instances, then attaching that map to a blueprint via the transports() method.
DimOS (dimensionalOS/dimos) provides a transport layer that connects its typed streams (In[T] / Out[T]) to external messaging systems without requiring ROS-specific code in your modules. The ROSTransport class in dimos/core/transport.py implements a complete bridge to ROS2, handling node initialization, message conversion, and lazy topic creation automatically.
Understanding the ROSTransport Architecture
The bridge consists of four core components that work together to map DimOS streams to ROS2 topics:
-
ROSTransport(dimos/core/transport.py, lines 251-283): A subclass ofPubSubTransportthat wraps ROS2 publishing and subscribing. It lazily instantiates aDimosROShelper on first use, then forwardspublishandsubscribecalls to the underlying ROS2 node. -
Blueprint.transports()(dimos/core/blueprints.py, lines 33-35): A method that accepts a mapping dictionary and attaches transport configurations to a blueprint. During the wiring phase, the blueprint looks up transports via_get_transport_forand binds them to the appropriate streams. -
DimosROS: An internal ROS2 client that handles node initialization, topic creation, and message serialization. It starts whenROSTransport.start()is called and shuts down automatically when the blueprint tears down. -
Typed streams: DimOS message definitions in
dimos.msgs(e.g.,sensor_msgs/Image,geometry_msgs/PoseStamped) serve as the ROS2 message type for the bridge, preserving type safety across the system boundary.
When a module publishes on an Out[Msg] stream with an attached ROSTransport, the broadcast() method forwards the message to the ROS2 topic. Conversely, incoming ROS2 messages trigger the subscribe() method to call the module's In[Msg] callback.
Step-by-Step Integration Guide
Follow these five steps to bridge any DimOS stream to a ROS2 topic:
-
Import the transport class
from dimos.core.transport import ROSTransport -
Select ROS2 message classes from the
dimos.msgspackage. These thin wrappers around standard ROS2 definitions enable automatic conversion.from dimos.msgs.geometry_msgs import PoseStamped from dimos.msgs.sensor_msgs import Image, PointCloud2 -
Create a transport map that pairs each DimOS stream name with a
ROSTransportinstance containing the target ROS2 topic name and message type.ros_map = { ("lidar", PointCloud2): ROSTransport("lidar", PointCloud2), ("global_map", PointCloud2): ROSTransport("global_map", PointCloud2), ("odom", PoseStamped): ROSTransport("odom", PoseStamped), ("color_image", Image): ROSTransport("color_image", Image), } -
Attach the map to a blueprint using the
transports()method. This example extends the Unitree Go2 robot blueprint.from dimos.robot.unitree.go2.blueprints.smart.unitree_go2 import unitree_go2 unitree_go2_ros = unitree_go2.transports(ros_map) -
Run the blueprint normally with
dimos run unitree_go2_ros. DimOS automatically initializes the ROS2 node, creates publishers for outbound topics, and starts subscribers for inbound mappings defined in your transport map.
Practical Code Examples
Minimal Image Stream Bridge
This example bridges a single camera stream from a Go2 robot to a standard ROS2 image topic:
# file: my_robot/blueprints/ros_image_bridge.py
from dimos.core.transport import ROSTransport
from dimos.msgs.sensor_msgs import Image
from dimos.robot.unitree.go2.blueprints.smart.unitree_go2 import unitree_go2
# Bridge the DimOS "color_image" stream to ROS2 topic "camera/image_raw"
ros_transport_map = {
("color_image", Image): ROSTransport("camera/image_raw", Image)
}
# Extend the original Go2 blueprint
unitree_go2_image = unitree_go2.transports(ros_transport_map)
__all__ = ["unitree_go2_image"]
Execute the bridge:
dimos run unitree_go2_image
The robot's color_image output now publishes to the ROS2 topic /camera/image_raw as standard sensor_msgs/Image messages.
Full Navigation Stack Bridge (Bidirectional)
For autonomous navigation, you typically need both inbound and outbound transports to interface with ROS2 navigation stacks:
# file: navigation/rosnav_bridge.py
from dimos.core.transport import ROSTransport
from dimos.msgs.geometry_msgs import PoseStamped
from dimos.msgs.sensor_msgs import TwistStamped, PointCloud2
from dimos.msgs.std_msgs import Bool, Int8, Joy, Path
from dimos.navigation.rosnav import RosNav # existing navigation module
nav_ros_map = {
# Outbound (DimOS → ROS2)
("ros_goal_pose", PoseStamped): ROSTransport("/goal_pose", PoseStamped),
("ros_cmd_vel", TwistStamped): ROSTransport("/cmd_vel", TwistStamped),
("ros_way_point", PoseStamped): ROSTransport("/way_point", PoseStamped),
("ros_registered_scan", PointCloud2): ROSTransport("/registered_scan", PointCloud2),
("ros_global_map", PointCloud2): ROSTransport("/terrain_map_ext", PointCloud2),
("ros_path", Path): ROSTransport("/path", Path),
# Inbound (ROS2 → DimOS)
("ros_goal_reached", Bool): ROSTransport("/goal_reached", Bool),
("ros_cancel_goal", Bool): ROSTransport("/cancel_goal", Bool),
("ros_soft_stop", Int8): ROSTransport("/stop", Int8),
("ros_joy", Joy): ROSTransport("/joy", Joy),
}
# Attach transports to the navigation blueprint
ros_nav = RosNav.transports(nav_ros_map)
__all__ = ["ros_nav"]
Running dimos run ros_nav creates a bidirectional bridge compatible with standard ROS2 navigation interfaces like Nav2 or custom planners.
Robot-Specific Velocity Command Bridge
For direct motor control, bridge the low-level command velocity stream:
# file: unitree/b1/ros_bridge.py
from dimos.core.transport import ROSTransport
from dimos.msgs.geometry_msgs import TwistStamped
from dimos.robot.unitree.b1.unitree_b1 import unitree_b1
b1_ros_map = {
("ros_cmd_vel", TwistStamped): ROSTransport("/cmd_vel", TwistStamped),
}
unitree_b1_ros = unitree_b1.transports(b1_ros_map)
__all__ = ["unitree_b1_ros"]
This exposes the B1 robot's velocity commands on the standard ROS2 cmd_vel topic, allowing control via teleop_twist_keyboard or similar ROS2 tools.
Advanced Configuration Options
Bidirectional Bridges: Define separate ROSTransport instances for the same ROS2 topic to enable both reading and writing. The bridge treats each direction independently, so you can have a module both publish to and subscribe from a topic by including the stream name in both Out and In definitions with appropriate transports.
Custom ROS2 Node Arguments: Pass additional keyword arguments to ROSTransport to configure the underlying ROS2 node. These parameters forward directly to the DimosROS constructor.
ROSTransport("/cmd_vel", TwistStamped, namespace="my_robot")
Dynamic Topic Creation: The transport layer creates ROS2 publishers and subscribers lazily upon the first broadcast or subscribe call. This allows you to declare transports for optional topics that may not exist at startup without causing initialization errors.
Summary
- ROSTransport in
dimos/core/transport.pyprovides the bridge between DimOS streams and ROS2 topics without requiring ROS-specific code in your modules. - Use Blueprint.transports() in
dimos/core/blueprints.pyto attach a transport mapping dictionary to any blueprint. - Import message types from
dimos.msgsto maintain type safety across the bridge. - Create bidirectional integrations by defining transports for both inbound and outbound streams.
- The DimosROS helper handles node lifecycle management automatically, starting lazily and shutting down with the blueprint.
Frequently Asked Questions
Do I need to manually initialize a ROS2 node to use ROSTransport?
No. The DimosROS helper instantiated by ROSTransport.start() handles ROS2 node initialization automatically when the blueprint begins execution. You do not need to write any rclpy.init() or node creation code in your application.
Can I use custom ROS2 message types with the bridge?
Yes, provided you have corresponding DimOS message definitions in the dimos.msgs namespace. The ROSTransport class uses these typed stream definitions to serialize and deserialize messages, so any message type available in dimos.msgs (or your custom extensions) works with the bridge.
How do I configure ROS2 namespaces for my topics?
Pass the namespace parameter (and other ROS2 node arguments) directly to the ROSTransport constructor. These keyword arguments forward to the underlying DimosROS client, allowing you to scope all topics under a specific robot namespace or remap topic names as needed.
Is the ROSTransport bridge bidirectional by default?
Each ROSTransport instance handles one direction. To create a bidirectional bridge, you must explicitly define separate transport mappings for both the In (subscribe) and Out (publish) streams. The blueprint wiring logic treats these as independent connections, giving you full control over which streams participate in each direction.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →