ROS Messages#

ltcodecs can encode and decode ROS messages. The ROS message path uses the same field configuration format described in Codec Configurations, but it can also infer fields from ROS message definitions.

ROS functionality is implemented using ARMW, so it works with ROS 1 and ROS 2.

Top-Level and Field Codecs#

Use ltcodecs.ros_message_codec.RosMessageCodec at application boundaries. It owns the top-level message codec, optional checksum handling, and metadata returned from encode().

Use ltcodecs.ros_msg_field_codec.RosMsgFieldCodec only when a field inside another codec is itself a ROS message. In YAML, this is the msg or ros_msg codec alias.

pose:
  codec: msg
  ros_type: geometry_msgs/Pose

Automatic Field Inference#

If RosMessageCodec is created without a field dictionary, ltcodecs uses ARMW to introspect for the ROS message fields and types:

codec = ltcodecs.RosMessageCodec("std_msgs/String")

For simple messages this is convenient. For production acoustic links, prefer an explicit YAML config so field bounds and lossy encodings are deliberate. Explicit configs are also the way to change array limits, string lengths, float resolution, optional fields, and nested message fields.

Explicit Field Configs#

Pass a field dictionary directly or load one from YAML:

codec = ltcodecs.RosMessageCodec.from_codec_file(
    "std_msgs/String",
    "string_msg_codec.yaml",
)
data:
  codec: string
  max_length: 10

When encoding, every configured payload field must exist on the ROS message. When decoding, ltcodecs constructs a new ROS message object from the decoded field values.

ROS 1 and ROS 2 Type Syntax#

ROS 1 and ROS 2 expose message type names differently. ltcodecs normalizes the common forms before constructing nested message codecs.

Type string

Meaning

pkg/Msg

Message type, ROS 1 style

pkg/msg/Msg

Message type, ROS 2 introspection style

pkg/Msg[]

Variable-length array, ROS 1 style

pkg/Msg[3]

Fixed-length array, ROS 1 style

sequence<pkg/msg/Msg>

Variable-length array, ROS 2 style

sequence<uint8, 10>

Bounded variable-length primitive array

array<pkg/msg/Msg, 3>

Fixed-length array, ROS 2 style

The ROS 2 form pkg/msg/Msg is normalized to pkg/Msg before calling ARMW’s message import helpers.

Arrays#

When arrays are inferred from ROS field definitions, ltcodecs maps them to the array codecs used by explicit configs:

  • fixed-size arrays become fixed_len_array

  • variable-size arrays become variable_len_array

  • primitive element types use their scalar codec alias

  • message element types use element_type: msg and element_params

For example, a ROS 2 field type like:

sequence<geometry_msgs/msg/Point>

is treated like this explicit configuration:

points:
  codec: variable_len_array
  max_length: 10
  element_type: msg
  element_params:
    ros_type: geometry_msgs/Point

The default maximum length for inferred variable-length arrays is currently 10. Use an explicit config when that limit should be different.

Nested Message Fields#

Nested ROS message fields can either be inferred or configured explicitly. An explicit config is useful when only a subset of the nested fields should be encoded or when individual nested fields need custom codecs.

point:
  codec: msg
  ros_type: geometry_msgs/Point
  fields:
    x:
      codec: float32
    y:
      codec: float32
    z:
      codec: float32

Without a fields mapping, RosMsgFieldCodec infers the nested message’s fields from the ROS message definition.

Metadata#

ROS message encoding returns (bits, metadata). The metadata value is usually None. When a configured field uses a metadata encoder alias, the field is copied into the returned metadata dictionary instead of being encoded into the bit stream.

During decoding, received_packet can provide packet metadata for configured metadata decoder fields:

decoded = codec.decode(bits_to_decode, received_packet=packet)

The available metadata aliases are shared with dictionary codecs and are listed in Codec Aliases.

Checksums#

RosMessageCodec supports the same top-level checksums as DictCodec:

codec = ltcodecs.RosMessageCodec(
    "std_msgs/String",
    fields_dict={
        "data": {
            "codec": "string",
            "max_length": 10,
        },
    },
    checksum="crc32",
)

The checksum is appended after the encoded message payload. Decoding raises ValueError if the received checksum does not match the decoded payload.

Testing Without ROS Middleware#

ARMW’s native backend can load ROS-style .msg definitions without running ROS 1 or ROS 2 middleware. This is useful for fast unit tests that need standard message classes but do not need a ROS graph.

Set the middleware and interface search path before importing ARMW:

ARMW_MIDDLEWARE=native
ROS_PACKAGE_PATH=/path/to/common_interfaces

Then tests can import messages with ARMW:

import armw

String = armw.import_message("std_msgs", "String")

Use full ROS 1 and ROS 2 CI jobs for behavior that depends on actual middleware, generated package layouts, or ROS-specific introspection behavior.