# Frames, units and time

> The conventions every processed recording follows, so your pipeline never has to guess.

Source: https://rayform.world/docs/reference/conventions
Last updated: 2026-10-18
Status: Rayform is in private preview. APIs, formats and the SDK described here may change before general availability.

Mismatched conventions are the most common reason robot data quietly fails: a quaternion read in the wrong order or a flipped camera frame trains a model on wrong data without any error. Rayform converts every recording to one convention on the way in, records it in the metadata, and converts again on the way out only when you ask.

## Space

| Item | Convention |
| --- | --- |
| World frame | One per site. Right-handed, Z up, metres (ROS REP-103) |
| Camera frame | OpenCV: X right, Y down, Z forward |
| Rotations | Unit quaternions, `w, x, y, z` order |
| Poses | `[x, y, z, qw, qx, qy, qz]`, child in parent |
| Camera model | Pinhole with radial-tangential distortion, from a versioned calibration |

## Time

| Item | Convention |
| --- | --- |
| Timestamps | `int64` nanoseconds (`t_ns`), monotonic within a session |
| Clock | The session clock; offsets estimated during alignment are already applied |
| Resampling | Only on request, with the method recorded (`nearest` or `linear`) |

## Values

| Quantity | Unit and type |
| --- | --- |
| RGB images | `uint8`, channels-last on disk, channels-first in `EpisodeDataset` |
| Depth | metres, `float32`; missing depth is `0` |
| Point clouds | metres, `float32`, in the sensor frame |
| Joint positions | radians (revolute) or metres (prismatic), `float32` |
| Joint velocities | per second, `float32` |
| Gripper | as reported by your robot; units stored in channel metadata |

## Converting on the way out

Exports, streams and the PyTorch dataset accept `frame` and `quat_order`:

| `frame` | World | Camera |
| --- | --- | --- |
| `rayform` (default) | Z up | OpenCV |
| `ros` | Z up, REP-103 | ROS optical (same axes as OpenCV) |
| `opencv` | Y down | OpenCV |
| `opengl` | Y up | OpenGL: X right, Y up, Z backward |
| `isaac` | Z up | USD camera (OpenGL axes) |
| `mujoco` | Z up | MuJoCo camera (OpenGL axes) |

```python
seg.channel("poses/base", frame="opengl")
client.exports.create(slice_, format="rlds", options={"frame": "ros", "quat_order": "xyzw"})
```

## Provenance

Every exported sample carries `slice`, `segment`, `segment_version` and `site`. Keep them in your training logs; any result can then be traced to the exact data it came from.
