捷联惯导系统(Strapdown Inertial Navigation System, SINS)是现代导航领域的核心技术之一。与平台式惯导不同,捷联系统直接将惯性测量单元(IMU)固定在载体上,通过数学算法完成导航解算。这种设计大幅降低了系统体积和成本,使其在无人机、自动驾驶、机器人等领域得到广泛应用。
很多人对捷联惯导存在误解,认为它需要复杂的数学理论和高端硬件支持。实际上,只要掌握核心原理和关键算法,用嵌入式系统就能实现基本功能。我在工业级IMU开发中积累了一些经验,本文将分享从传感器数据到导航解算的全过程,并提供可直接移植的C代码实现。
IMU通常包含三轴加速度计和三轴陀螺仪,分别测量比力(specific force)和角速度。以MPU6050为例,其典型参数为:
注意:实际应用中需根据载体动态特性选择合适的量程。过大量程会降低分辨率,过小则容易饱和。
捷联惯导的核心是姿态矩阵更新和速度/位置解算。主要涉及:
math复制\dot{q} = \frac{1}{2}q \otimes \omega
math复制\dot{v} = C_b^n f^b + g^n
math复制\dot{p} = v
c复制// 加速度计和陀螺仪原始数据读取
void IMU_ReadRawData(int16_t acc[3], int16_t gyro[3]) {
uint8_t buf[14];
I2C_Read(MPU6050_ADDR, ACCEL_XOUT_H, buf, 14);
acc[0] = (buf[0] << 8) | buf[1];
acc[1] = (buf[2] << 8) | buf[3];
acc[2] = (buf[4] << 8) | buf[5];
gyro[0] = (buf[8] << 8) | buf[9];
gyro[1] = (buf[10] << 8) | buf[11];
gyro[2] = (buf[12] << 8) | buf[13];
}
// 单位转换(示例:±2g量程)
float ConvertAccel(int16_t raw) {
return raw * 2.0f / 32768.0f; // 转换为g值
}
c复制typedef struct {
float q0, q1, q2, q3;
} Quaternion;
void Quaternion_Update(Quaternion *q, float wx, float wy, float wz, float dt) {
float norm = sqrtf(wx*wx + wy*wy + wz*wz);
if (norm > 0.0f) {
wx /= norm; wy /= norm; wz /= norm;
}
float halfTheta = 0.5f * norm * dt;
float sinHalf = sinf(halfTheta);
Quaternion delta = {
cosf(halfTheta),
wx * sinHalf,
wy * sinHalf,
wz * sinHalf
};
// 四元数乘法
Quaternion new_q = {
q->q0*delta.q0 - q->q1*delta.q1 - q->q2*delta.q2 - q->q3*delta.q3,
q->q0*delta.q1 + q->q1*delta.q0 + q->q2*delta.q3 - q->q3*delta.q2,
q->q0*delta.q2 - q->q1*delta.q3 + q->q2*delta.q0 + q->q3*delta.q1,
q->q0*delta.q3 + q->q1*delta.q2 - q->q2*delta.q1 + q->q3*delta.q0
};
*q = new_q;
// 归一化处理
float norm_q = sqrtf(q->q0*q->q0 + q->q1*q->q1 + q->q2*q->q2 + q->q3*q->q3);
q->q0 /= norm_q; q->q1 /= norm_q; q->q2 /= norm_q; q->q3 /= norm_q;
}
c复制void Navigation_Update(float accel[3], Quaternion q, float *vel, float *pos, float dt) {
// 姿态矩阵计算
float Cbn[3][3] = {
{1-2*(q.q2*q.q2+q.q3*q.q3), 2*(q.q1*q.q2-q.q0*q.q3), 2*(q.q1*q.q3+q.q0*q.q2)},
{2*(q.q1*q.q2+q.q0*q.q3), 1-2*(q.q1*q.q1+q.q3*q.q3), 2*(q.q2*q.q3-q.q0*q.q1)},
{2*(q.q1*q.q3-q.q0*q.q2), 2*(q.q2*q.q3+q.q0*q.q1), 1-2*(q.q1*q.q1+q.q2*q.q2)}
};
// 比力转换到导航系
float fn[3] = {0};
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
fn[i] += Cbn[i][j] * accel[j];
}
}
// 重力补偿(假设导航系为NED,g≈9.8m/s²)
fn[2] -= 9.8f;
// 速度更新
for (int i=0; i<3; i++) {
vel[i] += fn[i] * dt;
pos[i] += vel[i] * dt;
}
}
| 误差类型 | 典型表现 | 解决方法 |
|---|---|---|
| 零偏误差 | 静态时有输出 | 开机校准+在线估计 |
| 比例误差 | 输出与输入不成比例 | 标定实验补偿 |
| 安装误差 | 轴不对齐 | 标定旋转矩阵 |
| 温度漂移 | 随温度变化 | 温度补偿模型 |
纯惯性导航最大的挑战是姿态误差会随时间累积。实测数据显示:
解决方法:
c复制// 泰勒展开近似sin/cos(适用于小角度)
float fast_sin(float x) {
return x - x*x*x/6.0f + x*x*x*x*x/120.0f;
}
c复制// 系统状态结构体
typedef struct {
Quaternion att;
float vel[3];
float pos[3];
float accel_bias[3];
float gyro_bias[3];
} NavState;
// 主处理循环
void INS_Update(NavState *nav, float dt) {
// 1. 读取传感器原始数据
int16_t raw_acc[3], raw_gyro[3];
IMU_ReadRawData(raw_acc, raw_gyro);
// 2. 单位转换和误差补偿
float accel[3], gyro[3];
for (int i=0; i<3; i++) {
accel[i] = ConvertAccel(raw_acc[i]) - nav->accel_bias[i];
gyro[i] = ConvertGyro(raw_gyro[i]) - nav->gyro_bias[i];
}
// 3. 姿态更新
Quaternion_Update(&nav->att, gyro[0], gyro[1], gyro[2], dt);
// 4. 导航解算
Navigation_Update(accel, nav->att, nav->vel, nav->pos, dt);
// 5. 可选:零速检测与修正
if (DetectZeroVelocity()) {
for (int i=0; i<3; i++) {
nav->vel[i] = 0;
nav->accel_bias[i] = 0.9f*nav->accel_bias[i] + 0.1f*accel[i];
}
}
}
分阶段验证:
使用可视化工具:
python复制# 简单的MATLAB/Octave验证脚本示例
t = 0:0.01:10;
gyro = 0.1 * sin(t); % 模拟角速度输入
q = [1 0 0 0]; % 初始姿态
for i = 2:length(t)
dt = t(i)-t(i-1);
q = quatmultiply(q, [cos(gyro(i)*dt/2) 0 0 sin(gyro(i)*dt/2)]);
end
plot(t, quat2eul(q))
典型测试场景:
在实际项目中,我习惯先用Python/MATLAB验证算法,再移植到嵌入式平台。这种方法能快速发现算法问题,避免硬件调试的复杂性。对于资源受限的MCU,适当降低更新频率或简化算法往往是必要的取舍。