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

Lesson 2 · 16 min

Domain randomization: training for the varied world

If the simulator never gets the world right, you train the policy in many randomly drawn worlds. In this lesson you read the ranges Unitree itself randomized when training the policy, draw 20 worlds inside them and 20 outside, and measure the difference. The policy handles what it saw in training and fails just past it.

Start the lesson
A wall of monitors in a dark lab, each showing a simulated humanoid robot walking on a different floor.

In this lesson you will

  1. 1Where randomization lives in a real project: the Isaac Lab events
  2. 2The G1 policy's ranges: friction, mass, and push (and what was left out)
  3. 320/20 inside the range, 8/20 outside it
  4. 4Why randomizing everything to the max doesn't work either

Goal: read the randomization ranges of a real training run and measure the policy's survival inside and outside them.

01From randomized training to the out-of-range test

Click each block.

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

Randomization is not "more is better": it's covering the world where the robot will walk.

02What Unitree randomized (and what it didn't)

Read in velocity_env_cfg.py, unitree_rl_lab 4960b84 (Nov 19, 2025). Click each item.

1. Friction

physics_material event, at startup: static and dynamic friction between 0.3 and 1.0 on all of the robot's bodies.

In this lesson's out-of-range draw, the worlds with friction near 0.2 (2 and 5) fall; the floor of the training range is the floor the policy has seen.

03The kit's two ranges

treino.json copies Unitree's ranges; fora.json goes a bit beyond and adds kp and latency (delay).

File · kit/mundos/treino.json
{
  "nome": "treino",
  "descricao": "as faixas que a própria tarefa Unitree-G1-29dof-Velocity randomiza no treino (unitree_rl_lab 4960b84, velocity_env_cfg.py, classe EventCfg)",
  "atrito": [0.3, 1.0],
  "massa": [-1.0, 3.0],
  "empurrao_vel": [-0.5, 0.5]
}
File · kit/mundos/fora.json
{
  "nome": "fora",
  "descricao": "além do treino: atrito um pouco abaixo de 0.3, carga maior e kp e atraso, que o treino não randomizou",
  "atrito": [
    0.2,
    0.6
  ],
  "massa": [
    2.0,
    8.0
  ],
  "kp": [
    0.6,
    1.0
  ],
  "atraso_ms": [
    0,
    30
  ],
  "empurrao_vel": [
    -0.5,
    0.5
  ]
}

04Sample and count

randomizar.py samples 20 worlds per range with a fixed seed (7) and runs 10 s of walking in each one, with the push at t = 3 s.

Terminal · inside and outside the range
F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
python3 kit/randomizar.py --seeds 20
Terminal · the first worlds outside the range, one by one
F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
python3 kit/randomizar.py --seeds 20 --faixa fora --detalhe | head -6
Example from the repository
faixa treino  sobreviveu: 20/20
faixa fora    sobreviveu: 8/20

Inside the training range, 20 out of 20. Outside, 8 out of 20.

05Practice

Measure what the training covered.

E1 · Inside × outside

3 min

Run the sampling for both ranges.

Done when: python3 kit/randomizar.py --seeds 20 → faixa treino sobreviveu: 20/20 and faixa fora sobreviveu: 8/20.

E2 · Only kp outside

5 min

Write a range identical to the training one, but with kp between 0.5 and 0.8 (the block below already does it), and run it.

Done when: python3 kit/randomizar.py --seeds 20 --faixa ~/fth/so-kp.json → faixa so-kp sobreviveu: 11/20.

E3 · What you would randomize

10 min

For a G1 that will walk on a factory floor with a 2 kg camera on its head, write in three lines what you would add to the training ranges and why.

Done when: three lines, each with a parameter, a range, and the measured reason (e.g., "kp 0.5–1.0: only kp outside gave 11/20").

06The E2 range

Same ranges as the training, plus kp between 0.5 and 0.8.

Terminal · only kp outside the range
F=${FTH:-$HOME/fth}; cd $F/sim2real && . $F/.venv/bin/activate
cat > $F/so-kp.json <<'J'
{"nome": "so-kp", "atrito": [0.3, 1.0], "massa": [-1.0, 3.0], "kp": [0.5, 0.8], "empurrao_vel": [-0.5, 0.5]}
J
python3 kit/randomizar.py --seeds 20 --faixa $F/so-kp.json
Example from the repository
faixa so-kp   sobreviveu: 11/20

Just adding kp, which the training did not vary, knocks down 9 out of 20.

07Lesson quiz

The explanation appears after your answer.

Question 1 of 4

The policy survived 20/20 inside the training ranges and 8/20 outside. What does that measure?skill

08In three sentences

RangesG1 training randomizes friction 0.3–1.0, mass −1 to +3 kg, and pushes; it does not randomize kp or latency.
Score20/20 inside the range, 8/20 outside, 11/20 with only kp outside.
RuleRead what training covered and measure outside it before the robot.

Going further

Previous lessonNext lesson