4、陀螺仪传感器驱动开发:L3GD20/ICM-20602驱动实现、角速度数据校准、零偏稳定性分析
陀螺仪这东西,说白了就是测量角速度的。你拿着手机转一下,屏幕跟着转,背后就是陀螺仪在干活。但在嵌入式系统里,事情没那么简单——你得先把它驱动起来,再把数据弄准。
这一章,我带你搞定两个常用型号:L3GD20(ST 家的)和 ICM-20602(InvenSense 家的)。顺便聊聊角速度校准和零偏稳定性分析。嗯,都是实战中绕不开的硬骨头。
4.1 硬件接口与寄存器概览
这两个芯片都支持 SPI 和 I²C。我个人习惯用 SPI,速度快,时序可控。但如果你板子上 I²C 总线已经拉好了,用 I²C 也行,就是注意速率别超过 400kHz。
先看 L3GD20 的关键寄存器:
| 寄存器名 | 地址 | 说明 |
|---|---|---|
| WHO_AM_I | 0x0F | 读出来应该是 0xD4,用来验证通信 |
| CTRL_REG1 | 0x20 | 使能各轴、设置输出数据速率 |
| CTRL_REG4 | 0x23 | 量程选择:250/500/2000 dps |
| OUT_X_L / OUT_X_H | 0x28 / 0x29 | X 轴角速度原始值,16 位有符号 |
ICM-20602 的寄存器布局类似,但地址不同:
| 寄存器名 | 地址 | 说明 |
|---|---|---|
| WHO_AM_I | 0x75 | 固定值 0x12 |
| GYRO_CONFIG | 0x1B | 量程选择 + 低通滤波配置 |
| GYRO_XOUT_H / GYRO_XOUT_L | 0x43 / 0x44 | X 轴角速度,高字节在前 |
小提示:L3GD20 的 OUT 寄存器是低字节在前,ICM-20602 是高字节在前。写驱动时千万别搞反,否则读出来的数据会「跳来跳去」。我曾经因为这个 bug 调了一整个下午。
4.2 L3GD20 驱动实现
驱动代码其实不复杂。核心就三步:初始化、读数据、换算成物理值。
先看初始化:
void L3GD20_Init(void) {
// 1. 检查 WHO_AM_I
uint8_t who = SPI_ReadReg(L3GD20_WHO_AM_I);
if (who != 0xD4) {
// 通信有问题,建议打印错误
return;
}
// 2. 配置 CTRL_REG1:使能 X/Y/Z,ODR = 100Hz
SPI_WriteReg(L3GD20_CTRL_REG1, 0x0F);
// 3. 配置 CTRL_REG4:量程 ±500 dps
// 500 dps 对应的灵敏度是 17.5 mdps/digit
SPI_WriteReg(L3GD20_CTRL_REG4, 0x10);
}
读数据时要注意:L3GD20 支持自动递增地址,你可以一次读出 6 个字节:
void L3GD20_ReadGyro(int16_t *x, int16_t *y, int16_t *z) {
uint8_t buf[6];
SPI_ReadRegs(L3GD20_OUT_X_L | 0x80, buf, 6); // 0x80 表示自动递增
*x = (int16_t)(buf[1] << 8) | buf[0];
*y = (int16_t)(buf[3] << 8) | buf[2];
*z = (int16_t)(buf[5] << 8) | buf[4];
}
换算成角速度(单位:dps):
float gyro_x_dps = (float)(*x) * 17.5f / 1000.0f; // 17.5 mdps/digit
注意:L3GD20 的原始数据是补码格式。如果你用 int16_t 接收,直接左移再或操作就行。但如果你用 uint16_t,记得手动处理符号位。我见过有人用 uint16_t 然后直接强转 float,结果负数全变成了大正数。
4.3 ICM-20602 驱动实现
ICM-20602 的驱动思路一样,但细节有差异。它内置了数字低通滤波器(DLPF),这个很好用。
void ICM20602_Init(void) {
// 复位设备
SPI_WriteReg(ICM20602_PWR_MGMT_1, 0x80);
delay_ms(100);
// 配置陀螺仪:±500 dps,DLPF 带宽 41Hz
SPI_WriteReg(ICM20602_GYRO_CONFIG, 0x08 | 0x03);
// 0x08 表示 ±500 dps,灵敏度 65.5 LSB/dps
// 0x03 表示 DLPF 带宽约 41Hz
// 唤醒设备
SPI_WriteReg(ICM20602_PWR_MGMT_1, 0x01);
}
读数据时,ICM-20602 也是高字节在前:
void ICM20602_ReadGyro(int16_t *x, int16_t *y, int16_t *z) {
uint8_t buf[6];
SPI_ReadRegs(ICM20602_GYRO_XOUT_H, buf, 6);
*x = (int16_t)(buf[0] << 8) | buf[1];
*y = (int16_t)(buf[2] << 8) | buf[3];
*z = (int16_t)(buf[4] << 8) | buf[5];
}
换算:
float gyro_x_dps = (float)(*x) / 65.5f; // 65.5 LSB/dps
核心区别总结:
- L3GD20:低字节在前,灵敏度单位是 mdps/digit
- ICM-20602:高字节在前,灵敏度单位是 LSB/dps
- ICM-20602 有 DLPF,L3GD20 没有(需要外部滤波)
4.4 角速度数据校准
陀螺仪有个毛病:静止时输出不为零。这叫零偏(Zero-rate Offset)。你想想看,如果无人机悬停时陀螺仪报出 0.5 dps,飞控就会以为它在转,然后误动作。
校准方法很简单:采集静止时的数据,求平均,然后减去这个偏移。
#define CALIB_SAMPLES 200
void Gyro_Calibrate(float *offset_x, float *offset_y, float *offset_z) {
float sum_x = 0, sum_y = 0, sum_z = 0;
int16_t x, y, z;
for (int i = 0; i < CALIB_SAMPLES; i++) {
ReadGyro(&x, &y, &z);
sum_x += (float)x;
sum_y += (float)y;
sum_z += (float)z;
delay_ms(5); // 等 5ms,总共采集 1 秒
}
*offset_x = sum_x / CALIB_SAMPLES;
*offset_y = sum_y / CALIB_SAMPLES;
*offset_z = sum_z / CALIB_SAMPLES;
}
使用时:
float raw_dps = (float)raw_value / sensitivity;
float calibrated_dps = raw_dps - offset_dps;
实战经验:采集时一定要确保传感器完全静止。我曾在桌面上校准,结果桌子本身在振动(空调外机引起的),校准出来的偏移全是错的。后来我改用大理石平台,数据才稳定下来。
4.5 零偏稳定性分析
零偏稳定性(Bias Stability)是衡量陀螺仪质量的核心指标。它描述的是:校准之后,零偏随时间漂移的程度。
分析方法:
- 校准后,让陀螺仪静止,连续采集 N 个样本(比如 10 分钟)
- 计算每个样本的零偏残差
- 用 Allan 方差法分析
简单点说,你可以算标准差:
float ComputeBiasStability(float *data, int len) {
float mean = 0, std = 0;
for (int i = 0; i < len; i++) mean += data[i];
mean /= len;
for (int i = 0; i < len; i++) std += (data[i] - mean) * (data[i] - mean);
std = sqrt(std / (len - 1));
return std; // 单位 dps
}
典型值参考:
| 传感器 | 零偏稳定性(典型) | 说明 |
|---|---|---|
| L3GD20 | ±0.5 dps | 消费级,够用但别指望太高 |
| ICM-20602 | ±0.2 dps | 工业级,性价比不错 |
| 高精度光纤陀螺 | < 0.01 °/h | 那是另一个世界了 |
注意:零偏稳定性受温度影响很大。如果你做的是户外产品,建议做温度补偿。我曾经在 -20°C 到 60°C 范围内测过 ICM-20602,零偏漂移了将近 1 dps。不做补偿的话,导航精度根本没法看。
4.6 实战建议
- 先读 WHO_AM_I:这是验证通信最快的方法。读不到就查硬件,别急着调代码。
- 量程选合适:做姿态检测用 ±250 dps 就够了,做旋转检测用 ±2000 dps。量程越小,分辨率越高。
- 滤波别省:L3GD20 没有内置滤波器,建议加一个 20Hz 的 FIR 或 IIR 低通。ICM-20602 的 DLPF 开到 41Hz 带宽就挺好。
- 校准要定期做:每次上电都做一次零偏校准。如果条件允许,做个温度查表补偿。
嗯,这一章就到这里。驱动写好了,数据也校准了,下一章我们聊聊怎么把这些数据融合成姿态角。到时候你会看到,陀螺仪和加速度计是怎么「互相帮忙」的。