Diagnosing Physics Instability in Gazebo: Jittering and Exploding Models with ODE
A diagnostic workflow for Gazebo models that jitter or explode under the ODE physics engine: classify the symptom, check collisions, inertias, mass ratios, step size, and PID gains in order.
09 Mar 2026, 11:00 UTC

Your robot spawns, twitches for a second, then either vibrates in place like a paint mixer or launches itself out of the world. Both symptoms come from the same family of problems: the ODE physics engine is being asked to integrate a system it cannot solve stably at your chosen time step. The fix is almost never "restart Gazebo" — it is finding which of five specific modeling mistakes you have made. This guide assumes Gazebo Classic (gazebo 11) or Gazebo Sim with the default ODE engine; the model-level causes apply to both, though file locations and GUI menus differ slightly.
Recognize the failure mode first
Before changing anything, classify what you see, because the symptom narrows the cause:
- Explosion at spawn time: model flies apart within the first few frames, often before you can interact. Almost always overlapping collision geometry or a bad inertia tensor.
- High-frequency jitter in place: model stays roughly where it is but vibrates visibly. Usually a time step too large for the stiffness/mass of the system, or fighting PID controllers.
- Slow drift or sagging: joints creep under gravity. Usually a solver iteration or friction tuning issue, not covered in depth here.
- Instability only when two specific bodies touch or connect: suspect a high mass ratio across a joint.
Cause and diagnostic table
| Symptom | Likely cause | Quick check |
|---|---|---|
| Explodes at t=0 | Overlapping collision geometries | GUI: View → Collisions, look for interpenetrating shapes |
| Explodes or spins wildly | Zero/invalid inertia tensor | grep the SDF/URDF for <inertia> values of 0 |
| Jitter everywhere | max_step_size too large | Halve the step size; if jitter halves, this is it |
| Jitter at one joint | Mass ratio across joint > ~100:1 | Compare <mass> of parent and child links |
| Oscillation only when controllers run | Aggressive PID gains in ros_control | Stop the controllers; if stable, tune gains down |
Ordered checks, cheapest first
1. Visualize collisions
In the Gazebo GUI, open the View menu and enable Collisions (and Contacts if available in your version). Collision meshes are shown as orange wireframes. Look for two solids occupying the same space at spawn — a common mistake is a link whose visual mesh was duplicated as its collision, or a chassis link whose box overlaps the wheel cylinders it contains. ODE resolves penetration by applying a corrective force proportional to penetration depth; deep initial overlap means a huge force on the first step, which is the "explosion." Fix by shrinking or repositioning the collision elements so nothing interpenetrates at the initial pose.
2. Audit every inertia tensor
Every link with a mass must have a physically valid inertia. Search your model file (run this in a terminal, in your model directory, no special permissions needed):
grep -A6 '<inertia>' model.sdf
Red flags: any of ixx, iyy, izz equal to zero or negative, or values absurdly small relative to mass (e.g., mass 5 kg with ixx 1e-9). A zero diagonal element makes the inertia matrix non-invertible in the rotational dynamics, and the solver produces NaNs or unbounded torques. If you do not have CAD-derived values, compute a box or cylinder approximation from the link's dimensions — an approximate tensor is far better than a wrong one. Sanity check: for a solid box of mass m and sides a, b, c, ixx = m(b²+c²)/12.
3. Check mass ratios across joints
ODE's constraint solver struggles when a joint connects bodies with very different masses — a rule of thumb is to keep the ratio under roughly 100:1. A 0.1 kg camera bolted to a 50 kg base via a fixed joint is a classic instability source. Fixes, in order of preference: give the small link a more realistic mass (a real camera is not 1 gram), merge the bodies with a fixed joint reduction if your toolchain supports it, or add joint damping to bleed off the oscillation energy.
4. Reduce the physics step size
If the model is clean but still jitters, the integrator is undersampling the system's fastest dynamics. In your world file (edit with any text editor; you need write permission on the world file, and the change takes effect on the next simulation start):
<physics type="ode">
<max_step_size>0.0005</max_step_size>
<real_time_update_rate>2000</real_time_update_rate>
</physics>
The default is typically 0.001 s with a 1000 Hz update rate. Halving the step size while doubling the update rate keeps the same real-time target but doubles CPU cost per simulated second. Verify the cost: watch the real-time factor (RTF) shown in the GUI status bar or printed by a headless server. If the RTF drops well below 1.0, your machine cannot sustain the smaller step and the simulation now runs slower than real time — you must instead soften the model (lower stiffness, more damping, simpler collisions) rather than shrink the step further.
5. Isolate controller-induced oscillation
If instability appears only when gazebo_ros_control (or your controller plugin) is active, the physics may be fine and the PID loop is the oscillator. Test: launch the world without spawning controllers. If the model sits stably, reduce the P gain first, then add derivative gain to damp the response. A PID tuned at a 1 kHz physics rate will oscillate if you later change the step size, so re-tune after any step-size change.
Fixes to avoid
- Cranking up global damping: it can hide jitter while making everything move as if underwater. Treat it as a diagnostic, not a fix.
- Tiny contact margins or very high contact stiffness (kp): these raise the system's effective stiffness and force you into ever-smaller step sizes, or cause tunneling and erratic bounces.
- Shrinking the step size blindly: past a point you are paying CPU for no stability gain because the real problem is a bad inertia or overlap.
Escalation criteria
Stop tuning and escalate (to a colleague, the model's upstream maintainer, or a Gazebo community forum with your SDF attached) when: the model is stable alone but unstable in a multi-robot world; instability persists at step sizes below 0.0001 s with valid inertias and no overlaps; or NaN poses appear in the topic stream (gz topic -e /gazebo/default/model_states on Gazebo Classic, run from any terminal) even at spawn with controllers disabled. Those patterns suggest a solver configuration or plugin-level bug rather than a modeling error, and further parameter twiddling will not converge.
Limitations
Exact menu names, default step sizes, and topic commands vary between Gazebo Classic and the newer Gazebo Sim releases; verify against your installed version's documentation before scripting around them. The 100:1 mass-ratio guideline is a heuristic, not a hard threshold — marginal cases need empirical testing with the RTF and visual jitter as your acceptance criteria.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.