A generalized text-conditioned motion tracker (GTP) ONNX model that converts kinematic reference motion trajectories into PD-actuator control targets for the Unitree G1 humanoid (29 DoF, 33 bodies). Trained with the BeyondMimic-style RL pipeline from NVIDIA GEAR's ProtoMotions framework.
This is a tracker, not a text-to-motion generator. Pair it with a kinematic motion source like nvidia/Kimodo-G1-RP-v1 (text → motion diffusion) to get a full text → physics pipeline.
Compatible with the g1_29dof_rev_1_0 URDF/MJCF variant (torso waist_yaw+roll+pitch, wrists roll+pitch+yaw, no hand fingers — rubber hand end-effectors).
MJCF reference: mjcf/g1_holo_compat.xml
Body order (33 bodies)
0 pelvis
1 head
2-7 left leg: [hip_pitch, hip_roll, hip_yaw, knee, ankle_pitch, ankle_roll]_link
8-13 right leg (mirror)
14 waist_yaw_link
15 waist_roll_link
16 torso_link ← anchor
17-23 left arm: [shoulder_pitch, shoulder_roll, shoulder_yaw, elbow, wrist_roll, wrist_pitch, wrist_yaw]_link
24 left_rubber_hand
25-31 right arm (mirror)
32 right_rubber_hand
Quickstart (Python)
python
1import numpy as np, onnxruntime as ort, yaml
23sess = ort.InferenceSession("unified_pipeline.onnx", providers=["CPUExecutionProvider"])4cfg = yaml.safe_load(open("unified_pipeline.yaml"))56# Build observation from your robot state + kinematic reference motion7obs ={8"current_anchor_rot": np.zeros((1,4), dtype=np.float32),# torso quat9"current_dof_pos": np.zeros((1,29), dtype=np.float32),# rad10"current_dof_vel": np.zeros((1,29), dtype=np.float32),# rad/s11"current_root_local_ang_vel": np.zeros((1,3), dtype=np.float32),12"historical_processed_actions": np.zeros((1,1,29), dtype=np.float32),13"mimic_future_anchor_rot": np.zeros((1,4,4), dtype=np.float32),# 4 lookahead steps14"mimic_future_dof_pos": np.zeros((1,4,29), dtype=np.float32),15"mimic_future_dof_vel": np.zeros((1,4,29), dtype=np.float32),16}1718actions, joint_targets, stiff, damp = sess.run(19["actions","joint_pos_targets","stiffness_targets","damping_targets"],20 obs,21)22# → feed joint_targets/stiff/damp to G1 PD controller at 50 Hz
Text → Motion → Physics pipeline (with Kimodo)
python
1# 1) Generate kinematic reference from text (nvidia/Kimodo-G1-RP-v1)2from diffusers import DiffusionPipeline # or Kimodo's native API3motion = kimodo.generate("a person walking forward with confident strides")4# motion: (120, 3+29) root_pos + joint_pos at 30 Hz56# 2) Retime to 50 Hz (SLERP for rotations, linear for positions)7motion_50hz = slerp_retime(motion, from_fps=30, to_fps=50)# → (199, 32)89# 3) Roll out through GTP tracker in MuJoCo (see full script link below)10for step inrange(len(motion_50hz)):11 obs = build_obs(mj_data, motion_50hz, step, lookahead=[1,2,4,8])12 _, joint_targets, kp, kd = sess.run(None, obs)13 apply_pd(mj_data, joint_targets, kp, kd)14for _ inrange(20):# 20× physics substeps → 1 kHz15 mujoco.mj_step(mj_model, mj_data)
✅ strands_robots.policies.protomotions.ProtoMotionsPolicy — native ONNX GTP tracker consuming this model (#2287, merged 2026-08-16)
✅ Kimodo → Unitree G1 action-key bridge — pure key rename between the KIMODO_G1_JOINTS vocabulary and lerobot's unitree_sdk2 driver (#2279, merged 2026-08-16). RTPS/DDS transport is already available via use_rtps (pip-only cyclonedds, no ROS 2 install needed).
Text → physics with strands_robots, one call:
bash
1pip install"strands-robots[kimodo,protomotions,sim-mujoco]"2exportSTRANDS_TRUST_REMOTE_CODE=1# NVIDIA Open Model License gate3exportMUJOCO_GL=egl # headless render on Jetson/Thor
Real Unitree G1 hardware (same policy, same API — just swap the sim for a hardware bridge):
python
1from strands_robots.policies.kimodo.hardware import kimodo_action_to_lerobot_g1
23# Kimodo emits 29-DoF whole-body qpos targets in KIMODO_G1_JOINTS ordering.4# The bridge is a pure key rename to lerobot_unitree's driver vocabulary:5lerobot_action = kimodo_action_to_lerobot_g1(kimodo_action)
Wire that action dict into any DDS transport — use_rtps speaks the same RTPS wire that Unitree's unitree_sdk2 uses, so an advertise + publish cycle on rt/lowcmd reaches the real robot without a ROS 2 install. Kill-switch, torque clamps, and damp-mode watchdog live at the driver layer.
Chained multi-prompt long-horizon runs are stable end-to-end after PR #2353 (each new prompt eases off the last commanded pose so no pop between segments) and PR #2284 (per-episode seeds actually reach the sampler). See the 10k-episode dataset generated from this stack.
Citation
If you use this tracker, please cite ProtoMotions + BeyondMimic:
bibtex
1@article{beyondmimic,
2 title={BeyondMimic: From Motion Tracking to Versatile Humanoid Control via Guided Diffusion},
3 year={2024},
4 eprint={2408.07295},
5 archivePrefix={arXiv},
6}
7@misc{protomotions,
8 author={NVIDIA GEAR},
9 title={ProtoMotions},
10 howpublished={\url{https://github.com/NVlabs/ProtoMotions}},
11}
License
Apache-2.0 (matches ProtoMotions upstream). Robot assets (Unitree G1 MJCF) subject to Unitree's own license.