Skip to content

Commit 34fa71a

Browse files
hlym123lbuque
authored andcommitted
libs/module: Add attitude_estimator.py.
Signed-off-by: hlym123 <[email protected]>
1 parent 7e9f1c1 commit 34fa71a

File tree

2 files changed

+89
-10
lines changed

2 files changed

+89
-10
lines changed

m5stack/libs/attitude_estimator.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
2+
#
3+
# SPDX-License-Identifier: MIT
4+
import math
5+
import time
6+
7+
8+
class AttitudeEstimator:
9+
def __init__(self):
10+
self.gyro_x_offset = 0.0
11+
self.gyro_y_offset = 0.0
12+
self.gyro_z_offset = 0.0
13+
self.angle_gyro_x = 0.0
14+
self.angle_gyro_y = 0.0
15+
self.angle_gyro_z = 0.0
16+
self.angle_x = 0.0
17+
self.angle_y = 0.0
18+
self.angle_z = 0.0
19+
self.pre_interval = 0.0
20+
self.gyro_coef = 0.98
21+
self.acc_coef = 0.02
22+
23+
def calibrate_gyro(self, read_gyro_func, sample_count=100, delay=0.01):
24+
sum_gx = 0.0
25+
sum_gy = 0.0
26+
sum_gz = 0.0
27+
28+
print("Starting gyroscope calibration, please keep the sensor stationary...")
29+
for i in range(sample_count):
30+
gx, gy, gz = read_gyro_func()
31+
sum_gx += gx
32+
sum_gy += gy
33+
sum_gz += gz
34+
time.sleep(delay)
35+
36+
self.gyro_x_offset = sum_gx / sample_count
37+
self.gyro_y_offset = sum_gy / sample_count
38+
self.gyro_z_offset = sum_gz / sample_count
39+
40+
print("Gyroscope calibration completed:")
41+
print("gyro_x_offset = {:.2f} °/s".format(self.gyro_x_offset))
42+
print("gyro_y_offset = {:.2f} °/s".format(self.gyro_y_offset))
43+
print("gyro_z_offset = {:.2f} °/s".format(self.gyro_z_offset))
44+
45+
def update_attitude(self, gx, gy, gz, ax, ay, az, dt):
46+
"""
47+
Update attitude angles (fusing accelerometer and gyroscope data)
48+
49+
:param gx: Gyroscope X-axis angular velocity (°/s)
50+
:param gy: Gyroscope Y-axis angular velocity (°/s)
51+
:param gz: Gyroscope Z-axis angular velocity (°/s)
52+
:param ax: Accelerometer X-axis acceleration (m/s²)
53+
:param ay: Accelerometer Y-axis acceleration (m/s²)
54+
:param az: Accelerometer Z-axis acceleration (m/s²)
55+
:param dt: Sampling time interval (seconds)
56+
"""
57+
angle_acc_x = math.atan2(ay, az + abs(ax)) * 360 / (2 * math.pi)
58+
angle_acc_y = math.atan2(ax, az + abs(ay)) * 360 / (-2 * math.pi)
59+
60+
gyro_x = gx - self.gyro_x_offset
61+
gyro_y = gy - self.gyro_y_offset
62+
gyro_z = gz - self.gyro_z_offset
63+
64+
self.angle_gyro_x += gyro_x * dt
65+
self.angle_gyro_y += gyro_y * dt
66+
self.angle_gyro_z += gyro_z * dt
67+
68+
self.angle_x = (self.gyro_coef * (self.angle_x + gyro_x * dt)) + (
69+
self.acc_coef * angle_acc_x
70+
)
71+
self.angle_y = (self.gyro_coef * (self.angle_y + gyro_y * dt)) + (
72+
self.acc_coef * angle_acc_y
73+
)
74+
self.angle_z = self.angle_gyro_z
75+
self.angle_z = (self.angle_z + 360) % 360
76+
77+
self.pre_interval += dt
78+
79+
def get_angles(self):
80+
return self.angle_x, self.angle_y, self.angle_z

m5stack/libs/module/bala2.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
import struct
66
from micropython import const
77
from module.mbus import i2c1
8-
from pid import PIDController
9-
from imu import AttitudeEstimator
10-
from driver.mpu6886 import MPU6886
8+
import pid
9+
import attitude_estimator
1110
from driver import mpu6886
12-
from machine import Timer
11+
import machine
1312

1413

1514
REG_MTR1_CTRL = const(0x00) # motor 1 control register
@@ -44,13 +43,13 @@ def __init__(self, timer_id: int = 0):
4443
# imu
4544
if 0x68 not in self.i2c.scan():
4645
raise RuntimeError("MPU6886 not found")
47-
self.sensor = MPU6886(
46+
self.sensor = mpu6886.MPU6886(
4847
self.i2c,
4948
accel_fs=mpu6886.ACCEL_FS_SEL_4G,
5049
gyro_fs=mpu6886.GYRO_FS_SEL_1000DPS,
5150
gyro_sf=mpu6886.SF_DEG_S,
5251
)
53-
self.imu = AttitudeEstimator()
52+
self.imu = attitude_estimator.AttitudeEstimator()
5453
# PID controller
5554
self.pid_angle_kp = 30
5655
self.pid_angle_ki = 0
@@ -60,15 +59,15 @@ def __init__(self, timer_id: int = 0):
6059
self.pid_speed_ki = 0.18
6160
self.pid_speed_kd = 0
6261
self.pid_speed_target = 0
63-
self.pid_angle = PIDController(
62+
self.pid_angle = pid.PIDController(
6463
kp=self.pid_angle_kp,
6564
ki=self.pid_angle_ki,
6665
kd=self.pid_angle_kd,
6766
setpoint=self.pid_angle_target,
6867
direction=1,
6968
)
7069
self.pid_angle.set_output_limits(-1023, 1023)
71-
self.pid_speed = PIDController(
70+
self.pid_speed = pid.PIDController(
7271
kp=self.pid_speed_kp,
7372
ki=self.pid_speed_ki,
7473
kd=self.pid_speed_kd,
@@ -78,7 +77,7 @@ def __init__(self, timer_id: int = 0):
7877
self.pid_speed.set_integral_limits(-80, 80)
7978
self.pid_speed.set_output_limits(-1023, 1023)
8079
# others
81-
self.timer = Timer(timer_id)
80+
self.timer = machine.Timer(timer_id)
8281
self.dt = 0.01
8382
self.motor_speed = 0
8483
self.prev_enc = 0
@@ -273,7 +272,7 @@ def start(self) -> None:
273272
module_bala2_0.start()
274273
"""
275274
self.timer.init(
276-
period=int(self.dt * 1000), mode=Timer.PERIODIC, callback=self._balance_task
275+
period=int(self.dt * 1000), mode=machine.Timer.PERIODIC, callback=self._balance_task
277276
)
278277

279278
def stop(self) -> None:

0 commit comments

Comments
 (0)