第2章:传感器基础(一):惯性测量单元(IMU)原理、加速度计与陀螺仪的工作机制、IMU数据特性与误差来源
各位同学,欢迎来到传感器融合实战的第二讲。
今天咱们聊聊IMU——惯性测量单元。这东西在机器人、无人机、手机里到处都是。说白了,它就是你的设备感知自身运动的「内耳前庭」。没有它,你的设备就是个瞎子,不知道自己在动还是没动。
2.1 IMU是什么?
IMU,全称Inertial Measurement Unit。它通常由三个加速度计和三个陀螺仪组成。三轴加速度计测线加速度,三轴陀螺仪测角速度。有些IMU还会集成磁力计,那就是九轴了。
我个人习惯把IMU分成两类:
- 消费级IMU:比如MPU6050、ICM-20948。便宜,但噪声大,温漂明显。
- 工业/战术级IMU:比如ADIS系列、霍尼韦尔的HG系列。贵,但精度高一个数量级。
你想想看,手机里那个IMU成本可能不到两块钱,而导弹上用的IMU能顶一辆车。差距就在这。
2.2 加速度计的工作原理
加速度计测的是什么?很多人以为是「速度变化率」。其实不对。它测的是「比力」——也就是物体受到的惯性力与重力的合力。
内部结构呢?最常见的是MEMS电容式加速度计。里面有个微小的质量块,用弹簧悬着。当芯片加速时,质量块会偏移,导致电容变化。测出电容变化,就能反推出加速度。
关键点:静止时,加速度计测到的是重力加速度g。所以如果你把IMU平放,Z轴读数应该是+1g(约9.8 m/s²)。
我在项目中遇到过一件事。有个学生把IMU竖直放置,发现X轴读数是1g,他以为传感器坏了。其实没坏——重力方向变了而已。嗯,这里要注意:加速度计永远分不清「重力」和「运动加速度」。
2.3 陀螺仪的工作机制
陀螺仪测角速度,单位是°/s。MEMS陀螺仪用的是科里奥利效应。简单说,让一个质量块高速振动,当它旋转时,会产生一个垂直于振动方向的力。测这个力的大小,就知道转得多快。
听起来很玄乎?其实你想想看,小时候玩过那种旋转木马吧?你站在上面往外扔球,球会偏转。那就是科里奥利力在起作用。
我的经验:陀螺仪对温度极其敏感。我曾经在-20°C到60°C环境下测试同一颗IMU,零偏变化超过5°/s。所以量产产品必须做温补。
2.4 IMU数据特性
IMU的数据有几个典型特征:
- 高频噪声大:尤其是加速度计,高频抖动很明显。
- 低频漂移严重:陀螺仪的积分会随时间发散,因为零偏在累积。
- 动态响应快:IMU的采样率可以做到几百甚至上千Hz,比GPS快得多。
说白了,IMU适合短时间高动态的跟踪,不适合长时间绝对定位。这就是为什么我们要做传感器融合——用GPS或视觉来修正IMU的漂移。
2.5 误差来源分析
做IMU融合,不懂误差来源就是瞎搞。我列几个最常见的:
| 误差类型 | 来源 | 影响 | 典型值(消费级) |
|---|---|---|---|
| 零偏(Bias) | 制造工艺、温度 | 积分后角度漂移 | ±10°/s(陀螺) |
| 比例因子误差 | 标定不准确 | 测量值缩放偏差 | ±1% |
| 交叉轴耦合 | 安装不正 | 一个轴的运动串扰到另一轴 | ±2% |
| 随机游走 | 噪声积分 | 角度估计方差随时间增大 | 0.01°/√Hz |
| 温度漂移 | 温度变化 | 零偏和比例因子变化 | 0.1°/s/°C |
避坑指南:我曾经在一个项目中忽略了交叉轴耦合,结果IMU在水平旋转时,垂直轴竟然也读出了角速度。后来花了三天时间做六面标定才搞定。所以,量产前一定要做多位置标定。
2.6 如何读取原始数据?
以MPU6050为例,通过I2C读取原始数据。代码很简单:
// 伪代码示例:读取MPU6050加速度计和陀螺仪数据
#include <Wire.h>
#define MPU_ADDR 0x68
void setup() {
Wire.begin();
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x6B); // 电源管理寄存器
Wire.write(0x00); // 唤醒
Wire.endTransmission();
}
void loop() {
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x3B); // 加速度计数据起始地址
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADDR, 14, true);
int16_t ax = Wire.read() << 8 | Wire.read();
int16_t ay = Wire.read() << 8 | Wire.read();
int16_t az = Wire.read() << 8 | Wire.read();
int16_t gx = Wire.read() << 8 | Wire.read();
int16_t gy = Wire.read() << 8 | Wire.read();
int16_t gz = Wire.read() << 8 | Wire.read();
// 原始值转物理量
float accel_scale = 16384.0; // ±2g量程
float gyro_scale = 131.0; // ±250°/s量程
float ax_g = ax / accel_scale;
float ay_g = ay / accel_scale;
float az_g = az / accel_scale;
float gx_dps = gx / gyro_scale;
float gy_dps = gy / gyro_scale;
float gz_dps = gz / gyro_scale;
delay(10);
}
注意:原始数据是16位有符号整数。要转成物理量,必须除以对应的量程系数。我见过有人忘了除,结果角度算出来几千度——那肯定不对。
2.7 小结
这一章我们聊了IMU的基本原理。加速度计测比力,陀螺仪测角速度。它们各有各的毛病——加速度计怕振动,陀螺仪怕漂移。但组合起来,就能互补。
下一章我会讲怎么用卡尔曼滤波把这些数据融合起来。到时候你会发现,理解了误差模型,滤波器调参才有方向。否则就是瞎调。
课后思考:如果你把IMU放在匀速直线运动的车上,加速度计读数是多少?陀螺仪呢?想清楚这个问题,你就理解了IMU的本质。