Skip to main content

Tuning guide

All tunable parameters live in m5teleop/m5teleop/config.py.

GroupParameterDefaultDescription
EKFEKF_SIGMA_GYRO0.005 rad/sGyro noise std
EKFEKF_SIGMA_BIAS0.0001 rad/s²Bias-drift noise std
EKFEKF_SIGMA_ACC0.05 gAccelerometer noise std
EKFEKF_ACC_GATE0.30 gSkip accel update if |‖a‖−1| > this
EKFEKF_ZARU_THRESHOLD0.08 rad/sZARU active when |ω| < this
EKFEKF_SIGMA_ZARU0.02 rad/sZARU measurement noise
ControllerORIENT_KP_OUTER2.5 /sOuter error→ω gain
ControllerORIENT_KP_INNER0.5Inner velocity-error gain
ControllerORIENT_MAX_OMEGA1.2 rad/sOutput clamp
IKPOSTURE_COST0.1Neutral-pose regularization
LoopCONTROL_HZ50Loop rate

Symptom → fix​

  • Arm oscillates / overshoots → lower ORIENT_KP_OUTER (e.g. 1.5) or ORIENT_MAX_OMEGA.
  • Arm too sluggish → raise ORIENT_KP_OUTER; if still slow, raise ORIENT_KP_INNER.
  • Small tremors when holding still → raise EKF_ZARU_THRESHOLD to suppress more motion, or lower EKF_SIGMA_ZARU to trust ZARU more.
  • EKF yaw drifts in the controller → confirm ZARU is active (|ω| < EKF_ZARU_THRESHOLD while stationary); raise EKF_SIGMA_GYRO if the gyro is noisier than expected.
  • IK jumps to strange poses → increase POSTURE_COST.
  • Servo overload warning → lower MAX_RELATIVE_TARGET.
  • Tracking error stays large after zero-reset → confirm the console prints [OrientController] Zero reset; if not, the on_zero_reset callback isn't wired up.

Automated EKF tuning (tune_ekf.py)​

Rather than hand-tuning the EKF constants, m5teleop/tune_ekf.py finds the parameter set that minimizes stationary yaw drift and pitch/roll error:

ModeDescription
liveStream live orientation/bias/ZARU status in real time
stationaryRecord N seconds of still data; score the current config.py
sweepGrid-search ~240 combinations offline; print the top 5 + a config snippet
optimizeNelder-Mead fine-tune from the sweep winner (requires scipy)

Recommended offline workflow:

cd m5teleop
python tune_ekf.py stationary --duration 90 --save rec.npz # 1. record (device must be still)
python tune_ekf.py sweep --load rec.npz # 2. grid search
python tune_ekf.py optimize --load rec.npz # 3. fine-tune (optional)

No hardware handy? --dry-run runs any of the above against synthetic bias data.

On a synthetic benchmark (1.5 dps Z-axis bias, 30 s), a sweep improved score from 15.77 to 7.18 — yaw drift from 4.4°/min down to 1.6°/min.

Known limitations​

  • No magnetometer fusion — yaw can still drift during sustained rotation; ZARU only corrects bias while the device is at rest. For long sessions, an external heading reference (e.g. an ArUco marker) would be needed.
  • Linear position teleoperation isn't implemented — the controller drives orientation only; the end-effector position stays at its value from the last zero-reset.