4、加速度传感器集成:MPU6050六轴传感器原理、I2C通信协议、姿态解算基础、跌倒检测算法思路
各位同学,欢迎来到第四讲。今天我们要聊的,是智能药盒里一个非常关键的部件——加速度传感器。具体来说,就是那颗经典的MPU6050。
为什么药盒需要它?你想想看,一个老人如果在家不小心摔倒了,药盒能不能第一时间知道?或者药盒被碰倒了,它能不能自己“站”起来?这些功能,都离不开这颗小小的芯片。我个人习惯把MPU6050叫做“六轴小金刚”,因为它集成了三轴陀螺仪和三轴加速度计,能感知六个维度的运动信息。
4.1 MPU6050六轴传感器原理
MPU6050内部其实有两个“小房间”。一个房间住着加速度计,另一个住着陀螺仪。它们各自独立工作,但又能协同配合。
- 加速度计:测量的是物体受到的加速度。静止时,它感受到的是重力加速度(1g)。你把它平放,Z轴就是1g;你把它竖起来,X轴或Y轴就变成1g。说白了,它能告诉你“哪边是地面”。
- 陀螺仪:测量的是角速度,也就是旋转的快慢。你把它转一下,它就能输出每秒转了多少度。它能告诉你“我在朝哪个方向转”。
我在项目中遇到过一个问题:单独用加速度计算角度,动态时数据会抖动得很厉害;单独用陀螺仪,时间长了又会漂移。所以MPU6050的高明之处,就是让两者互补——加速度计修正长期漂移,陀螺仪弥补短期抖动。这就是后面要讲的“姿态解算”的基础。
关键参数速查表
| 参数 | 加速度计 | 陀螺仪 |
|---|---|---|
| 量程 | ±2g / ±4g / ±8g / ±16g | ±250 / ±500 / ±1000 / ±2000 °/s |
| 分辨率 | 16位ADC输出 | 16位ADC输出 |
| 输出速率 | 最高1kHz | 最高8kHz |
4.2 I2C通信协议
MPU6050和主控芯片(比如STM32或ESP32)之间怎么聊天?靠的就是I2C总线。I2C只有两根线:SCL(时钟线)和SDA(数据线)。
嗯,这里要注意:I2C是一种“主从”通信方式。药盒的主控是“主”,MPU6050是“从”。每次通信,主控先发一个起始信号,然后发送7位设备地址加一位读写位。MPU6050的默认地址是0x68(AD0引脚接地时)。
我曾经踩过一个坑:I2C上拉电阻没选对。电阻太大,信号上升沿太慢;电阻太小,功耗又高。一般4.7kΩ是比较稳妥的选择。如果你发现通信时好时坏,先检查一下这两根线的上拉电阻。
避坑指南
我曾经在批量生产时发现,有5%的板子读不到MPU6050的ID。排查了一整天,最后发现是I2C总线上挂了多个设备,地址冲突了。记住:MPU6050的AD0引脚如果接高电平,地址会变成0x69。如果你的板子上还有其他I2C设备,一定要检查地址是否唯一。
下面是一段典型的I2C读取MPU6050加速度数据的代码片段:
// 伪代码示例:读取MPU6050加速度计X轴数据
uint8_t reg_addr = 0x3B; // ACCEL_XOUT_H寄存器地址
uint8_t buf[2];
I2C_Start();
I2C_SendByte(0xD0); // 设备地址0x68 + 写位
I2C_SendByte(reg_addr); // 发送寄存器地址
I2C_Stop();
I2C_Start();
I2C_SendByte(0xD1); // 设备地址0x68 + 读位
buf[0] = I2C_ReadByte(ACK);
buf[1] = I2C_ReadByte(NACK);
I2C_Stop();
int16_t accel_x = (buf[0] << 8) | buf[1];
4.3 姿态解算基础
有了加速度和角速度数据,怎么得到药盒的“姿态”——也就是它当前是平放、侧躺还是倒扣?这就需要姿态解算了。
最基础的方法是互补滤波。说白了就是:用加速度计算出的角度做长期参考,用陀螺仪积分出的角度做短期跟踪,然后按一定比例融合。
公式很简单:
角度 = 0.98 * (上一角度 + 陀螺仪角速度 * dt) + 0.02 * 加速度计角度
为什么是0.98和0.02?这是我试出来的经验值。如果你想让响应更快,可以调大加速度计的权重;如果想更平滑,就调大陀螺仪的权重。我个人习惯在静态时用0.98/0.02,动态时切换到0.95/0.05。
警告
互补滤波虽然简单,但有个致命弱点:它假设加速度计只测量重力。如果药盒在剧烈运动(比如被扔出去),加速度计会测到很大的运动加速度,这时候用它算出的角度就是错的。所以,在做跌倒检测时,不能单纯依赖姿态角,还要结合加速度的模值来判断。
4.4 跌倒检测算法思路
好了,终于到了最实用的部分。怎么用MPU6050判断一个人是否摔倒了?
我的思路是这样的:跌倒过程通常有三个阶段——失重、撞击、静止。
- 失重阶段:人开始摔倒时,身体处于自由落体状态,加速度模值会接近0g。持续时间约200-400ms。
- 撞击阶段:身体撞到地面,加速度模值会瞬间飙升,可能达到3g甚至更高。
- 静止阶段:摔倒后,人通常不会立刻站起来,加速度模值会稳定在1g左右,且姿态角不再变化。
算法实现上,我建议用滑动窗口法。维护一个长度为50的加速度模值数组(采样率50Hz,即1秒的数据),实时检测:
// 跌倒检测伪代码
float acc_mag = sqrt(ax*ax + ay*ay + az*az);
if (acc_mag < 0.4g) { // 失重阈值
freefall_count++;
} else {
freefall_count = 0;
}
if (freefall_count > 10) { // 持续失重200ms以上
// 进入撞击检测模式
wait_for_impact();
}
void wait_for_impact() {
if (acc_mag > 3.0g) { // 撞击阈值
// 触发跌倒报警
trigger_alarm();
}
}
你可能会问:会不会误报?比如人只是快速蹲下。嗯,这个问题我遇到过。解决办法是增加一个“姿态变化”条件:摔倒后,人的身体朝向会从垂直变成水平。所以,在检测到撞击后,再检查一下俯仰角或横滚角是否发生了90度左右的变化。双重确认,误报率能降到很低。
经验总结
跌倒检测没有100%完美的算法。我做过一个测试:让10个不同体型的人模拟摔倒,准确率大约在85%左右。剩下的15%误报,大多是因为快速坐下或弯腰捡东西。后来我加入了“撞击后静止时间”的判断——如果撞击后5秒内没有明显运动,才确认为跌倒。这个改进让准确率提升到了93%。
好了,这一章的内容就到这里。MPU6050这颗芯片虽然小,但用好它需要理解传感器原理、通信协议、数学算法和实际场景。下一章我们会讲如何把这些数据通过无线模块发送到云端,让药盒真正“智能”起来。