Introduction

Pick up almost any drone, phone or game controller and it knows, instantly, which way is up. Inside sits an IMU (inertial measurement unit): a gyroscope that measures how fast it's spinning, and an accelerometer that feels gravity pulling down. Neither sensor is trustworthy alone.

The gyroscope is precise for an instant but drifts: tiny errors pile up every millisecond until "up" quietly wanders away from reality. The accelerometer knows true "down" on average, but every step, vibration or bump adds noise that has nothing to do with orientation.

In 2010, engineer Sebastian Madgwick published a filter that fuses the two into a single rotating quaternion — a stable estimate of orientation, corrected dozens of times per second, cheap enough to run on the tiny processor inside a toy drone.

Try It

Below, a simulated sensor tilts and wobbles while its gyroscope drifts and its accelerometer picks up noisy vibration. The raw needle jitters and slowly wanders off; the filtered needle, run through the Madgwick update, stays close to the true tilt.

<p class="hint">{{hint_para}}</p>
<div class="stage">
  <div class="dial-wrap">
    <canvas id="dialRaw" width="160" height="160"></canvas>
    <span class="dial-label">{{label_raw}}</span>
  </div>
  <div class="dial-wrap">
    <canvas id="dialFiltered" width="160" height="160"></canvas>
    <span class="dial-label">{{label_filtered}}</span>
  </div>
</div>
<div class="controls">
  <label>{{lbl_noise}} <span id="noiseVal">0.30</span>
    <input type="range" id="noise" min="0" max="1" step="0.01" value="0.30">
  </label>
  <label>{{lbl_drift}} <span id="driftVal">0.20</span>
    <input type="range" id="drift" min="0" max="1" step="0.01" value="0.20">
  </label>
  <label>{{lbl_beta}} <span id="betaVal">0.15</span>
    <input type="range" id="beta" min="0" max="0.5" step="0.01" value="0.15">
  </label>
</div>
<div class="btns">
  <button id="toggle" type="button">{{btn_pause}}</button>
  <button id="reset" type="button" class="ghost">{{btn_reset}}</button>
</div>
<div class="status" id="status">{{status_ready}}</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.stage { display: flex; gap: 1.6rem; align-items: center; justify-content: center; margin: .5rem 0 1rem; flex-wrap: wrap; }
.dial-wrap { display: flex; flex-direction: column; align-items: center; gap: .35rem; }
.dial-label { font-size: .8rem; font-weight: 700; color: #1d3557; text-transform: uppercase; letter-spacing: .03em; }
canvas { background: #eef2f5; border: 1px solid #cdd9e3; border-radius: 50%; }
.controls { display: flex; flex-direction: column; gap: .55rem; max-width: 420px; margin: 0 auto .8rem; }
.controls label { font-size: .85rem; font-weight: 600; color: #333; display: flex; flex-direction: column; gap: .2rem; }
.controls input[type="range"] { width: 100%; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; justify-content: center; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
         background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.status { font-size: .85rem; color: #444; margin: .6rem 0 0; text-align: center; min-height: 1.4em; }
// Code not found

Turn up the noise and drift sliders and watch the raw reading fall apart while the filtered one barely flinches. Then drag beta — the correction gain — down toward zero and watch the filter start trusting the drifting gyroscope again, exactly like a real IMU with the wrong tuning.

The Real Complexity

The Madgwick filter is not a search over possibilities — there is nothing to check or brute-force. It is a fixed, tiny calculation repeated at every sample:

  • Predict. Integrate the gyroscope's angular velocity to advance the current orientation quaternion qq by one time step: qω=q+q˙ωΔtq_{\omega} = q + \dot{q}_{\omega}\,\Delta t.
  • Measure the error. Rotate the reference "down" vector by the predicted qq and compare it against what the accelerometer actually feels. The mismatch is an error function f(q,a)f(q, a) — zero exactly when the orientation is consistent with gravity.
  • Descend the gradient. Compute f\nabla f, the analytic gradient of that error with respect to the four quaternion components, and nudge qq a small step down that gradient, scaled by a gain β\beta: qε=βffq_{\varepsilon} = -\beta \cdot \dfrac{\nabla f}{\lVert \nabla f \rVert}.
  • Fuse and normalize. Blend the gyroscope prediction with the gradient correction, then rescale the quaternion back to unit length so it stays a valid rotation.

Every step is a fixed handful of multiplications and one square root — O(1) per sample, no matrix inversion, unlike a full Kalman filter that must propagate and invert a covariance matrix. That is the whole trick: β\beta is a single dial that trades trusting the gyroscope (smooth but drifting) against trusting the accelerometer (noisy but unbiased), and gradient descent finds a good blend every single sample without ever "solving" anything in the traditional sense.

Where It Matters

Fusing a drifting rotation sensor with a noisy reference direction is a problem that shows up anywhere something needs to know its own orientation, cheaply and in real time:

  • Drones and flight controllers: the attitude estimate that keeps a quadcopter level runs a Madgwick-style filter thousands of times a second on a tiny microcontroller.
  • Phones and wearables: screen auto-rotate, step counting and fitness tracking all depend on a stable "which way is this device pointing" signal from cheap MEMS chips.
  • VR and AR headsets: head-tracking latency has to be near zero, so lightweight gradient-descent fusion beats heavier alternatives for the orientation part of the pipeline.
  • Robotics: legged and wheeled robots fuse IMUs the same way to keep their internal sense of "which way is up" from drifting during a mission.

Understand why a single gradient-descent step is enough to fuse two unreliable sensors, and you've met the same idea behind every Kalman filter-based estimator: combine a noisy model with a noisy measurement, weighted by how much you trust each.

Conclusion

Neither the gyroscope nor the accelerometer inside your phone is, on its own, good enough to track orientation. The Madgwick filter's insight is that you don't need a heavyweight solver to fuse them — one small step down an error gradient, applied every sample, is enough to keep a quaternion honest.

So the next time a drone hovers steady in the wind, or your phone's screen flips at exactly the right moment, there's a tiny piece of calculus running underneath, quietly turning noise into a rotation you can trust.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/madgwick-ahrs/Content licensed under CC BY-NC 4.0.