Sim-to-Real Troubleshooting

A cookbook of symptoms → likely causes → fixes. When a deployment goes sideways, start here.

High-frequency joint oscillation / “buzz”

Likely cause

Check

Fix

State estimator velocity bias

Log base_lin_vel vs ground truth (motion capture)

Tune the state estimator

PD gains too high vs trained

Compare driver Kp/Kd against owner YAML

Match training values; or retrain with realistic Kp/Kd DR

Action latency too low in training

Sweep torque_delay_ms in DR

Retrain with measured-latency × 1.5

Velocity noise too low

Compare encoder σ in sim vs hardware

Increase joint_vel_noise_std in DR

Drift while standing / walking

Likely cause

Check

Fix

IMU bias not calibrated

Robot static, check gyro_bias

Run 30-second calibration before policy start

Foot contact misclassified

Check contact event timestamps

Hysteresis on contact force threshold

Policy succeeds in sim, falls on hardware immediately

Almost always one of:

  1. Joint order swapped. Inspect policy.onnx input width and the joint order in your motor driver. Use unilab-export-scene to dump the training joint order.

  2. Action scale unit mismatch. Policy outputs unscaled values; the driver expects rad, but you fed it normalized [-1, 1]. Apply the env.actions.joint_pos.scale / default-angle convention from the training owner YAML before sending targets to the driver, reproducing the owner’s resolved per-actuator scale exactly.

  3. Observation layout mismatch. Compare what your hardware loop assembles against the owner’s env.observations.actor.terms — term order first, then per-term history ordering.

Cube drops in Allegro inhand

Likely cause

Check

Fix

Friction mismatch

Real cube vs sim μ

Sweep friction DR wider, retrain

Grasp distribution mismatch

Log operator grip poses

Augment grasp generator

Pose estimator latency

Measure vision pipeline ms

Add observation lag DR

Policy succeeds in MuJoCo but fails in Motrix (or vice versa)

That’s a sim-to-sim problem, not sim-to-real. See Aligning Contact and Friction Between Backends and Reward Parity Across Backends.

What to capture before opening a bug report

When the lab WhatsApp blows up at 11 pm, save the following so the investigation tomorrow takes 30 minutes instead of 4 hours:

  • Full hardware trace (obs / action / wall_clock for the entire run).

  • Sim-side YAML used to train: runs/<run>/config.yaml.

  • policy.onnx and the exact task owner YAML path it was trained from.

  • One sim rollout video using the same seed: eval --seed <same> --render-mode record.

  • A git diff between the run’s commit and main if any.

See also