Skip to main content

robot_msg

Typed, transport-independent robot messages for Python and C++. JSON schemas produce shared models, validation, JSON/binary codecs, a message registry, and installed schema resources. The runtime has no third-party dependencies. The distribution is robot-msg-irsl; the import package and CMake target remain robot_msg and robot_msg::robot_msg. Licensed under the MIT License.

Requirements and distribution

  • Python 3.11 or later, with no upper version bound.
  • C++14 or later; CMake 3.10 or later.
  • Wheel construction uses setuptools; uv is required only for the optional CMake bootstrap and uv-based installation commands.

The wheel includes Python code, C++ sources and headers, CMake configurations, schemas and typing information. It does not contain a platform-specific C++ binary or compile C++ at installation time. The consuming CMake project builds these sources with its own compiler. Generated sources are included, so ordinary consumers do not run the generator.

Install a locally built wheel into your virtual environment:

uv pip install /path/to/robot_msg_irsl-0.3.0-py3-none-any.whl
python -m robot_msg --cmake-dir

After a release is published under the robot-msg-irsl distribution name, consumers can use uv add robot-msg-irsl==0.3.0 instead. Preparing this version's artifacts does not imply that it has been published or that PyPI name ownership is established.

cmake -S . -B build \
  -Drobot_msg_DIR="$(uv run python -m robot_msg --cmake-dir)"
cmake --build build
find_package(robot_msg CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE robot_msg::robot_msg)

Source-tree add_subdirectory and conventional CMake install/find_package remain supported. Plain find_package does not download dependencies. The opt-in uv bootstrap described in the C++ guide installs a pinned package into a build-local environment; it does not change system Python or the application's existing venv.

Protocol 2 is a breaking release

Distribution version 0.3.0, envelope protocol version 2, binary magic R2, and the message's string/FNV-1a wire ID are separate concepts. This release requires consumer adapters to be updated; it does not transparently replace protocol 1 submodules.

  • Physical geometry uses shared types rather than unrelated arrays/matrices.
  • Public message.metadata replaces _robot_msg_timestamp and the encoder's timestamp= argument.
  • Native SMPL-X parameters use SmplState, not the Meta-specific old class.
  • Viewer legacy duplicates are removed, batches use paired items, and lifetime semantics are explicit.

Both decoders reject protocol 1 before decoding its changed payload. The earlier RM to R2 magic change already separated the old bit-packed format from the byte-aligned format; keeping that historical envelope version at 1 did not cause old binary frames to be interpreted as the new layout.

Shared geometry and coordinate meaning

Import common models from robot_msg.types or include <robot_msg/types.hpp>.

Type Meaning
Vec2(x,y), Vec3(x,y,z) Ordered Cartesian components
Quaternion(x,y,z,w) XYZW unit rotation; finite, abs(norm²-1) <= 1e-6
Pose(position,orientation) Child-to-reference transform, p_ref = R p_child + position
Wrench(force,torque) Force in N and moment in N·m; the containing field defines axes and moment origin
RotationVector(x,y,z) Axis-angle model parameter in radians, not necessarily an absolute physical rotation
ColorRGBA(r,g,b,a) Four components in [0,1]

Quaternions are validated, never silently normalized or sign-flipped. Sender adapters convert matrices or internal WXYZ values to wire XYZW. Frame identifiers name coordinate systems; they do not establish calibration, transform discovery, or a common world origin. Do not relabel an unknown device frame as world.

Distances are meters and times are seconds. q, dq, and viewer qpos remain model-coordinate arrays: nq may differ from nv, and their components are not all necessarily revolute joint angles. Matrix-shaped numerical auxiliary arrays use row-major flattening.

Controller messages

RobotState carries q, dq, tau_ext, a publication sequence, named joint and end-effector poses in frame_id, and base_wrench_ext. The latter's torque is about the reference-frame origin, not automatically the sensor or center of mass. A publication counter is not a clock.

RobotAction preserves the independent optional real-time and trajectory branches. End-effector trajectories are [arm][waypoint]; configuration trajectories are [waypoint][coordinate]. Contact link IDs are one-based; contact points and forces are expressed in the referenced joint frame.

Optional command fields preserve presence. In particular, the existing controller treats the presence of ctrl_update as an event; changing its bool interpretation requires an explicit consumer migration. The message library adds no acknowledgement or delivery guarantee.

SMPL-X parameters

SmplState separates root orientation, 21 body rotation vectors, 15 left-hand and 15 right-hand rotation vectors, translation, optional 10 betas and gender. hand_pose_convention distinguishes absolute from mean_offset. A mean-offset parameter must not be converted into an absolute quaternion without the model's hand-mean information.

root_orientation_valid and translation_valid distinguish measured estimates from deliberately unspecified model parameters. In particular, a camera pipeline that zeroes root/translation does not thereby estimate a world pelvis pose. Translation remains the SMPL-X model translation parameter. Missing betas are different from explicitly supplied zeros. The prior 169-value storage layout can be reconstructed at the adapter boundary without changing model assets or recording formats. No SMPL-X, Torch, NumPy or model files are dependencies here.

Viewer messages

Geometric messages identify their reference frame. Box dimensions are half_extents, ellipsoid dimensions are axis radii, and arrow direction is a full displacement vector from its origin. The previous viewer multiplied vector length by two; preserving an old visualization therefore requires an explicit adapter conversion, not an undocumented factor in the new message.

Lifetime has one_frame, persistent, or seconds modes. Only seconds requires a positive seconds value. Old marker timeout zero maps to one-frame; mesh colors that previously persisted map to persistent. Lifetimes begin at receiver receipt on its local monotonic clock, not at the sender's timestamp. Joint updates have separate queue-age semantics; expiration does not remove the last applied robot pose.

Batch items keep associated geometry together, with shared appearance where applicable. Preserve order, ID replacement, visible=false removal and empty batch removal. Clear clears markers and message-applied colors, not the robot model or last joint state. In MessageBatch, clear/update order is significant.

Metadata and optional diagnostic arrays

Every message has independent public metadata. Payload conversion intentionally excludes it.

import robot_msg as rm
from robot_msg.types import MessageMetadata, NamedArray, Timestamp

message = rm.ControllerCommand(
    ctrl_mode=2,
    metadata=MessageMetadata(
        sample_time=Timestamp(seconds=2.5, clock_kind="simulation", clock_id="sim-run-1"),
        send_time=Timestamp(seconds=1730000000.25, clock_kind="unix"),
        aux=[NamedArray(name="joint_error", shape=[2], data=[0.02, -0.01], unit="rad")],
    ),
)
restored = rm.decode(rm.encode(message))
assert restored.metadata == message.metadata
  • sample_time: source sample time, if known.
  • receive_time: time at which the sender received upstream data; not the recipient's current receive time.
  • send_time: time selected by the sender for transmission.

Each Timestamp declares unix, monotonic, simulation, or device clock kind. Non-Unix clocks require a clock_id. Identical IDs do not imply a clock synchronization accuracy guarantee. Encoding/decoding never stamps automatically or overwrites metadata. Batch children retain their complete envelopes and independent metadata; the outer envelope is not an implicit override.

NamedArray(name,shape,data,unit?,frame_id?) carries float64 diagnostics without JSON strings or schema changes for each new name. shape=[] means one scalar; a shape containing zero requires empty data. Names are unique per message. Limits are 64 arrays, rank 8, 65,536 total values and bounded labels. Dimension products, finite values and array lengths are validated. Integers and booleans that require exact typed meaning belong in the payload, not float64 aux.

The aux limit is not a guarantee that the full payload or batch fits a consumer's 1 MiB frame limit. Check the complete encoded frame size at the transport boundary; batching several individually valid envelopes adds their sizes.

Ports, not sockets or hosts

Python: from robot_msg import ports. C++: <robot_msg/ports.hpp>.

Constant TCP port
ROBOT_STATE 28400
CONTROLLER_COMMAND 28401
ROBOT_ACTION 28402
CONTACT_INFO 28403
VIEWER 28410
META 28420
SMPL_STATE 28421

schemas/ports.json generates both languages. These are project defaults, not IANA/OS reservations. Consumers choose hosts, bind/connect roles and explicit overrides. External VR input and Viewer HTTP ports are separate. No port scan, collision fallback, address discovery or socket dependency is provided.

ZMQ PUB/SUB does not guarantee every message or deadline. CONFLATE keeps the latest socket message, not the latest message per type. Use complete batch snapshots when related data must survive together, or separate traffic policies. Reliable commands require acknowledgements/retries outside this library.

Installed schema introspection

from robot_msg import message_catalog, get_payload_schema, get_envelope_schema

for info in message_catalog():
    print(info.msg_type, info.wire_id, info.message_class, info.schema_id)
    payload_schema = get_payload_schema(info.msg_type)
    envelope_schema = get_envelope_schema(info.msg_type)

The default resolved schemas contain no $ref, and defaults are JSON-compatible objects. resolved=False returns the original form. Returned dictionaries are independent copies. Resources come from the installed package, not a search for a source checkout. Concrete dataclasses, type hints, to_payload and from_payload remain available for editors and adapters.

Schema development

Canonical message schemas live in schemas/messages/, shared types in schemas/types/, and the envelope in schemas/envelope.json. The generator uses only the standard library. Modify schemas, not generated files.

./internal/tools/generate
./internal/tools/check-generated

Supported types are object, array, string, signed int64, float64 and boolean; constraints include ranges, lengths and string enums. Objects forbid additional properties. Optional fields without defaults use None/optional; null values are not accepted. Required fields cannot declare defaults. Recursive object defaults produce independent objects and are compared bit-exactly for binary omission.

Shared $ref is restricted to relative file-root references inside the shared types directory, with description/default annotations. Remote references, fragments, path escapes and cycles are rejected. x-field-order explicitly fixes shared constructor/wire component order; otherwise fields are sorted by name. x-semantic selects the small set of built-in cross-field checks. Unsupported keywords are errors, not silently ignored rules.

The compatibility manifest detects unintended changes to field layout, constraints/defaults and shared dependencies under the same protocol identity. Updating a released contract requires an explicit version/release decision; ordinary generation must not silently accept a new baseline.

Wire format

JSON uses {version,msg_type,metadata?,payload}. Duplicate keys, extra fields, invalid Unicode, nonfinite numbers and unsupported versions/types are rejected. to_payload/from_payload operate only on the payload. Public validate and both codecs validate the metadata as well.

The byte-aligned little-endian binary format starts with:

2 bytes  magic R2
2 bytes  protocol version 2
4 bytes  FNV-1a message ID
1 byte   metadata presence flags: sample, receive, send, aux (bits 0..3)
N bytes  present metadata fields, in that order
M bytes  schema-driven payload

Fields use float64 numbers, bounded integer/enum/length widths, object flag bits for presence/boolean values, and no names on the wire. Defaults are omitted only when bit-exactly equal, including signed zero. Unknown flags, truncation, invalid lengths/values and trailing bytes are rejected. One decoder call consumes exactly one complete envelope; framing and transport are the caller's responsibility.

Verification

The test suite additionally needs pip, setuptools and wheel in its selected Python environment; offline bootstrap tests also need uv. These are development tools, not runtime dependencies.

uv venv .venv --python 3.11
uv pip install --python .venv/bin/python pip 'setuptools>=77.0.3' wheel
cmake -S . -B build -DPython3_EXECUTABLE="$(pwd)/.venv/bin/python"
cmake --build build
ctest --test-dir build --output-on-failure

Tests cover deterministic generation, shared references/defaults, JSON/binary validation, cross-language byte equality, metadata/aux boundaries, consumer semantic contracts, installed schema APIs, wheel/sdist and external CMake consumption. Packaging tests use isolated environments and the local wheel; no robot or model inference is required.

The library provides no ZMQ, robot control, calibration, model loading, discovery, or delivery scheduling. Consumer migrations remain separate changes.

Metadata

Release files for robot-msg-irsl 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for robot-msg-irsl 0.3.0
File Size Uploaded
robot_msg_irsl-0.3.0.tar.gz 176.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for robot-msg-irsl 0.3.0
File Interpreter ABI Platform
robot_msg_irsl-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 370.4 kB

Release files / robot_msg_irsl-0.3.0.tar.gz

Download URL robot_msg_irsl-0.3.0.tar.gz
Size 176.4 kB
Tags Source
SHA-256 checksum
How to use checksums
75e0af3c48d4bae6505492f46ff4a2e3d9b0fe77b66346d853fa0c58eb728916
BLAKE2b-256 checksum
How to use checksums
8e59ab4b6033fe1d88cfe828efe60f9cecff9efc30aa2f7e87b76a835dfff480
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.4

Release files / robot_msg_irsl-0.3.0-py3-none-any.whl

Download URL robot_msg_irsl-0.3.0-py3-none-any.whl
Size 194.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d94792c260bd17887b28344ab5101f212f43704b61edc0264c973088a0ba04c6
BLAKE2b-256 checksum
How to use checksums
77440afc8a73bc91e001f324d5cbf5fba96a62d9b843f8f758fcf1b14601d7db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.4

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page