# Robot description and coordinate frames

> What a RosieOS robot description contains, how its hashed identity is pinned, served and checked at Load, how a cell's calibration narrows it, and the frames, joints and units of the Rosie 1400 and Rosie 1420.

URL: https://advancedmetalresearch.com/docs/concepts/robot-description-and-frames
Section: RosieOS docs / Concepts
Last updated: 2026-10-10

A **robot description** is one directory that says everything intrinsic to a robot model: its kinematics and meshes, its limits, the planner's policy, its collision spheres and what rt-core needs to drive it. Every cell that mounts that model shares the directory. How one particular cell differs from the model lives on that cell, in a separate calibration file.

The description is hashed. A plan records the hash it was made against, and the cell refuses a plan whose hash does not match its own. This page explains what the files are, how that identity moves through the system, and the frames and joints you program against.

For the exact file schemas see [Robot description files](https://advancedmetalresearch.com/docs/reference/robot-description). To create a new model see [Add a robot model](https://advancedmetalresearch.com/docs/guides/add-a-robot-model).

## What is in a description

A description lives at `robot_description/robots/<model_id>/`. The directory name is the model id, and every file in it must state the same id.

| File | Read by | Holds |
|---|---|---|
| `robot_description_manifest.json` | everyone | Which files the robot is made of. Schema `rosie.robot-manifest.v1`. |
| `robot.urdf` | planner, OLP, rt-core compiler | Links, joints, meshes, joint limits (rad, rad/s), the torch link. The URDF `robot name` must equal the model id. |
| `robot.srdf` | Tesseract planning | The `manipulator` planning group and its named `home` state. |
| `config.json` | planner, OLP, rt-core compiler | What URDF cannot say: frames, planning rates and accelerations, driven and held axes, reset pose, torch convention, calibration caps. Schema `rosie.robot-config.v1`. |
| `spheres.json` | weld planner | The arm's collision spheres, in each link's own frame, with the hashes of the URDF and meshes they were fitted to. |
| `rtcore_definition.json` | rt-core only | How each joint maps to a drive: drive profile, gearing, encoder scale, direction, Home policy. Schema `rosie.robot-definition.v1`. |
| `meshes/` | planner, viewers | The link meshes the URDF names, plus any fixture bodies the planner uses. |
| `provenance/`, `README.md` | people | How the files were made. Not registered, so not part of the identity. |

The planner never reads `rtcore_definition.json`, and rt-core never reads `spheres.json`. The two halves meet only through the shared URDF, `config.json` and the identity.

> [!NOTE] `rtcore_definition.json` holds the drive gearing and encoder scale for each joint. These docs describe its fields, not its values.

### Bench descriptions

`bench_one_motor_1to1` and `bench_nine_motors_1to1` describe bare motors on a table. They have only a manifest and `rtcore_definition.json`, with no URDF. rt-core can run them. OLP does not list them and the planner cannot plan for them.

## Identity

The description's identity is a SHA-256 over the manifest and every file it registers, written `sha256:<64 hex>`. Files that are not registered, such as a CAD export or a measurement report, are outside the identity: changing them never makes a plan stale. Registering a file, or changing a registered one, changes the identity. That is deliberate: the robot changed.

Print the identity of any description:

```bash
cd robot_description/go
go run ./cmd/identity ../robots/rosie_1400_v3
# sha256:<64 hex> ../robots/rosie_1400_v3
```

The exact byte layout is in [the reference](https://advancedmetalresearch.com/docs/reference/robot-description#identity-algorithm).

### How the identity travels

![A machine config pins a robot description by path and identity. rtctl compile snapshots the registered files and the cell's calibration into the compiled configuration, which rt-control serves through Describe and the resources endpoint. OLP reads the description and the cell's calibration, the planner receives settled numbers, and the .rdt header records the identities. At Load, OLP describes the cell again and refuses a program whose identities differ.](https://advancedmetalresearch.com/assets/docs/robot-description-identity.svg)

1. **A machine config pins it.** `robot.robot_description` names the directory and its identity. `rtctl compile` refuses a pin that does not match the files on disk.
2. **The compiled configuration snapshots it.** Compiling copies every registered file into the compiled directory as a content-addressed resource. The cell no longer needs the checkout.
3. **rt-control serves it.** Describe lists the model id, the identity and each resource's SHA-256. Clients fetch the bytes with `GET /v1/resources/<sha256>`, or with [`rtctl resources fetch`](/docs/reference/rtctl#resources-fetch).
4. **OLP plans against it.** OLP's robot configuration is the one place that opens a description and a cell's calibration. The seam worker and the weld planner receive the resulting numbers, never the files.
5. **The program records it.** Every dense trajectory carries a `robot_cell` block in its header with `model_id`, `robot_description_sha256` and `machine_planning_calibration_sha256`. See [Dense trajectory format](https://advancedmetalresearch.com/docs/reference/rdt-format).
6. **Load checks it.** Before sending a program, OLP describes the cell again (it never trusts a cached answer) and compares those three fields.

### Load refusals

| Reason | When |
|---|---|
| `robot_cell_unavailable` | The cell binds no robot description, or its robot identity is not valid. |
| `robot_cell_missing` | The program's header names no robot or cell. It was planned before this check existed. Plan it again. |
| `robot_cell_mismatch` | The model, description identity or calibration identity differs. The message lists each field, with the plan's value and the cell's. |

A machine with flat axes and no robot binding has nothing to compare a plan against, so Load does not run this check on it.

The header can also record the machine's whole configuration digest, `machine_configuration_sha256`. That is a record, not a check: the digest also changes for things no plan depends on, such as a CPU number or a bus timeout. When it differs, Load adds a note and continues.

## Cell calibration

Two cells built from the same model are never quite identical. A cell states its own deviation in `machine_planning_calibration.json` (schema `rosie.machine-planning-calibration.v1`). It is per cell and is not stored in the repository. When a cell has one, its machine config pins it by path and SHA-256, and the compiled configuration serves it beside the description.

The calibration can:

- **narrow** a joint's lower and upper limit and its velocity, never widen them
- **hold** a joint at a value inside its limits
- set a **speed ceiling** no higher than the URDF's fastest joint
- apply a small **kinematic correction** to a joint origin, `xyz_m` and `rpy_rad`, each component capped by the description's `kinematic_correction_caps`

Every key is checked. An unknown key, a joint the URDF does not have, a widened limit or an over-cap correction is an error, not something quietly ignored. A cell with no calibration serves a fixed empty document for its model, and a plan made against that empty document matches it exactly.

The calibration's identity is the SHA-256 of its exact bytes. Reformatting the file changes the identity, so plans made against the old bytes are refused.

## Coordinate frames

`config.json` names the frames the URDF cannot:

| Frame | Key | Meaning |
|---|---|---|
| Base | `frames.base` | What everything is measured in. `world` on both Rosie models. |
| Tool | `frames.tool` and `torch.tool_frame` | The tool centre point: `tool0`. |
| Work | `frames.work` | What a workpiece is fixtured to. On a positioner this is a surface on a table link, not the link origin. |
| Work surface | `frames.work_surface` | Optional. Adds the work frame as a fixed child of `parent`, offset by `xyz_m`. |

The torch's electrode axis is `torch.electrode_axis`, a unit vector in the tool frame. On both Rosie models it is `[1, 0, 0]`: the wire points along +x of `tool0`.

OLP's robot catalogue also reports two positions it computes from the URDF at the zero pose: `work_world_m`, the work frame's origin in world, and `arm_base_world_m`, the child link of the first driven joint. The Cartesian motion server expresses TCP poses relative to the arm base, while viewers use world, so clients convert with that offset.

### Units

- Revolute positions are in **rad**, prismatic positions in **m**.
- Velocities are in **rad/s**, accelerations in **rad/s²**.
- Offsets are `xyz_m` in metres and `rpy_rad` in radians. URDF rotations compose as Rz(yaw)·Ry(pitch)·Rx(roll).
- A calibration correction is applied as T' = T_origin · Trans(xyz) · R(rpy).

## Rosie 1400

Model `rosie_1400_v3`: a six-axis arm and an H-frame two-table positioner, nine axes in all. This is the layout of the War Machine cell.

```text
world ─ floor ─┬─ J1 ─ link_1 ─ J2 ─ link_2 ─ J3 ─ link_3 ─ J4 ─ link_4 ─ J5 ─ link_5 ─ J6 ─ link_6 ─ end_effector ─ tool0
               └─ J9 ─ h_frame ─┬─ J7 ─ positioner_table_a ─ positioner_table_a_top (work)
                                └─ J8 ─ positioner_table_b
```

| Joint | Parent → child | Axis | Limits (rad) | URDF velocity (rad/s) | Planning velocity (rad/s) | Planning acceleration (rad/s²) |
|---|---|---|---|---|---|---|
| J1 | `floor` → `link_1` | +z | −π to π | 3.14 | 0.785 | 5.0 |
| J2 | `link_1` → `link_2` | +y | −1.9 to 1.9 | 3.14 | 0.785 | 5.0 |
| J3 | `link_2` → `link_3` | +y | −1.57 to 1.53 | 3.14 | 0.785 | 5.0 |
| J4 | `link_3` → `link_4` | +x | −π to π | 17.45 | 1.571 | 10.0 |
| J5 | `link_4` → `link_5` | +y | −3.37 to 1.3 | 23.27 | 1.571 | 10.0 |
| J6 | `link_5` → `link_6` | +z | −2.094 to 3.14 | 31.42 | 1.571 | 10.0 |
| J7 | `h_frame` → `positioner_table_a` | +y | −π to π | 3.14 | 0.785 | 2.5 |
| J8 | `h_frame` → `positioner_table_b` | −y | −π to π | 3.14 | 0.785 | 2.5 |
| J9 | `floor` → `h_frame` | +z | −π to π | 3.14 | 0.785 | 2.5 |

- **Frames:** base `world`, tool `tool0`, work `positioner_table_a_top`, a surface on `positioner_table_a` at `xyz_m` [0, −0.622, 0.1].
- **Driven axes:** J1 to J7. **Held axes:** J8 = 0 and J9 = 0. The planner moves the arm and table A together and keeps table B and the H-frame turn fixed.
- **Planning speed ceiling:** 1.571 rad/s.
- **Reset pose:** all driven joints at 0. The SRDF `home` state is all nine joints at 0.

How the positioner is built and why the planner holds J8 and J9 is covered in [Cells, machines and positioners](https://advancedmetalresearch.com/docs/concepts/cells-and-positioners#the-h-frame-positioner).

## Rosie 1420

Model `rosie_1420_v1`: a six-axis arm on a pedestal.

```text
world ─ pedestal ─ base ─ J1 ─ link_1 ─ J2 ─ link_2 ─ J3 ─ link_3 ─ J4 ─ link_4 ─ J5 ─ link_5 ─ J6 ─ link_6 ─ end_effector ─ tool0
```

| Joint | Axis | Limits (rad) | URDF velocity (rad/s) | Planning velocity (rad/s) | Planning acceleration (rad/s²) |
|---|---|---|---|---|---|
| J1 | +z | −6.266 to 6.266 | 3.14 | 3.0 | 2.094 |
| J2 | −y | −1.9 to 1.9 | 3.14 | 3.0 | 2.094 |
| J3 | +y | −4.2 to 1.53 | 3.14 | 3.0 | 2.094 |
| J4 | +x | −6.266 to 6.266 | 17.45 | 10.0 | 4.189 |
| J5 | +y | −6.266 to 6.266 | 3.14 | 3.0 | 4.189 |
| J6 | −z | −10 to 10 | 31.42 | 10.0 | 4.189 |

- **Frames:** base `world`, tool `tool0`, work `world`.
- **Driven axes:** J1 to J6. No held axes.
- **Planning speed ceiling:** 10.0 rad/s.

## Which limit applies where

The same description feeds two consumers, and they use different numbers:

- **The planner** uses `config.json`'s planning velocity and acceleration, narrowed further by the cell calibration. A description may lower a planning rate below the URDF rating, never raise it: a planning velocity above the URDF's `velocity` is refused when the description loads.
- **rt-core** takes each robot-bound axis's travel and maximum velocity from the URDF, and its trajectory and jog acceleration from `config.json`. A machine config cannot override them. rt-core's limit check is therefore the URDF rating, which is higher than the planning rate on most joints.

> [!WARNING] rt-core checks joint position, velocity and continuity. It does not check collisions. Only planned weld programs are verified against the cell model before they can be loaded. See [What is verified before motion](https://advancedmetalresearch.com/docs/get-started/safety-model#what-is-verified).

## Related pages

- [Robot description files](https://advancedmetalresearch.com/docs/reference/robot-description): every field and the identity algorithm
- [Add a robot model](https://advancedmetalresearch.com/docs/guides/add-a-robot-model)
- [Cells, machines and positioners](https://advancedmetalresearch.com/docs/concepts/cells-and-positioners)
- [Configuration files](https://advancedmetalresearch.com/docs/reference/configuration): how a machine config pins a description
- [Dense trajectory format](https://advancedmetalresearch.com/docs/reference/rdt-format): the `robot_cell` header block

## Sources

Written from these files in the RosieOS repository (https://github.com/advanced-metal-research/RosieOS):

- `robot_description/go/identity.go:32-167`
- `robot_description/go/cell.go:26-300`
- `robot_description/go/description.go:20-285,292-330`
- `robot_description/go/cmd/identity/main.go:1-32`
- `robot_description/tools/manifest.py:33-63`
- `robot_description/robots/rosie_1400_v3/config.json`
- `robot_description/robots/rosie_1400_v3/robot.urdf:133-218`
- `robot_description/robots/rosie_1400_v3/robot.srdf`
- `robot_description/robots/rosie_1400_v3/robot_description_manifest.json`
- `robot_description/robots/rosie_1400_v3/spheres.json:1-8`
- `robot_description/robots/rosie_1420_v1/config.json`
- `robot_description/robots/rosie_1420_v1/robot.urdf:36-157`
- `robot_description/robots/bench_one_motor_1to1/robot_description_manifest.json`
- `rt-core/tools/rtctl/compiled_resources.go:130-154`
- `rt-core/tools/rtctl/compiler.go:180-200`
- `rt-core/tools/rtctl/robot_definition.go:250-290`
- `rt-core/adapters/rosie/control/resources.go:334-380`
- `rt-core/config/templates/machine.json (robot.robot_description`
- `robot.machine_planning_calibration)`
- `offline-programming/v1/internal/denseexec/robot.go:12-110`
- `offline-programming/v1/internal/denseexec/rt_core_robot.go:236-260`
- `offline-programming/v1/internal/denseexec/rt_core.go:766`
