Robot Learning · Theory · M10
M10-T1 — The Reality Gap
A policy that is perfect in the simulator is a policy that has met exactly one world. The floor is another.
Module 10 asks what happens when a policy trained in a simulator meets the world it was trained for. In robotics this is called the reality gap, or the sim-to-real gap: every difference between the simulator and the real robot, and there are always many, is a place where the policy’s learned behaviour can stop working. This lesson names the sources of the gap, says how each shows up in the data, and measures them on a toy. Please read the next sentence as the frame for every number below: this is a simplified transfer study. "Reality" is the floor world of Scout with momentum (module 7), "simulation" is the nominal world, and no real robot data is used anywhere. The numbers say what happens in this toy and illustrate the pattern; they do not tell you how large a gap on any real robot will be. Lessons M10-T2 and M10-T3 then try the two main cures, randomizing the simulator and identifying the real robot’s parameters, lesson M10-T4 asks how to test robustness, and lab M10-L1 lets you choose parameter ranges yourself.
Learning objectives
After this lesson, you should be able to:
- Name four sources of the reality gap (dynamics, sensing, latency, contact) and say how each shows up in logged data and in behaviour.
- Decompose a policy’s failure in a changed world by changing one factor at a time, report each success rate with its uncertainty, and explain why the factors interact.
- Explain why a policy trained in one simulator can exploit it, using the closest-approach margin as the example.
- State what the toy world includes (gain, drag, delay, observation noise, process noise) and what it leaves out (contact, friction, wear, anything not parameterised).
The intuition
Scout’s policy in this module has three numbers, θ = {clear, kp, ff}. clear is how far beyond the pillar’s contact circle (0.95 m from the centre) it swings, kp is velocity feedback and ff a feedforward term. "Training in simulation" means choosing θ from a small grid by the score success − 0.01 × steps, averaged over episodes in the simulator (RLCore.trainTheta). Trained in the nominal world, with gain 3, drag 3, no delay and no noise, the grid’s winner is clear = 0.3, kp = 0 for every seed 1 to 5: the tightest path that is still perfect in the simulator. It reaches the goal in all 200 nominal episodes.
The figure shows the same θ, from one start, in three worlds. In the nominal world (blue) the swerve starts near x = 4.1 and Scout passes the pillar at 1.03 m from its centre, which is 0.08 m outside contact. Adding only the floor’s lower gain and higher drag (green) makes Scout slower, 46 steps instead of 31, but it still gets there. Adding only a one-step delay between command and effect (orange) starts the swerve one step, 0.25 s, late: Scout has moved about 0.2 m further toward the pillar by then, more than the 0.08 m margin, and it crashes at step 11. A change that costs time in one place costs the whole mission in another.
Three ideas carry the lesson. A simulator is a set of assumptions, and a policy trained in it can come to depend on the assumptions in ways nobody wrote down. The gap has several independent parts, which differ in how loudly they announce themselves: some make the robot slow, one makes it crash. And because the parts interact, "which factor matters most" depends on which direction you ask the question.
The mathematics
Where the gap comes from
Write the true system as xt+1 = freal(xt, at−d) with observations ot = hreal(xt) + noise, and the simulator as the same two functions with different parameters or a different structure. The policy was optimised for (fsim, hsim) and is run on (freal, hreal). The gap is every way in which these differ, and it is useful to sort the ways.
| Source | What differs | How it shows up | In this toy |
|---|---|---|---|
| Dynamics | masses, motor gain, friction and drag, wheel slip, battery sag, wear | the robot is slower or faster than predicted; one-step velocity prediction errors that are systematic, not random; tracking error that does not average out | gain 3 → 2.2 and drag 3 → 3.4 (parameters of the module-7 model) |
| Sensing | noise, bias, dropouts, calibration, a state estimator the simulator does not have | jitter in logged readings; a policy that reacts to noise; failures that look random and grow with the noise level | Gaussian noise of 0.05 (m on position, m/s on velocity) added to the readings; nothing else |
| Latency | delay between command and effect (communication, computation, actuator lag), sampling-rate mismatch | the response lags the command by a whole number of steps; oscillation or overshoot, because the policy acts on a state that is no longer current | a delay of 1 step (0.25 s) |
| Contact | friction coefficients, compliance, impact, objects that deform or slide | behaviour near contact is very different from free motion; small parameter changes can change which side an object falls | not modelled: a collision is just entering a circle, and the circle’s radius is the same in both worlds |
The table is deliberately short. Other differences that matter on real robots include rendering and lighting for camera-based policies (the target of the original domain randomization work, lesson M10-T2), terrain, payloads, and unmodelled wiring or software behaviour. I put only the first three in the toy because they can be parameterised by a handful of numbers; the fourth I name so that you remember the toy is silent on it.
The simulator as a family
The toy world has five parameters, w = (gain, drag, delay, obsNoise, noise). With the module-7 dynamics, the velocity on each axis moves by
- gain
- Acceleration per unit command in m/s² (nominal 3, floor 2.2).
- drag
- Velocity decay in 1/s (nominal 3, floor 3.4). With a constant command a, the speed settles at gain a / drag: 1.00 m/s nominal, 0.647 m/s on the floor.
- delay
- Whole steps of 0.25 s between the command being issued and acting (nominal 0, floor 1).
- obsNoise
- Standard deviation of Gaussian noise on the position and velocity readings (nominal 0, floor 0.05).
- noise
- Standard deviation of process noise on the velocity update (nominal 0, floor 0.03).
- ξ, η
- Independent standard normal draws.
The nominal world is the point (3, 3, 0, 0, 0); the floor is the point (2.2, 3.4, 1, 0.05, 0.03). A policy trained at the first and run at the second experiences a gap of five numbers. Saying which of the five matters is the experimental question.
One factor at a time
Let S(w) be the policy’s success probability in world w. The gap in success is S(nominal) − S(floor). To attribute it, change one factor at a time in two directions:
forward: S(nominal with factor k set to its floor value) backward: S(floor with factor k set back to its nominal value)
If the factors acted independently, the two sweeps would give consistent answers and the losses would add. When the effect of one factor depends on the level of another (an interaction), they do not, and the sweeps disagree. The example below shows a case of exactly this.
A worked example
All numbers are real runs in Node against RLCore: RLCore.transferScore(θ, world, seed, 40) for seeds 1 to 5, so each rate is out of 200 episodes (the 200 starts are the first 40 from each seed’s starts(seed, 40, 'all'); a seed also fixes the noise stream). The policy is the one the nominal simulator picks, θ = {clear 0.3, kp 0, ff 1}. The intervals are 95% Wilson intervals on the pooled 200 episodes, which ignore that the five seeds are not independent draws of the world.
By hand: why the delay hurts and the gain does not
Command (1, 0) from rest. Nominal, dynStep gives v = 0.25 × 3 × 1 = 0.75 m/s after the first step, then 0.938, 0.984, 0.996, ... settling at 1.00; the position after four steps is 0.917 m. On the floor (gain 2.2, drag 3.4, delay 1) the first step has no effect (v = 0), then v = 0.25 × 2.2 = 0.55, 0.633, 0.645, 0.647 settling at 0.647 m/s; after four steps the robot has moved 0.457 m, half as far. Two distinct things happen. The speed is lower and the response is slower to build, and both are just time: the same path takes longer, which is what the green line shows. The delay is different: the policy computes each command from the position it sees now, and the command acts a quarter of a second later, when the robot is somewhere else. At the 0.65 to 1 m/s the robot moves at, that is 0.16 to 0.25 m. The nominal policy’s closest approach to the pillar centre over the 200 nominal episodes was 0.97 m at the minimum (median 1.09, 5th percentile 0.99), against contact at 0.95 m: a margin of 2 to 14 cm. A swerve that is late by a quarter metre has no margin to spend.
Decomposition, forward
| World (θ fixed) | Success / 200 | 95% interval | Crashes | Mean steps (successes) |
|---|---|---|---|---|
| nominal | 200 | 98% to 100% | 0 | 29.5 |
| only gain 3 → 2.2 | 200 | 98% to 100% | 0 | 39.7 |
| only drag 3 → 3.4 | 200 | 98% to 100% | 0 | 32.9 |
| gain and drag together | 200 | 98% to 100% | 0 | 44.5 |
| only delay 0 → 1 | 88 | 37% to 51% | 112 | 29.5 |
| only observation noise 0 → 0.05 | 185 | 88% to 95% | 15 | 29.6 |
| only process noise 0 → 0.03 | 200 | 98% to 100% | 0 | 29.5 |
| delay and observation noise | 87 | 37% to 50% | 113 | 29.2 |
| the full floor | 145 | 66% to 78% | 55 | 45.3 |
Per seed, the full floor gave 0.675, 0.65, 0.725, 0.8 and 0.775 (the seed-to-seed spread of 0.15 is about the width of the pooled interval, so the pooled number is the fairer summary); delay alone gave 0.35, 0.425, 0.4, 0.575 and 0.45. Reading the table: the dynamics change (gain, drag) costs time, from 29.5 to 44.5 steps, and nothing else; process noise at 0.03 costs nothing visible; sensing noise at 0.05 costs 15 crashes in 200; the delay alone costs 112. All failures were crashes (no timeouts). Look at the last two columns together: the cheapest-looking change in the simulator, one more step of latency, is worth more than any other single factor.
Decomposition, backward, and the interaction
| World | Success / 200 | Crashes | Mean steps |
|---|---|---|---|
| floor | 145 | 55 | 45.3 |
| floor with gain back to 3 | 100 | 100 | 33.1 |
| floor with drag back to 3 | 118 | 82 | 40.0 |
| floor with delay back to 0 | 200 | 0 | 45.0 |
| floor with observation noise back to 0 | 138 | 62 | 44.7 |
| floor with process noise back to 0 | 138 | 62 | 45.1 |
Removing the delay repairs everything (200 of 200), as the forward sweep said it should. But restoring the nominal gain lowers success from 145 to 100: with a one-step delay, a faster robot travels further during the delay, so the late swerve misses by more. Dynamics are harmless on their own and harmful in the presence of latency. That is an interaction, and it is why a table of "the effect of each factor" is not a budget that adds up. (Removing noise barely changes anything because with that set of factors the 55 floor crashes are dominated by the delay; the 138 versus 145 difference is within what five seeds of 40 starts can produce.)
Size of the gap in the one factor that matters
Setting only the delay to 0, 1, 2 and 3 steps, the same policy succeeded in 200, 88, 68 and 64 of 200 episodes, and with observation noise alone at 0, 0.05, 0.1 and 0.2 it succeeded in 200, 185, 168 and 140. The first curve falls off a cliff at the first step of delay; the second falls gradually. Robustness is not one number: the policy is fragile in one direction and sturdy in another, which is lesson M10-T4’s subject.
What this toy simplifies. The five parameters are the only things that differ; the structure of the dynamics is identical in both worlds, which is almost never true of a real robot. The policy has three numbers. There is no contact model. The "floor" is itself a simulator I wrote, so I know its parameters exactly, which no engineer does. The real lesson of the toy is the method (change one factor at a time, in both directions, with intervals), not the particular ranking.
Engineering implications and limitations
Practical rules for dealing with the gap
- Assume a gap exists and measure it. The question is how large and in which parts, not whether. Run the simulation-trained policy on the real robot (or the best proxy you have) in the safest staged way (lesson M10-T5), and log commands, states and outcomes.
- Decompose before you fix. Change one factor at a time in the simulator, forward and backward, and report each with an interval and several seeds (lesson M11-T2). A decomposition tells you whether to improve the model, randomize it, or add a layer such as the safety wrapper of lesson M9-T4.
- Check for margins the policy consumes. The nominal policy passed the pillar 2 to 14 cm from contact. A tight margin in the simulator is a warning, not a success: the simulator’s reward asked for it (shorter paths score better) and the real world will not honour it.
- Measure latency explicitly. Timestamp a command and the first visible effect. Latency is the gap factor that can be measured cheaply and hurts most, and it often hides inside "dynamics" (lesson M10-T3 shows that a fit that ignores it goes wrong).
- Do not trust one scalar. A policy can be sturdy against noise and brittle against delay. Report the curve, not one point (lesson M10-T4, lab M10-L2).
- Keep the simulator honest about what it leaves out. Write down the list of unmodelled effects (here: contact, friction, anything not in the five parameters). Those are where surprises will come from.
- Expect the gap to move. Wear, payload, temperature, a software update and a different floor each shift parameters. A result for one floor is a result for that floor on that day.
The cures, in one table
| Approach | Idea | Needs | Where |
|---|---|---|---|
| Make the simulator closer | identify the real parameters from logs and train in the calibrated world | real logs with enough variety; a model with the right structure | lesson M10-T3, M7-T2 |
| Make the policy indifferent | train over many simulated worlds so that the real one looks like one of them | parameter ranges that cover the real world; training budget | lesson M10-T2, lab M10-L1 |
| Catch the failure | wrap the policy in a monitor and stage the rollout | a safe-set model; monitoring | lessons M9-T4, M10-T5 |
| Fine-tune on the real robot | continue learning with real data | safe data collection | not treated in this toy |
Common misconceptions
“The gap is a single number: how different the simulator is.”
It has parts, and they matter very differently. With the policy fixed, the floor’s gain and drag cost only time (29.5 to 44.5 steps), a delay of one step cost 112 crashes in 200. A single "difference" would hide that.
“If one factor is responsible for the failure, fixing it alone is enough, and the others are irrelevant.”
Removing the delay fixed everything here, but that is because the others are harmless with the delay absent. Restoring the nominal gain on the floor lowered success from 145 to 100, because a faster robot with a delay travels further before the policy’s command acts.
“A policy with a perfect simulation score has a margin.”
The nominal policy had success 200 of 200 and a closest approach of 0.97 to 1.09 m against a contact radius of 0.95. The score does not show how little room remained; it was a shorter path that the reward preferred.
“This lesson’s numbers describe real sim-to-real transfer.”
They describe a toy: five parameters, a three-number policy, a simulator I wrote on both sides, no real robot data. The method transfers; the sizes do not.
Engineering takeaways
- The reality gap is the sum of many differences: dynamics, sensing, latency and contact are the four to name first; the toy includes the first three and says nothing about contact.
- In this toy, the policy trained in the nominal world reached the goal in 200 of 200 nominal episodes and in 145 of 200 on the floor (95% interval 66% to 78%).
- One factor at a time: the floor’s gain and drag alone cost time (29.5 to 44.5 steps) and no successes; observation noise cost 15 crashes; a single step of delay cost 112.
- Factors interact: resetting the gain to its nominal value on the floor reduced success from 145 to 100, because delay and speed together set how far the robot travels before a command acts, so decompose in both directions.
- A policy that exploits the simulator (here a pillar margin of 2 to 14 cm) scores perfectly and has nothing in reserve; the next lessons spend that reserve on randomization (M10-T2) and on identification (M10-T3).
References and further reading
The terms and the concerns of this lesson (reality gap, latency, unmodelled dynamics) are standard in the sim-to-real literature. The first two references are early works that targeted specific gap sources; all numbers above come from my own toy runs and are not results from these papers.
- J. Tobin, R. Fong, A. Ray, J. Schneider, W. Zaremba and P. Abbeel, “Domain Randomization for Transferring Deep Neural Networks from Simulation to the Real World”, IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS), 2017.
- X. B. Peng, M. Andrychowicz, W. Zaremba and P. Abbeel, “Sim-to-Real Transfer of Robotic Control with Dynamics Randomization”, IEEE International Conference on Robotics and Automation (ICRA), 2018.
- J. Tan et al., “Sim-to-Real: Learning Agile Locomotion For Quadruped Robots”, Robotics: Science and Systems (RSS), 2018.
Knowledge check
Three conceptual questions. Write an answer, then reveal the explanation. Your answers stay in your browser.
Question 1
A policy reaches the goal in all 200 simulator episodes and in 145 of 200 on the "floor". Changing one factor at a time from nominal to floor gives 200, 200, 88, 185 and 200 of 200 for gain, drag, delay, observation noise and process noise. Which factor explains the gap, and what do the other columns tell you?
Explanation
The delay (88 of 200, 112 crashes). Gain, drag and process noise alone cost no successes (the gain and drag cost time, 29.5 to 39.7 and 32.9 steps), and observation noise cost 15. They are not irrelevant: the factors interact, and resetting the gain to nominal on the floor made success worse (100 of 200).
Question 2
The nominal policy’s closest approach to the pillar centre is about 1.0 m and contact is at 0.95 m. A one-step delay makes it crash. Why does a late command matter this much, and what number would you want before deploying?
Explanation
The command acts one step (0.25 s) after it is computed, by which time Scout, moving at 0.65 to 1 m/s, has travelled about 0.16 to 0.25 m. That is more than the 2 to 14 cm margin. Before deployment you would want the measured latency of the real robot and the policy’s success as a function of delay (lesson M10-T4).
Question 3
The toy has no contact model. Why does the lesson still list contact among the sources of the reality gap, and what should you write down about it?
Explanation
Contact (friction, compliance, impact) is a major source of gap in real manipulation and locomotion, and a toy that omits it says nothing about its size. Write it down as an unmodelled effect, so that the toy’s results are not read as covering it.
What’s next
Lesson M10-T2 tries the first cure: train over many simulated worlds instead of one. The central questions are which parameters to vary and over what ranges, what cautiousness costs in steps, and what happens when the ranges are too narrow to contain the floor or too wide to be useful.
Related content
Nothing below is required to finish this lesson.
- LabM10-L1: Randomize the Simulator
The policy was trained on one friction value: choose parameter ranges and see whether it survives the real floor.
Get notified when the next lesson and new labs are released.
One email per new lesson or lab.