Timsty's picture
Add files using upload-large-folder tool
700dd75 verified
|
Raw
History Blame Contribute Delete
4.71 kB
# Coordinate Frame and Rotation Conventions
This page documents the coordinate frame, quaternion, and rotation conventions
used throughout the SONIC codebase. Getting these wrong causes silent bugs β€”
the robot will move but in the wrong direction or with wrong orientation.
## Coordinate Frames
### Isaac Lab / MuJoCo (simulation)
- **Z-up**: Gravity is along -Z. Ground plane is XY.
- **Right-handed**: X forward, Y left, Z up.
- This is the convention used during training and evaluation.
### SMPL / BVH (human motion data)
- **Y-up**: Gravity is along -Y. Ground plane is XZ.
- When loading SMPL or BVH data, set `smpl_y_up: true` in the motion library
config. The motion library automatically converts Y-up to Z-up internally.
### Summary
| System | Up axis | Convention |
|--------|---------|------------|
| Isaac Lab | Z | Z-up, right-handed |
| MuJoCo | Z | Z-up, right-handed |
| SMPL body model | Y | Y-up |
| BVH motion files | Y | Y-up |
| Retargeted PKL data | Z | Z-up (already converted) |
## Quaternion Convention
### Scalar-first (wxyz) β€” default throughout SONIC
The SONIC codebase uses **scalar-first (wxyz)** quaternions everywhere:
```
q = [w, x, y, z]
```
This applies to:
- `gear_sonic/trl/utils/torch_transform.py` β€” all rotation utilities
- `gear_sonic/isaac_utils/rotations.py` β€” Isaac Lab rotation helpers (use `w_last=False`)
- Isaac Lab APIs (`body_quat_w`, `root_quat_w`, etc.)
- Motion library internal storage
- Retargeted PKL data (`root_rot` field)
### Scalar-last (xyzw) β€” scipy only
[SciPy's Rotation class](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Rotation.html)
uses **scalar-last (xyzw)** convention:
```
q = [x, y, z, w]
```
This is only used in the **data processing scripts** (`data_process/`) when
calling `scipy.spatial.transform.Rotation`. The scripts convert to wxyz
before saving:
```python
# In data processing (scipy xyzw β†’ wxyz for storage)
root_quat_xyzw = Rotation.from_euler("xyz", euler_angles).as_quat() # scipy: xyzw
root_quat_wxyz = root_quat_xyzw[:, [3, 0, 1, 2]] # convert to wxyz
```
### The `w_last` parameter
Functions in `gear_sonic/isaac_utils/rotations.py` accept a `w_last` boolean:
```python
quat_rotate(q, v, w_last=False) # q is wxyz (scalar-first) β€” this is the default
quat_rotate(q, v, w_last=True) # q is xyzw (scalar-last)
```
**Always use `w_last=False`** unless you're interfacing with scipy or a system
that explicitly uses xyzw.
### Quick reference
| System | Convention | Order | Identity |
|--------|-----------|-------|----------|
| SONIC (torch_transform.py) | wxyz | `[w, x, y, z]` | `[1, 0, 0, 0]` |
| Isaac Lab | wxyz | `[w, x, y, z]` | `[1, 0, 0, 0]` |
| SciPy | xyzw | `[x, y, z, w]` | `[0, 0, 0, 1]` |
| MuJoCo | wxyz | `[w, x, y, z]` | `[1, 0, 0, 0]` |
| ROS | xyzw | `[x, y, z, w]` | `[0, 0, 0, 1]` |
### Converting between conventions
```python
# wxyz β†’ xyzw
q_xyzw = q_wxyz[..., [1, 2, 3, 0]]
# xyzw β†’ wxyz
q_wxyz = q_xyzw[..., [3, 0, 1, 2]]
```
## Rotation Representations
The codebase uses multiple rotation representations depending on context:
| Representation | Shape | Used in |
|---------------|-------|---------|
| Quaternion (wxyz) | `(..., 4)` | Simulation, motion library, observations |
| Axis-angle | `(..., 3)` | `pose_aa` field in motion PKLs |
| Rotation matrix | `(..., 3, 3)` | Forward kinematics, 6D rotation encoding |
| 6D rotation | `(..., 6)` | Some observation terms (first 2 columns of rotation matrix) |
| Euler angles | `(..., 3)` | CSV motion data input (converted immediately) |
### Axis-angle in motion data
The `pose_aa` field in retargeted PKL files stores per-body **local** rotations
as axis-angle vectors. The direction is the rotation axis, the magnitude is
the angle in radians:
```python
pose_aa # (T, num_bodies, 3) β€” axis-angle per body, MuJoCo body order
```
## Joint Ordering
Isaac Lab and MuJoCo traverse the kinematic tree in different orders. The
codebase provides bidirectional index mappings per robot:
```python
from gear_sonic.envs.manager_env.robots.g1 import (
G1_ISAACLAB_TO_MUJOCO_DOF, # Reorder DOFs: IsaacLab β†’ MuJoCo
G1_MUJOCO_TO_ISAACLAB_DOF, # Reorder DOFs: MuJoCo β†’ IsaacLab
)
# Convert joint positions from IsaacLab order to MuJoCo order:
mujoco_joints = isaaclab_joints[..., G1_ISAACLAB_TO_MUJOCO_DOF]
```
Motion PKL data (`dof`, `pose_aa`) is stored in **MuJoCo order**. Isaac Lab
simulation uses **IsaacLab order**. The training pipeline handles the conversion
automatically via `order_converter.py`.
See [Training on New Embodiments](../user_guide/new_embodiments.md) for how to
define these mappings for a new robot.