简介:面向惯性导航与组合导航方向的开发者、学生及研究人员,这套MATLAB开源程序基于NaveGo框架,聚焦GPS与IMU数据融合,重点展示扩展卡尔曼滤波的实际落地方式。压缩包共66个文件,核心为56个m源码脚本,搭配mat数据、txt说明与kml轨迹文件,整体约50.4MB;目录覆盖IMU/GPS传感器模型、姿态与速度更新、滤波器实现、误差分析及真实/仿真数据示例等模块。该资源已有8546人学习下载。通过运行主程序及核心函数,可理解从IMU误差建模到EKF状态预测与校正的融合流程;包内自带合成与实测数据,可对照验证不同噪声条件下的融合效果,并借助误差曲线和轨迹图评估定位精度。对于想深入学习惯性导航原理、复现组合导航算法或二次开发的读者,这份资源提供了完整可运行的实验平台。 做惯性导航和GPS、IMU数据融合,绕不开matlab里的开源程序。第一次跑这类工程,我的感觉是:代码看懂了,轨迹出来却对不上。这套开源的GPS+IMU融合程序,解决的就是组合导航中最核心的问题——用IMU的高频内推填补GPS低频的空白,再用GPS的绝对信息拉住IMU不断飘走的误差。它很适合刚接触组合导航的学生、做机器人或车载定位的工程师,以及那些已经会用四元数解算姿态、但还不会把GPS观测真正引入状态估计的人。
很多人一开始对着论文看卡尔曼滤波,公式推得很顺,但一落到matlab里就懵:状态到底写几维?GPS数据怎么进滤波器?为什么单独用IMU积分一会儿就飘出几十米?这套程序的价值在于,它把“惯性导航”从理论公式变成了一条能跑出平滑曲线的可执行代码,让GPS和IMU的互补关系不再停留在PPT上,而是能亲眼看到融合前与融合后的差距。你可以把它当学习模板,也可以直接在此基础上改参数、加传感器,往实际工程方向走。
1. 这套程序到底在解决什么问题
1.1 GPS和IMU各自的“坑”
先说GPS。普通单点GPS的定位误差通常在米级,多径环境下可能到十几米,更新率一般只有1Hz到10Hz。优点也明确:误差是有限的、长期稳定的,不会越走越偏。换句话说,GPS像一个每隔一段时间才出现的路牌,你能确认自己大概在哪,但没法靠它给出连续平滑的运动估计,尤其在两个定位点之间,车到底是直行还是绕了一个弯,GPS完全看不出来。
IMU由加速度计和陀螺仪组成,能输出比力和角速度,更新率可以到100Hz甚至更高。问题是惯性导航的解算需要积分,拿加速度对时间二重积分得到位移,拿角速度积分得到姿态。每一次积分都会把噪声和零偏累积进去,时间一长,位置漂移非常明显。我见过最夸张的案例是,某个中等精度的微惯性模块,在纯积分状态下30秒就能漂出好几米,低成本的消费级模块漂得更快。
所以这两个传感器的特性刚好互补:IMU擅长高频短时,GPS擅长低频长时。数据融合要做的,就是把IMU当主角,在两次GPS更新之间用IMU推算姿态和位置;每次GPS数据来了之后,用GPS的位置和速度观测去修正IMU推算出的累计误差。这个思路在惯性导航领域就是最经典的松耦合组合导航,也是这套matlab程序的核心逻辑。
1.2 松耦合融合的整体思路
程序采用松耦合而不是紧耦合,这里多解释一句。松耦合指的是GPS先在内部完成位置解算,把最终的经纬高或本地平面坐标当成“观测量”送进融合滤波器;紧耦合则是直接使用GPS的伪距、载波相位等原始观测去参与滤波,精度更高但复杂度也上了一个台阶。对绝大多数学习和前期工程项目,松耦合的精度已经够用,调试成本却低很多。
整个程序框架可以概括成三块:IMU数据进“状态预测”,GPS数据进“观测更新”,卡尔曼滤波器负责把两者拧在一起。系统状态一般取15维,包括位置、速度、姿态角、陀螺零偏、加速度计零偏。IMU每来一帧,就按运动学模型往前推一步;GPS每来一帧,就把当前的推算误差投影到GPS观测方向上做一次修正。输出结果就是一条比任何单一传感器都平滑、准确的轨迹。
选择matlab做这件事,说实话不是因为它性能最好,而是因为矩阵运算和画图太方便了。组合导航核心就是矩阵乘法和协方差递推,matlab里几行代码就能完成;调试时把滤波前后轨迹叠在一张图上,立刻能看出算法有没有跑对。对新手来说,这是最合理的验证环境,也是很多课程和开源项目选择matlab的根本原因。
2. 核心模块拆解:数据、方程和滤波器
2.1 程序结构总览
这套开源程序按职责拆成几个文件,建议按这个顺序去读。main.m是主入口,负责初始化、主循环和调用各模块;config.m集中放所有参数,比如采样率、噪声强度、初始位置;dataGen.m生成仿真数据,包括真实轨迹、IMU采样数据和带GPS噪声的定位结果;imuPropagation.m做IMU状态预测;gpsUpdate.m做GPS量测更新;plotResult.m负责绘图。
主循环的逻辑很简单:先把状态向量和协方差矩阵初始化,再逐帧读取IMU数据,调用imuPropagation做一步预测;如果当前时刻有GPS观测,再调用gpsUpdate做更正。每一步都记录下当前状态,最后统一绘图。整个流程用一句话概括就是“IMU推,GPS纠,从头推到尾”。
为什么这样拆而不是写成一个几百行的脚本?因为组合导航的调试经常需要单独改某一环。比如GPS更新率变了,只需要改数据生成和观测方程;IMU换成另一款器件,只需要改Q矩阵和噪声参数。文件拆开以后,你可以用一小段测试脚本单独验证imuPropagation是否发散,不用每次把整个主流程跑一遍,排查问题的效率会高很多。
2.2 状态方程与观测方程
程序里的状态向量可以这样定义:x = [位置(3), 速度(3), 姿态(3), 陀螺零偏(3), 加计零偏(3)],一共15维。姿态可以用欧拉角,但大姿态角下存在万向锁问题,所以程序里通常采用四元数表达。四元数状态不直接用欧拉角做加法更新,而是把角速度换算成四元数微分,再做积分并归一化。
离散化后的运动学公式可以写成:p_k = p_{k-1} + v_{k-1}·dt,v_k = v_{k-1} + (C_b^n·f_{k-1} - g)·dt,姿态用四元数q更新。这里的C_b^n是载体坐标系到导航坐标系的旋转矩阵,f是加速度计测量到的比力,g是重力加速度。零偏则建模为随机游走,保持不变或加一点很小的高斯噪声。整个状态方程在代码里就对应F矩阵和G矩阵。
观测方程更直接。GPS给出的位置观测和状态里的位置直接对应,所以H矩阵就是一个只挑出位置分量的选择矩阵,z = H·x + v,v代表GPS的位置噪声。如果你还想用GPS测速数据,也可以把速度分量加进H矩阵。松耦合程序的工程量大多不在这里,而在推导状态转移矩阵F和噪声驱动矩阵G。这部分理论不熟没关系,先套程序跑通,回头再补推导。
2.3 卡尔曼滤波的五个公式在代码里长什么样
卡尔曼滤波在matlab里其实就五步,但落到代码里我会分成两段:预测段和更新段。预测段固定执行,更新段只有在收到GPS观测时才执行。核心代码如下:
% 预测:状态和协方差向前推一步 x = F * x; P = F * P * F' + Q; % 更新:GPS观测到达时修正 if hasGps S = H * P * H' + R; K = P * H' / S; % 卡尔曼增益 x = x + K * (z - H * x); P = (eye(size(P)) - K * H) * P; end这段代码看着短,但有两个坑。第一,P的更新如果直接用P - KHP,数值误差可能让协方差失去对称性,工程上一般用Joseph形式或直接把对称后的结果赋值回去。第二,K的计算用P*H'/S而不是inv(S)HP,matlab的右除运算在数值稳定性上更好,速度也更快。
很多人第一次跑通后觉得滤波输出和GPS轨迹差不多,就以为算法没起作用。其实这说明融合结果被GPS牵住了,属于正常状态。想看区别,可以把GPS更新暂时停掉,单独观察IMU预测轨迹的漂移情况。当你看到纯IMU轨迹快速飞走、而融合结果还能被拉回来时,就会明白滤波器到底在压制什么。
3. 从运行到调参:一次完整的实操记录
3.1 环境准备与文件清单
运行这套程序不需要特别高的配置,matlab R2021b及以上的版本都能直接跑,不需要额外安装工具箱。下载后先把整个文件夹加入matlab路径,当前目录设为工程根目录,然后打开main.m运行。第一次运行建议先别改任何参数,直接看输出曲线,给自己建立一个基准印象。
文件清单我列一份常见的:main.m负责主流程,config.m集中放参数,dataGen.m负责生成仿真数据,propagate.m是预测核心,gpsUpdate.m是观测更新,plotResult.m画图。我之前调试时很喜欢把单位、采样率、噪声强度这类常量直接放在config里,而不是散落在各个函数中,改起来一目了然。
如果运行后报“未定义函数”的错误,大概率是路径没设对,或者函数文件名与函数名不一致。matlab对文件名大小写有一定的宽容度,但建议统一用小写,否则换到Linux或Mac环境下容易出问题。这些细节不处理好,会在后续改代码时反复踩坑。
3.2 仿真数据怎么生成
程序跑之前必须有数据,仿真数据生成的质量直接决定后面的调试体验。我常用的做法是先生成一条“真值轨迹”,再根据轨迹反推IMU测量值和GPS测量值。比如设计一条300秒的轨迹,包含匀速、转弯、加速、爬坡几个典型工况,按100Hz生成IMU数据,按1Hz生成GPS数据。
IMU数据的生成公式是:角速度真值加陀螺零偏加白噪声,比力真值由加速度和重力合成,再加加速度计零偏和白噪声。GPS数据则是在真值轨迹的本地坐标系坐标上叠加高斯白噪声。噪声方差要和后续滤波器里的R矩阵保持一致,否则算法会觉得观测比实际更好或更差,输出结果就失真。
matlab里生成这类数据只需要randn函数,不需要特殊工具箱。我注意到很多人喜欢让轨迹一直转圈,觉得好看,但对调试不利。建议加入几个长直线段,这样能清楚观察到IMU漂移的方向,也能验证GPS修正的收敛速度。仿真数据的价值在于可控,你可以把噪声设成零,先确认程序逻辑完全正确,再逐步加噪声。
3.3 协方差矩阵的调参套路
不用怕调参,组合导航里最有价值的就是把Q、R、P这三个矩阵调明白。Q是过程噪声协方差,对应IMU的陀螺和加速度计噪声水平;R是量测噪声协方差,对应GPS位置误差;P是状态协方差初始值,表示一开始对状态估计的不信任程度。
我调参的经验是,先固定R和初始P,只调Q。Q如果太小,滤波器过于相信IMU,融合轨迹会延续IMU漂移趋势,现象是“轨迹很顺但和GPS离得越来越远”;Q如果太大,滤波器会过度依赖GPS,每次GPS一跳,输出轨迹也跟着跳。调到一个临界点后,再微调R。R太小会让轨迹出现锯齿状的抖动,R太大会让GPS的修正作用变弱,漂移恢复慢。
| 参数 | 含义 | 经验初值 | 调大影响 | 调小影响 |
|---|---|---|---|---|
| Q | 过程噪声 | 按IMU噪声密度换算 | 融合结果更“活泼”,容易有毛刺 | 更平滑,但长期漂移增大 |
| R | GPS量测噪声 | 0.5~3 m^2 | 修正作用弱,发散风险高 | 修正作用强,但轨迹抖动 |
| P0 | 初始协方差 | 位置1、速度1、姿态0.1 | 收敛慢,前期波动大 | 收敛快,但可能过信初始值 |
表格里的单位要特别注意,位置对应的方差单位是m^2,速度是(m/s)^2,姿态是rad^2或deg^2。混着用一定会出问题,这是滤波发散的高发原因之一。单位问题在仿真阶段不明显,因为数据由自己生成;一旦切换到真实设备采集的数据,单位就成了第一个需要审核的地方。
4. 我踩过的坑与排查技巧
4.1 滤波器发散:问题出在Q和R的“量级错觉”
我最早遇到的现象是滤波结果在第50秒左右突然飞掉,位置飙到几万米。排除了代码逻辑错误后,发现是Q矩阵中对位置的过程噪声被设成了0.1 m^2,但位置状态本身的协方差在IMU积分下早就涨到了几十,GPS更新又很稀疏,于是增益震荡。说白了就是Q给得太小,滤波器误以为IMU非常准,结果被一次不准的GPS观测“带飞”。
排查发散问题有个笨但有效的办法:把P矩阵对角线打印出来,看有没有某个方差迅速归零。如果某个状态方差在更新后变成极端小值,说明对应的Q或者R设置不合理。另外要检查S矩阵是不是奇异,S = H·P·H‘+R,如果R里出现0,或者GPS位置观测中有重复时间戳,矩阵就会奇异,滤波结果自然不稳定。
这里还有一个量级敏感点:位置、速度、姿态的方差在数值上可能差好几个数量级。位置方差可能是10,速度方差可能是0.1,姿态方差可能是0.0001。放在同一个P矩阵里会导致数值求解困难,所以有的程序会对变量做归一化,比如以ENU坐标的米为单位,姿态用弧度,确保状态量纲之间不至于差异过大。
4.2 坐标系和单位的隐含错误
这套程序里最容易隐藏的bug是坐标系不统一。导航系可以用ENU(东北天)或NED(北东地),GPS给的是经纬度和椭球高度,一般要转换成平面坐标。如果转换时少了一个基准点或高度符号反了,融合结果会出现持续偏置,而且卡尔曼滤波不一定能修正它,因为状态和观测可能用了不同坐标系。
单位方面也见过不少例子。陀螺数据来自习惯使用°/s的器件,但代码里以为单位是rad/s,姿态更新直接放大57倍;加速度数据有的给m/s²,有的给g,混在一起看曲线完全错乱。建议在数据入口处统一转换成SI单位,然后用assert检查数据范围。比如陀螺在正常运动下应该不超过±5 rad/s,超过就说明单位有问题。
四元数归一化是另一个细节。长时间运行后,数值误差会让四元数模长偏离1,姿态漂移会随之出现。每次更新完四元数状态后,要立即做一次归一化。代码里加一行q = q / norm(q),成本极低,收益极大。这个操作看起来不起眼,但在100Hz运行300秒后,累计误差足以把航向角拉偏好几度。
4.3 时间对齐:数据融合里最容易忽略的细节
仿真数据里时间戳是整齐排列的,一到了真实数据,IMU是100Hz,GPS是1Hz,两边时间戳不可能完全对齐。如果简单地把下标对齐,但GPS数据本身有0.1秒延迟,滤波器更新用的“当前GPS观测”其实对应的是0.1秒之前的状态。这种延迟在车载场景里会表现为转弯时融合轨迹落后,且卡尔曼增益越大越明显。
处理办法有两个:一种是在GPS时间戳到达时,向前找最接近的IMU状态做更新;另一种是把系统状态里增加一个时间偏差状态,用滤波器在线估计。后者更复杂,开源程序里我常用第一种。调试时可以画一条时间轴,把IMU采样点和GPS采样点标出来,一眼就能看出是否有固定延迟。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 轨迹发散、飞走 | Q/R量级不当、S矩阵奇异 | 打印P对角线和S行列式 |
| 输出有持续偏置 | 坐标系/单位不统一 | 检查导航系定义和单位转换 |
| 轨迹抖动剧烈 | R太小或GPS噪声大 | 增大观测噪声 |
| 转弯时轨迹落后 | GPS时间戳延迟 | 检查时间对齐逻辑 |
| 姿态漂移 | 四元数未归一化 | 每次更新后归一化 |
5. 后续扩展:从仿真到实际系统
5.1 松耦合升级到紧耦合
松耦合程序跑通后,如果还想深入,下一步通常是紧耦合。紧耦合不再把GPS位置解算结果当作观测,而是直接使用伪距和载波相位。好处是在卫星数量不足或城市峡谷环境下仍能保持一定精度,坏处是观测模型非线性,需要把卫星位置、钟差都纳进去,代码量会明显增加。对新手来说,先用松耦合把卡尔曼滤波的预测-更新节奏练熟,再升级不晚。
5.2 多传感器融合的延伸方向
这套程序稍作修改,就能接进其他传感器。比如加入气压计,可以在高度通道上增加一个观测,抑制无人机高度漂移;加入磁力计,可以为航向提供绝对参考,但要注意磁干扰环境;加入轮速计,在GPS拒止的隧道里也能维持一段时间定位。现在很多人谈“多模态感知数据融合与质量评估”,本质就是不同传感器在不同频段、不同误差特性下如何互相纠正,而不是简单叠加数据。
如果你后续要接视觉或激光雷达,其实思路还是一样:IMU做高频预测,视觉/LiDAR做低频修正,只是观测从GPS的位置变成了重投影误差或点云配准残差。这套matlab程序里的滤波器框架,完全能当做一个可替换模块的基座。先把GPS+IMU这条主线吃透,再往外扩会轻松很多。
5.3 程序使用建议
最后给一个很实际的建议:先看数据,再改代码。我每次拿到一个惯性导航数据,第一件事不是跑卡尔曼,而是先把GPS轨迹、IMU原始角速度和比力分别画出来,确认数据时间长度、频率、单位都符合预期,再开始调滤波器。很多滤波发散问题,根源不是算法,而是数据本身有问题。这个习惯帮我省掉过无数次无意义的调参,也让这套开源程序真正从“能跑”变成了“能用”。
本文还有配套的精品资源,点击获取