# Robotics — Details

> Topic pub/sub for a robot fleet over MoQT. ROS 2 CDR on the wire, Zenoh-over-QUIC routing, MQTT 3.1.1 bridge, per-QoS lanes.

<img src="/images/diagrams/robotics.png" alt="robotics data flow" />

The **robotics** modality is topic pub/sub for a robot fleet, built on the same
QUIC relay mesh ClutchCall uses for voice, streams, and games. One client
speaks for one `(tenant, robot)` pair. Two namespaces are baked in — telemetry
*from* the robot and commands *to* the robot — so the two directions never
collide and a controller can never accidentally subscribe to the robot's own
announce.

```
robot/<id>          telemetry topics   (robot → cloud)
robot/<id>/ctl      command topics     (cloud → robot)
```

Payloads are opaque bytes — typically ROS 2 **CDR** — with the message type name
prefixed on the wire so a cross-language subscriber can pick the right
deserializer without any out-of-band schema exchange.

## When to use it

  - **Fleet telemetry** — Stream odometry, LIDAR, battery, and pose from hundreds of robots to a cloud
    dashboard or a recorder, each on its own `robot/<id>` namespace.
  - **Teleoperation** — Drive a robot from the cloud — `cmd_vel` and goal commands flow down
    `robot/<id>/ctl` on a low-latency lane, telemetry flows back up the same
    session.
  - **Cross-language demux** — A TypeScript dashboard and a Python analytics worker subscribe to the same
    CDR track; the type-name prefix lets each pick its own decoder.
  - **Drop-in ROS 2 transport** — Swap the local middleware for a ClutchCall-backed transport so a ROS 2
    node reaches the fleet over WAN-grade QUIC with zero code change.

## Three transports onto the mesh

A robot can reach the relay mesh three different ways. All three land on the same
namespaces and the same type-name-prefixed wire frame, so producers and
consumers interoperate regardless of how each one connected.

  <Tab title="ROS 2 (CDR)">
    The native path. A ROS 2 node serializes its messages to **CDR** (the DDS
    Common Data Representation) and the raw CDR bytes ride the track unchanged.
    The type name (`nav_msgs/msg/Odometry`) is what the SDK prefixes, so a
    subscriber anywhere can hand the payload straight to a CDR decoder.
  </Tab>
  <Tab title="Zenoh-over-QUIC">
    A robot already speaking **Zenoh** routes its key-expressions onto MoQT
    namespaces over the same QUIC connection. The bridge compiles Zenoh
    key-expressions to `robot/<id>/...` track names; matching and fan-out then
    happen at the relay.
  </Tab>
  <Tab title="MQTT 3.1.1">
    Constrained or legacy devices that only speak **MQTT 3.1.1** bridge in: an
    MQTT topic maps to a `robot/<id>` track, retained messages map onto the
    transient-local lane, and QoS 0/1 map onto best-effort / reliable lanes.
  </Tab>

> **NOTE:**
> However a device connects, the on-the-wire frame is identical. The transport is
> a property of *how a peer reached the mesh*, not of the data — so a CDR
> publisher, a Zenoh node, and an MQTT sensor can all feed one subscriber.

## Wire model & track conventions

Each robot maps to a pair of MoQT namespaces. A **topic** becomes a **track
name** under the relevant namespace; one MoQT track carries one topic.

| Direction        | Namespace        | Example track                         |
| ---------------- | ---------------- | ------------------------------------- |
| Telemetry (up)   | `robot/<id>`     | `robot/spot-12` · `odom`              |
| Commands (down)  | `robot/<id>/ctl` | `robot/spot-12/ctl` · `cmd_vel`       |

Each published message opens its own MoQT group (one discrete ROS message per
group), and the relay holds a small recent-group window so a late subscriber
catches the latest frames — the closest analog to a ROS 2 history depth.

### Type-name-prefixed CDR frame

Every object on the wire is a self-describing envelope:

```
┌────────────────────┬──────────────────────┬─────────────────────┐
│ u16 BE type_len    │ type_name (UTF-8)    │ CDR payload         │
│ (2 bytes)          │ e.g. nav_msgs/msg/…  │ opaque bytes        │
└────────────────────┴──────────────────────┴─────────────────────┘
```

- The 2-byte big-endian length bounds type names at 65 535 bytes; real ROS 2
  names fit in ~64.
- The CDR payload is **opaque to the SDK** — you hand it the bytes your DDS,
  Zenoh, or `serialize_message` already produced.
- The prefix is what makes the stream **cross-language**: a subscriber gets
  `(cdr, typeName)` and routes to the matching decoder with no schema registry.

> **TIP:**
> The frame is byte-identical across the TypeScript, Python, Go, and native
> bridges. A frame published by a Python robot decodes unchanged in a browser
> dashboard.

## QoS → lanes

ROS 2 QoS hints map onto MoQT lanes at the relay. You pass a `qos` profile when
you create a publication; the relay routes and admits the track by the resulting
capability.

| QoS hint                            | Lane                       | Capability        |
| ----------------------------------- | -------------------------- | ----------------- |
| `reliability: "reliable"`           | subgroup stream (ordered)  | `ros.reliable`    |
| `reliability: "best_effort"`        | QUIC datagram (lossy)      | `ros.best_effort` |
| `durability: "transient_local"`     | adds late-join retain      | `ros.tl_*`        |
| `depth: N`                          | per-track group window     | —                 |

- **Reliable** rides subgroup streams — guaranteed, in order per-publisher. Use
  it for commands and for telemetry you can't drop (a map update, a goal).
- **Best-effort** rides QUIC datagrams — lowest latency, lossy. Use it for
  high-rate sensor streams (pose, LIDAR) where the freshest sample wins.
- **transient_local** makes the relay retain the last group(s) so a subscriber
  that joins late immediately receives the most recent value — the analog of a
  latched MQTT retained message.
- **depth** caps the per-track group window the relay holds for late
  subscribers, mirroring ROS 2 keep-last history.

Reliable publications also default to a higher send priority (lower priority
number) than best-effort; you can override priority per message on `write`.

## Drop-in ROS 2 transport

A robot doesn't have to know the relay exists. A small native **bridge** runs
beside the ROS 2 graph and presents itself as an ordinary middleware transport —
the same role a vendor RMW (ROS Middleware) plugin fills. Local nodes publish
and subscribe their topics exactly as before; the bridge mirrors those topics
into and out of MoQT:

  1. **Local nodes stay local**
Nodes talk to the bridge over the standard middleware interface — DDS or
    Zenoh on the robot's loopback. No application code changes.
  2. **Bridge mirrors topics**
For each configured topic, the bridge subscribes locally and republishes the
    raw CDR onto a `robot/<id>` telemetry track, and subscribes a `robot/<id>/ctl`
    command track and republishes onto the local graph.
  3. **Mesh fan-out**
The relay fans each telemetry frame out to every cloud subscriber and routes
    each command down to the addressed robot — over WAN-grade QUIC, across NAT,
    with one auth token.

> **NOTE:**
> The bridge is described here by **role**, not by file. From the robot's point of
> view it is "the transport that carries my topics off-box"; from the cloud's it is
> "the fleet ingress". The SDK in [SDK Methods](/modalities/robotics/sdk-methods)
> gives you the same pub/sub surface programmatically when you want to bridge
> selected topics yourself.

## Architecture

The data plane is one QUIC connection per robot multiplexing every track via
**MoQT**; the relay mesh fans tracks out to subscribers across hosts. Underneath,
the engine is **shard-per-core** with an **AF_XDP** kernel-bypass NIC fast path,
**lock-free mcache / dcache rings** between shards, and **io_uring** for async
I/O — so a single host fans a high-rate fleet out at line rate without copying
through the kernel network stack.

- **One connection, many tracks.** Telemetry and commands for a robot share one
  QUIC session; MoQT multiplexes the tracks.
- **One token.** The tenant token that authorizes voice / streams / games
  authorizes the fleet — no separate broker credentials.
- **No broker to run.** The relay mesh is the fan-out. There's no MQTT broker or
  DDS discovery domain to operate in the cloud.

## Related

- [SDK Methods](/modalities/robotics/sdk-methods) — the typed `Robotics` surface.
- [Cookbook](/modalities/robotics/cookbook) — short task-oriented snippets.
- [Recipes](/modalities/robotics/recipes) — end-to-end worked examples.
- [Realtime Tracks](/concepts/realtime-tracks) — the MoQT primitive underneath.
