Topics¶
Every mesh topic by its exact key, how often each is published, which ones the bridge sends to the cloud.
At the end of this page you can read any mesh topic by its exact key, know how often each is published and which environment variable changes that, and which topics the bridge sends cloudward.
Keys are strands/... by default; STRANDS_MESH_NAMESPACE changes the prefix for a whole fleet, and two fleets with different prefixes cannot exchange messages. Keys are written where each publisher lives (mesh/core.py, mesh/sensors.py, mesh/input.py); this table is hand-checked against those files at this commit, not generated.
a.mesh.subscribe("strands/arm-b/imu", lambda key, payload: print(payload), name="imu")
a.mesh.subscribe("strands/*/state", lambda key, payload: print(key, payload["joints"]), name="all-state")
Per-peer topics¶
| key | default rate | env | payload |
|---|---|---|---|
strands/{peer}/presence |
2 Hz | fixed | heartbeat: peer_id, robot name, host, capabilities |
strands/{peer}/state |
10 Hz | fixed (STATE_HZ) |
joints, sim time, task status, robots_running, degraded probe reasons |
strands/{peer}/health |
0.5 Hz | STRANDS_MESH_HEALTH_HZ |
battery, CPU, memory |
strands/{peer}/pose |
10 Hz | STRANDS_MESH_POSE_HZ |
SE(3) base pose from SLAM, odometry or VIO; the head IMU on a Reachy Mini |
strands/{peer}/imu |
10 Hz | STRANDS_MESH_IMU_HZ |
orientation, gyro, accel |
strands/{peer}/odom |
10 Hz | STRANDS_MESH_ODOM_HZ |
wheel or leg odometry |
strands/{peer}/lidar/summary |
5 Hz | STRANDS_MESH_LIDAR_SUMMARY_HZ |
ranges reduced to a summary |
strands/{peer}/lidar/state |
1 Hz | fixed | lidar device state |
strands/{peer}/map/info |
0.2 Hz | STRANDS_MESH_MAP_INFO_HZ |
occupancy map metadata |
strands/{peer}/hand/{hand}/state |
50 Hz | fixed | dexterous hand joints |
strands/{peer}/camera/{camera} |
off | STRANDS_MESH_CAMERA_HZ (STRANDS_MESH_CAMERA_DISABLED forces off) |
one encoded frame; capped at STRANDS_MESH_MAX_CAMERA_BYTES (1 MiB) |
strands/{peer}/stream |
10 Hz while a policy runs | STRANDS_MESH_STREAM_HZ |
VLA execution steps: observation summary, action, step index |
strands/{peer}/input/{device} |
the device's rate, capped at 100 Hz | STRANDS_MESH_INPUT_MAX_HZ; receivers refuse a value over STRANDS_MESH_INPUT_VALUE_ABS (720) or a step over STRANDS_MESH_INPUT_SLEW_ABS |
teleoperation frames from teleoperate(publish=True) |
strands/{peer}/safety/event |
on event | fixed | this peer's own e-stop and resume events |
A sensor topic exists only when the robot exposes the attribute behind it (_pose, _imu, _battery, _lidar_state, ...): a driver without an IMU publishes no imu. Every *_HZ variable is parsed alike: a value that is not a positive finite number is reported once; the default stands.
Command topics¶
| key | direction | payload |
|---|---|---|
strands/{peer}/cmd |
to one peer | {"action": ..., ...} from ALLOWED_ACTIONS, with sender_id and turn_id; 20 Hz and 16 KiB caps (STRANDS_MESH_CMD_RATE_HZ, STRANDS_MESH_MAX_CMD_BYTES) |
strands/{requester}/response/{responder}/{turn_id} |
reply | {"type": "response", "responder_id", "turn_id", "result", "timestamp"} |
strands/broadcast |
to every peer | the same command shape; the sender drops its own copy |
Fleet safety topics¶
| key | payload |
|---|---|
strands/safety/estop |
{"peer_id", "t", ...}: every receiver engages its lockout (safety) |
strands/safety/resume |
{"peer_id", "t", "lockout_elapsed_s", "assertion"}: receivers verify the operator's signature before clearing |
Both are capped at 2 Hz and 4 KiB per message (STRANDS_MESH_SAFETY_RATE_HZ, STRANDS_MESH_MAX_SAFETY_BYTES) at the transport, before any deserialisation.
What crosses the bridge¶
Under STRANDS_MESH_BACKEND=bridge a topic goes to MQTT only if its suffix is in STRANDS_MESH_BRIDGE_TOPICS; bridges lists the default set. MQTT wildcards map * to + and ** to #; the keys themselves are unchanged.
ACL matching¶
In an ACL file, key_exprs are matched against the namespaced key, so **/cmd is the pattern that admits or denies commands and strands/*/cmd matches nothing.