H2 Whole-Body Motion Tracking Policy
A whole-body control policy for the Unitree H2 (31 DoF, 32 bodies), trained with NVIDIA SONIC / GR00T-WholeBodyControl on the Bones-SEED motion corpus retargeted to H2.
SONIC's released config targets the Unitree G1. This is a port to H2, which required correcting several G1-specific assumptions that resolve on H2 with different meaning (body naming, a reversed ankle chain, wrist DoF indices, actuator limits). Trained from scratch β the released G1 checkpoint cannot warm-start a different embodiment.
Simulation only. Never validated on hardware or in a second simulator.
Which file do I want?
| Goal | File |
|---|---|
| VR teleoperation (headset + 2 controllers) | onnx/model_step_100000_teleop.onnx |
| Replay motion clips / retargeted data | onnx/model_step_100000_g1.onnx |
| Drive from SMPL human poses | onnx/model_step_100000_smpl.onnx |
| Continue training, or evaluate in Isaac Lab | model_step_100000.pt |
| Split inference (encode off-robot, decode on-robot) | onnx/*_encoder.onnx + onnx/*_decoder.onnx |
On the
g1in the filename:g1is the name of the encoder, not the robot. SONIC names its three encodersg1/teleop/smpl, whereg1means "robot-native full-body motion tracking" β so called because the framework was built around the Unitree G1 first.model_step_100000_g1.onnxis an H2 policy: 31 outputs, H2 body names, H2 actuator config. The decoder is likewise namedg1_dynfor the same historical reason. Nothing in these files targets a G1.
Always ship onnx/model_config.yaml alongside any ONNX file. It carries the
observation layout; the model is not usable without it.
The action contract β read before deploying
The network outputs 31 dimensionless numbers, not joint angles. Converting them to something the robot can execute is your side of the contract:
target_angle[j] = default_angle[j] + action[j] * action_scale[j]
torque[j] = stiffness[j] * (target_angle[j] - actual_angle[j])
- damping[j] * actual_velocity[j]
- policy runs at 50 Hz, PD control at 200 Hz
action_scale[j] = 0.25 * effort_limit[j] / stiffness[j]- gains derive from rotor inertia:
stiffness = armature * w^2,damping = 2 * zeta * armature * w, withw = 2*pi*10 Hz,zeta = 2.0
These constants live in gear_sonic/envs/manager_env/robots/h2.py, not in
the ONNX graph. Using the wrong action scale will produce behaviour unrelated to
the reported numbers.
H2's rotor inertia is not published anywhere we could find, so the armature values are inherited from G1. We measured the sensitivity: perturbing simulated inertia by 0.5x / 2x / 3x while holding the trained gains fixed changed success rate by under 2 points (paired test over 500 motions, p = 0.16). It is not a significant source of error at that scale, but it is a known unknown.
Three input modes, one policy
One network with three encoders feeding a shared token, so the same weights serve dense and sparse inputs:
- g1 (full body) β all 14 tracked body positions plus future frames (encoder name is historical; this is the H2 full-body path)
- teleop β 3 points only (2 wrists + head) plus lower-body joint angles
- smpl β SMPL human skeleton joints
Evaluation
500 held-out motions, Isaac Lab, encoder mode pinned per run. Termination at 0.25 m root or end-effector error.
| mode | success | mpjpe_l | mpjpe_g | accel |
|---|---|---|---|---|
| full body | 93.8% | 33.3 mm | 246.0 mm | 1.84 |
| teleop | 92.0% | 36.2 mm | 241.5 mm | 2.09 |
| smpl | 91.8% | 34.3 mm | 305.9 mm | 2.06 |
mpjpe_l is root-relative per-joint error, mpjpe_g global.
Teleop landing within 1.8 points of full body β given only three points plus leg angles β is the result we find most interesting here.
Against NVIDIA's published targets
SONIC's own docs give G1 convergence targets "after 100K iterations", the same unit as this checkpoint:
| success | mpjpe_l | mpjpe_g | |
|---|---|---|---|
| NVIDIA target (G1) | > 97% | < 30 mm | < 200 mm |
| this policy (H2, full body) | 93.8% | 33.3 mm | 246.0 mm |
Under target on all three. H2 is a heavier, higher-DoF robot and the corpus differs, so some gap is expected β but the figure above is pinned full-body, the most favourable reading, and it is still short. Reported rather than omitted because a user deciding whether to build on this should know.
Known failure concentration, measured over the 500-motion set:
- reference root below 0.9 m (crouching): ~21% termination vs 6.6% upright
- peak root speed 1.0β1.5 m/s: 21.7% termination, the worst band
- reference travel distance does not predict failure
Training
| iterations | 100,000 |
| envs | 4096 |
| wall time | 160 h on 8 GPUs |
| episodes / timesteps | 3.28e9 / 7.86e10 |
| corpus | Bones-SEED, 142,220 motions retargeted to H2 |
| algorithm | ppo_im_phc |
Limitations
- Simulation only. Isaac Sim / Isaac Lab. No MuJoCo cross-check, no hardware.
- Under NVIDIA's reference bar on every eval metric.
- ~6% of motions fail, concentrated in crouching and mid-speed locomotion.
- Global placement lags pose accuracy β
mpjpe_gis ~7xmpjpe_l. The robot performs the right motion more reliably than it arrives at the right place. - Armature inherited from G1 (see the note above).
Attribution
- NVIDIA SONIC / GR00T-WholeBodyControl β training framework, algorithm, universal-token architecture, and the H2 asset files. https://github.com/NVlabs/GR00T-WholeBodyControl
- SOMA Retargeter β motion retargeting to H2. https://github.com/NVIDIA/soma-retargeter
- Bones-SEED β source motion corpus. Its terms apply to anything derived from this policy.
- Actuator gain formulation traces to BeyondMimic via SONIC's G1 config.
Produced at the USC PSI Lab.