简介:一套面向惯性导航与组合导航学习者的 MATLAB 开源实现,基于 NaveGo 项目整合 GPS 与 IMU 数据融合流程。资源包含扩展卡尔曼滤波、IMU/GPS 误差建模、姿态/位置/速度更新等完整模块,既有纯算法函数也有针对实测数据的仿真脚本,适合有一定导航基础的研究生、工程师和开源爱好者深入学习。压缩包共 66 个文件,以 56 个 .m 源文件为核心,配合 5 个 .mat 数据文件、说明文档及许可文件,包体约 50.4MB,目录按算法、转换、示例等模块划分,便于按需查阅。已有 8546 人学习使用。通过研读代码可掌握 EKF 在机载或车载组合导航中的实际实现思路,理解 IMU 零偏、随机游走等噪声建模方法,并能基于开源框架修改传感器模型,快速搭建自己的 GPS/INS 融合原型并输出轨迹与误差对比图表。 写在一篇项目总结前面的话:惯性导航这个方向,很多人一上来就被“捷联解算”“四元数”“卡尔曼滤波”这类词劝退了。其实换个角度,先把手上的数据跑通,再回头补理论,会顺畅很多。这篇博文我就以一套 Matlab 开源程序为线索,把 GPS 和 IMU 数据融合这件事的来龙去脉、核心代码逻辑、调参踩坑过程,一次性讲清楚。
1. 项目整体思路:为什么偏偏要用卡尔曼滤波做数据融合
这套程序解决的核心问题很直接:单一传感器的定位结果不可信。GPS 在开阔环境下精度不错,但进隧道、桥下、高楼密集区就“飘”得离谱,而且更新频率一般只有 10Hz 左右,动态稍微一快就脱靶。IMU 的输出频率可以到 100Hz 甚至 200Hz,短时间内姿态和位置推算很平滑,但它的致命伤是误差随时间累积,几秒不修正,轨迹就能漂到不知道哪里去。
所以 GPS 和 IMU 天生是互补的:GPS 低频但绝对准确,IMU 高频但短期可靠。把两者用算法融合起来,让短期的靠 IMU 平滑,长期靠 GPS 拉回来,这就是组合导航的基本逻辑。而卡尔曼滤波,恰恰是这个逻辑的最佳载体。
卡尔曼滤波的核心思想,通俗点说就是把传感器当“证人”:IMU 是那种对眼前发生的事描述很生动但记性差的人,GPS 是那种说话慢但句句靠谱的人。卡尔曼滤波的工作就是不停听两边描述,再根据对各自信任程度的评估,给出一个最接近真相的估计。这套开源程序里用的是扩展卡尔曼滤波(EKF),因为系统状态转移本身是非线性的,标准的 KF 处理不了,EKF 的做法是在估计点附近做一阶泰勒展开,把非线性问题近似成线性问题。工程上这个近似已经非常够用,尤其在车载、无人机这类场景下。
程序整体架构分四个模块:IMU 数据仿真与姿态解算、GPS 模拟观测生成、EKF 融合核心、误差分析与轨迹可视化。每一块都可以单独拆出来跑,这对我判断问题出在哪个环节特别有帮助——我强烈建议你拿到代码后也按模块去 debug,而不是一把梭把整条链路跑完再说。
2. 核心前提:坐标系定义和姿态解算方法选型
很多初学者搭融合模型,第一件事就是死在坐标系上。这套程序里用到的坐标系有三个,你必须先分清:
- 地球坐标系(ECEF,地心地固系):原点在地心,X 轴指向本初子午线与赤道交点,Z 轴指向北极。GPS 输出的原始位置一般就是这个坐标系下的 XYZ。
- 导航坐标系(n 系):也叫当地水平坐标系,通常取“东—北—天”(ENU)或“北—东—地”(NED)。这套程序采用的是 ENU,因为做车辆导航时,我们更关心东西向和南北向的位移。
- 载体坐标系(b 系):固连在 IMU 上,X 轴朝前(车头方向),Y 轴朝右,Z 轴朝下(或朝上,看传感器定义)。IMU 输出的加速度和角速度全是在这个坐标系下的。
融合的时候,GPS 输出的 ECEF 坐标必须先转成 ENU 系下的位置和速度,IMU 的加速度也要从 b 系转到 n 系。这个转换错误是代码里最常见的“隐形杀手”,因为程序能跑、曲线也出来,但位置就是偏。
再说姿态解算。IMU 里的陀螺仪给的是角速度,对它积分可以得到角度,但这东西有两个问题:一是零偏导致积分漂移,二是欧拉角在俯仰角接近 ±90° 时会出现万向节锁定(Gimbal Lock),把三自由度退化成两自由度,直接解不出来。这个坑当年我确实踩过,后来才意识到主角其实应该是四元数。
四元数用四个参数表示旋转,没有欧拉角的奇异性问题,而且运算全是加法和乘法,计算效率也高。这套程序里就是用它做姿态更新的,核心代码逻辑大致长这样:
% 四元数姿态更新(基于陀螺仪角速度) function q = attitudeUpdate(q, gyro, dt) % gyro: 三轴角速度(rad/s) omega = [0, -gyro(1), -gyro(2), -gyro(3); gyro(1), 0, gyro(3), -gyro(2); gyro(2), -gyro(3), 0, gyro(1); gyro(3), gyro(2), -gyro(1), 0]; q = (eye(4) + 0.5 * omega * dt) * q; q = q / norm(q); % 归一化,防止累积误差 end注意最后一步归一化,这是必须的。四元数如果不归一化,长时间递推后模长会漂移,姿态矩阵就不再是正交阵,后面所有坐标变换的结果都会是错的。这行代码看似不起眼,实则是准度的一道保险杠。
3. EKF 状态方程与观测方程的构建过程解析
这套程序里 EKF 的状态向量取的是 15 维,这个选择比较经典:
状态: [位置误差(3), 速度误差(3), 姿态误差(3), 陀螺零偏(3), 加速度计零偏(3)]熟悉组合导航的人看到这个结构应该不陌生。用“误差”而不是“绝对量”作为状态,是这整套程序最关键的设计决策。原因在于:
- 误差量在短时间内变化缓慢,线性化误差小,EKF 的近似更准确
- 直接用绝对位置、速度做状态,数值动辄几千米、几百米,和姿态角的量级差出几个数量级,矩阵运算中容易产生病态问题
- 误差模型符合惯性器件的真实物理特性:零偏是随机游走过程,可以用一阶马尔可夫或随机游走建模
状态转移矩阵 F 的推导是这套代码里最烧脑的部分,但它有个很好的性质:大部分元素是 0 或 I,核心耦合在于姿态误差会通过重力加速度耦合进速度误差方程里。通俗点说,如果姿态估计有个小角度误差,它就会“误导”加速度计数据的投影方向,让速度估计逐渐偏掉,最终反映到位置误差上。这个耦合关系不建进去,滤波器就等于瞎了。
观测方程相对来说好理解。GPS 给出位置和速度,对应到状态向量里就是位置误差和速度误差。测量矩阵 H 长这样:
% 观测矩阵: GPS提供位置和速度观测 % 对应状态中的位置误差和速度误差 H = [eye(3), zeros(3, 6), zeros(3, 6); zeros(3, 3), eye(3), zeros(3, 6)];GPS 观测更新的时候有个大坑:位置在 ENU 坐标系下是“东、北、天”的顺序,速度也是一样。如果你在代码里把顺序搞成“东北天”或“北东地”,滤波结果会直接发散。我调试这套程序时有一半的时间是在抓这种顺序问题,最后统一在代码开头用注释写明坐标系顺序、单位、更新频率,才彻底根治。
4. 实操全过程:从模拟数据到融合结果,手把手跑通
4.1 生成仿真轨迹和 IMU 数据
没有真实硬件的时候,跑仿真数据才是调算法的正确起点。程序里用了一个比较聪明的做法:先设计一条“真实轨迹”,然后反推 IMU 应该输出什么,再加上噪声和零偏模拟真实传感器。这样你手里就同时有了真值和传感器数据,可以定量评估滤波器的表现。
轨迹我建议不要设计得太温柔。很多教程喜欢用匀速直线或缓慢转弯,跑出来的融合结果当然漂亮,但完全无法暴露问题。我用了一段包含急加速、急减速、大角度转弯的轨迹,把 IMU 的比力输出逼到极限,这样算法行不行一眼就能看出来。
IMU 噪声模拟的核心代码大概是这样:
% 模拟IMU测量: 真值 + 常值零偏 + 高斯白噪声 gyro_true = ...; % 由真实姿态变化率计算 accel_true = ...; % 由真实加速度和重力分解得到 gyro_bias = [0.02; -0.015; 0.025] * pi / 180; % 20度/小时量级零偏 gyro_noise_std = 0.1 * pi / 180; % 0.1度/s,典型MEMS IMU水平 gyro_meas = gyro_true + gyro_bias + randn(3,1) * gyro_noise_std;零偏的量级别拍脑袋设。MEMS 陀螺仪的零偏稳定性一般在几度/小时到几十度/小时之间,加速度计零偏在几十 mg 左右。你设的数据越贴近真实器件,EKF 里对应的过程噪声协方差 Q 才越有参考价值。
4.2 GPS 观测生成与坐标转换链路
GPS 观测的生成是另一条线。真实场景下 GPS 输出的是 WGS-84 坐标系下的经纬度和高度,程序里为了简化,直接生成 ENU 系下位置加高斯白噪声。别嫌这个简化太粗暴,处理真实 GPS 数据时经纬度转 ENU 的公式本来就该自己去实现,这里重点是把融合逻辑验证对。
GPS 噪声方差我初始设的是水平方向 3 米、垂直方向 8 米。注意垂直方向误差明显大于水平的,这是 GPS 的系统性特征——卫星几何分布导致高度精度天然差一截。如果你设置的观测噪声矩阵 R 是各方向同性的,反而和实际情况不符。
4.3 EKF 主循环的参数设置与调优
EKF 主循环分两个阶段交替执行:预测(用 IMU 数据)和更新(用 GPS 数据)。
预测阶段,每来一帧 IMU 数据就递推一次状态和协方差:
% 状态预测 x_pred = F * x_est; P_pred = F * P_est * F' + Q;更新阶段,来一帧 GPS 数据才算一次卡尔曼增益:
% 卡尔曼增益 K = P_pred * H' / (H * P_pred * H' + R); % 状态更新 x_est = x_pred + K * (z_gps - H * x_pred); % 协方差更新 P_est = (eye(15) - K * H) * P_pred;这里有个工程细节被无数教程忽略——IMU 的频率是 100Hz,GPS 是 10Hz,意味着 GPS 每来一帧,IMU 已经跑了 10 帧。程序主循环必须以 IMU 速率运行,GPS 数据到达时才触发更新分支,而不是反过来。反过来的话,IMU 数据会全被丢弃,输出就没有高频姿态了,融合的意义直接少了一半。
参数 Q 矩阵的调节是个细活儿。Q 描述的是对系统模型的信任程度,R 描述的是对 GPS 观测的信任程度。Q 设得太大,滤波结果会剧烈跳动,趋于只信 GPS;Q 设得太小,输出会过度平滑,GPS 的真实误差修正作用被压制,轨迹会往漂移的方向滑。这套程序我给了一组初始可用的参数,但你要理解它们的物理含义去调节,而不是照抄。我调参时的经验法则是:先调 R(因为 GPS 的误差特性可以查文献或标定得到),固定 R 后再动 Q,优先调节陀螺仪零偏对应的过程噪声项,它是最影响姿态估计的。
4.4 结果输出与误差评估指标
程序跑完会生成三个关键图:
- 轨迹对比图:真实轨迹、GPS 原始轨迹、IMU 纯惯性推算轨迹、EKF 融合轨迹,四者画在同一张图上。观察重点是融合轨迹是不是比 GPS 平滑、是否贴近真实轨迹。
- 位置误差图:融合后的位置误差和纯 GPS 误差随时间的变化曲线。评估标准是融合后的误差均值是否接近零、标准差是否小于 GPS 原始误差。
- 姿态角输出图:融合解算出的横滚、俯仰、航向角,重点看航向角有没有随时间漂移。
评估指标我记得最清楚的,是拿融合轨迹与真实轨迹的均方根误差(RMSE)作为量化标准。实测下来,如果 IMU 零偏和噪声参数模拟得比较合理,EKF 融合的水平位置 RMSE 能比纯 GPS 减少 20% 到 40%,同时输出频率提升到 100Hz。这组数据很有参考价值——融合不是为了推翻 GPS,而是让定位结果更稳定、更顺滑、可依赖。
5. 这套程序的高潮和坑:调试实录与规避指南
坑一:协方差矩阵不正定导致滤波发散
现象是跑了几百步后,输出直接变成 NaN。排查发现 P 矩阵在多次递推后失去对称正定性。解决方案有两个方向:一是数值上强制对称化,每次更新后执行P = (P + P') / 2;二是用平方根滤波等数值更稳定的形式。对于这套教学程序,对称化就够了。
坑二:GPS 跳变引起的滤波震荡
GPS 偶尔会出现比正常噪声大一个数量级的跳变。EKF 的增益如果没来得及调整,输出会突然“扯”向错误位置。我处理的办法是加一个异常值检测:计算新息(观测残差)的马氏距离,如果超过阈值就跳过这次观测更新。所谓马氏距离,简单说就是“残差相对于噪声大小的比值”。这套逻辑在实际场景中相当实用——城市峡谷和立交桥场景下,GPS 跳变是常态,不加防护的融合算法根本没法落地。
坑三:状态更新顺序导致姿态和位置“打架”
EKF 更新出来的状态是误差量,包括姿态误差。但在把误差修正回状态时,姿态误差不是直接减掉就完事,而是有精确的乘法修正形式。如果代码里对姿态误差处理方式不对,会出现飞行器等场景中先有姿态跳变、再有位置发散的连锁反应。实践中我是在调试姿态输出时发现横滚角出现了不正常的瞬间跳变,才意识到修正方式写错了。
提示:EKF 公式本身不复杂,真正复杂的是你对状态的物理定义是否足够清晰。写代码前,先拿笔把每个状态量的单位、坐标系、误差修正公式写清楚,代码写完基本一遍跑通。
6. 常见问题速查表与我的调参经验分享
% 一个关键参数的参考范围(以车载场景为例) % GPS观测噪声(m): 水平方向 R_pos = 3^2 = 9 % 垂直方向 R_alt = 8^2 = 64 % 加速度计过程噪声(m/s^2): 0.5^2 到 1.0^2 % 陀螺仪过程噪声(rad/s): 0.01^2 到 0.05^2下面这个表是我在实测中沉淀下来的排查手册,基本覆盖了新手最容易遇到的情况:
| 故障现象 | 大概率原因 | 检查手段 | 解决方案 |
|---|---|---|---|
| 滤波直接发散(NaN) | P矩阵失去对称正定性 | 检查P矩阵特征值 | 对称化或改用平方根滤波 |
| 轨迹对GPSCS过于敏感 | Q设置过小 | 查看新息序列统计量 | 适当增大Q对应项 |
| 融合结果过于平滑、转弯滞后 | R设置过小或Q过大 | 对比纯GPS轨迹差异 | 调小Q或增大R |
| 姿态角持续漂移 | 陀螺零偏估计不收敛 | 查看零偏状态估计曲线是否能稳定 | 调整零偏对应Q,或初始化给合理初值 |
| 高度方向误差特别大 | GPS高度噪声模型不准 | 检查R_alt设置 | 单独设置高度方向的R值 |
| 代码跑通但坐标系符号相反 | 姿态矩阵或轴顺序错误 | 将航向角旋转90度测试东向北向速度变化是否符合预期 | 统一坐标系定义,逐步验证每个转换 |
关于调参,我想分享一个让我少走几年弯路的经验:不要同时调多个参数。你先固定 R,只调 Q 里的一个数,然后看误差曲线的变化趋势;记录参数与结果的对应关系后,再动下一个。一次动两个以上的参数,出了问题你根本不知道是谁造成的。这样一遍遍试下来,对滤波器的“手感”就建立起来了。
我试过把这段开源程序搬到真实的车载数据上跑,主要的修改集中在三块:一是把仿真数据替换成真实传感器数据并加了时间戳对齐;二是增加 GPS 异常值检测逻辑;三是把 R 矩阵按照卫星颗数做动态调整——卫星少时增大 R,对应降低 GPS 的信任度。整体框架完全不需要改动,这从侧面验证了这套程序的设计是靠谱的。
最后再分享一个小技巧:学好这套 EKF 后,你可以把它轻松平移到其它传感器融合场景,比如视觉与 IMU 融合、雷达与 IMU 融合,零偏建模和误差状态的思想是通用的。真正理解了“误差状态”这个建模思路,你以后再看到任何组合导航的代码,都会觉得豁然开朗。
本文还有配套的精品资源,点击获取