How to Convert ROS Messages to LCM in DimOS for Cross-Language Interoperability

DimOS converts ROS messages to LCM by bridging ROSTransport subscribers to LCMTransport publishers using the Bridge class or functional bridge() helper defined in dimos/protocol/pubsub/bridge.py.

The dimensionalOS/dimos repository abstracts all inter-process communication through unified pub-sub transports, enabling seamless conversion between ROS and LCM protocols. This architecture allows systems running C++, Java, or other LMC-compatible clients to consume ROS data without requiring a full ROS runtime environment.

Understanding Pub-Sub Transports in DimOS

DimOS defines all messaging interactions through the generic Transport[T] interface implemented in dimos/core/transport.py. Two critical implementations handle the protocol conversion:

  • ROSTransport (lines 51-84): Wraps ROS 1/2 topics using dimos.msgs.* types for publishing and subscribing within the ROS ecosystem.
  • LCMTransport (lines 12-41): Implements Lightweight Communications and Marshalling (LCM) multicast messaging for high-performance, language-agnostic data distribution.

Both classes inherit from PubSubTransport[T], providing standardized broadcast() and subscribe() methods that make protocol bridging possible through simple composition rather than complex adapters.

The Bridging Architecture

The conversion logic resides in dimos/protocol/pubsub/bridge.py, which offers two implementation patterns:

bridge() function (lines 50-66): A low-level functional helper for immediate, imperative bridging between two transports.

Bridge class (lines 70-98): A service-oriented wrapper configured via BridgeConfig for lifecycle-managed conversion suitable for long-running applications.

Both approaches operate by subscribing to the source transport, applying a translator function to map topics and messages, and publishing results to the destination transport. When converting ROS to LCM, ROSTransport serves as the source and LCMTransport as the destination.

Implementing ROS to LCM Conversion

Follow these three steps to establish message flow from ROS topics to LCM channels:

  1. Instantiate transports: Create ROSTransport for the ROS source topic and LCMTransport for the LCM destination topic.
  2. Configure the bridge: Use BridgeConfig to wire source to destination, specifying a translator function (identity for same message types).
  3. Start the service: Call start() on the bridge instance to begin asynchronous message forwarding.

Here is a minimal implementation converting ROS Image messages to LCM:

from dimos.core.transport import ROSTransport, LCMTransport
from dimos.msgs.sensor_msgs import Image
from dimos.protocol.pubsub.bridge import Bridge, BridgeConfig

# 1. Define transports

ros_image = ROSTransport("/camera/image_raw", Image)
lcm_image = LCMTransport("/camera_image", Image)

# 2. Configure bridge with identity translator

bridge_cfg = BridgeConfig(
    source=ros_image,               # ROS side (subscribe)

    destination=lcm_image,          # LCM side (publish)

    translator=lambda topic, msg: (topic, msg),  # Pass through unchanged

    subscribe_topic=None,           # Bridge all messages on this transport

)

# 3. Initialize and start

ros_to_lcm = Bridge(bridge_cfg)
ros_to_lcm.start()   # Messages now flow from ROS → LCM

# ... system operation ...

ros_to_lcm.stop()

The identity translator lambda topic, msg: (topic, msg) works when both systems utilize identical ROS message definitions (e.g., sensor_msgs/Image). For heterogeneous conversions—such as mapping ROS Image to custom LCM structures—implement a translator that constructs the target message type from the source data.

Integrating Bridges into Blueprints

Production deployments typically extend existing blueprints to expose ROS streams on LCM topics. The Unitree Go2 ROS blueprint in dimos/robot/unitree/go2/blueprints/smart/unitree_go2_ros.py demonstrates this pattern by adding LCM mirror transports:

from dimos.core.transport import ROSTransport, LCMTransport
from dimos.msgs.geometry_msgs import PoseStamped
from dimos.msgs.sensor_msgs import PointCloud2
from dimos.protocol.pubsub.bridge import Bridge, BridgeConfig

# Define both ROS and LCM transports

transports = {
    ("lidar", PointCloud2): ROSTransport("lidar", PointCloud2),
    ("odom", PoseStamped): ROSTransport("odom", PoseStamped),
    ("lidar_lcm", PointCloud2): LCMTransport("/lidar_lcm", PointCloud2),
    ("odom_lcm", PoseStamped): LCMTransport("/odom_lcm", PoseStamped),
}

# Bridge helper function

def _bridge_pair(ros_transport, lcm_transport):
    cfg = BridgeConfig(
        source=ros_transport,
        destination=lcm_transport,
        translator=lambda t, m: (t, m)
    )
    bridge = Bridge(cfg)
    bridge.start()
    return bridge

# Initialize bridges in blueprint construction

bridges = [
    _bridge_pair(transports[("lidar", PointCloud2)], transports[("lidar_lcm", PointCloud2)]),
    _bridge_pair(transports[("odom", PoseStamped)], transports[("odom_lcm", PoseStamped)]),
]

This pattern enables any LCM client—including C++ or Java implementations—to subscribe to /lidar_lcm or /odom_lcm without ROS dependencies, creating true cross-language interoperability.

Bidirectional Conversion Support

The bridge architecture supports bidirectional flows. To convert LCM messages back to ROS, instantiate LCMTransport as the source and ROSTransport as the destination in the BridgeConfig. This flexibility allows DimOS to serve as a universal protocol adapter in heterogeneous robotic systems.

Summary

  • DimOS unifies ROS and LCM through the Transport[T] interface defined in dimos/core/transport.py.
  • Conversion relies on ROSTransport (lines 51-84) receiving messages and LCMTransport (lines 12-41) publishing them.
  • Bridging implementations reside in dimos/protocol/pubsub/bridge.py, offering both functional (bridge()) and object-oriented (Bridge class) APIs.
  • Configuration requires a BridgeConfig with source, destination, and translator parameters.
  • Integration into existing blueprints follows the pattern demonstrated in unitree_go2_ros.py, enabling incremental LCM exposure of ROS topics.

Frequently Asked Questions

What is the purpose of the translator function in DimOS bridges?

The translator function maps source topic names and message objects to the destination format required by the target transport. It accepts (topic, message) tuples and returns transformed (new_topic, new_message) pairs. When converting ROS to LCM with identical message schemas, an identity lambda suffices; for protocol mismatches, implement custom conversion logic to build target-specific message structures.

Can I convert LCM messages back to ROS using the same bridge?

Yes. Reverse the source and destination assignments in BridgeConfig by setting source to your LCMTransport instance and destination to your ROSTransport instance. The bridge is protocol-agnostic and handles message flow in either direction, though you may need to adjust the translator function to accommodate LCM-specific message structures.

Which file contains the transport implementations for ROS and LCM?

The transport implementations are located in dimos/core/transport.py. ROSTransport occupies lines 51-84, while LCMTransport is defined at lines 12-41. Low-level client implementations reside in dimos/protocol/pubsub/impl/rospubsub.py (ROS) and dimos/protocol/pubsub/impl/lcmpubsub.py (LCM).

How do I handle message type differences between ROS and LCM?

Implement a custom translator function in BridgeConfig that constructs the destination message type from the source data fields. For example, when converting ROS Image to a custom LCM image structure, the translator would extract height, width, and encoding fields from the ROS message and instantiate the equivalent LCM object with those values mapped to the appropriate fields.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →