Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ For a full list of extensions and detailed documentation, visit **[robotcontrols
For full documentation, including advanced installation, modular usage, and API references, please visit:
👉 **[robotcontrolstack.org](https://robotcontrolstack.org)**

Useful quick-reference pages:
- **[RCS Conventions](https://robotcontrolstack.org/user_guide/conventions)** for quaternion order, frames, Euler angles, and gripper semantics
- **[Sim Scene Configuration](https://robotcontrolstack.org/user_guide/scene_configuration)** for `SimEnvCreatorConfig`, scene frames, and example setup patterns

## 🤝 Contribution

We welcome contributions from the robotics and ML community! For contribution guidelines, please check out **[robotcontrolstack.org/contributing](https://robotcontrolstack.org/contributing)**.
Expand Down
5 changes: 5 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@

## Documentation

If you are looking for the most common frame, pose, and scene-setup details first, start here:

- [RCS Conventions](user_guide/conventions.md)
- [Sim Scene Configuration](user_guide/scene_configuration.md)

```{toctree}
:maxdepth: 2
:caption: User Guide
Expand Down
221 changes: 221 additions & 0 deletions docs/user_guide/conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# RCS Conventions

This page is the quick reference for coordinate frames, pose encodings, gripper semantics, and units used across RCS.
```text
Simple 3D sketch of the same right-handed frame (robot base is at the origin)

+z (up)
^
|
o---> +y (left)
/
/
+x (forward)
```

## At a glance

- **Quaternion order in RCS is `xyzw`** (`[qx, qy, qz, qw]`), matching Eigen's coefficient order.
- **MuJoCo free-joint `qpos` uses `wxyz`**, so direct MuJoCo state access needs an explicit reorder.
- **Robot/base frames are right-handed** with `x` forward, `y` left, and `z` up.
- **Gripper commands are normalized**: `0` means closed and `1` means open.
- **Euler angles are `roll`, `pitch`, `yaw`** around the `x`, `y`, and `z` axes.
- **Translations are in meters and angles are in radians** unless explicitly documented otherwise.

If you are working with multi-robot scene composition, also see the [Sim scene configuration guide](scene_configuration.md).

## Frames

### World frame

In simulation, scenes and free objects live in the global **world frame**.

Use world coordinates when you want to place something in the room itself, for example a cube on a table or a fixed camera in the lab.

The core robot API exposes explicit conversions between world and robot coordinates:

- `Robot.get_base_pose_in_world_coordinates()`
- `Robot.to_pose_in_world_coordinates(...)`
- `Robot.to_pose_in_robot_coordinates(...)`

### Robot base frame

Kinematics and low-level robot poses are expressed in the robot's **base frame**.

This is the frame assumed by the kinematics backends, for example in `src/rcs/Kinematics.cpp`, where `forward(...)` says the pose is assumed to be in the robot's coordinate frame.

When you interpret or command robot poses, the expected base-frame axis orientation is:

```text
x = forward
y = left
z = up
```

This is also the convention documented for Franka Quest teleoperation in `examples/teleop/README.md`.

### End-effector frame: `attachment_site`

Each robot config defines an `attachment_site`. This is the end-effector frame used by the kinematics stack.

Common examples in the repository are:

- `attachment_site_0` for FR3 / Panda
- `attachment_site` for UR5e / XArm7
- `gripper` for SO101

If you are unsure which frame a robot uses, check its config or the relevant example scene in `python/rcs/envs/configs.py`.

### Tool frame: `tcp_offset`

`tcp_offset` is applied on top of the attachment site to define the actual tool center point (TCP) used by motion commands and IK.

In practice:

- `attachment_site` = frame coming from the robot model
- `tcp_offset` = extra transform from that frame to the tool you actually want to control

A typical example is a wrist-mounted gripper where the tool center point is slightly in front of the model's attachment site.

## Pose representations

RCS uses several pose encodings. The important ones are:

### `Pose`

`rcs.common.Pose` is the main transform type. It supports construction from:

- translation only
- quaternion + translation
- `RPY` + translation
- rotation matrix + translation

### Quaternion order

Within RCS, quaternions are stored in **xyzw** order.

This is visible in two places:

- `src/rcs/Pose.cpp`: `rotation_q()` returns `this->m_rotation.coeffs()`
- `python/tests/test_common.py` checks that the identity quaternion is `[0, 0, 0, 1]`

So the convention is:

```text
[qx, qy, qz, qw]
```

This matches Eigen's quaternion coefficient layout.

### `tquat`

`tquat` means translation plus quaternion and is used by the environment API.

The value layout is:

```text
[x, y, z, qx, qy, qz, qw]
```

This comes directly from `python/rcs/envs/base.py`, where `tquat` is built from `pose.translation()` followed by `pose.rotation_q()`.

### `xyzrpy`

`xyzrpy` is the translation plus roll-pitch-yaw representation used by the environment API.

The value layout is:

```text
[x, y, z, roll, pitch, yaw]
```

The `RPY` type in `python/rcs/_core/common.pyi` exposes the fields in exactly that order:

- `roll`
- `pitch`
- `yaw`

These angles are rotations around the `x`, `y`, and `z` axes, respectively.

### Rotation vector / `rotvec`

Some hardware integrations, notably UR, also use a 6D rotation-vector pose:

```text
[x, y, z, rx, ry, rz]
```

You can see this in `extensions/rcs_ur5e/src/rcs_ur5e/hw.py`, where `common.RotVec(...).as_quaternion_vector()` is converted into an RCS `Pose`, and `Pose.rotvec()` is sent back to the robot.

## Gripper convention

Gripper actions and widths are normalized to the range `[0, 1]`.

The convention used across RCS is:

```text
0 = closed
1 = open
```

This is documented directly in multiple places:

- `python/rcs/envs/base.py`: `# 0 for closed, 1 for open (>=0.5 for open)`
- `src/sim/SimGripper.h`: `// normalized width of the gripper, 0 is closed, 1 is open`
- `extensions/rcs_fr3/src/hw/FrankaHand.h`: `// normalized width of the gripper, 0 is closed, 1 is open`

For binary grippers, the environment wrapper rounds and clips the command, and values `>= 0.5` are treated as open.

## Units

Unless a specific API says otherwise:

- translations, distances, and Cartesian offsets are in **meters**
- joint angles and Euler angles are in **radians**
- camera depth is stored as **metric depth scaled by 1000** in `uint16`, so values are effectively in **millimeters**

In practice, that means:

- moving `0.01` in `tquat[0]` means moving **1 cm** in `x`
- `np.pi / 2` means **90 degrees**
- a depth value of `723` means about **0.723 m**

The relevant camera path is:

- `python/rcs/camera/sim.py` converts the MuJoCo depth buffer to **meters** when `physical_units=True`
- `python/rcs/camera/interface.py` defines `BaseCameraSet.DEPTH_SCALE = 1000`
- the resulting metric depth is multiplied by that scale factor and stored as `uint16`

The same scaled-metric convention is also used by camera integrations such as RealSense and ZED.

## MuJoCo caveat: free-joint quaternions use `wxyz`

A common source of confusion is that **RCS uses `xyzw`**, but MuJoCo free-joint `qpos` stores the quaternion as **`wxyz`**.

RCS exposes both forms explicitly:

- `Pose.rotation_q()` returns `xyzw`
- `Pose.rotation_q_wxyz()` returns `wxyz`

You can see the `wxyz` form used when writing MuJoCo-facing state in files such as `python/rcs/sim/sim.py` and `python/rcs/envs/tasks.py`.

So:

- **RCS `Pose` / env API**: `xyzw`
- **MuJoCo free-joint state**: `wxyz`

If you manipulate MuJoCo state directly, convert between the two explicitly.

## Practical checklist

When something looks wrong, check these first:

1. Are you working in **world frame** or **robot/base frame**?
2. Does your robot/base frame follow **x forward, y left, z up**?
3. Is your end-effector frame the correct `attachment_site`?
4. Did you apply the right `tcp_offset`?
5. Are your quaternions **xyzw** in RCS?
6. Are you accidentally feeding **MuJoCo `wxyz`** into an RCS API?
7. Are your Euler angles ordered as **roll, pitch, yaw** around **x, y, z**?
8. Are your gripper commands using **0 = closed, 1 = open**?
9. Are your translations in **meters** and your angles in **radians**?
7 changes: 7 additions & 0 deletions docs/user_guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@

The User Guide provides in-depth information about the core concepts and components of the Robot Control Stack.

If you want the quickest orientation first, start with:

- [RCS Conventions](conventions.md)
- [Sim Scene Configuration](scene_configuration.md)

```{toctree}
:maxdepth: 2

architecture
gym_interface
low_level_api
conventions
scene_configuration
```
Loading
Loading