在智能硬件项目中,姿态数据是一类非常常见的底层能力。无人小车判断车身是否倾斜、机器人检测运动姿态、手持设备识别方向变化、云台判断当前角度,这些场景背后都离不开加速度计和陀螺仪的数据采集。对于编程学习而言,MPU6050 实验的价值不只是读取一组数字,而是把传感器采集、I2C 通信、数据校准、单位换算和姿态解算串成一条完整的数据链路。
本实验使用 CanMV K210 开发板通过 I2C 总线读取 MPU6050 六轴传感器数据。程序启动后会扫描 I2C 设备,初始化 MPU6050 寄存器,解除模块休眠,配置陀螺仪和加速度计量程,随后进行陀螺仪零偏校准。校准期间模块需要保持静止,运行后程序会持续读取加速度计和陀螺仪原始数据,并通过四元数姿态解算函数输出姿态结果。代码中还保留了一阶互补滤波函数,便于后续对比不同姿态融合方式。
| 学习目标 | 说明 |
|---|---|
| 理解六轴姿态传感器 | 认识 MPU6050 中三轴加速度计和三轴陀螺仪的作用 |
| 掌握 I2C 传感器读取 | 使用 CanMV K210 通过 I2C 总线读取 MPU6050 寄存器数据 |
| 理解数据换算 | 将加速度计和陀螺仪原始值转换成 g、角速度和姿态计算输入 |
| 掌握零偏校准 | 通过静止采样计算陀螺仪零偏,减少姿态解算漂移 |
| 建立姿态融合思路 | 使用四元数融合加速度计和陀螺仪数据,输出 Pitch、Roll 等姿态结果 |
这个实验比单纯读取传感器数值更进一步。MPU6050 输出的是原始字节数据,程序需要完成寄存器读取、字节合成、零偏修正、单位换算和算法融合,才能得到更有工程意义的姿态结果。后续智能小车防侧翻、云台姿态反馈、手势控制、平衡车基础实验等内容,都可以在这类姿态感知能力上继续扩展。
文章目录
理论基础
MPU6050 是常见的六轴姿态传感器,内部包含三轴加速度计和三轴陀螺仪。加速度计可以感知重力方向和运动加速度,适合用来估计设备相对于重力方向的倾斜状态;陀螺仪可以感知绕 X、Y、Z 三个轴的角速度,适合捕捉短时间内的旋转变化。单独使用加速度计容易受到运动干扰,单独使用陀螺仪又容易出现长期积分漂移,因此姿态解算通常需要把两类数据结合起来。
本实验使用 I2C 总线读取 MPU6050。CanMV K210 通过 IO7 连接 MPU6050 的 SCL,通过 IO6 连接 MPU6050 的 SDA。程序先创建 I2C 对象,再访问默认地址 0x68 的传感器设备。初始化阶段会向 MPU6050 的寄存器写入配置,例如解除休眠、设置采样分频、配置低通滤波、设置陀螺仪量程和加速度计量程。完成初始化后,程序从 0x3B 起始寄存器连续读取 14 个字节,得到加速度、温度和陀螺仪原始数据。
姿态解算的核心不是直接把传感器读数当作角度,而是先把原始数据转换成算法可用的物理量。代码中陀螺仪量程配置为 ±500°/s,因此使用 65.5 作为原始值到角速度的换算系数;加速度计量程配置为 ±4g,因此使用 8192 作为原始值到 g 单位的换算系数。陀螺仪角速度还需要乘以 0.0174533,从角度每秒转换成弧度每秒,才能进入四元数姿态融合函数。
MPU6050
三轴加速度计 + 三轴陀螺仪
I2C 总线
SCL / SDA
CanMV K210
IO7 / IO6
寄存器读取
0x3B 起始 14 字节
数据处理
字节合成 / 零偏修正 / 单位换算
姿态融合算法
四元数更新
姿态结果
Pitch / Roll / 角速度信息
串口输出
姿态解算结果 q
VCC / GND
模块供电与公共地
这张流程图展示的是从传感器数据到姿态结果的完整链路。MPU6050 负责采集真实世界中的运动和倾斜信息,I2C 总线负责把寄存器数据传给 CanMV K210,驱动代码负责把原始字节转换成有符号整数,校准和换算逻辑负责修正数据,四元数算法负责融合加速度计和陀螺仪信息,最终通过串口输出姿态结果。
零偏校准是本实验中非常关键的一步。陀螺仪在静止状态下理论上应该接近 0,但实际模块常常会存在一定偏移。如果不校准,这些偏移会在姿态积分过程中逐渐放大,导致姿态角漂移。代码中的 error_gy() 会在启动后等待并采集 10 次陀螺仪数据,计算 X、Y、Z 三轴零偏,后续读取数据时再减去这些偏移值。因此程序启动校准期间,MPU6050 必须保持静止。
硬件设施
本实验真正使用到的硬件对象是 CanMV K210 开发板和 MPU6050 姿态传感器。软件侧主要依赖 machine.I2C、time 和 math。其中 I2C 负责开发板与 MPU6050 之间的数据通信,time 负责校准和采样间隔控制,math 负责三角函数、平方根和姿态角计算。代码没有使用 LCD、按键、蜂鸣器、摄像头、电机等模块,因此这些内容不作为本节展开对象。
接线关系可以先通过下面这张图片建立整体印象。当前实验重点关注四根线:VCC、GND、SCL 和 SDA。CanMV K210 的 IO7 连接 MPU6050 的 SCL,IO6 连接 MPU6050 的 SDA,供电端和 GND 需要根据模块标识正确连接。

| 硬件 / 软件 | 作用 | 说明 |
|---|---|---|
| CanMV K210 开发板 | 实验运行平台 | 负责执行 MicroPython 程序,并通过 I2C 总线读取 MPU6050 数据 |
| MPU6050 | 六轴姿态传感器 | 内部包含三轴加速度计和三轴陀螺仪,用于采集运动与姿态变化数据 |
| I2C 总线 | 传感器通信接口 | 通过 SCL 时钟线和 SDA 数据线完成开发板与 MPU6050 的数据交互 |
machine.I2C |
I2C 通信模块 | 用于创建 I2C 对象、扫描设备地址、读写 MPU6050 寄存器 |
time |
时间控制模块 | 用于校准等待、采样延时和循环节奏控制 |
math |
数学计算模块 | 用于姿态解算中的平方根、反三角函数和角度计算 |
实验中使用的核心模块如下。MPU6050 负责提供加速度和角速度数据,CanMV K210 负责运行姿态解算程序。与普通开关量传感器不同,MPU6050 不只是输出高低电平,而是通过 I2C 寄存器提供多字节原始数据,因此接线正确后还需要通过 i2c.scan() 确认设备地址是否能被识别。

接线关系已经在代码注释中给出,主程序也明确使用 scl=7、sda=6 创建 I2C 对象。因此本实验的接线映射可以直接按照代码和注释整理,不需要额外猜测。MPU6050 常见 I2C 地址为 0x68,代码中的 accel 类默认地址也设置为 addr=0x68。
| MPU6050 引脚 | CanMV 接口 | 代码参数 | 对应功能 | 说明 |
|---|---|---|---|---|
| VCC | 3.3V / 5V | 无 | 模块供电 | 根据模块版本选择合适供电,代码中不参与控制 |
| GND | GND | 无 | 公共地 | 传感器与开发板必须共地,保证 I2C 信号参考一致 |
| SCL | IO7 | scl=7 |
I2C 时钟线 | CanMV 通过该引脚提供 I2C 通信时钟 |
| SDA | IO6 | sda=6 |
I2C 数据线 | CanMV 通过该引脚与 MPU6050 交换寄存器数据 |
| I2C 地址 | 0x68 |
addr=0x68 |
设备地址 | accel 类默认使用该地址读取 MPU6050 |
完成接线后的整体状态如下。检查时重点确认 SCL 是否接到 IO7,SDA 是否接到 IO6,VCC 与 GND 是否可靠连接。程序运行后,串口会先打印 I2C 扫描结果,再进入陀螺仪零偏校准。校准期间模块需要保持静止,否则后续姿态角容易出现漂移。

| 实验现象 | 正常表现 | 异常提示 |
|---|---|---|
| 程序启动 | 串口打印 MPU6050 attitude test start |
如果无输出,检查程序运行和串口连接 |
| I2C 扫描 | 串口能看到设备地址,常见为 104,对应十六进制 0x68 |
如果扫描为空,优先检查 SCL、SDA、VCC、GND |
| 零偏校准 | 串口提示保持 MPU6050 静止,并输出 Gyro error |
校准时晃动模块会影响后续姿态稳定性 |
| 姿态采样 | 程序持续读取 MPU6050 数据并计算姿态结果 | 如果输出异常,检查原始 AcX、GyX 等数据是否变化 |
| 倾斜模块 | Pitch、Roll 相关结果会随姿态变化 | 如果变化方向不符合预期,可能与模块安装方向有关 |
| 长时间运行 | 姿态结果应能持续更新 | 如果漂移严重,重新静止校准并检查供电稳定性 |
软件代码
本实验代码是四个文件合并后的单文件版本,整体包含一阶互补滤波函数、MPU6050 读取驱动、四元数姿态解算和主程序。程序结构比较适合教学拆解:驱动层负责寄存器读写,算法层负责姿态融合,主程序负责创建 I2C 对象、执行校准、循环采样和打印结果。
| 软件环境 | 作用 | 检查重点 |
|---|---|---|
| CanMV IDE | 编辑、运行和调试 K210 程序 | 能识别开发板串口,并能正常运行基础 print() 测试 |
| CanMV 固件 | 提供 machine.I2C、time、math 等模块 |
固件环境需要支持当前 I2C 写法 |
| USB 串口驱动 | 让电脑识别开发板串口 | 串口工具中能看到对应端口 |
| 串口终端 | 查看扫描、校准和姿态输出 | 能看到 I2C 扫描结果、Gyro error 和姿态结果 |
| MPU6050 模块 | 姿态数据来源 | 校准期间保持静止,运行时观察姿态变化 |
python
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
CanMV K210 MPU6050 姿态解算实验 - 四文件合并单文件版
原始文件:
1. 31_mpu6050.py 主程序
2. mpu6050.py MPU6050 读取驱动
3. q4.py 四元数姿态解算
4. mpu.py 一阶互补滤波函数
接线说明:
MPU6050 VCC -> 3.3V / 5V
MPU6050 GND -> GND
MPU6050 SCL -> CanMV IO7
MPU6050 SDA -> CanMV IO6
说明:
程序会先进行陀螺仪零偏校准,校准期间请保持模块静止。
运行后每累计 256 次采样,打印一次姿态解算结果 q。
"""
from machine import I2C
from time import sleep, sleep_ms
import time
import math
# =========================
# 一阶互补滤波函数
# 原 mpu.py
# =========================
i1 = 0
x = 0
y = 0
angleAx = 0
angleAy = 0
one_filter_angle = [0, 0, 0, 0, 0, 0, 0, 0]
def one_filter(ax, ay, az, gx, gy, gz):
global i1
global angleAy
global angleAx
global x
global y
global one_filter_angle
K1 = 0.3
dt = 0.008
gyo = gy / 65.5
gzo = gz / 65.5
gxo = gx / 65.5
if abs(gyo) < 0.07:
gyo = 0
if abs(gxo) < 0.07:
gxo = 0
if ay != 0 or az != 0:
angleAx = math.atan(ax / math.sqrt(ay * ay + az * az)) * 180 / math.pi
if ax != 0 or az != 0:
angleAy = math.atan(ay / math.sqrt(ax * ax + az * az)) * 180 / math.pi
if i1 == 0:
one_filter_angle[0] = angleAx
one_filter_angle[1] = angleAy
x = angleAx
y = angleAy
print("dd", x, y, gyo * dt)
i1 = i1 + 1
if i1 == 10:
i1 = 1
one_filter_angle[0] = K1 * angleAx + (1 - K1) * (one_filter_angle[0] - gyo * dt)
one_filter_angle[1] = K1 * angleAy + (1 - K1) * (one_filter_angle[1] + gxo * dt)
x = x - gyo * dt
y = y + gxo * dt
one_filter_angle[2] = angleAx
one_filter_angle[3] = angleAy
one_filter_angle[4] = x
one_filter_angle[5] = y
one_filter_angle[6] = gyo
one_filter_angle[7] = gxo
return one_filter_angle
# =========================
# MPU6050 读取驱动
# 原 mpu6050.py
# =========================
error = [0, 0, 0]
class accel:
def __init__(self, i2c, addr=0x68):
self.iic = i2c
self.addr = addr
sleep_ms(1)
self.iic.writeto(self.addr, bytearray([107, 0])) # 解除休眠
sleep_ms(1)
self.iic.writeto_mem(self.addr, 0x19, b"\x07") # gyro 125Hz
sleep_ms(1)
self.iic.writeto_mem(self.addr, 0x1A, b"\x04") # low filter 21Hz
sleep_ms(1)
self.iic.writeto_mem(self.addr, 0x1B, b"\x08") # gyro ±500°/s,65.5 LSB/(°/s)
sleep_ms(1)
self.iic.writeto_mem(self.addr, 0x1C, b"\x08") # acceler ±4g,8192 LSB/g
sleep_ms(1)
def get_raw_values(self):
return self.iic.readfrom_mem(self.addr, 0x3B, 14)
def get_ints(self):
raw_data = self.get_raw_values()
data = []
for item in raw_data:
data.append(item)
return data
def bytes_toint(self, firstbyte, secondbyte):
if not firstbyte & 0x80:
return firstbyte << 8 | secondbyte
return -(((firstbyte ^ 255) << 8) | ((secondbyte ^ 255) + 1))
def error_gy(self):
global error
print("Gyro calibration start, keep MPU6050 still...")
sleep(3)
error = [0, 0, 0]
vals = {}
for i in range(0, 10):
raw_ints = self.get_raw_values()
vals["GyX"] = self.bytes_toint(raw_ints[8], raw_ints[9])
vals["GyY"] = self.bytes_toint(raw_ints[10], raw_ints[11])
vals["GyZ"] = self.bytes_toint(raw_ints[12], raw_ints[13])
error[0] = error[0] + vals["GyX"]
error[1] = error[1] + vals["GyY"]
error[2] = error[2] + vals["GyZ"]
sleep_ms(8)
error[0] = error[0] / 10.0
error[1] = error[1] / 10.0
error[2] = error[2] / 10.0
print("Gyro error:", error)
def get_values(self):
global error
vals = {}
raw_ints = self.get_raw_values()
vals["AcX"] = self.bytes_toint(raw_ints[0], raw_ints[1])
vals["AcY"] = self.bytes_toint(raw_ints[2], raw_ints[3])
vals["AcZ"] = self.bytes_toint(raw_ints[4], raw_ints[5])
vals["Tmp"] = self.bytes_toint(raw_ints[6], raw_ints[7]) / 340.00 + 36.53
vals["GyX"] = self.bytes_toint(raw_ints[8], raw_ints[9]) - error[0]
vals["GyY"] = self.bytes_toint(raw_ints[10], raw_ints[11]) - error[1]
vals["GyZ"] = self.bytes_toint(raw_ints[12], raw_ints[13]) - error[2]
return vals
# =========================
# 四元数姿态解算
# 原 q4.py
# =========================
Kp = 0.8
Ki = 0.001
halfT = 0.004
q0 = 1
q1 = 0
q2 = 0
q3 = 0
exInt = 0
eyInt = 0
ezInt = 0
def IMUupdate(gx, gy, gz, ax, ay, az):
global Kp
global Ki
global halfT
global q0
global q1
global q2
global q3
global exInt
global eyInt
global ezInt
K = 0.7
result = [0, 0, 0, 0, 0, 0, 0, 0]
# 加速度计归一化,避免静止异常或读数异常时除 0
norm = math.sqrt(ax * ax + ay * ay + az * az)
if norm == 0:
return result
ax = ax / norm
ay = ay / norm
az = az / norm
# 估计重力方向
vx = 2 * (q1 * q3 - q0 * q2)
vy = 2 * (q0 * q1 + q2 * q3)
vz = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3
# 加速度计测量方向与估计方向之间的误差
ex = ay * vz - az * vy
ey = az * vx - ax * vz
ez = ax * vy - ay * vx
# 积分误差
exInt = exInt + ex * Ki
eyInt = eyInt + ey * Ki
ezInt = ezInt + ez * Ki
# 修正陀螺仪
gx = gx + Kp * ex + exInt
gy = gy + Kp * ey + eyInt
gz = gz + Kp * ez + ezInt
# 四元数积分
q0 = q0 + (-q1 * gx - q2 * gy - q3 * gz) * halfT
q1 = q1 + (q0 * gx + q2 * gz - q3 * gy) * halfT
q2 = q2 + (q0 * gy - q1 * gz + q3 * gx) * halfT
q3 = q3 + (q0 * gz + q1 * gy - q2 * gx) * halfT
# 四元数归一化
norm = math.sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3)
if norm == 0:
return result
q0 = q0 / norm
q1 = q1 / norm
q2 = q2 / norm
q3 = q3 / norm
# 计算 Pitch / Roll
pitch = math.asin(-2 * q1 * q3 + 2 * q0 * q2) * 57.3
roll = 0
denominator = -2 * q1 * q1 - 2 * q2 * q2 + 1
if denominator != 0:
roll = math.atan((2 * q2 * q3 + 2 * q0 * q1) / denominator) * 57.3
result[0] = pitch
result[1] = roll
if ay * ay + az * az != 0:
result[2] = -math.atan(ax / math.sqrt(ay * ay + az * az)) * 57.2957795
if ax * ax + az * az != 0:
result[3] = math.atan(ay / math.sqrt(ax * ax + az * az)) * 57.2957795
result[4] = gx
result[5] = gy
result[6] = gz
result[0] = -K * pitch - (1 - K) * result[2]
result[1] = K * roll + (1 - K) * result[3]
return result
# =========================
# 主程序
# 原 31_mpu6050.py
# =========================
print("MPU6050 attitude test start")
# SCL -> IO7,SDA -> IO6
i2c = I2C(I2C.I2C0, freq=100000, scl=7, sda=6)
try:
print("I2C scan:", i2c.scan())
except Exception as e:
print("I2C scan failed:", e)
mpu_sensor = accel(i2c)
# 陀螺仪零偏校准,校准时保持 MPU6050 静止
mpu_sensor.error_gy()
i = 0
q = []
while True:
values = mpu_sensor.get_values()
i = i + 1
if i == 256:
print(q)
i = 0
# 陀螺仪:原始值 / 65.5 得到 °/s,再乘 0.0174533 转为 rad/s
# 加速度计:原始值 / 8192 得到 g
q = IMUupdate(
values["GyX"] / 65.5 * 0.0174533,
values["GyY"] / 65.5 * 0.0174533,
values["GyZ"] / 65.5 * 0.0174533,
values["AcX"] / 8192,
values["AcY"] / 8192,
values["AcZ"] / 8192
)
time.sleep(0.0035)
这段程序可以分成三个层级。底层是 accel 类,负责 MPU6050 寄存器初始化、原始字节读取和有符号整数转换;中间层是 error_gy()、单位换算和 IMUupdate(),负责校准、修正和姿态融合;上层是主程序,负责创建 I2C 对象、扫描设备、启动校准和循环输出姿态结果。
accel.__init__() 是传感器初始化入口。它会解除 MPU6050 休眠,并配置采样分频、低通滤波、陀螺仪量程和加速度计量程。get_raw_values() 从 0x3B 起始地址读取 14 字节原始数据,bytes_toint() 把两个字节合成为有符号整数,get_values() 再把 AcX、AcY、AcZ、Tmp、GyX、GyY、GyZ 整理成字典。这样主循环不需要关心寄存器细节,只需要读取字典中的传感器值。
| 函数 / 语句 | 功能 | 对应现象 |
|---|---|---|
one_filter() |
一阶互补滤波姿态估计 | 当前主循环未直接调用,可作为后续算法对比 |
accel.__init__() |
初始化 MPU6050 | 解除休眠,配置采样率、低通滤波、陀螺仪量程和加速度计量程 |
get_raw_values() |
读取原始数据 | 从 0x3B 起始寄存器读取 14 字节传感器数据 |
get_ints() |
转换原始字节列表 | 将读取到的字节数据整理成普通列表 |
bytes_toint() |
两字节转有符号整数 | 把高低字节合成为加速度、温度和陀螺仪原始值 |
error_gy() |
陀螺仪零偏校准 | 静止状态下采样陀螺仪数据,计算 X、Y、Z 三轴零偏 |
get_values() |
获取传感器数据字典 | 返回 AcX、AcY、AcZ、Tmp、GyX、GyY、GyZ 等数据 |
IMUupdate() |
四元数姿态解算 | 融合陀螺仪和加速度计数据,输出 Pitch、Roll 等姿态结果 |
i2c.scan() |
扫描 I2C 设备 | 打印当前总线上可识别的 I2C 设备地址 |
time.sleep(0.0035) |
控制采样间隔 | 让循环保持较短延时,持续进行姿态更新 |
IMUupdate() 是姿态融合的核心函数。函数先对加速度计数据归一化,再根据当前四元数估计重力方向。加速度计测量方向与四元数估计方向之间会形成误差,这个误差经过比例项和积分项修正后反馈给陀螺仪角速度。随后程序进行四元数积分和归一化,并计算 Pitch、Roll 等姿态结果。这个过程体现了姿态解算中常见的思路:陀螺仪负责短时间动态变化,加速度计提供重力方向校正。
主程序中的 i == 256 用来控制打印频率。姿态解算每一轮循环都会执行,但并不是每次都打印结果,而是累计 256 次采样后输出一次 q。这种写法可以减少串口打印对采样节奏的影响。嵌入式实验中,频繁打印会明显拖慢循环速度,尤其在姿态解算这类连续采样场景中,控制输出频率比每次都打印更稳。
扩展应用
MPU6050 实验比普通 LED 实验更容易受到接线、供电、I2C 地址、模块静止状态和采样参数影响。排查时应围绕 I2C 扫描结果、传感器初始化、原始数据读取、零偏校准和姿态输出稳定性逐步定位。
| 问题现象 | 可能原因 | 处理思路 |
|---|---|---|
| I2C 扫描结果为空 | SCL / SDA 接反、供电异常、GND 未共地 | 核对 SCL 接 IO7、SDA 接 IO6,确认 VCC 和 GND 连接正常 |
扫描不到 0x68 |
模块地址不同或 AD0 电平状态改变 | 观察 i2c.scan() 打印结果,如果地址不是 104,需要调整 accel(i2c, addr=实际地址) |
| 程序卡在校准阶段 | sleep(3) 校准等待属于正常现象 |
启动后保持模块静止,等待校准完成并观察 Gyro error 输出 |
| 姿态数据抖动明显 | 模块晃动、供电不稳、采样环境振动较大 | 校准期间保持静止,缩短杜邦线,保证电源稳定,减少桌面震动 |
| 姿态角漂移严重 | 陀螺仪零偏校准不准确或模块启动时不静止 | 重新运行程序,并在 error_gy() 校准期间保持 MPU6050 静止 |
| 输出一直接近 0 | 传感器数据没有正常变化或模块摆放变化较小 | 打印 values 原始数据,确认 AcX、AcY、AcZ、GyX、GyY、GyZ 是否有变化 |
| 程序出现除 0 异常 | 加速度或四元数归一化数据异常 | 当前代码已对 norm == 0 和部分分母为 0 场景做保护,可继续检查传感器读数是否异常 |
| 输出速度过慢 | 串口打印频率过高或循环延时较长 | 当前代码每 256 次采样打印一次,如果调试阶段频繁打印原始数据,运行速度会下降 |
| 姿态方向与预期不同 | 模块安装方向、坐标轴方向与代码假设不同 | 根据模块实际安装方向重新理解 X、Y、Z 轴,必要时调整显示或判断逻辑 |
MPU6050 姿态解算实验的价值不只在于打印 Pitch 和 Roll,而是建立"传感器数据到空间状态"的编程思维。开发板通过 I2C 获取真实世界中的运动数据,程序再通过滤波、校准、单位换算和姿态融合,把原始字节转换成具有业务意义的角度信息。这套流程可以自然迁移到机器人、运动检测、设备姿态判断和交互控制等场景中。
| 应用场景 | 实现思路 | 可扩展能力 |
|---|---|---|
| 姿态检测演示 | 使用 MPU6050 采集加速度和陀螺仪数据,计算 Pitch、Roll 变化 | 可配合 LCD 显示姿态角 |
| 平衡车基础实验 | 利用倾角变化判断车身前倾或后仰状态 | 可接入电机控制形成闭环 |
| 智能小车防侧翻 | 通过姿态角判断车身倾斜是否超过阈值 | 可在异常倾斜时触发蜂鸣器或 LED 提示 |
| 手势控制设备 | 根据模块旋转方向识别简单手势 | 可扩展为按方向切换菜单、控制灯效或发送串口指令 |
| 运动数据采集 | 周期性记录加速度、角速度和姿态角 | 可扩展为数据上传、文件保存或可视化曲线展示 |
| 云台姿态反馈 | 使用姿态角作为当前角度反馈参考 | 可接入舵机或电机调节方向 |
| 算法教学实验 | 对比一阶互补滤波和四元数融合结果 | 可进一步讲解滤波、漂移、采样周期和姿态解算算法 |
从工程角度看,当前代码已经把传感器读取和姿态解算做了比较清晰的拆分。accel 类负责硬件通信,IMUupdate() 负责算法融合,主循环负责调度流程。这样的结构方便后续继续改造,例如把打印结果换成屏幕显示,把姿态角作为控制条件,把采样数据保存成文件,或者把同一套姿态数据接入可视化界面。只要驱动层和算法层保持清楚边界,复杂硬件项目也能逐步扩展。
总结
本实验通过 CanMV K210 开发板完成了 MPU6050 姿态解算流程,核心能力包括 I2C 通信、寄存器初始化、原始数据读取、有符号整数转换、陀螺仪零偏校准、加速度计单位换算、陀螺仪角速度换算、四元数姿态融合和循环采样调度。程序从硬件接线进入 I2C 对象创建,再通过传感器驱动读取原始字节,经过校准和算法融合输出姿态结果,完整展示了传感器实验从"读取数据"到"理解状态"的过程。
这类实验非常适合作为 AI 硬件课程中的姿态感知入门案例。加速度计提供重力方向参考,陀螺仪提供短时间旋转变化,四元数融合负责把两类数据转换成稳定姿态。后续课程可以在这个基础上继续扩展 LCD 姿态显示、LED 倾斜提示、蜂鸣器异常报警、智能小车平衡控制、姿态数据可视化和 AI 摄像头联动等方向。理解了传感器通信、数据校准和算法融合之间的关系,更多智能硬件项目就能从简单采样逐步走向可感知、可判断、可控制的系统设计。