IMU fusion and encoders
Fuse a gyro, accelerometer and magnetometer five ways side by side in 3-D — gyro-only, accelerometer-only, complementary, Mahony and Madgwick — from a simulated IMU, your phone or an MPU-6050/ICM-20948/BNO085 over Web Serial; calibrate gyro bias, the accelerometer and the magnetometer's hard and soft iron; and learn quadrature encoders, speed estimation, Gray code, AS5600/AS5048 and wheel odometry with gyro fusion.
Added
Works with
- Kalman filter playground Mathematicsthe Kalman-filter view of the same tilt problem, with a gyro-bias state and every matrix shown
- Compass and level Sensorsa phone compass and spirit level built on the same orientation sensors and tilt-compensated heading
- Phone seismograph Sensorsthe phone's accelerometer as a vibration sensor, with gravity filtered out instead of used
- Sensor register decoder IoT and embeddeddecodes the MPU-6050's raw register bytes and builds its configuration registers
- Serial monitor and ESP flasher IoT and embeddeda general Web Serial terminal and plotter to check the sketches' output first
- Stepper motors CNC and 3D printingsteps per mm and step rates — the open-loop side of what an encoder measures
How to use it
A gyroscope, an accelerometer and a magnetometer each know part of how something is oriented, and
each is wrong in its own way. This page puts five ways of combining them side by side on the same
samples, so you can watch the differences: gyro-only integration drifts away by its bias, the
accelerometer alone is noisy and fooled by every acceleration, and the fusion filters keep the best
of both. Then it calibrates the sensors, and turns to the other half of motion sensing — encoders,
from the A/B waveforms of a quadrature disc to wheel odometry. Each tab has its own address:
/tools/imu-encoders/#imu, #cal, #encoders, #odometry.
How to use it
-
Live IMU — choose a source. Simulated needs nothing: pick a motion (wobble, spin, tumble) or choose Drag it and turn the truth model in the top-left cell with the mouse or a finger. This phone reads the phone’s gyro and accelerometer (an iPhone asks for permission first). Web Serial reads a microcontroller: open Arduino / ESP32 sketches, flash the one for your sensor, press Connect and pick the port.
-
Watch the six cells: the reference (the simulation’s truth, or the phone’s own built-in fusion), then gyro only, accelerometer only, complementary, Mahony and Madgwick. Each shows its roll, pitch and yaw and, when there is a truth, its error in degrees. The plot below shows the error (or one angle) for the last 20 seconds; the table gives the quaternion and Euler angles.
-
Tune the filters: raise the gyro bias and watch gyro-only drift and Mahony’s integral term learn the bias; add linear acceleration and watch accelerometer-only (and a low α) get thrown around; untick Use the magnetometer and see yaw left to the gyro.
-
Gimbal lock — move the pitch slider to 90°. The yaw and roll rings line up, the two sliders do the same thing, and converting the orientation back to Euler angles can only give their difference. The yaw-rate readout shows why Euler-angle integrators blow up there.
-
Calibration — measure the gyro bias by keeping the sensor still; record the accelerometer in six positions (each axis up, then down); collect magnetometer samples while turning the sensor through every direction, and fit a sphere (hard iron only) or an ellipsoid (hard and soft iron). The point cloud shows the raw samples in red and the calibrated ones on a sphere in green, and the results come out as C code. Tick Apply these calibrations to use them on the Live IMU tab. With the simulated source, first turn on Uncalibrated accelerometer and magnetometer on the Live IMU tab so there is something to find.
-
Encoders has five parts. Quadrature: a slotted disc turns past two sensors; watch A, B and the index pulse, the 2-bit state step round the state machine, and the count in x1, x2 or x4. Turn on contact bounce, inject a noise spike, or make the decoder miss a state, and compare the table decoder with a naive rising-edge interrupt. Resolution: lines, decoding, gearing and a leadscrew or wheel give counts per turn, the angle and travel per count, the frequencies at your top speed and the interrupt load on your microcontroller, with the hardware counter to use instead. Speed: the M, T and M/T methods' errors against speed, with simulated readings. Absolute: binary and Gray code discs with misaligned read heads, and decoders for the AS5600 and AS5048A/B magnetic encoders. Live serial: plot a real encoder (a sketch is provided).
-
Dead reckoning — a differential-drive robot drives a square, the UMBmark test, circles or a slalom. Give it a slightly bigger wheel, a wrong wheelbase, slip, and a gyro with bias, and compare wheel-only odometry, wheel distance with gyro heading, and a fused estimate that re-learns the gyro bias whenever the robot stops.
The filters
All five use the same frames: the quaternion turns body coordinates into earth coordinates, the earth’s x axis is magnetic north and z is up, and a resting accelerometer reads +1 g along the body’s up axis.
-
Gyro only integrates the body rate exactly, q ← q ⊗ exp(ω·dt/2). A 1 °/s bias is 60° a minute.
-
Accelerometer only takes roll = atan2(ay, az) and pitch = atan2(−ax, √(ay² + az²)), and yaw from the tilt-compensated magnetometer (or zero). It is only right when the sensor is not accelerating.
-
Complementary: integrate the gyro, then turn the estimate a fraction 1 − α of the way to what the accelerometer says is up (about a horizontal axis, so it never touches yaw) and, with a magnetometer, that fraction of the way to north about the vertical. α is per sample, so the time constant is τ = α·dt / (1 − α).
-
Mahony (Mahony, Hamel & Pflimlin, 2008, as in Mahony’s AHRS code): the cross product between measured and predicted gravity (and field) is an error that feeds back into the gyro rate through a PI controller, ω′ = ω + Kpe + Ki∫e. The integral learns the gyro bias, which the page shows.
-
Madgwick (Madgwick, 2010): one normalised gradient-descent step per sample on the misfit between the predicted and measured gravity and field, q̇ = ½ q ⊗ ω − β ∇f/‖∇f‖. β is the rate (rad/s) at which it may correct; Madgwick suggests √(3/4) times the gyro’s error rate. The Jacobian here is derived for a field of [bx, 0, bz] and tested against finite differences.
The magnetometer’s field reference is rebuilt each sample from the measurement rotated into the
earth frame (its horizontal magnitude and its vertical part), so a wrong inclination does not tilt
the estimate. On a phone, browsers do not give the raw magnetometer (Chrome on Android has a
Magnetometer behind a flag, used when it exists); otherwise the page makes a virtual
magnetometer from the phone’s own compass heading, which shows how yaw fusion behaves but is not
an independent sensor.
Calibration methods
-
Gyro bias: the mean of a still recording; the standard deviation is the white noise, and the bias is known to about σ/√N. MEMS gyro bias changes with temperature — measure it at start-up.
-
Accelerometer: with each axis up then down, offset = (up + down)/2 and scale = (up − down)/2 for that axis (the method in ST’s AN3192 and similar app notes). Corrected = (raw − offset) / scale.
-
Magnetometer: a least-squares fit of the general quadric ax² + by² + cz² + 2fyz + 2gxz + 2hxy
2px + 2qy + 2rz = 1 to the samples, after centring and scaling them. The centre is the hard-iron offset; the shape matrix M gives the soft-iron correction W = r·√M (its symmetric square root, scaled to the geometric-mean field strength r), so W(raw − centre) lies on a sphere (as in NXP’s AN4246). Coverage counts how many of 26 directions have a sample within 30°; below about 85 % the ellipsoid is poorly constrained, and a sphere fit is the safer choice.
Encoders: assumptions and sources
-
The quadrature state is (A, B) with A leading B for forward rotation: 00 → 10 → 11 → 01. The 16-entry table is +1 for forward steps, −1 for reverse, 0 for no change and invalid when both lines change. x2 counts only A’s transitions, x1 only the 00 ↔ 10 pair, both with the table’s sign, which makes them bounce-proof too. The Arduino sketch uses exactly this table.
-
Interrupt costs in the resolution panel are rough figures for a lean ISR (including entry and exit) — measure yours with a scope on a spare pin.
-
Speed: the M method’s error is one count in N (N edges per window), the T method’s one clock tick in m (ticks per edge), the M/T method’s (Ohmae et al., IEEE Trans. Industrial Electronics, 1982) one tick over the whole window. The dots are simulated readings at a random phase.
-
AS5600: datasheet register map (RAW ANGLE 0x0C/0x0D, STATUS bits MD/ML/MH, AGC, MAGNITUDE, ZPOS/MPOS). AS5048A: 16-bit SPI frames with even parity in bit 15 and the error flag in bit 14; reading the angle is the command 0xFFFF. AS5048B: I²C angle registers 0xFE/0xFF.
-
Odometry uses the midpoint heading; the truth integrates exact arcs. The fused estimate blends the bias-corrected gyro with the wheels' turn rate (weight α on the gyro) and re-estimates the bias whenever the robot is stopped. UMBmark is from Borenstein & Feng, 1996.
The engine, imu-encoders-core.js, is plain JavaScript with no libraries. Its tests check
quaternion algebra and Euler round trips (including the gimbal-lock branch), exact gyro
integration, each filter converging to a known orientation from synthetic data, Mahony’s integral
recovering a gyro bias, Madgwick’s Jacobian, the six-position and ellipsoid calibrations recovering
known offsets and soft-iron matrices, the quadrature decoder on sequences with bounce and invalid
transitions, Gray code, the AS5600/AS5048 decoders, the speed estimators against their
quantisation bounds, and the odometry.
Browser support and privacy
The simulation, calibration and encoder demos work in any current browser. The phone source needs a phone with a gyroscope (Safari on iPhone asks for motion access; Chrome on Android starts by itself). Web Serial needs desktop Chrome, Edge or Opera, and only one program can open a port at a time. Everything runs in your browser; sensor readings never leave the page, and your settings and calibrations are kept in this browser only.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser; nothing you type leaves this page.