Software
GMR (General Motion Retargeting)
From github.com/YanjieZe/GMR (opens in a new tab)
- Humanoids and robot learning
- Tool
- MIT
GMR is an open-source Python library that retargets human motion data onto many humanoid robots in real time, for whole-body teleoperation and training motion-tracking policies.
Facts
- GitHub stars
- 2,818read on 6 Nov 2026
- Last update
- 22 Oct 2026Newest commit on master
- Maintenance
- Activea commit in the last 90 days
- Difficulty
- Advanced
- Hardware it needs, as the project states it
- Tested on Ubuntu 22.04/20.04 with Python 3.10 in a conda environment. Speed benchmark: an AMD Ryzen Threadripper 7960X reaches 60~70 FPS and a 13th Gen Intel Core i9-13900K reaches 35~45 FPS. SMPL-X body model files are required for SMPL-X data.
Checked on GitHub on 6 Nov 2026.
Licenses
- Code
- MIT (opens in a new tab)
Read by Robotheca from LICENSE (opens in a new tab) (MIT) at commit 25f56db (opens in a new tab), on 23 Oct 2026.
- Commercial use
- Yes
Overview
What it does: Robotheca's summary
- Retargets human motion to humanoid robot joint positions in real time, suited to whole-body teleoperation.
- Tuned to give good performance for reinforcement-learning motion tracking policies.
- Supports numerous humanoid robots, switchable by changing one argument.
- Accepts several human motion formats: SMPL-X (AMASS, OMOMO), BVH (LAFAN1, Nokov, Xsens), FBX from OptiTrack, PICO streaming, and GVHMR video pose extraction.
- Provides scripts for single-motion and batch retargeting, live streaming, and visualizing saved robot motion in MuJoCo.
- Can convert retargeted pickle files to CSV for BeyondMimic.
Written by Robotheca from the project's README (opens in a new tab) at commit bb1bbe4, not by its creator.
The README
The creator's own README, copied as written: README.md by @YanjieZe, at commit 25f56db, under MIT (opens in a new tab). The original on GitHub (opens in a new tab).
Read the README here
GMR: General Motion Retargeting
(opens in a new tab) (opens in a new tab) (opens in a new tab) (opens in a new tab) (opens in a new tab) (opens in a new tab) (opens in a new tab)


Key features of GMR:
- Real-time high-quality retargeting, unlock the potential of real-time whole-body teleoperation, i.e., TWIST (opens in a new tab).
- Carefully tuned for good performance of RL tracking policies.
- Support multiple humanoid robots and multiple human motion data formats (See our table below).
[!NOTE] If you want this repo to support a new robot or a new human motion data format, send the robot files (
.xml,.urdf, and meshes) / human motion data to Yanjie Ze (opens in a new tab) or create an issue, we will support it as soon as possible. And please make sure the robot files you sent can be open-sourced in this repo.
This repo is licensed under the MIT License (opens in a new tab).
News & Updates
- 2026-01-21: GMR now supports Xsens (opens in a new tab) BVH offline data.
- 2026-01-12: GMR now supports Fourier GR3 (opens in a new tab), the 17th humanoid robot in the repo.
- 2025-12-02: GMR now supports TWIST2 (opens in a new tab), which utilizes XRoboToolkit SDK (opens in a new tab).
- 2025-11-17: To join our community for discussions, you can add my WeChat contact QR Code (opens in a new tab) with info like "[GMR] [Your Name] [Your Affiliation]".
- 2025-11-08: [MimicKit] from Jason Peng now supports GMR format. Check here (opens in a new tab).
- 2025-10-15: Now supporting PAL Robotics' Talos (opens in a new tab), the 15th humanoid robot.
- 2025-10-14: GMR now supports Nokov (opens in a new tab) BVH data.
- 2025-10-14: Add a doc on ik config. See DOC.md (opens in a new tab)
- 2025-10-09: Check TWIST (opens in a new tab) open-sourced code for RL motion tracking.
- 2025-10-02: Tech report for GMR is now on arXiv (opens in a new tab).
- 2025-10-01: GMR now supports converting GMR pickle files to CSV (for beyondmimic), check
scripts/batch_gmr_pkl_to_csv.py. - 2025-09-25: An introduction on GMR is available on Bilibili (opens in a new tab).
- 2025-09-16: GMR now supports to use GVHMR (opens in a new tab) for extracting human pose from monocular video and retargeting to robot.
- 2025-09-12: GMR now supports Tienkung (opens in a new tab), the 14th humanoid robot in the repo.
- 2025-08-30: GMR now supports Unitree H1 2 (opens in a new tab) and PND Adam Lite (opens in a new tab), the 12th and 13th humanoid robots in the repo.
- 2025-08-28: GMR now supports Booster T1 (opens in a new tab) for both 23dof and 29dof.
- 2025-08-28: GMR now supports using exported offline FBX motion data from OptiTrack (opens in a new tab).
- 2025-08-27: GMR now supports Berkeley Humanoid Lite (opens in a new tab), the 11th humanoid robot in the repo.
- 2025-08-24: GMR now supports Unitree H1 (opens in a new tab), the 10th humanoid robot in the repo.
- 2025-08-24: GMR now supports velocity limits for the robot motors,
use_velocity_limit=Trueby default inGeneralMotionRetargetingclass (and we use 3*pi as the velocity limit by default); we also add printing of robot DoF/Body/Motor names and their IDs by default, and you can access them viarobot_dof_names,robot_body_names, androbot_motor_namesattributes. - 2025-08-10: GMR now supports Booster K1 (opens in a new tab), the 9th robot in the repo.
- 2025-08-09: GMR now supports Unitree G1 with Dex31 hands.
- 2025-08-07: GMR now supports Galexea R1 Pro (opens in a new tab) (this is a wheeled humanoid robot!) and KUAVO (opens in a new tab), the 7th and 8th humanoid robots in the repo.
- 2025-08-06: GMR now supports HighTorque Hi (opens in a new tab), the 6th humanoid robot in the repo.
- 2025-08-04: Initial release of GMR. Check our twitter post (opens in a new tab).
Demos
Retargeting LAFAN1 dancing motion to 5 robots.
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center" width="20%">
<b>Demo 2</b>
Galexea R1 Pro robot (view 1).
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center" width="20%">
<b>Demo 3</b>
Galexea R1 Pro robot (view 2).
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center" width="20%">
<b>Demo 4</b>
Switching robots by changing one argument.
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center" width="20%">
<b>Demo 5</b>
HighTorque robot doing a twist dance.
Watch this video on GitHub (opens in a new tab)
</td>
Kuavo robot picking up a box.
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center">
<b>Demo 7</b>
Unitree H1 robot doing a ChaCha dance.
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center">
<b>Demo 8</b>
Booster T1 robot jumping (view 1).
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center">
<b>Demo 9</b>
Booster T1 robot jumping (view 2).
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center">
<b>Demo 10</b>
Unitree H1-2 robot jumping.
Watch this video on GitHub (opens in a new tab)
</td>
PND Adam Lite robot.
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center">
<b>Demo 12</b>
Tienkung robot walking.
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center">
<b>Demo 13</b>
Extracting human pose (GVHMR + GMR).
▶ Watch on Bilibili (opens in a new tab) Demo 14
PAL Robotics’ Talos robot fighting.
Watch this video on GitHub (opens in a new tab)
</td>
<td align="center">
<b>Demo 15</b>
(Optional placeholder if you add a new one later!)
<i>Coming soon...</i>
</td>
Supported Robots and Data Formats
| Assigned ID | Robot/Data Format | Robot DoF | SMPLX (AMASS (opens in a new tab), OMOMO (opens in a new tab)) | BVH LAFAN1 (opens in a new tab) | FBX (OptiTrack (opens in a new tab)) | BVH Nokov (opens in a new tab) | PICO (XRoboToolkit (opens in a new tab)) | More formats coming soon |
|---|---|---|---|---|---|---|---|---|
| 0 | Unitree G1 unitree_g1 | Leg (2*6) + Waist (3) + Arm (2*7) = 29 | ✅ | ✅ | ✅ | ✅ | ✅ | |
| 1 | Unitree G1 with Hands unitree_g1_with_hands | Leg (2*6) + Waist (3) + Arm (2*7) + Hand (2*7) = 43 | ✅ | ✅ | ✅ | TBD | TBD | |
| 2 | Unitree H1 unitree_h1 | Leg (2*5) + Waist (1) + Arm (2*4) = 19 | ✅ | TBD | TBD | TBD | TBD | |
| 3 | Unitree H1 2 unitree_h1_2 | Leg (2*6) + Waist (1) + Arm (2*7) = 27 | ✅ | TBD | TBD | TBD | TBD | |
| 4 | Booster T1 booster_t1 | TBD | ✅ | TBD | TBD | TBD | ||
| 5 | Booster T1 29dof booster_t1_29dof | TBD | ✅ | ✅ | TBD | TBD | ||
| 6 | Booster K1 booster_k1 | Neck (2) + Arm (2*4) + Leg (2*6) = 22 | ✅ | TBD | TBD | TBD | ||
| 7 | Stanford ToddlerBot stanford_toddy | TBD | ✅ | ✅ | TBD | TBD | ||
| 8 | Fourier N1 fourier_n1 | TBD | ✅ | ✅ | TBD | TBD | ||
| 9 | ENGINEAI PM01 engineai_pm01 | TBD | ✅ | ✅ | TBD | TBD | ||
| 10 | HighTorque Hi hightorque_hi | Head (2) + Arm (2*5) + Waist (1) + Leg (2*6) = 25 | ✅ | TBD | TBD | TBD | ||
| 11 | Galaxea R1 Pro galaxea_r1pro (this is a wheeled robot!) | Base (6) + Torso (4) + Arm (2*7) = 24 | ✅ | TBD | TBD | TBD | ||
| 12 | Kuavo kuavo_s45 | Head (2) + Arm (2*7) + Leg (2*6) = 28 | ✅ | TBD | TBD | TBD | ||
| 13 | Berkeley Humanoid Lite berkeley_humanoid_lite (need further tuning) | Leg (2*6) + Arm (2*5) = 22 | ✅ | TBD | TBD | TBD | ||
| 14 | PND Adam Lite pnd_adam_lite | Leg (2*6) + Waist (3) + Arm (2*5) = 25 | ✅ | TBD | TBD | TBD | ||
| 15 | Tienkung tienkung | Leg (2*6) + Arm (2*4) = 20 | ✅ | TBD | TBD | TBD | ||
| 16 | PAL Robotics' Talos pal_talos | Head (2) + Arm (2*7) + Waist (2) + Leg (2*6) = 30 | ✅ | TBD | TBD | TBD | ||
| 17 | Fourier GR3 fourier_gr3 | Head (2) + Arm (2*7) + Waist (3) + Leg (2*6) = 31 | ✅ | TBD | TBD | TBD | ||
| 18 | Unitree H2 unitree_h2 | Leg (2*6) + Waist (3) + Arm (2*7) = 29 (head fixed in the MJCF) | TBD | ✅ | TBD | TBD | TBD | |
| More robots coming soon ! | ||||||||
| 19 | AgiBot A2 agibot_a2 | TBD | TBD | TBD | TBD | TBD | ||
| 20 | OpenLoong openloong | TBD | TBD | TBD | TBD | TBD |
Installation
[!NOTE] The code is tested on Ubuntu 22.04/20.04.
If you do not have uv (opens in a new tab) installed, run:
curl -LsSf https://astral.sh/uv/install.sh | sh
Then, from the repo root:
uv sync
That's it. You can now run every command in this README directly through uv run, no environment activation needed.
First create your conda environment:
conda create -n gmr python=3.10 -y
conda activate gmr
Then, install GMR:
pip install -e .
With this setup, run the uv run <script> commands in this README as python <script> inside the activated environment.
And to resolve some possible rendering issues:
conda install -c conda-forge libstdcxx-ng -y
Data Preparation
[SMPLX (opens in a new tab) body model] download SMPL-X body models to assets/body_models from SMPL-X (opens in a new tab) and then structure as follows:
- assets/body_models/smplx/
-- SMPLX_NEUTRAL.pkl
-- SMPLX_FEMALE.pkl
-- SMPLX_MALE.pkl
[AMASS (opens in a new tab) motion data] download raw SMPL-X data to any folder you want from AMASS (opens in a new tab). NOTE: Do not download SMPL+H data.
[OMOMO (opens in a new tab) motion data] download raw OMOMO data to any folder you want from this google drive file (opens in a new tab). And process the data into the SMPL-X format using scripts/convert_omomo_to_smplx.py.
[LAFAN1 (opens in a new tab) motion data] download raw LAFAN1 bvh files from the official repo (opens in a new tab), i.e., lafan1.zip (opens in a new tab).
Human/Robot Motion Data Formulation
To better use this library, you can first have an understanding of the human motion data we use and the robot motion data we obtain.
Each frame of human motion data is formulated as a dict of (human_body_name, 3d global translation + global rotation). The rotation is usually represented as quaternion (with wxyz order by default, to align with mujoco).
Each frame of robot motion data can be understood as a tuple of (robot_base_translation, robot_base_rotation, robot_joint_positions).
Usage
[NEW] PICO Streaming to Robot (TWIST2)
Install PICO SDK:
- On your PICO, install PICO SDK: see here (opens in a new tab).
- On your own PC,
- Download deb package for ubuntu 22.04 (opens in a new tab), or build from the repo source (opens in a new tab).
- To install, use command
then you should seesudo dpkg -i XRoboToolkit_PC_Service_1.0.0_ubuntu_22.04_amd64.debxrobotoolkit-pc-servicein your APPs. remember to start this app before you do teleopperation. - Build PICO PC Service SDK and Python SDK for PICO streaming:
git clone https://github.com/YanjieZe/XRoboToolkit-PC-Service-Pybind.git cd XRoboToolkit-PC-Service-Pybind mkdir -p tmp cd tmp git clone https://github.com/XR-Robotics/XRoboToolkit-PC-Service.git cd XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK bash build.sh cd ../../../.. mkdir -p lib mkdir -p include cp tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/PXREARobotSDK.h include/ cp -r tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/nlohmann include/nlohmann/ cp tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/build/libPXREARobotSDK.so lib/ # rm -rf tmp # Install into GMR's environment from the GMR repo root. cd PATH/TO/GMR uv pip install pybind11 cmake uv pip install --no-build-isolation PATH/TO/XRoboToolkit-PC-Service-Pybind
You should be all set!
To try it, check this script from TWIST2 (opens in a new tab):
bash teleop.sh
You should be able to see the retargeted robot motion in a mujoco window.
Retargeting from SMPL-X (AMASS, OMOMO) to Robot
[!NOTE] NOTE: after install SMPL-X, change
extinsmplx/body_models.pyfromnpztopklif you are using SMPL-X pkl files.
Retarget a single motion:
uv run scripts/smplx_to_robot.py --smplx_file <path_to_smplx_data> --robot <path_to_robot_data> --save_path <path_to_save_robot_data.pkl> --rate_limit
By default you should see the visualization of the retargeted robot motion in a mujoco window.
If you want to record video, add --record_video and --video_path <your_video_path,mp4>.
--rate_limitis used to limit the rate of the retargeted robot motion to keep the same as the human motion. If you want it as fast as possible, remove--rate_limit.
Retarget a folder of motions:
uv run scripts/smplx_to_robot_dataset.py --src_folder <path_to_dir_of_smplx_data> --tgt_folder <path_to_dir_to_save_robot_data> --robot <robot_name>
By default there is no visualization for batch retargeting.
Retargeting from GVHMR to Robot
First, install GVHMR by following their official instructions (opens in a new tab).
And run their demo that can extract human pose from monocular video:
cd PATH/TO/GVHMR
python tools/demo/demo.py --video=docs/example_video/tennis.mp4 -s
Then you should obtain the saved human pose data in GVHMR/outputs/demo/tennis/hmr4d_results.pt.
Then, run the command below to retarget the extracted human pose data to your robot:
uv run scripts/gvhmr_to_robot.py --gvhmr_pred_file <path_to_hmr4d_results.pt> --robot unitree_g1 --record_video
Retargeting from BVH (LAFAN1, Nokov) to Robot
Retarget a single motion:
# single motion
uv run scripts/bvh_to_robot.py --bvh_file <path_to_bvh_data> --robot <path_to_robot_data> --save_path <path_to_save_robot_data.pkl> --rate_limit --format <format>
By default you should see the visualization of the retargeted robot motion in a mujoco window.
--rate_limitis used to limit the rate of the retargeted robot motion to keep the same as the human motion. If you want it as fast as possible, remove--rate_limit.--formatis used to specify the format of the BVH data. Supported formats arelafan1andnokov.
Retarget a folder of motions:
uv run scripts/bvh_to_robot_dataset.py --src_folder <path_to_dir_of_bvh_data> --tgt_folder <path_to_dir_to_save_robot_data> --robot <robot_name>
By default there is no visualization for batch retargeting.
Retargeting from Xsens to Robot
Offline: Xsens BVH to Robot
Visualize Xsens BVH Data using MuJoCo
Install PyQt6:
pip install PyQt6 PyQt6-Qt6 PyQt6-sip
uv run general_motion_retargeting/utils/xsens_vendor/mujoco_xsens_bvh_view.py \
--bvh_file <path_to_dir_of_bvh_data> \
--scale <displacement scaling size> \
--reset_to_zero
like
uv run general_motion_retargeting/utils/xsens_vendor/mujoco_xsens_bvh_view.py \
--scale 0.01 \
--bvh_file assets/xsens_bvh_test/251021_04_boxing_120Hz_cm_3DsMax.bvh \
--reset_to_zero
-
--startis used to specify the initial processing frame. If no input is given, processing will start from the first frame by default. -
--endis used to specify the final processing frame. If not input, it will be processed by default to the last frame. -
--reset_to_zerois used to reset the displacement and Z-axis rotation to zero.This function, when used in combination with--start, will set the data to the initial zero position very well.Because sometimes the first one or two frames of some datasets differ too much from the subsequent data, these data need to be discarded. -
--scaleis used to set the scaling value of the displacement, which depends on the relationship between the unit used for the displacement in the dataset and the meter. -
Before using it, you must install PyQt6.
pip install PyQt6 -
Upon executing this command, a UI interface will be launched, enabling you to adjust the angle values for each channel in the x, y, and z directions of every joint. After completing the adjustments, click the
"Apply and Preview"button, which will generate anoffset.jsonfile locally and perform a MuJoCo visualization playback of the BVH file. When runningxsens_bvh_to_robot.py, it will read the data from this JSON file. Therefore, you need to executemujoco_xsens_bvh_view.pyprior to usingxsens_bvh_to_robot.pyfor motion retargeting, to ensure that theoffset.jsonfile exists locally.
Retarget a single motion:
# single motion
uv run scripts/xsens_bvh_to_robot.py \
--bvh_file <path_to_bvh_data> \
--robot <path_to_robot_data> \
--save_path <path_to_save_robot_data.pkl> \
--rate_limit \
--start <number of the first frame> \
--scale <displacement scaling size> \
--reset_to_zero \
--bvh_format <exported bvh format>
like
uv run scripts/xsens_bvh_to_robot.py \
--robot unitree_h1_2 \
--scale 0.01 \
--reset_to_zero \
--bvh_format 3DSM \
--bvh_file assets/xsens_bvh_test/251021_04_boxing_120Hz_cm_3DsMax.bvh \
--save_path retargeting_data/h1/251021_04_boxing_120Hz_cm_3DsMax.pkl
By default you should see the visualization of the retargeted robot motion in a mujoco window.
-
--rate_limitis used to limit the rate of the retargeted robot motion to keep the same as the human motion. If you want it as fast as possible, remove--rate_limit. -
--startis used to specify the initial processing frame. If no input is given, processing will start from the first frame by default. -
--endis used to specify the final processing frame. If not input, it will be processed by default to the last frame. -
--reset_to_zerois used to reset the displacement and Z-axis rotation to zero.This function, when used in combination with--start, will set the data to the initial zero position very well.Because sometimes the first one or two frames of some datasets differ too much from the subsequent data, these data need to be discarded. -
--scaleis used to set the scaling value of the displacement, which depends on the relationship between the unit used for the displacement in the dataset and the meter.
!!!!!!!!!!!!!!!!!! ATTENTION !!!!!!!!!!!!!!!!!!!!
-
--bvh_formatis used to set the format of the bvh being parsed. In the Xsens MVN software, BVH files in three formats can be exported. There will be some differences among BVH files in different formats. Here I recommend using the 3D Studio Max format.(In fact, I have not yet completed the parsing of data in other formats.) -
The exported pkl file will represent quaternions in the
wxyzformat. ^ _ ^
Online Streaming (Xsens MVN)
Stream live motion data from Xsens MVN Software directly into GMR for real-time robot retargeting.
1. Install the Xsens MVN UDP Data Parser
The xsens_mvn_robot_python library parses the Xsens MVN network datagram (Position + Orientation in quaternion format) into Python-accessible data structures. Install the correct .whl file for your Python version.
# Clone the parser repository
git clone https://github.com/jiminghe/xsens_mvn_robot_python.git
cd xsens_mvn_robot_python
# Install the matching wheel for your Python version
# Example for Python 3.10:
pip install xsens_mvn_robot_python-*-cp310-*.whl
Select the
.whlfile whose filename contains your Python version tag (e.g.cp310for Python 3.10,cp38for Python 3.8). This library handles UDP socket binding and datagram unpacking automatically.
2. Configure the Xsens MVN Network Streamer
Launch Xsens MVN Software on either Windows or Linux. You can stream from a live recording session while wearing the Xsens Link / Awinda suit, or replay a previously recorded .mvn file.
| Step | Action |
|---|---|
| 1 | Click Options → Network Streamer |
| 2 | In the pop-up window, click Add to create a new stream destination |
| 3 | Set the Host Address (see table below) |
| 4 | Under Network Streamer Options, tick Position + Orientation (Quaternion) only |
| 5 | No other data sources are needed for GMR retargeting |
| 6 | Click OK — confirm green status on the streamer |
Host Address Reference:
| Scenario | Host Address Setting |
|---|---|
| MVN on the same Linux machine (MVN Linux) | 127.0.0.1 (localhost) |
| MVN on Windows → streaming to Ubuntu (same LAN) | Ubuntu IP address, e.g. 192.168.1.10 |
Important: When streaming from a Windows PC to an Ubuntu computer, ensure both machines are on the same LAN. Disable Windows Firewall for the MVN application or create an inbound UDP rule on the MVN default port (
9763).
3. Run the GMR Live Streaming Script
With the Xsens MVN Network Streamer active, run the live-streaming retargeting script. A MuJoCo window will open showing the retargeted Unitree G1 robot mirroring your movements in real time.
uv run scripts/xsens_live_streaming.py
Retargeting from FBX (OptiTrack) to Robot
Offline FBX Files
Retarget a single motion:
-
Install
fbx_sdkby following these instructions (opens in a new tab) and these instructions (opens in a new tab). You will probably need a new conda environment for this. -
Activate the conda environment where you installed
fbx_sdk. Use the following command to extract motion data from your.fbxfile:
cd third_party
python poselib/fbx_importer.py --input <path_to_fbx_file.fbx> --output <path_to_save_motion_data.pkl> --root-joint <root_joint_name> --fps <fps>
- Then, run the command below to retarget the extracted motion data to your robot:
# single motion
uv run scripts/fbx_offline_to_robot.py --motion_file <path_to_saved_motion_data.pkl> --robot <path_to_robot_data> --save_path <path_to_save_robot_data.pkl> --rate_limit
By default you should see the visualization of the retargeted robot motion in a mujoco window.
--rate_limitis used to limit the rate of the retargeted robot motion to keep the same as the human motion. If you want it as fast as possible, remove--rate_limit.
Online Streaming
We provide the script to use OptiTrack MoCap data for real-time streaming and retargeting.
Usually you will have two computers, one is the server that installed with Motive (Desktop APP for OptiTrack) and the other is the client that installed with GMR.
Find the server ip (the computer that installed with Motive) and client ip (your computer). Set the streaming as follows:

And then run:
uv run scripts/optitrack_to_robot.py --server_ip <server_ip> --client_ip <client_ip> --use_multicast False --robot unitree_g1
You should see the visualization of the retargeted robot motion in a mujoco window.
Visualize saved robot motion
Visualize a single motions:
uv run scripts/vis_robot_motion.py --robot <robot_name> --robot_motion_path <path_to_save_robot_data.pkl>
If you want to record video, add --record_video and --video_path <your_video_path,mp4>.
Visualize a folder of motions:
uv run scripts/vis_robot_motion_dataset.py --robot <robot_name> --robot_motion_folder <path_to_save_robot_data_folder>
After launching the MuJoCo visualization window and clicking on it, you can use the following keyboard controls::
[: play the previous motion]: play the next motionspace: toggle play/pause
Speed Benchmark
| CPU | Retargeting Speed |
|---|---|
| AMD Ryzen Threadripper 7960X 24-Cores | 60~70 FPS |
| 13th Gen Intel Core i9-13900K 24-Cores | 35~45 FPS |
| TBD | TBD |
Citation
If you find our code useful, please consider citing our related papers:
@article{joao2025gmr,
title={Retargeting Matters: General Motion Retargeting for Humanoid Motion Tracking},
author= {Joao Pedro Araujo and Yanjie Ze and Pei Xu and Jiajun Wu and C. Karen Liu},
year= {2025},
journal= {arXiv preprint arXiv:2510.02252}
}
@article{ze2025twist,
title={TWIST: Teleoperated Whole-Body Imitation System},
author= {Yanjie Ze and Zixuan Chen and João Pedro Araújo and Zi-ang Cao and Xue Bin Peng and Jiajun Wu and C. Karen Liu},
year= {2025},
journal= {arXiv preprint arXiv:2505.02833}
}
and this github repo:
@software{ze2025gmr,
title={GMR: General Motion Retargeting},
author= {Yanjie Ze and João Pedro Araújo and Jiajun Wu and C. Karen Liu},
year= {2025},
url= {https://github.com/YanjieZe/GMR},
note= {GitHub repository}
}
Known Issues
Designing a single config for all different humans is not trivial. We observe some motions might have bad retargeting results. If you observe some bad results, please let us know! We now have a collection of such motions in TEST_MOTIONS.md (opens in a new tab).
Acknowledgement
Our IK solver is built upon mink (opens in a new tab) and mujoco (opens in a new tab). Our visualization is built upon mujoco (opens in a new tab). The human motion data we try includes AMASS (opens in a new tab), OMOMO (opens in a new tab), and LAFAN1 (opens in a new tab).
The original robot models can be found at the following locations:
- Berkley Humanoid Lite (opens in a new tab): CC-BY-SA-4.0 license
- Booster K1 (opens in a new tab)
- Booster T1 (opens in a new tab) (English (opens in a new tab))
- EngineAI PM01 (opens in a new tab): Link to file (opens in a new tab)
- Fourier N1 (opens in a new tab): Link to file (opens in a new tab)
- Galaxea R1 Pro (opens in a new tab): MIT license
- HighToqure Hi (opens in a new tab)
- LEJU Kuavo S45 (opens in a new tab): MIT license
- PAL Robotics' Talos (opens in a new tab): Link to file (opens in a new tab)
- Toddlerbot (opens in a new tab): Link to file (opens in a new tab)
- Unitree G1 (opens in a new tab): Link to file (opens in a new tab)
Files and versions
- Repository on GitHubgithub.com/YanjieZe/GMR (opens in a new tab)
- The version Robotheca read25f56db (opens in a new tab)
- Latest releaseNone published on GitHub
Robotheca read the repository at two versions: the summary at bb1bbe4 and the facts and the README at 25f56db (read on 6 Nov 2026).
Robotheca never hosts the code: each link goes to the creator's own page.
Use it
How to install it, in the creator's words: The project's README (opens in a new tab)
Robotheca doesn't review code. Read it before running it.
Robotheca lists software as a reference: nothing is hosted or run here, and every fact links to where it was read.