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.
- Python usage
- C++ and CMake usage
- Runnable examples: Python, C++
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.metadatareplaces_robot_msg_timestampand the encoder'stimestamp=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)
| File | Size | Uploaded | |
|---|---|---|---|
| robot_msg_irsl-0.3.0.tar.gz | 176.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|