File size: 18,978 Bytes
700dd75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
# Training on New Embodiments

SONIC's training pipeline is designed around the Unitree G1 (29 DOF) but can be
extended to other humanoid robots. This guide walks through every file you need
to touch, using the Unitree H2 (31 DOF) as a concrete example.

## What You Need

To train SONIC on a new robot, you need:

1. **Robot model files** β€” URDF or USD (for Isaac Lab) and MJCF/XML (for the motion library)
2. **Retargeted motion data** β€” Human motions retargeted to your robot's skeleton (PKL format)
3. **Robot configuration** β€” Joint/body definitions, actuator parameters, action scales
4. **Experiment config** β€” Hydra YAML connecting everything together

## Files You Need to Add or Modify

Here is every file that needs attention, in the order you should work through them:

| File | Action | Purpose |
|------|--------|---------|
| `gear_sonic/data/assets/robot_description/urdf/<robot>/` | **Add** | URDF + mesh files for Isaac Lab simulation |
| `gear_sonic/data/assets/robot_description/mjcf/<robot>.xml` | **Add** | MuJoCo XML for motion library forward kinematics |
| `gear_sonic/envs/manager_env/robots/<robot>.py` | **Add** | Robot config: joints, actuators, mappings, action scales |
| `gear_sonic/envs/manager_env/robots/__init__.py` | **Modify** | Import your new robot module |
| `gear_sonic/envs/manager_env/modular_tracking_env_cfg.py` | **Modify** | Add robot to `robot_mapping` dict (~line 998) |
| `gear_sonic/trl/utils/order_converter.py` | **Modify** | Add converter class for joint/body reordering |
| `gear_sonic/config/exp/manager/universal_token/all_modes/sonic_<robot>.yaml` | **Add** | Experiment config |
| Config YAMLs (terminations, rewards, commands) | **Check** | Body names must exist on your robot |

## Step 1: Robot Model Files

Place your URDF and meshes under `gear_sonic/data/assets/robot_description/`:

```
gear_sonic/data/assets/robot_description/
β”œβ”€β”€ urdf/h2/
β”‚   β”œβ”€β”€ h2.urdf
β”‚   └── meshes/          # STL/OBJ mesh files
└── mjcf/
    └── h2.xml           # MuJoCo XML
```

The **URDF** is loaded by Isaac Lab for physics simulation. The **MJCF** is used
by the motion library to compute forward kinematics on reference motion data.
Both must represent the same robot with consistent joint names and tree structure.

Make sure your URDF mesh paths are correct (relative paths like `meshes/pelvis.stl`
work best). If your URDF uses `package://` paths, update them to match the
directory layout.

## Step 2: Robot Configuration

Create `gear_sonic/envs/manager_env/robots/<robot>.py`. This is the most
important file β€” it defines how your robot integrates with the training pipeline.

### Joint and body ordering

Isaac Lab and MuJoCo traverse the kinematic tree in different orders. You must
define bidirectional index mappings. Get these by loading your URDF in Isaac Lab
and your MJCF in MuJoCo, printing the joint/body lists, and computing the
reorder indices.

```python
# All bodies in IsaacLab traversal order (including root "pelvis")
H2_ISAACLAB_JOINTS = [
    "pelvis",
    "left_hip_pitch_link",
    "right_hip_pitch_link",
    # ... all 32 bodies for H2
]

# Index arrays: position i in the output = position mapping[i] in the input
H2_ISAACLAB_TO_MUJOCO_DOF = [...]   # len = num_dof (31 for H2)
H2_MUJOCO_TO_ISAACLAB_DOF = [...]
H2_ISAACLAB_TO_MUJOCO_BODY = [...]  # len = num_bodies (32 for H2)
H2_MUJOCO_TO_ISAACLAB_BODY = [...]

H2_ISAACLAB_TO_MUJOCO_MAPPING = {
    "isaaclab_joints": H2_ISAACLAB_JOINTS,
    "isaaclab_to_mujoco_dof": H2_ISAACLAB_TO_MUJOCO_DOF,
    "mujoco_to_isaaclab_dof": H2_MUJOCO_TO_ISAACLAB_DOF,
    "isaaclab_to_mujoco_body": H2_ISAACLAB_TO_MUJOCO_BODY,
    "mujoco_to_isaaclab_body": H2_MUJOCO_TO_ISAACLAB_BODY,
}
```

**Getting the mappings right is critical.** If they are wrong, the policy will
receive scrambled observations and produce scrambled actions. Verify by loading a
known pose in both simulators and checking that joint values match after reordering.

### Actuator parameters (KP/KD tuning)

The actuator stiffness (KP) and damping (KD) are critical for sim-to-real
transfer and training stability. SONIC uses implicit PD actuators in Isaac Lab.

```python
# Derive from motor specs β€” these need tuning for your robot
NATURAL_FREQ = 10 * 2.0 * 3.1415926535  # 10Hz natural frequency
DAMPING_RATIO = 2.0                      # Overdamped for stability

# Per-motor stiffness: KP = armature * omega^2
STIFFNESS_5020 = ARMATURE_5020 * NATURAL_FREQ**2
# Per-motor damping: KD = 2 * zeta * armature * omega
DAMPING_5020 = 2.0 * DAMPING_RATIO * ARMATURE_5020 * NATURAL_FREQ
```

**Tuning guidance:**

- Start with the real motor's **armature** (rotor inertia) from the datasheet.
- The **natural frequency** controls responsiveness. 10 Hz is a good starting point
  for humanoids. Increase for stiffer/faster tracking, decrease for compliance.
- The **damping ratio** should be >= 1.0 (critically damped or overdamped) to avoid
  oscillation. 2.0 works well for SONIC.
- **Different joint groups need different gains.** Hip/knee motors are much stronger
  than wrist motors. Group joints by motor type (see G1/H2 configs for examples).
- If training is unstable (robot explodes or falls immediately), your KP/KD values
  are likely wrong. Try reducing KP or increasing KD.
- The **effort limits** (max torque) per joint should match the real motor specs.

### Articulation config

```python
H2_CFG = ArticulationCfg(
    spawn=sim_utils.UrdfFileCfg(
        asset_path="gear_sonic/data/assets/robot_description/urdf/h2/h2.urdf",
        fix_base=False,
        replace_cylinders_with_capsules=True,
        activate_contact_sensors=True,
        ...
    ),
    init_state=ArticulationCfg.InitialStateCfg(
        pos=(0.0, 0.0, 1.04),       # Standing height β€” must match your robot
        joint_pos={
            ".*_knee_joint": -0.363,  # Slight knee bend for stability
            # ... default standing pose for all joints
        },
    ),
    actuators={
        "legs": ImplicitActuatorCfg(
            joint_names_expr=[".*_hip_.*", ".*_knee_.*"],
            effort_limit={...},       # Max torque per joint (Nm)
            stiffness={...},          # KP values
            damping={...},            # KD values
            armature={...},           # Rotor inertia
        ),
        # ... one group per motor type (arms, waist, feet, etc.)
    },
)
```

**Important init_state notes:**

- `pos` z-value is the spawn height. Set this so the robot starts standing with
  feet slightly above ground. Too low = feet clip through ground on first frame.
- `joint_pos` should be a stable standing pose. Get this from your robot's real
  default calibration pose or a MuJoCo keyframe.

### Action scale

Action scale maps normalized policy outputs to joint position targets. Compute
from effort limit and stiffness:

```python
H2_ACTION_SCALE = {}
for joint_name in joint_names:
    H2_ACTION_SCALE[joint_name] = effort_limit[joint_name] / stiffness[joint_name]
```

Larger action scale = larger joint movements per policy output. If the robot
moves too aggressively, reduce the action scale.

### Register in __init__.py

Add your module to `gear_sonic/envs/manager_env/robots/__init__.py` so it's
importable.

### Register in modular_tracking_env_cfg.py

Add your robot to the `robot_mapping` dict (around line 998):

```python
from gear_sonic.envs.manager_env.robots import g1, h2  # Add your import

robot_mapping = {
    "g1_model_12_dex": {...},
    "h2": {
        "robot_cfg": h2.H2_CFG,
        "action_scale": h2.H2_ACTION_SCALE,
        "isaaclab_to_mujoco_mapping": h2.H2_ISAACLAB_TO_MUJOCO_MAPPING,
    },
}
```

The string key (e.g., `"h2"`) is what you'll use as `robot.type` in the
experiment config.

## Step 3: Order Converter

In `gear_sonic/trl/utils/order_converter.py`, add a converter class. This is used
by the evaluation and export pipeline:

```python
class H2Converter(IsaacLabMuJoCoConverter):
    def __init__(self):
        from gear_sonic.envs.manager_env.robots.h2 import (
            H2_ISAACLAB_JOINTS, H2_ISAACLAB_TO_MUJOCO_BODY,
            H2_ISAACLAB_TO_MUJOCO_DOF, H2_MUJOCO_TO_ISAACLAB_BODY,
            H2_MUJOCO_TO_ISAACLAB_DOF,
        )
        self.JOINT_NAMES = H2_ISAACLAB_JOINTS
        self.DOF_MAPPINGS = {
            ("isaaclab", "mujoco"): H2_ISAACLAB_TO_MUJOCO_DOF,
            ("mujoco", "isaaclab"): H2_MUJOCO_TO_ISAACLAB_DOF,
        }
        self.BODY_MAPPINGS = {
            ("isaaclab", "mujoco"): H2_ISAACLAB_TO_MUJOCO_BODY,
            ("mujoco", "isaaclab"): H2_MUJOCO_TO_ISAACLAB_BODY,
        }

    # Bodies used for VR tracking and foot contact β€” update for your robot
    VR_3POINTS_BODY_NAMES = ["torso_link", "left_wrist_pitch_link", "right_wrist_pitch_link"]
    FOOT_BODY_NAMES = ["left_ankle_roll_link", "right_ankle_roll_link"]
```

Use lazy imports (inside `__init__`) to avoid circular dependencies.

## Step 4: Body Name Compatibility

This is a common source of errors. The training configs reference specific body
names that must exist on your robot. Check **all** of these:

### Command config (`config/manager_env/commands/terms/motion.yaml`)

```yaml
anchor_body: "pelvis"                           # Root body
vr_3point_body: ["left_wrist_yaw_link", "right_wrist_yaw_link", "torso_link"]
reward_point_body: ["pelvis", "left_wrist_yaw_link", "right_wrist_yaw_link",
                    "left_ankle_roll_link", "right_ankle_roll_link"]
body_names: [                                   # 14 tracked bodies
    "pelvis", "left_hip_roll_link", "left_knee_link", "left_ankle_roll_link",
    "right_hip_roll_link", "right_knee_link", "right_ankle_roll_link",
    "torso_link", "left_shoulder_roll_link", "left_elbow_link",
    "left_wrist_yaw_link", "right_shoulder_roll_link", "right_elbow_link",
    "right_wrist_yaw_link",
]
```

### Termination configs (`config/manager_env/terminations/terms/`)

- `ee_body_pos_adaptive.yaml`: references `left_ankle_roll_link`, `right_ankle_roll_link`,
  `left_wrist_yaw_link`, `right_wrist_yaw_link`
- `foot_pos_xyz.yaml`: references `left_ankle_roll_link`, `right_ankle_roll_link`

### Reward configs (`config/manager_env/rewards/terms/`)

- `undesired_contacts.yaml`: regex pattern excluding specific bodies from contact
  penalty β€” references ankle and wrist link names
- `anti_shake_ang_vel.yaml`: references `left_wrist_yaw_link`, `right_wrist_yaw_link`,
  `head_link`

### What to do if names differ

If your robot uses different names for equivalent bodies (e.g., H2 has
`head_pitch_link` instead of G1's `head_link`), you have two options:

1. **Override in experiment config** (recommended): Add overrides in your
   `sonic_<robot>.yaml` for the specific fields that differ.

2. **Create robot-specific config variants**: Copy the affected term YAML files
   and create robot-specific versions (e.g., `anti_shake_ang_vel_h2.yaml`).

For H2, most G1 body names happen to exist (both are Unitree humanoids), but
`head_link` does not β€” H2 has `head_yaw_link` instead. Override in the
experiment config:

```yaml
manager_env:
  rewards:
    anti_shake_ang_vel:
      params:
        body_names: ["left_wrist_yaw_link", "right_wrist_yaw_link", "head_yaw_link"]
```

**Tip:** Run training with `num_envs=1` first. If a body name doesn't exist, Isaac
Lab will raise a clear error telling you which name failed. Fix it and retry.

## Step 5: Motion Data

SONIC expects retargeted motion data as PKL files (joblib format). Each file
contains a dict keyed by motion name:

```python
{
    "motion_name": {
        "root_trans_offset": np.ndarray,  # (T, 3) β€” root translation
        "pose_aa": np.ndarray,            # (T, num_bodies, 3) β€” axis-angle per body
        "dof": np.ndarray,                # (T, num_dof) β€” joint positions in MuJoCo order
        "root_rot": np.ndarray,           # (T, 4) β€” root quaternion (wxyz)
        "smpl_joints": np.ndarray,        # (T, 24, 3) β€” SMPL joint positions (optional)
        "fps": int,                       # Frame rate (typically 30)
    }
}
```

**Important data format notes:**

- `num_bodies` and `num_dof` must match your robot (e.g., 32 bodies / 31 DOF for H2).
- `dof` values must be in **MuJoCo joint order**, not IsaacLab order.
- `pose_aa` must be in **MuJoCo body order**.
- Mirrored variants (filename ending in `_M.pkl`) double your effective dataset
  size and improve symmetry.
- The `smpl_joints` field is used by the SMPL encoder. Set it to zeros if you
  don't have SMPL data.

The motion library loads PKL files **recursively** from a directory:

```
data/h2_motions/
β”œβ”€β”€ session_01/
β”‚   β”œβ”€β”€ walk_forward_001.pkl
β”‚   └── walk_forward_001_M.pkl
└── session_02/
    └── ...
```

### Source motion data

The recommended source is [Bones-SEED](https://huggingface.co/datasets/bones-studio/seed)
β€” a large-scale human motion dataset (142K+ motions, ~288 hours) that provides:

- **Raw BVH files** β€” full-body human motion capture
- **G1 retargeted CSVs** β€” already retargeted to the Unitree G1 (29 DOF)

For a new robot, you need to **retarget** the raw human motions to your robot's
skeleton. This is the most labor-intensive step.

### Retargeting options

1. **[SOMA Retargeter](https://github.com/NVIDIA/soma-retargeter)** (recommended) β€”
   NVIDIA's BVH-to-humanoid motion retargeting library built with Newton and
   NVIDIA Warp. Supports any humanoid robot via JSON configuration. Includes a
   viewer for inspecting source and retargeted motions side by side. This is
   the same tool used to produce the Bones-SEED G1 retargeted data.

2. **[GMR](https://github.com/YanjieZe/GMR)** (General Motion Retargeting) β€”
   retargets human motions to arbitrary humanoid robots in real time on CPU.
   Supports any URDF. A lighter-weight alternative.

3. **This repo's data processing** (`gear_sonic/data_process/`) β€” converts
   retargeted CSVs/BVHs into the PKL format SONIC expects. Use this as the
   final step after retargeting:

   ```bash
   # Convert retargeted CSVs to motion library PKLs
   python gear_sonic/data_process/convert_soma_csv_to_motion_lib.py \
       --input /path/to/retargeted_csvs/ \
       --output data/my_robot_motions/robot \
       --fps 30 --fps_source 120 --individual --num_workers 16

   # Filter out motions that are physically impossible for your robot
   python gear_sonic/data_process/filter_and_copy_bones_data.py \
       --source data/my_robot_motions/robot \
       --dest data/my_robot_motions/robot_filtered
   ```

### SMPL data (optional but recommended)

The SMPL encoder gives the policy an additional human-skeleton input signal.
You need SMPL retargeted data matching the same motion keys as your robot data.

- For Bones-SEED motions, pre-computed SMPL data is available on
  [Hugging Face](https://huggingface.co/nvidia/GEAR-SONIC):
  `python download_from_hf.py --training`
- If you use custom motions, extract SMPL joints from the BVH files:

  ```bash
  python gear_sonic/data_process/extract_soma_joints_from_bvh.py \
      --input /path/to/bvh_files/ \
      --output data/my_robot_motions/soma \
      --fps 30 --num_workers 16
  ```

- If you don't have SMPL data, set `smpl_motion_file: dummy` in the config.
  The training pipeline will generate minimal placeholder SMPL data from the
  robot motions. This works but produces weaker SMPL encoder performance.

## Step 6: Experiment Config

Create `gear_sonic/config/exp/manager/universal_token/all_modes/sonic_<robot>.yaml`.
Start by copying `sonic_release.yaml` and modify:

```yaml
# @package _global_
defaults:
  - /algo: ppo_im_phc
  - /manager_env: base_env
  # ... same defaults as sonic_release.yaml

project_name: TRL_H2_Track                     # Change project name

manager_env:
  config:
    robot:
      type: h2                                  # Must match robot_mapping key
  commands:
    motion:
      motion_lib_cfg:
        motion_file: null                       # Provide on command line
        asset:
          assetFileName: "h2.xml"               # Your MJCF filename
```

**Fields to review and potentially override:**

- `robot.type` β€” must match the key in `robot_mapping`
- `motion_lib_cfg.asset.assetFileName` β€” your MJCF file
- `reward_point_body` / `reward_point_body_offset` β€” key bodies for reward computation
- `vr_3point_body` / `vr_3point_body_offset` β€” if doing VR teleoperation
- `upper_body_augment_prefixes` β€” remove if your motion data uses different naming
- Body names in reward/termination overrides β€” see Step 4

## Step 7: Train

```bash
python gear_sonic/train_agent_trl.py \
    +exp=manager/universal_token/all_modes/sonic_h2 \
    num_envs=16 headless=False \
    ++manager_env.commands.motion.motion_lib_cfg.motion_file=<path/to/h2_motions>
```

Start with `num_envs=16 headless=False` to visually verify the robot loads and
motions play correctly. Then scale up to `num_envs=4096 headless=True` for
full training.

## Example: H2 (included)

The codebase includes full H2 support as a reference:

| Component | File |
|-----------|------|
| Robot config | `gear_sonic/envs/manager_env/robots/h2.py` |
| URDF + meshes | `gear_sonic/data/assets/robot_description/urdf/h2/` |
| MJCF | `gear_sonic/data/assets/robot_description/mjcf/h2.xml` |
| Experiment config | `gear_sonic/config/exp/manager/universal_token/all_modes/sonic_h2.yaml` |
| Order converter | `gear_sonic/trl/utils/order_converter.py` (`H2Converter`) |
| Robot mapping | `gear_sonic/envs/manager_env/modular_tracking_env_cfg.py` |

## Checklist

When adding a new robot, verify each of these:

- [ ] URDF + meshes in `gear_sonic/data/assets/robot_description/urdf/<robot>/`
- [ ] MJCF in `gear_sonic/data/assets/robot_description/mjcf/<robot>.xml`
- [ ] Robot config in `gear_sonic/envs/manager_env/robots/<robot>.py`:
  - [ ] Joint/body name lists
  - [ ] IsaacLab ↔ MuJoCo index mappings (verified correct!)
  - [ ] `ArticulationCfg` with tuned KP/KD/effort for each motor group
  - [ ] Correct init_state (standing height + default joint angles)
  - [ ] Action scale dict
- [ ] Robot imported in `robots/__init__.py`
- [ ] Robot added to `robot_mapping` in `modular_tracking_env_cfg.py`
- [ ] Order converter class in `order_converter.py`
- [ ] Experiment config YAML with correct `robot.type` and `assetFileName`
- [ ] All body names in config YAMLs exist on your robot (check with `num_envs=1`)
- [ ] Human motion source data (e.g., [Bones-SEED](https://huggingface.co/datasets/bones-studio/seed) BVH/CSV files)
- [ ] Motions retargeted to your robot's skeleton (e.g., via [SOMA Retargeter](https://github.com/NVIDIA/soma-retargeter))
- [ ] Retargeted data converted to PKL format (MuJoCo joint/body order)
- [ ] Motions filtered for physical feasibility (`filter_and_copy_bones_data.py`)
- [ ] Mirrored motion variants (`_M.pkl`) for symmetric training
- [ ] SMPL data matching the same motion keys (or `smpl_motion_file: dummy`)