FTH 2 · From simulator to ground|INEMA.CLUB-PRO

Lesson 1 · 16 min

Where the simulator lies about a humanoid

A policy that walks in the simulator and falls on the robot: that is the problem of this course. In this lesson you set up the bench, run the ready-made Unitree G1 gait policy in the "sim" and in a "simulated real" with hidden parameters, and find out that no single difference knocks it down; the sum of them does.

Start the lesson
Humanoid robot mid-step on a smooth lab floor, with the foot slipping and a safety cable attached to the gantry.

In this lesson you will

  1. 1The five sources of the gap in a humanoid: actuator, latency, friction, mass, and contact
  2. 2The ready-made G1 gait policy (unitree_rl_lab) running in MuJoCo
  3. 3Sim × "simulated real": 4.7 m walked versus a fall in 2.3 s
  4. 4Change one parameter at a time to find the culprit

Goal: measure the gap by running the same policy in "sim" and "simulated real", and find that no single parameter brings it down.

01What the simulator simplifies

Click each block. These are the five sources of gap this course measures.

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

None of these differences go away. Sim-to-real means making the policy robust to them, measuring the ones you can measure, and testing before the floor.

02The policy and the two worlds

What runs in this course's practice sessions. Click each step.

1. The policy

Unitree publishes in unitree_rl_lab a G1 29-DoF walking policy already trained in Isaac Lab and exported to ONNX, with the deployment file deploy.yaml (joint order, kp and kd, default pose, scales). Apache-2.0 license.

It takes 480 numbers (5 timesteps of angular velocity, projected gravity, command, joints, and last action) and returns 29 position targets at 50 Hz.

03Set up the bench (reuses the one from course 0)

If you did course 0, the venv and the Menagerie are already in ~/fth: this block only adds onnxruntime, the G1 in the sparse-checkout, and the unitree_rl_lab policy folder, pinned to the tested commit.

Terminal · prepare ~/fth
F=${FTH:-$HOME/fth}; mkdir -p $F && cd $F
[ -d .venv ] || python3.12 -m venv .venv
. .venv/bin/activate && pip install -q mujoco==3.16.0 numpy onnxruntime==1.31.0 pyyaml
[ -d sim2real ] || git clone -q --depth 1 https://github.com/inematds/formacao-fth-sim2real-v72 sim2real
[ -d mujoco_menagerie ] || git clone -q --filter=blob:none --sparse https://github.com/google-deepmind/mujoco_menagerie
git -C mujoco_menagerie sparse-checkout add unitree_g1 && git -C mujoco_menagerie checkout -q 62ab00f
[ -d unitree_rl_lab ] || git clone -q --filter=blob:none --sparse https://github.com/unitreerobotics/unitree_rl_lab
git -C unitree_rl_lab sparse-checkout set deploy/robots/g1_29dof/config/policy/velocity
git -C unitree_rl_lab checkout -q 4960b84 && git -C unitree_rl_lab log -1 --format='unitree_rl_lab %h (%ad)' --date=short
python3 -c "import mujoco, onnxruntime; print('mujoco', mujoco.__version__, '· onnxruntime', onnxruntime.__version__)"
Example from the repository
unitree_rl_lab 4960b84 (2025-11-19)
mujoco 3.16.0 · onnxruntime 1.31.0

Two lines and no errors. The policy comes from commit 4960b84 of unitree_rl_lab: pin it, because a new policy.onnx may have a different input order.

04The same policy in both worlds

dois_mundos.py runs 10 s walking at 0.5 m/s in each world. Fall = pelvis below 0.5 m. Exits with 1 if any world fell.

Terminal · sim × simulated real
F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
python3 kit/dois_mundos.py; echo "saída: $?"
Terminal · one parameter at a time
F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
for p in atrito massa kp atraso_ms; do python3 kit/dois_mundos.py --so $p | tail -1; done
Example from the repository
política: unitree_rl_lab G1 29 GDL velocity v0 (ONNX, 480 entradas) · comando 0.50 m/s · 10 s
sim                          queda: não · andou 4.7 m em 10 s (vel. 0.47 m/s; pedida 0.50)
real simulado                queda: sim em t = 2.3 s (andou 1.9 m)
saída: 1

In the sim, 4.7 m in 10 s (0.47 m/s for 0.50 commanded). In the "simulated real," a fall at 2.3 s.

05Practice

Measure the gap before you try to close it.

E1 · Bench ready

10 min

Run the setup block. If course 0 already built ~/fth, it only adds what's missing.

Done when: the block ends with unitree_rl_lab 4960b84 (2025-11-19) and mujoco 3.16.0 · onnxruntime 1.31.0.

E2 · The measured gap

3 min

Run the same policy in both worlds and note how far it walked in each.

Done when: python3 kit/dois_mundos.py; echo "saída: $?" → real simulado … queda: sim em t = 2.3 s and saída: 1.

E3 · Find the culprit

5 min

Run the "one parameter at a time" loop and write down which parameter alone changes the behavior most, and what changes.

Done when: the 4 lines show queda: não and the kp line shows vel. 0.75 m/s; pedida 0.50; you wrote "none knocks it down alone; kp changes the speed".

06Lesson quiz

The explanation appears after your answer.

Question 1 of 3

Each parameter of the "simulated real" robot, on its own, does not knock the policy down; all four together do. What does this teach?skill

07In three sentences

SourcesActuator, latency (delay), friction, mass, and contact set the simulator apart from the robot.
MeasurementThe same policy walks 4.7 m in sim and falls at 2.3 s in the "simulated real" robot.
MethodOne parameter at a time finds the culprit; it's the sum that knocks it down.

Going further

Next lesson