Skip to content

Commit 6a25d7c

Browse files
committed
kalman filter for pitch and roll added
1 parent f43a614 commit 6a25d7c

File tree

1 file changed

+90
-17
lines changed

1 file changed

+90
-17
lines changed

examples/03-mpu9250/main.py

+90-17
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,97 @@
1-
# MicroPython MPU-9250 (MPU-6500 + AK8963) I2C driver
1+
# MicroPython MPU-9250 (MPU-6500 + AK8963) I2C driver:
22
# https://github.com/tuupola/micropython-mpu9250
33

4-
# Kalman filter
5-
# https://github.com/micropython-IMU/micropython-fusion?tab=readme-ov-file
6-
# Kalman for Arduino
4+
# Arduino + IMU + VPython:
5+
# https://toptechboy.com/arduino-based-9-axis-inertial-measurement-unit-imu-based-on-bno055-sensor/
6+
7+
# Kalman filter:
78
# https://how2electronics.com/measure-pitch-roll-yaw-with-mpu6050-hmc5883l-esp32/
9+
# https://github.com/micropython-IMU/micropython-fusion?tab=readme-ov-file
810
# https://github.com/nhatuan84/Arduino-KalmanFilter/tree/main
9-
# Kalman for ESP-IDF
1011
# https://github.com/JChunX/imu-kalman/tree/main
1112

1213

1314
import utime
1415
from machine import SoftI2C, Pin
1516
import math
16-
1717
from mpu9250 import MPU9250
1818
# from mpu6500 import MPU6500, SF_G, SF_DEG_S
1919
from ak8963 import AK8963
2020

21+
# Adjust yaw using magnetic declination angle
22+
# https://www.magnetic-declination.com/
23+
# +5°22' for Brno
24+
magnetic_declination = 0.094 # rad = pi * (d+m/60) / 180
25+
26+
# --- KALMAN FILTER PARAMETERS ---
27+
Q_angle = 0.001 # Process noise variance for the accelerometer
28+
Q_bias = 0.003 # Process noise variance for the gyroscope bias
29+
R_measure = 0.03 # Measurement noise variance
30+
angle = 0 # Kalman filter state variables
31+
bias = 0
32+
rate = 0
33+
P = [[0, 0], [0, 0]] # Error covariance matrix
34+
pitch, roll, yaw = 0, 0, 0
35+
36+
37+
# Kalman filter function
38+
def Kalman_filter(angle, gyroRate, accelAngle):
39+
global bias, P, dt, Q_angle, Q_bias, R_measure
40+
41+
# Predict
42+
rate = gyroRate - bias
43+
angle += dt * rate
44+
45+
# Update the error covariance matrix
46+
P[0][0] += dt * (dt * P[1][1] - P[0][1] - P[1][0] + Q_angle)
47+
P[0][1] -= dt * P[1][1]
48+
P[1][0] -= dt * P[1][1]
49+
P[1][1] += Q_bias * dt
50+
51+
# Update
52+
S = P[0][0] + R_measure # Estimate error
53+
K = [P[0][0] / S, P[1][0] / S] # Kalman gain
54+
55+
y = accelAngle - angle # Angle difference
56+
angle += K[0] * y
57+
bias += K[1] * y
58+
59+
# Update the error covariance matrix
60+
P00_temp = P[0][0]
61+
P01_temp = P[0][1]
62+
63+
P[0][0] -= K[0] * P00_temp
64+
P[0][1] -= K[0] * P01_temp
65+
P[1][0] -= K[1] * P00_temp
66+
P[1][1] -= K[1] * P01_temp
67+
68+
return angle
69+
70+
71+
toRad = 2.0*math.pi / 360
72+
toDeg = 1 / toRad
73+
2174
i2c = SoftI2C(scl=Pin(22), sda=Pin(21))
2275
print(f"I2C configuration: {str(i2c)}")
2376

2477
# mpu6500 = MPU6500(i2c, accel_sf=SF_G, gyro_sf=SF_DEG_S)
2578
# sensor = MPU9250(i2c, mpu6500=mpu6500)
2679
dummy = MPU9250(i2c) # this opens the bybass to access to the AK8963
27-
ak8963 = AK8963(
28-
i2c,
29-
offset=(-7.423243, 18.58711, -124.3011),
30-
scale=(1.113371, 0.9323783, 0.971533))
80+
ak8963 = AK8963(i2c,
81+
offset=(47.79844, 46.37666, 66.57422),
82+
scale=(1.060139, 0.9904738, 0.9550097))
83+
# ak8963 = AK8963(i2c)
84+
# print("Calibrating! Rotate the sensor in all directions... ", end="")
3185
# offset, scale = ak8963.calibrate(count=256, delay=200)
32-
# print(offset, scale)
86+
# print("Done")
87+
# print(f"offset=({offset}), scale=({scale})")
3388

3489
sensor = MPU9250(i2c, ak8963=ak8963)
3590

36-
# print("MPU9250 id: " + hex(sensor.whoami))
91+
# Set the initial time for the loop
92+
lastTime = utime.ticks_ms()
93+
94+
print("MPU9250 id: " + hex(sensor.whoami))
3795
print("\nPress `Ctrl+C` to stop\n")
3896

3997
try:
@@ -43,20 +101,35 @@
43101
mx, my, mz = sensor.magnetic
44102
temp = sensor.temperature
45103

104+
# --- CALCULATE TIME STEP ---
105+
currentTime = utime.ticks_ms()
106+
dt = (currentTime - lastTime) / 1000.0 # Calculate time in seconds
107+
if (dt == 0):
108+
dt = 0.001 # Prevent division by zero
109+
lastTime = currentTime
110+
46111
# Calculate pitch and roll from accelerometer data
47112
# https://engineering.stackexchange.com/questions/3348/calculating-pitch-yaw-and-roll-from-mag-acc-and-gyro-data
48-
pitch = math.atan2(-ax, math.sqrt(ay**2 + az**2)) * 180.0 / math.pi
49-
roll = math.atan2(ay, az) * 180.0 / math.pi
113+
# pitch = math.atan2(-ax, math.sqrt(ay**2 + az**2)) * toDeg
114+
# roll = math.atan2(ay, az) * toDeg
115+
116+
# --- KALMAN FILTER FOR PITCH AND ROLL ---
117+
pitch = Kalman_filter(pitch, gx, math.atan2(-ax, math.sqrt(ay**2 + az**2)) * toDeg)
118+
roll = Kalman_filter(roll, gy, math.atan2(ay, az) * toDeg)
50119

120+
# Calculate and adjust yaw from magnetometer data
51121
yaw = math.atan2(my, mx)
122+
yaw += magnetic_declination
123+
# Normalize yaw to 0--360 degrees
52124
if yaw < 0:
53125
yaw += 2.0 * math.pi
54126
if yaw > 2.0 * math.pi:
55127
yaw -= 2 * math.pi
56-
yaw = yaw * 180.0 / math.pi
128+
yaw = yaw * toDeg
57129

58-
print(f"{pitch:.1f} \t{roll:.1f} \t{yaw:.1f} \t{temp:.1f}")
59-
utime.sleep_ms(1000)
130+
# print(f"{roll:.1f},{pitch:.1f},{yaw:.1f},{temp:.1f}")
131+
print(f"{roll:.1f},{pitch:.1f},{yaw:.1f}")
132+
utime.sleep_ms(20) # 50 Hz
60133

61134
except KeyboardInterrupt:
62135
# This part runs when Ctrl+C is pressed

0 commit comments

Comments
 (0)