FTH 3 · Whole body|INEMA.CLUB-PRO

Lesson 3 · 15 min

Retargeting: from the human skeleton to the robot

A person is 1.70 m tall and has joints the robot does not; the G1 is 1.32 m tall and, in the Menagerie model, has 29 joints with limits. Retargeting is the computation that takes one to the other, frame by frame. In this lesson you install GMR in its own environment, take 10 s of boxing to the G1, and check every frame against the joint limits.

Start the lesson
A person in a motion capture suit with arms raised, next to a humanoid robot with a dark visor in the same pose.

In this lesson you will

  1. 1What retargeting solves: scale, different joints, and limits
  2. 2One environment per tool: the .venv-gmr with pinned versions
  3. 3Per-frame IK with mink: human points become robot targets
  4. 4Why the limit depends on the robot file

Goal: take a human clip to the G1 with GMR in its own environment and check that no frame goes past the joint limits.

01One retargeting frame

Click each block. GMR repeats this for every frame of the clip.

Click each block of the diagram to see what it does.

GMR runs on the CPU, in real time according to the paper (ICRA 2026); here, 10 s of clip in a few seconds.

02Why a separate environment just for GMR

Click each point.

1. torch

The GMR package imports PyTorch as soon as it loads (the kinematic model uses tensors), even for BVH retargeting. Putting torch in the .venv from the MuJoCo practices would make that environment heavy for no reason.

Solution: a .venv-gmr just for it, with the CPU torch from the official PyTorch index.

03Install GMR in .venv-gmr

Adds only the G1's code and model to the GMR clone (sparse-checkout add), creates the second environment and pins the tested versions. First time: the whole environment took 1.2 GB on a fresh bench, almost all of it the CPU build of torch (measured on 10/19/2026).

Terminal · GMR environment
F=${FTH:-$HOME/fth}; cd $F/GMR
git sparse-checkout add general_motion_retargeting scripts assets/unitree_g1
cd $F && [ -d .venv-gmr ] || python3.12 -m venv .venv-gmr
. .venv-gmr/bin/activate
pip install -q --index-url https://download.pytorch.org/whl/cpu torch==2.14.1
pip install -q mujoco==3.16.0 numpy==2.5.4 scipy==1.18.1 mink==1.3.0 qpsolvers==4.13.0 daqp==0.10.3 rich tqdm loop_rate_limiters imageio
pip install -q --no-deps -e GMR
python3 -c "from importlib.metadata import version as v; print(*[f'{p} {v(p)}' for p in ('torch', 'mink', 'mujoco', 'general_motion_retargeting')], sep=' · ')"
Example from the repository
torch 2.14.1+cpu · mink 1.3.0 · mujoco 3.16.0 · general_motion_retargeting 0.2.0

One line with the four versions. GMR installed in editable mode points to the ~/fth/GMR folder.

0410 s of boxing on the G1

retarget.py reads the BVH the way GMR's script for Xsens does (without its graphical interface), calls GMR frame by frame and checks each frame against the limits of the Menagerie G1.

Terminal · retargeting
F=${FTH:-$HOME/fth}; cd $F/corpo-inteiro && . $F/.venv-gmr/bin/activate
python3 kit/retarget.py $F/GMR/assets/xsens_bvh_test/251021_04_boxing_120Hz_cm_3DsMax.bvh --fim 1200 --nome boxe_g1; echo "saída: $?"
Excerpt · kit/retarget.py
    folga = np.deg2rad(FOLGA_GRAUS)
    fora = (q < lo - folga) | (q > hi + folga)
Example from the repository
clipe: 251021_04_boxing_120Hz_cm_3DsMax.bvh · 1200 quadros a 120 Hz (10.0 s) · 28 pontos do esqueleto
altura do humano no clipe: 1.67 m · robô: unitree_g1 (GMR, config bvh_xsens_to_g1.json)
qpos: 1200 × 36 (base 7 + 29 juntas, mesma ordem do Menagerie)
menor margem até o limite: right_ankle_roll_joint (0.0°, 0 = encostado)
violações de limite (Menagerie, folga 0.5°): 0
tempo de retargeting: menos de 1 min em CPU
gravado: ~/fth/movimentos/boxe_g1.npz
saída: 0

1200 frames, no violations. The smallest margin is the ankle touching the limit: a flat foot on the ground takes the ankle to the end of its range.

05Practice

Install, retarget, check.

E1 · GMR environment

10 min

Run the install block and confirm the versions.

Done when: ~/fth/.venv-gmr/bin/python -c "import general_motion_retargeting" 2>/dev/null; echo "saída: $?" → saída: 0.

E2 · Retargeting with no violations

5 min

Run the retargeting of the 10 s of boxing.

Done when: python3 kit/retarget.py ~/fth/GMR/assets/xsens_bvh_test/251021_04_boxing_120Hz_cm_3DsMax.bvh --fim 1200 --nome boxe_g1 → violações de limite (Menagerie, folga 0.5°): 0 and saída: 0.

E3 · Another segment

5 min

Retarget from frame 1200 to 2400 with --inicio 1200 --fim 2400 --nome boxe_trecho2 and compare the smallest margin with the one from the first segment.

Done when: ls ~/fth/movimentos/boxe_trecho2.npz → the file exists, and the retarget output ends in saída: 0.

06Lesson quiz

The explanation appears after your answer.

Question 1 of 3

Why does the course install GMR with --no-deps?skill

07In three sentences

Per-frame IKScales the human and solves the robot's pose frame by frame.
EnvironmentOne venv per tool, pinned versions, dependencies set by code.
LimitsCheck it on the model that will run it, not the one that generated it.

Going further

Previous lessonNext lesson