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: truein 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 utilitiesgear_sonic/isaac_utils/rotations.pyβ Isaac Lab rotation helpers (usew_last=False)- Isaac Lab APIs (
body_quat_w,root_quat_w, etc.) - Motion library internal storage
- Retargeted PKL data (
root_rotfield)
Scalar-last (xyzw) β scipy only
SciPy's Rotation class 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:
# 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:
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
# 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:
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:
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 for how to define these mappings for a new robot.