Discrete Kalman Filter IMU Sensor Fusion for Tilt & Orientation
Fuse high-frequency gyroscope angular rates with noisy low-frequency accelerometer tilt angles using a discrete 2-state Kalman filter.
2. Prerequisites & Background Theory:
Matrix algebra, Gaussian noise covariance, discrete linear state-space, and C++ pointer manipulation.
Goal & Engineering Objectives
Estimate true planar roll and pitch tilt angles in real-time on an embedded microcontroller, eliminating gyro drift and accelerometer vibration artifacts.
Governing Mathematical Formulation & Physics
System Architecture & Block Flow Diagram
[MPU-6050 Accelerometer (ax,ay,az)] --(I2C 400kHz)--> [Raw Tilt Calculation]
[MPU-6050 Gyroscope (gx,gy,gz)] --(I2C 400kHz)--> [Rate Integration]
|
[Kalman Predict Step]
[Kalman Innovation (y)]
[Kalman Update Step]
|
v
[Optimal Filtered Angle (theta)]
Required Hardware Bill of Materials (BOM)
| Hardware Component | Qty | Engineering Specification |
|---|---|---|
| Arduino Nano Every or STM32 BluePill | 1 | High-resolution hardware timer, hardware math |
| MPU-6050 6-Axis MotionTracking IMU | 1 | 16-bit ADCs for Gyro and Accel, I2C interface |
| 0.96 inch I2C OLED Display (SSD1306) | 1 | 128x64 pixels, visual pitch/roll horizon display |
| Breadboard & Dupont Jumper Wires | 1 | Short solid-core leads for minimal I2C capacitance |
Wiring, Pinout & Schematics Mapping
Firmware Implementation & Algorithms
// 2-State Discrete Kalman Filter for Tilt Angle Estimation (C++)
class KalmanAngle {
public:
KalmanAngle() {
q_angle = 0.001f;
q_bias = 0.003f;
r_measure = 0.03f;
angle = 0.0f;
bias = 0.0f;
P[0][0] = 0.0f; P[0][1] = 0.0f;
P[1][0] = 0.0f; P[1][1] = 0.0f;
}
float getAngle(float newAngle, float newRate, float dt) {
// Step 1: Predict
float rate = newRate - bias;
angle += dt * rate;
P[0][0] += dt * (dt * P[1][1] - P[0][1] - P[1][0] + q_angle);
P[0][1] -= dt * P[1][1];
P[1][0] -= dt * P[1][1];
P[1][1] += q_bias * dt;
// Step 2: Measurement Update
float S = P[0][0] + r_measure;
float K[2];
K[0] = P[0][0] / S;
K[1] = P[1][0] / S;
float y = newAngle - angle; // Innovation
angle += K[0] * y;
bias += K[1] * y;
float P00_temp = P[0][0];
float P01_temp = P[0][1];
P[0][0] -= K[0] * P00_temp;
P[0][1] -= K[0] * P01_temp;
P[1][0] -= K[1] * P00_temp;
P[1][1] -= K[1] * P01_temp;
return angle;
}
private:
float q_angle, q_bias, r_measure;
float angle, bias;
float P[2][2];
};