| # Motion Reference Data |
|
|
| This page describes the motion reference data format used by the C++ deployment stack, how to create your own reference motions, and how to verify and deploy them. |
|
|
| The deployment stack plays back pre-loaded **reference motions** — sequences of joint positions, velocities, and full body kinematics that the policy tracks. These motions are stored as CSV files in a structured folder hierarchy. You can generate them from any source (motion capture, simulation, retargeting pipeline, etc.) as long as the output matches the format described below. |
|
|
| --- |
|
|
| ## Folder Structure |
|
|
| Each motion dataset is a directory containing one subfolder per motion clip. The C++ reader (`MotionDataReader`) auto-discovers all subfolders at startup. |
|
|
| ``` |
| reference/my_motions/ |
| ├── motion_name_1/ |
| │ ├── joint_pos.csv # Joint positions |
| │ ├── joint_vel.csv # Joint velocities |
| │ ├── body_quat.csv # Body quaternions |
| │ ├── body_pos.csv # Body positions |
| │ ├── metadata.txt # Body part indexes |
| │ ├── body_lin_vel.csv # Body linear velocities |
| │ ├── body_ang_vel.csv # Body angular velocities |
| │ ├── smpl_joint.csv # SMPL joint positions |
| │ ├── smpl_pose.csv # SMPL body poses |
| │ └── info.txt # Detailed motion information |
| └── motion_name_2/ |
| └── ... |
| ``` |
|
|
| The C++ reader scans the base directory for subfolders, reads each subfolder as one motion, and validates frame-count consistency across all files in that folder. |
| --- |
|
|
| ## File Formats |
|
|
| The C++ reader loads whichever files are present and skips missing files gracefully. However, **in practice**, most policies require: |
| - `joint_pos.csv`, `joint_vel.csv` — for joint-based motion tracking |
| - `body_quat.csv` — for anchor orientation observations (the control loop will stop if this is missing when gathering observations) |
| - `body_pos.csv` — for heading computation and VR 3-point observations |
| - `metadata.txt` — for body part index alignment when body data is present |
|
|
| A motion must have **at least one valid data source** (joint, body, or SMPL) to load at startup. |
|
|
| ### `joint_pos.csv` |
| |
| Joint positions in **IsaacLab order** (29 joints). Each row is one timestep at 50 Hz. The first row is a header. |
| |
| | Column | Description | |
| |--------|-------------| |
| | `joint_0` … `joint_28` | Joint angles in radians (IsaacLab ordering) | |
| |
| Shape: `(timesteps, 29)` |
| |
| ### `joint_vel.csv` |
|
|
| Joint velocities in **IsaacLab order** (29 joints). Each row is one timestep at 50 Hz. Frame count must match `joint_pos.csv`. |
|
|
| | Column | Description | |
| |--------|-------------| |
| | `joint_vel_0` … `joint_vel_28` | Joint angular velocities in rad/s (IsaacLab ordering) | |
|
|
| Shape: `(timesteps, 29)` |
|
|
| ### `body_pos.csv` |
| |
| Body part positions in the **world frame**. Each body contributes 3 columns (x, y, z). The number of bodies varies per motion. Needed for heading computation and VR 3-point observations. |
| |
| | Column | Description | |
| |--------|-------------| |
| | `body_0_x`, `body_0_y`, `body_0_z` | Position of body 0 (root/pelvis) in meters | |
| | `body_1_x`, `body_1_y`, `body_1_z` | Position of body 1 in meters | |
| | … | … | |
| |
| Shape: `(timesteps, num_bodies * 3)` |
|
|
| **We assume the root/pelvis is always at column group 0** (the first 3 columns). |
|
|
| ### `body_quat.csv` |
| |
| Body part orientations as quaternions in the **world frame**. Each body contributes 4 columns. The quaternion ordering is **(w, x, y, z)**. **Required for most policies** — the `motion_anchor_orientation` observation (used by most policies) will fail and stop the control system if this file is missing. |
| |
| | Column | Description | |
| |--------|-------------| |
| | `body_0_w`, `body_0_x`, `body_0_y`, `body_0_z` | Quaternion of body 0 (root/pelvis) | |
| | `body_1_w`, `body_1_x`, `body_1_y`, `body_1_z` | Quaternion of body 1 | |
| | … | … | |
| |
| Shape: `(timesteps, num_bodies * 4)` |
|
|
| **We assume the root/pelvis is always at column group 0** (the first 4 columns). |
|
|
| ```{note} |
| The number of bodies in `body_quat.csv` can differ from `body_pos.csv`. The C++ reader tracks them independently (`num_bodies` vs `num_body_quaternions`). However, the root body (first column group) must be present for heading computation to work. You can use zero if you don't need root pos. |
| ``` |
|
|
| ### `metadata.txt` |
|
|
| Contains the **body part indexes** array, which maps each column group in `body_pos.csv` / `body_quat.csv` to the corresponding IsaacLab body index. This is needed when body data is present. |
|
|
| ``` |
| Metadata for: motion_name |
| ============================== |
| |
| Body part indexes: |
| [ 0 4 10 18 5 11 19 9 16 22 28 17 23 29] |
| |
| Total timesteps: 497 |
| ``` |
|
|
| The C++ reader parses `Body part indexes:` followed by a line of space-separated integers in brackets. For example, `[0, 4, 10, 18, ...]` means column group 0 → IsaacLab body 0 (pelvis/root), column group 1 → body 4, etc. |
|
|
| For a **root-only** motion (only 1 body), use: |
|
|
| ``` |
| Body part indexes: |
| [0] |
| ``` |
|
|
| ### `body_lin_vel.csv` / `body_ang_vel.csv` |
|
|
| Body part linear and angular velocities in the world frame. Same layout as `body_pos.csv` (3 columns per body). The number of bodies must match `body_pos.csv`. |
|
|
| ### `smpl_joint.csv` |
| |
| SMPL joint positions (typically 24 joints × 3 coordinates). Each row is one timestep. |
| |
| Shape: `(timesteps, num_smpl_joints * 3)` |
| |
| ### `smpl_pose.csv` |
|
|
| SMPL body poses in axis-angle representation (typically 21 poses × 3 coordinates). Each row is one timestep. |
|
|
| Shape: `(timesteps, num_smpl_poses * 3)` |
|
|
| ```{note} |
| The **current reference motion tracking pipeline uses joint-based tracking only** (encoder mode 0). To enable SMPL-based reference tracking (encoder mode 2), you would need to modify the code to detect the presence of SMPL data and switch the encoder mode accordingly. |
| ``` |
|
|
| ### `info.txt` |
|
|
| Human-readable summary with shapes, dtypes, and value ranges. Not read by the C++ stack — purely for documentation. |
|
|
| --- |
|
|
| ## Creating Your Own Reference Motions |
|
|
| You can generate reference motions from any source — the only requirement is producing CSV files in the format above. Common approaches: |
|
|
| 1. **Motion capture retargeting** — retarget human mocap to the G1 model, export joint positions/velocities and body kinematics. |
| 2. **Simulation recording** — record joint states from an IsaacLab or MuJoCo simulation at 50 Hz. |
| 3. **Procedural generation** — programmatically create joint trajectories. |
|
|
| ### Minimal Files Needed |
|
|
| The **minimum** set of files to create a working motion for SONIC policy: |
|
|
| 1. **`joint_pos.csv`** — 29 joint positions (IsaacLab order), header + one row per timestep |
| 2. **`joint_vel.csv`** — 29 joint velocities (IsaacLab order), header + one row per timestep |
| 3. **`body_quat.csv`** — Root quaternion (w, x, y, z), header + one row per timestep |
| 4. **`body_pos.csv`** — Root position (x, y, z), header + one row per timestep. You can use all zeros if you don't need position tracking. |
| 5. **`metadata.txt`** — Body part indexes (just `[0]` for root-only) |
|
|
| **Example files:** |
|
|
| `joint_pos.csv`: |
| ``` |
| joint_0,joint_1,joint_2,...,joint_28 |
| 0.128441,0.102713,0.020116,...,0.045231 |
| 0.130124,0.104532,0.021045,...,0.046112 |
| ... |
| ``` |
|
|
| `joint_vel.csv`: |
| ``` |
| joint_vel_0,joint_vel_1,...,joint_vel_28 |
| 0.143671,0.143864,...,0.012345 |
| ... |
| ``` |
|
|
| `body_quat.csv` (root quaternion only): |
| ``` |
| body_0_w,body_0_x,body_0_y,body_0_z |
| 0.999123,0.000456,0.001234,0.040567 |
| ... |
| ``` |
|
|
| `body_pos.csv` (root position, can be all zeros): |
| ``` |
| body_0_x,body_0_y,body_0_z |
| 0.000000,0.000000,0.000000 |
| ... |
| ``` |
|
|
| `metadata.txt`: |
| ``` |
| Metadata for: my_motion |
| ============================== |
| |
| Body part indexes: |
| [0] |
| |
| Total timesteps: 100 |
| ``` |
|
|
| This gives you a **root-only** motion (1 body = pelvis/root) that most policies can track. |
|
|
|
|
| ### Provided Conversion Script |
|
|
| A convenience script `reference/convert_motions.py` is included for converting **joblib pickle** (`.pkl`) files to this format. This is just one possible source — you can use any tool or pipeline that produces the correct CSV output. |
|
|
| ```bash |
| cd gear_sonic_deploy |
| python reference/convert_motions.py <pkl_file> [output_dir] |
| ``` |
|
|
| The pickle should be a dictionary where each key is a motion name and each value contains `joint_pos`, `joint_vel`, `body_pos_w`, `body_quat_w`, `body_lin_vel_w`, `body_ang_vel_w`, `_body_indexes`, and `time_step_total`. |
|
|
| --- |
|
|
| ## Verifying Reference Motions |
|
|
| ### MuJoCo Visualization |
|
|
| Use the included visualizer to check that the motion looks correct on the G1 model: |
|
|
| ```bash |
| cd gear_sonic_deploy |
| python visualize_motion.py --motion_dir reference/my_motions/motion_name_1/ |
| ``` |
|
|
| **Controls:** |
| - **Space**: Pause / resume playback |
| - **R**: Reset to frame 0 |
| - **,** / **.**: Step backward / forward one frame |
| - **-** / **=**: Previous / next motion (if multiple loaded) |
|
|
| Verify that: |
| - The robot stands upright and does not clip through the floor |
| - Joint angles look reasonable (no extreme poses) |
| - The motion plays smoothly without sudden jumps |
| - Body positions track the expected trajectory |
|
|
| --- |
|
|
| ## Using Reference Motions |
|
|
| ### With `deploy.sh` |
|
|
| Pass the motion directory via `--motion-data`: |
|
|
| ```bash |
| ./deploy.sh --motion-data reference/my_motions/ sim |
| ``` |
|
|
| Or use the default motions (configured in `deploy.sh`): |
|
|
| ```bash |
| ./deploy.sh sim |
| ``` |
|
|
| ### At Runtime |
|
|
| Once deployed, use the keyboard or gamepad to browse and play motions: |
|
|
| - **T**: Play current motion |
| - **N / P**: Next / Previous motion |
| - **R**: Restart from frame 0 |
|
|
| See the [Keyboard tutorial](../tutorials/keyboard.md) for the full control reference. |
|
|
| --- |
|
|
| ## Validation Rules |
|
|
| The C++ reader enforces the following during loading: |
|
|
| - **Frame count consistency**: All CSV files within a motion folder must have the same number of rows (excluding headers). Mismatches cause the motion to be skipped with an error. |
| - **Joint count consistency**: `joint_pos.csv` and `joint_vel.csv` must have the same number of columns. |
| - **Body count consistency**: `body_lin_vel.csv` and `body_ang_vel.csv` must have the same number of body columns as `body_pos.csv`. |
| - **At least one data source**: A motion must have at least some valid data (joint, body, or SMPL) to be loaded. |
| - **Metadata parsing**: The `metadata.txt` file must contain a `Body part indexes:` line followed by a bracketed list of integers for the motion to have correct body-part alignment. |
|
|
| If a motion fails validation, it is skipped and a warning is printed. The deployment continues with the remaining valid motions. |
|
|
| --- |
|
|
| ## Notes |
|
|
| - All data is at **50 Hz** (0.02 s per timestep), matching the control loop frequency. |
| - Joint ordering follows **IsaacLab convention** (not MuJoCo). The C++ stack handles the conversion internally when sending motor commands. |
| - Body quaternions use **(w, x, y, z)** ordering. |
| - The first body (column group 0) must correspond to the root/pelvis for heading computation and anchor orientation observations to work correctly. |
| - While the C++ reader can load motions without `body_quat.csv`, the control loop will fail during observation gathering if the policy observes `motion_anchor_orientation` (which most policies do). |
| - CSV files must have a **header row** as the first line — the C++ reader skips the first line of every CSV. |
| - Values are parsed as `double` precision internally. |
|
|