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 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.