Lock Gazebo's Physics Clock for Repeatable Robot Tests
Control how Gazebo advances simulation time so robot control-loop tests produce repeatable results across different machines.
12 Dec 2025, 06:46 UTC

The problem: simulation time that follows your CPU
If you run the same Gazebo robot scenario on two different laptops and get different collision outcomes, the simulator is usually not at fault. Gazebo ties simulation time to wall-clock time by default. A fast CPU can advance more physics steps per second than a slow one, so identical code and world files can produce different results depending on the hardware.
The useful takeaway is that you can lock Gazebo’s physics step size, turn off real-time synchronization, and verify that simulation time advances in fixed increments no matter which machine runs the simulation.
Why wall-clock-coupled simulation breaks repeatability
Gazebo distinguishes between wall-clock time—the actual time passing on your computer—and simulation time, the virtual time inside the physics engine. By default, Gazebo tries to keep simulation time close to wall-clock time using a real-time factor. If your computer finishes a physics step quickly, Gazebo may pause or advance extra steps to catch up. That behavior is useful for watching a robot walk in real time, but it makes automated control-loop tests hardware-dependent.
Physics step size is the interval the solver uses to integrate forces and collisions. When that interval changes from run to run, integrators accumulate different rounding errors, and contact events can happen in a different order.
Configure a fixed physics step in SDF
Gazebo worlds are described in SDF (Simulation Description Format), an XML-based format for models, sensors, and physics settings. The following snippet targets Gazebo Sim, the current Gazebo release formerly known as Ignition Gazebo. Tag names can differ in Gazebo Classic, so check your version before copying.
<world name="deterministic_test">
<physics type="ode">
<max_step_size>0.001</max_step_size>
<real_time_factor>0</real_time_factor>
<real_time_update_rate>1000</real_time_update_rate>
</physics>
<!-- Your robot, sensors, and objects go here -->
</world>
Save this as deterministic_world.sdf using any text editor. No special permissions are required; the file lives in your project directory.
max_step_sizefixes the physics integration interval to one millisecond.real_time_factorset to0tells Gazebo not to throttle simulation to wall-clock time.real_time_update_ratetells the engine to target 1,000 physics updates per simulated second.
Launch and verify the clock
Run Gazebo from a terminal where the gz command is on your PATH:
gz sim -v 4 -r deterministic_world.sdf
Required permissions: normal user. The -r flag starts the simulation immediately, and -v 4 prints verbose console output so you can confirm the physics plugin loaded.
Expected check: the Gazebo console should show physics initialization messages without step-size errors. In a second terminal, confirm that simulation time is being published. If you are using the ROS 2 bridge, run:
ros2 topic echo /clock
You should see /clock messages whose simulation time increases by roughly the configured step size on each update. Because real-time synchronization is disabled, the message rate depends on how fast your CPU steps the physics engine, but the increment between messages stays consistent.
Risk: on underpowered hardware, simulation time can lag wall-clock time significantly. That is acceptable for offline testing, but it means the scenario will not run in real time.
Fixed step versus real-time mode
| Mode | Determinism | Wall-clock speed | Best for |
|---|---|---|---|
| Real-time, default factor | Variable across hardware | Matches wall clock | Visualization and human-in-the-loop testing |
| Fixed step, real-time factor off | High across identical world files | As fast as the CPU allows | Repeatable control-loop tests and CI |
| Fixed step, real-time factor one | High if CPU keeps up | Matches wall clock | Hardware-in-the-loop where timing matters |
Worked example: prove two runs match
- Launch the fixed-step world and record the clock topic:
ros2 topic echo /clock > run_a.log
- Stop Gazebo, launch the same world again, and record a second log:
ros2 topic echo /clock > run_b.log
- Compare the logs:
diff run_a.log run_b.log
If the output is empty, the simulation clock advanced identically across runs. Any difference points to non-determinism in the world, a plugin, or the host environment.
Limitation: a fixed clock does not fix every plugin
Locking the physics step makes the solver deterministic, but it does not make every Gazebo plugin deterministic. Plugins that read the actual wall clock, generate random numbers, or depend on unordered ROS 2 callback delivery can still cause run-to-run differences.
Practical check: run the same scenario twice with stochastic plugins disabled or seeded, and compare sensor logs. If the logs match, the remaining variation is likely in plugin logic rather than the physics clock.
Closing
For repeatable robot control tests, treat Gazebo’s physics clock as a configuration problem, not a simulator bug. Set a fixed max_step_size, disable real-time synchronization, and verify with /clock logs. Once two runs produce identical simulation-time traces, you have a stable baseline for testing controllers, comparing world revisions, or running regression tests in continuous integration.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.