What Is an IMU?
An Inertial Measurement Unit (IMU) is like a drone's inner ear — it tells the drone how it's moving through space without needing external references. Just as your inner ear helps you balance and know which way is up, an IMU provides the orientation and motion data a drone needs to stay stable.
A typical drone IMU contains three types of sensors:
- Accelerometer — measures linear acceleration (how fast speed is changing)
- Gyroscope — measures angular velocity (how fast it's rotating)
- Magnetometer — measures magnetic field direction (compass heading)
The 6-Axis IMU
A 6-axis IMU combines a 3-axis accelerometer and a 3-axis gyroscope into a single chip. The 7th and 8th axes come from the magnetometer.
Accelerometer Deep Dive
An accelerometer measures proper acceleration — the acceleration relative to freefall. When sitting on a table, it reads +9.81 m/s² upward (countering gravity). Think of it like the feeling you get in an elevator: you feel heavier accelerating up, lighter when accelerating down.
Key characteristics:
- Great for detecting gravity direction (tilt sensing)
- Noisy during fast movements (motor vibrations)
- Drift-free over long periods for gravity reference
# Simulated accelerometer reading
ax, ay, az = 0.12, -0.05, 9.78 # m/s²
# Calculate roll and pitch from accelerometer
roll = np.arctan2(ay, az) # ~ -0.005 rad (-0.3°)
pitch = np.arctan2(-ax, np.sqrt(ay**2 + az**2)) # ~ 0.012 rad (0.7°)
print(f"Roll: {np.degrees(roll):.1f}°")
print(f"Pitch: {np.degrees(pitch):.1f}°")
Gyroscope Deep Dive
A gyroscope measures how fast something is spinning around each axis. Think of a spinning top — the faster it spins, the higher the angular velocity reading. Unlike the accelerometer, gyroscopes are clean during fast motion but slowly drift over time due to bias errors.
Key characteristics:
- Excellent for short-term rotation tracking
- Low noise during dynamic maneuvers
- Suffers from bias drift (integrates error over time)
# Simulated gyroscope reading (degrees per second)
gx, gy, gz = 15.2, -3.8, 0.5 # °/s
# Integrate to get angle change over dt
dt = 0.01 # 100Hz sampling
angle_change = gz * dt # 0.005° around Z-axis
print(f"Yaw rate: {gz}°/s")
print(f"Angle change in {dt}s: {angle_change:.4f}°")
Magnetometer: The Digital Compass
The magnetometer detects Earth's magnetic field to provide heading information. It's like a compass in your phone — it tells you which direction you're facing. However, it's sensitive to nearby metal objects and electromagnetic interference from motors.
Sensor Fusion: Combining the Best of Each
No single sensor is perfect. Sensor fusion combines all three to get the best estimate:
| Sensor | Strength | Weakness |
|---|---|---|
| Accelerometer | Stable long-term (gravity) | Noisy during motion |
| Gyroscope | Clean during motion | Drifts over time |
| Magnetometer | Provides heading | Sensitive to interference |
Complementary Filter
The complementary filter is a simple yet effective fusion approach. It uses a high-pass filter on gyroscope data (trust gyro for fast changes) and a low-pass filter on accelerometer data (trust accel for slow/steady changes).
class ComplementaryFilter:
def __init__(self, alpha=0.96):
self.alpha = alpha
self.angle = 0.0
def update(self, gyro_angle, accel_angle, dt):
# High-pass on gyro (fast response), low-pass on accel (drift correction)
self.angle = self.alpha * (self.angle + gyro_angle * dt) + \
(1 - self.alpha) * accel_angle
return self.angle
# Usage
filter = ComplementaryFilter(alpha=0.96)
for gyro, accel in zip(gyro_readings, accel_readings):
angle = filter.update(gyro, accel, dt=0.01)
Kalman Filter: The Gold Standard
The Kalman filter is more sophisticated — it maintains an estimate of the state and its uncertainty, then optimally combines predictions with measurements.
When to Use Each Filter
| Filter | Complexity | CPU Usage | Accuracy | Best For |
|---|---|---|---|---|
| Complementary | Low | Very Low | Good | Simple drones, resource-limited |
| Madgwick | Medium | Low | Very Good | Most drone applications |
| Kalman | High | Medium | Excellent | Precision navigation, GPS-denied |
Hands-On Project: IMU Data Logger
Build a basic IMU data logger using an MPU-6050 sensor with Raspberry Pi Pico:
from machine import I2C, Pin
class MPU6050:
def __init__(self, i2c, addr=0x68):
self.i2c = i2c
self.addr = addr
# Wake up sensor
self.i2c.writeto_mem(self.addr, 0x6B, b'\x00')
# Set accelerometer range ±2g
self.i2c.writeto_mem(self.addr, 0x1C, b'\x00')
# Set gyroscope range ±250°/s
self.i2c.writeto_mem(self.addr, 0x1B, b'\x00')
def read_accel(self):
data = self.i2c.readfrom_mem(self.addr, 0x3B, 6)
ax, ay, az = struct.unpack('>hhh', data)
return ax/16384.0, ay/16384.0, az/16384.0
def read_gyro(self):
data = self.i2c.readfrom_mem(self.addr, 0x43, 6)
gx, gy, gz = struct.unpack('>hhh', data)
return gx/131.0, gy/131.0, gz/131.0
# Initialize
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
mpu = MPU6050(i2c)
# Log data for 10 seconds
print("timestamp,ax,ay,az,gx,gy,gz")
start = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), start) < 10000:
ax, ay, az = mpu.read_accel()
gx, gy, gz = mpu.read_gyro()
t = time.ticks_diff(time.ticks_ms(), start) / 1000.0
print(f"{t:.3f},{ax:.3f},{ay:.3f},{az:.3f},{gx:.1f},{gy:.1f},{gz:.1f}")
time.sleep_ms(10)
Project Extensions:
- Add a simple complementary filter to compute real-time tilt
- Log data to SD card for post-flight analysis
- Visualize live data using a web dashboard
- Compare raw vs filtered readings during aggressive maneuvers
Key Takeaways
- An IMU provides 6-DOF motion sensing (accelerometer + gyroscope)
- Accelerometers are stable long-term but noisy; gyroscopes are clean but drift
- Sensor fusion combines strengths of each sensor for accurate orientation
- Start with a complementary filter; upgrade to Kalman/Madgwick for precision
- Calibration (bias removal) is essential before any filtering