11using StaticArrays
2+ export IMUKalmanFilter, predict!, correct!, update!, compute_angles, bias, angle
23
34# System: x = [angle; bias], dynamics: ẋ = A_c*x + B_c*u, measurement: y = C*x
45# Continuous: A_c = [0 -1; 0 0], B_c = [1; 0]
@@ -11,75 +12,72 @@ const Cd = @SMatrix [1.0f0 0.0f0] # Measurement matrix (1×2)
1112const I2 = @SMatrix [1.0f0 0.0f0 ; 0.0f0 1.0f0 ] # Identity for covariance update
1213
1314"""
14- DiscreteKalmanFilter
15+ IMUKalmanFilter
1516
1617Discrete-time Kalman filter for angle estimation from gyro (input) and accelerometer (measurement).
1718State is `[angle, gyro_bias]`.
1819"""
19- mutable struct DiscreteKalmanFilter
20+ mutable struct IMUKalmanFilter
2021 x:: SVector{2, Float32} # State [angle, bias]
2122 P:: SMatrix{2, 2, Float32, 4} # Covariance
2223 const Q:: SMatrix{2, 2, Float32, 4} # Process noise covariance
2324 const R:: SMatrix{1, 1, Float32, 1} # Measurement noise covariance
2425end
2526
2627"""
27- DiscreteKalmanFilter (; Q_angle=0.001f0, Q_bias=0.005f0, R_angle=0.5)
28+ IMUKalmanFilter (; Q_angle=0.001f0, Q_bias=0.005f0, R_angle=0.5)
2829
2930Create a discrete Kalman filter with specified noise covariances.
3031Default values from BalanceCar.h.
3132"""
32- function DiscreteKalmanFilter (; Q_angle:: Float32 = 0.001f0 , Q_bias:: Float32 = 0.005f0 , R_angle:: Float32 = 0.5f0 )
33+ function IMUKalmanFilter (; Q_angle:: Float32 = 0.001f0 , Q_bias:: Float32 = 0.005f0 , R_angle:: Float32 = 0.5f0 )
3334 x = @SVector zeros (Float32, 2 )
3435 P = I2
3536 Q = @SMatrix [Q_angle 0.0f0 ; 0.0f0 Q_bias]
3637 R = @SMatrix [R_angle;;]
37- DiscreteKalmanFilter (x, P, Q, R)
38+ IMUKalmanFilter (x, P, Q, R)
3839end
3940
4041"""
4142 predict!(kf, u)
4243
43- Prediction step: propagate state and covariance using gyro measurement `u` as input.
44-
45- x = A * x + B * u
46- P = A * P * A' + Q
44+ Prediction step: propagate state and covariance using gyro tilt rate (x-axis) measurement `u` as input.
4745"""
48- @inline @fastmath function predict! (kf:: DiscreteKalmanFilter , u:: Float32 )
49- kf. x = Ad * kf. x + Bd * u
50- kf. P = Ad * kf. P * Ad' + kf. Q
46+ @inline @fastmath function predict! (kf:: IMUKalmanFilter , u:: Float32 )
47+ kf. x = Ad* kf. x + Bd* u
48+ kf. P = Ad* kf. P* Ad' + kf. Q
5149 nothing
5250end
5351
5452"""
5553 correct!(kf, y)
5654
5755Update step: correct state and covariance using accelerometer angle measurement `y`.
58-
59- S = C * P * C' + R
60- K = P * C' / S
61- x = x + K * (y - C * x)
62- P = (I - K * C) * P
6356"""
64- @inline @fastmath function correct! (kf:: DiscreteKalmanFilter , y:: Float32 )
57+ @inline @fastmath function correct! (kf:: IMUKalmanFilter , y:: Float32 )
6558 # Innovation covariance (1×1 matrix)
66- S = Cd * kf. P * Cd' + kf. R
59+ S = Cd* kf. P* Cd' + kf. R
6760
6861 # Kalman gain (2×1)
69- K = kf. P * Cd' / S[1 , 1 ]
62+ K = kf. P* Cd' / S[1 , 1 ]
7063
7164 # Innovation (scalar wrapped as 1×1 for matrix multiply)
72- innovation = SA[y] - Cd * kf. x
65+ innovation = SA[y] - Cd* kf. x
7366
7467 # State update
75- kf. x = kf. x + K * innovation
68+ kf. x = kf. x + K* innovation
7669
7770 # Covariance update
78- kf. P = ( I2 - K * Cd) * kf. P
71+ kf. P = symmetrize (( I2 - K * Cd) * kf. P)
7972
8073 nothing
8174end
8275
76+ function symmetrize (P)
77+ m = P[2 ,1 ] + P[1 ,2 ]
78+ SA[P[1 ,1 ] m; m P[2 ,2 ]]
79+ end
80+
8381"""
8482 update!(kf, u, y)
8583
@@ -88,7 +86,7 @@ Combined predict + correct step.
8886- `u`: gyro measurement (angular velocity)
8987- `y`: accelerometer angle measurement
9088"""
91- function update! (kf:: DiscreteKalmanFilter , u:: Float32 , y:: Float32 )
89+ function update! (kf:: IMUKalmanFilter , u:: Float32 , y:: Float32 )
9290 predict! (kf, u)
9391 correct! (kf, y)
9492 nothing
9997
10098Get the estimated angle from the filter state.
10199"""
102- angle (kf:: DiscreteKalmanFilter ) = kf. x[1 ]
100+ angle (kf:: IMUKalmanFilter ) = kf. x[1 ]
103101
104102"""
105103 bias(kf)
106104
107105Get the estimated gyro bias from the filter state.
108106"""
109- bias (kf:: DiscreteKalmanFilter ) = kf. x[2 ]
107+ bias (kf:: IMUKalmanFilter ) = kf. x[2 ]
110108
111109# IMU calibration constants (from original C++ code)
112110const GYRO_OFFSET = 128.1f0
@@ -117,7 +115,7 @@ const GYRO_SCALE = 131.0f0
117115
118116Apply calibration to raw IMU readings and compute
119117- `angle`: Tilt angle from accelerometer (radians)
120- - `gyro_x`: Calibrated gyro x (angular velocity) for tile derivative control
118+ - `gyro_x`: Calibrated gyro x (angular velocity) for tilt derivative control
121119- `gyro_z`: Calibrated gyro z (angular velocity) for turn derivative control
122120
123121Applies calibration to gyro readings and computes angle from accelerometer.
@@ -131,11 +129,11 @@ That is, the gyro x reading is used as control input, and the angle computed fro
131129
132130
133131# Arguments
134- - `kf`: DiscreteKalmanFilter instance
132+ - `kf`: IMUKalmanFilter instance
135133- `ax, ay, az`: Raw accelerometer readings (int16)
136134- `gx, gy, gz`: Raw gyroscope readings (int16)
137135"""
138- function compute_angles (kf:: DiscreteKalmanFilter , ax:: Integer , ay:: Integer , az:: Integer ,
136+ function compute_angles (kf:: IMUKalmanFilter , ax:: Integer , ay:: Integer , az:: Integer ,
139137 gx:: Integer , gy:: Integer , gz:: Integer )
140138 # Calculate angle from accelerometer (radians to degrees)
141139 angle = atan (ay, az) * (180.0f0 / pi )
@@ -150,13 +148,3 @@ function compute_angles(kf::DiscreteKalmanFilter, ax::Integer, ay::Integer, az::
150148 angle, gyro_x, gyro_z
151149end
152150
153-
154-
155- # test
156- f = DiscreteKalmanFilter ()
157-
158- m_gyro_x = 0.1f0 # rad/s
159- m_angle = 0.05f0 # rad
160- update! (f, m_gyro_x, m_angle)
161- @show angle (f)
162- @show bias (f)
0 commit comments