Diagnosing Low FPS in Gazebo Classic 11: An ODE Physics Performance Guide
A diagnostic guide for Gazebo Classic 11 users experiencing low FPS and real-time factor below 1.0 with the ODE physics engine. Identify causes, run ordered checks, and apply targeted fixes.
16 Sept 2026, 15:21 UTC

Recognizable Condition
If your Gazebo Classic 11 simulation runs slower than wall-clock time—visible as choppy robot motion, delayed sensor topics, or a real-time factor (RTF) below 1.0—the bottleneck is usually the ODE physics engine, the rendering pipeline, or both. RTF is the ratio of simulated time to real elapsed time; a value of 0.5 means the simulator advances half as fast as reality. Low frames per second (FPS) in the GUI is a symptom, not a root cause, so the first task is to separate physics load from rendering load.
Cause and Diagnostic Table
| Finding | Likely Cause | Diagnostic Signal |
|---|---|---|
| RTF below 1.0 with high CPU usage | ODE solver iterations too high for the model complexity | Physics step time approaches or exceeds the target step size |
| Jittery or sticky contacts | Complex collision meshes generating excessive contact points | High message rate on /gazebo/default/contact_info |
| Slowdown scales with robot count | Too many dynamic bodies for available CPU cores | System CPU saturation; low parallelism in gz stats |
| GUI sluggish but headless run is fast | GPU-bound rendering (high-poly visuals, shadows, large textures) | Large RTF improvement when running without the GUI |
Ordered Checks
Baseline the real-time factor. In a terminal with the Gazebo environment sourced, run:
gz topic -e /gazebo/default/performance_metricsExpected check: the stream reports
real_time_factor. If it stays below 1.0, the simulation is compute-bound. This command is read-only and requires only normal user permissions.Inspect solver step time. Watch the console for ODE step-size messages, or launch with verbose logging:
gz sim --verbose <world_file>Replace
<world_file>with your.worldpath. Expected check: if the actual step time is close to the maximum step size, the solver is struggling to keep up.Count active contacts. Run:
gz topic -e /gazebo/default/contact_infoExpected check: a large number of simultaneous contact points usually indicates that collision geometry is too detailed.
Review visual complexity. Open the SDF world file and count mesh references, texture sizes, and shadow settings. High-resolution visual meshes do not affect physics, but they can saturate the GPU and indirectly slow the GUI update loop.
Isolate rendering impact. Run the same scenario headless:
gz sim -s --headless <world_file>Expected check: compare the headless RTF to the GUI RTF. A significant improvement points to a GPU bottleneck; little change points to a physics or CPU bottleneck.
Fixes Tied to Findings
High solver iterations or step time
Reduce ODE solver iterations or enable quick step in the world file. The following fragment illustrates the kind of tuning available; confirm exact tag names against your SDF schema because Gazebo Classic 11 accepts multiple SDF versions.
<physics type="ode">
<max_step>0.001</max_step>
<real_time_update_rate>1000</real_time_update_rate>
<ode>
<solver>
<iters>20</iters>
</solver>
<quick_step>true</quick_step>
</ode>
</physics>
Risk: lowering iterations or switching to quick step reduces constraint accuracy. Validate with a representative contact-rich scenario before production use.
Excessive contact points
Replace detailed collision meshes with primitive shapes or convex hulls. Keep the visual mesh separate so appearance is preserved while physics cost drops.
<collision name="collision">
<geometry>
<box>
<size>1.0 1.0 1.0</size>
</box>
</geometry>
</collision>
Expected check: the message rate on /gazebo/default/contact_info should fall after reloading the world.
GPU-heavy visuals
Reduce mesh resolution, shrink textures, and disable shadows in the SDF or GUI. If the headless test showed a large gap, this is the highest-leverage change.
CPU saturation from many models
Review <real_time_update_rate>; if it is set higher than the physics engine can deliver, lower it to reduce step backlog. Alternatively, reduce the number of simultaneously active dynamic models or increase available CPU cores.
Continuous integration and batch runs
Use headless mode for automated testing to remove GPU load entirely. Because this changes how you launch the simulator, document the command in your CI configuration so visual debugging remains available locally.
Escalation Criteria
If RTF remains below 0.8 after applying the fixes above, escalate by profiling with gz sim --verbose or a custom plugin that reports gazebo::physics::World::GetPhysicsEngine()->GetStepTime(). At that point, consider switching the physics engine to Bullet or Simbody in the SDF <physics> block and re-running the baseline test. If performance is still unacceptable, collect the verbose logs and share them with the Gazebo community, including your SDF world file and a minimal reproduction case.
Verification and Limitations
Baseline test: launch an empty world containing a single unit cube and record the RTF from /gazebo/default/performance_metrics. Use this as a reference after each change.
Rendering isolation: run the identical scenario headless and compare RTF; a large delta confirms GPU-bound behavior.
Solver confirmation: enable verbose logging and confirm that iteration counts or step times change as intended.
Limitations: these recommendations target Gazebo Classic 11 with ODE. Gazebo Sim (the Ignition-based successor) uses different SDF tags and physics APIs, so do not apply these fixes directly there. Solver parameter changes can affect contact stability; always keep a backup of the original world file so you can revert if behavior degrades.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.