做组合导航这几年经常被刚接触PSINS的朋友问同一个问题我想给IMU加个误差模型是不是直接在数据上加几个噪声就行每次听到我都得按住额头。IMU误差模型这件事说小了是几行参数说大了是整个惯性导航精度的地基。PSINS作为国内惯导、组合导航领域流传很广的MATLAB工具箱把误差建模的底层逻辑封装得相当完整但恰恰因为它封装得太好很多刚入门的朋友反而不清楚自己到底在调什么。这篇文章我就带你从零开始把你手里的第一个IMU误差模型真正搭起来并且把这套模型如何放进仿真、如何用实测数据标定参数、如何避开那些坑都讲透。1. 先搞清楚你建模的对象IMU误差到底从哪来1.1 确定性误差零偏、标度因数、安装误差IMU误差听起来抽象其实拆开看就是两类一类是每次上电都稳定存在、可以通过标定消除的确定性误差另一类是随时间随机变化、只能靠统计特性描述的随机误差。确定性误差里最典型的就是零偏也就是陀螺和加速度计在静止时输出不为零的那个偏移量。陀螺零偏通常用deg/h表示加速度计零偏通常用mg或者ug表示。一个MEMS陀螺的零偏可能到几十甚至几百deg/h而光纤陀螺可以做到0.01deg/h以下这个差异直接决定了你能不能用纯惯性导航撑过几十秒还是几分钟。标度因数误差则是输出值与真实值之间的比例偏差。你转90度传感器可能输出89.98度这0.02%的偏差就是标度因数误差。安装误差更形象一点三个陀螺轴和三个加速度计轴理论上应该构成一个正交坐标系但加工装配不可能绝对垂直这个非正交角就是安装误差。全站仪或者转台标定的一大半工作量都是在标这三个参数。1.2 随机误差白噪声、随机游走、零偏不稳定性随机误差这一块才是PSINS建模时真正花时间的地方。最常见的几个概念我刚接触时也绕了很久角度随机游走ARW陀螺输出中的高频噪声积分之后形成的姿态随机游走单位常用deg/√h也有用deg/h/√Hz的两者其实可以换算。速度随机游走加速度计噪声积分之后的速度漂移单位常用ug/√Hz或者m/s/√h。零偏不稳定性Bias Instability低频段那种缓慢起伏的误差单位就是deg/h或mg是判断陀螺档次的核心指标。一阶马尔可夫过程有相关性的慢变漂移用相关时间描述很多工程模型里把零偏的慢变部分近似成一阶马尔可夫。有时候很多人问为什么仿真数据不能只是加个白噪声。原因很简单白噪声积分后只会造成随机游走误差增长是随时间平方根线性增长的而真实IMU还存在慢变相关误差积分后会像抛物线一样往上跑。两者对导航精度的影响模式完全不同如果你做组合导航滤波状态方程里需要估计的误差状态自然也不一样。1.3 误差模型的工程含义你要建立IMU误差模型本质上就是要回答三个问题当前这个传感器有哪些误差项、每个误差项有多大、它们随时间怎么变化。PSINS把这些问题抽象成了imuerr结构体里面有陀螺零偏、加速度计零偏、角度随机游走、速度随机游走、马尔可夫相关时间等字段。你在建模时做的事情就是把传感器数据手册上的指标或者标定结果填进这个结构体。这里就要引入一个非常重要的观点误差模型不是越复杂越好。对低成本MEMS你塞进去一个15阶误差状态滤波未必收敛还可能把位置估计带偏对高精度光纤陀螺如果你只建模白噪声和常值零偏系统在长航时场景下又不够用。所以建模是一个“按需分配”的过程这也是后面我在PSINS里最深的体会之一。2. PSINS工具箱里误差模型的落点到底在哪2.1 工具箱的整体逻辑PSINS的全称是Precise Strapdown Inertial Navigation System由西北工业大学严恭敏老师团队开发和维护。我第一次打开这个工具箱的时候第一反应是结构怎么这么乱函数名全是imuerrset、insinit、kfinit这种缩写没有花里胡哨的GUI。但用久了才发现这种设计反而高效每个函数各管一摊组合起来就是一条完整的仿真链路。和误差模型相关的核心组件就三个imuerrset用来设置误差参数并生成imuerr结构体imuadderr用来给干净的IMU数据加误差以及卡尔曼滤波初始化时kfinit读取误差参数来配置状态协方差。理解这条链路你就理解了PSINS里误差模型的全部用途既用来生成仿真数据也用来定义滤波器“认为”的误差范围。2.2 imuerrset函数参数的填写逻辑imuerrset这个函数是很多新手第一个接触的。它的典型调用方式是imuerr imuerrset(gyro_eb, acc_eb, gyro_db, acc_db, ... gyro_arw, acc_vrw, gyro_markov, acc_markov);如果不传参数直接imuerrset会生成一套默认的MEMS级参数。你可能会问这些参数的单位到底是什么这是最容易踩坑的地方。PSINS里大多数代码遵循的单位惯例是陀螺零偏用deg/h加速度计零偏用ug角度随机游走用deg/√h速度随机游走用ug/√Hz而马尔可夫相关时间用秒。工具箱虽然做了很多单位转换但你在自己填参数时如果混用了rad/s和deg/h出来的结果会非常离谱。下面是常用单位对照表建议直接贴在工位上误差项PSINS常用单位常见换算关系陀螺常值零偏deg/h1 deg/h ≈ 4.85e-6 rad/s陀螺角度随机游走deg/√h1 deg/√h ≈ 2.91e-4 rad/√s加速度计常值零偏ug1 g 1e6 ug加速度计速度随机游走ug/√Hz需结合带宽换算到 m/s/√h陀螺一阶马尔可夫相关时间s无2.3 数据生成与误差注入的完整链路PSINS里要生成一段带误差的IMU数据逻辑是先用轨迹生成得到“理想”的比力输出和角增量再用imuadderr把误差叠加上去。整个链路看起来是这样的glvs trj trjsimu(avp0, trjgen([300, 100, 10], 9/45/180)); % 生成300s轨迹 imuerr imuerrset(0.01, 100, 0.001, 10, 0.001, 10, 300, 300); trj.imu imuadderr(trj.imu, imuerr); % 给理想IMU数据注入误差这段代码里trjsimu生成的trj.imu是干净的理想IMU输出而imuadderr做的事情就是把你设置的零偏、随机游走、马尔可夫过程按时间序列加到每组数据上。这样做的好处是你可以完全控制误差的“配方”知道每一份误差从哪里来这对于验证滤波器设计特别方便。3. 手把手构建你的第一个IMU误差模型3.1 明确你的传感器等级建模之前你要先确定自己手里的IMU大概是什么水平。我把常见的分成三档低端MEMS陀螺零偏几十到几百deg/h角度随机游走0.5到几deg/√h适合手机、玩具级无人机纯惯性导航只能撑几秒钟。中端MEMS/工业级陀螺零偏1~10deg/h角度随机游走0.05~0.5deg/√h适合工业无人机、自动驾驶、机器人纯惯性导航可以撑几十秒到几分钟。光纤/激光陀螺级陀螺零偏小于0.01deg/h角度随机游走小于0.001deg/√h适合矿用、舰载、高精度测绘等长航时场景。下面这个例子我按中端MEMS来写这也是大多数人做无人车、无人机原型验证最常用的一档。3.2 完整示例从设置误差参数到纯惯性解算我直接给出一段可运行的完整脚本你把它粘到PSINS环境里就能跑%% 建模与纯惯性解算示例 glvs ts 0.01; % IMU采样周期 100Hz % 定义初始姿态、位置、速度 avp0 [0; 0; 0; 34.246048*glv.deg; 108.909843*glv.deg; 380; 0; 0; 0]; % 生成300秒的S形运动轨迹 trj trjsimu(avp0, trjgen(300, ts, S)); % 设置IMU误差陀螺零偏0.01deg/h加计零偏100ug % 陀螺ARW 0.001deg/sqrt(h)加计VRW 10ug/sqrt(Hz) imuerr imuerrset(0.01, 100, 0.001, 10, 0.001, 10, 300, 300); trj.imu imuadderr(trj.imu, imuerr); % 纯惯性解算 avp inspure(trj.imu, avp0, ts); % 与真实轨迹对比 avpcmpplot(avp, trj.avp);跑完之后你能看到三组对比曲线姿态、速度、位置。很多新手第一次跑这个的时候会发出灵魂拷问为什么位置误差像坐火箭一样往上飙这是因为纯惯性解算没有外部修正陀螺零偏造成的姿态误差会引入重力分量在水平方向的投影从而让水平加速度估计出错位置误差以近似t²的关系增长。这个增长趋势和你设置的陀螺零偏大小直接相关也是后面判断模型是否合理的直观线索。3.3 结果曲线应该怎么看怎么看这几张图是有讲究的。重点看姿态误差曲线俯仰角误差和横滚角误差如果振荡说明初始对准或轨迹机动有问题航向误差如果持续单方向漂移通常对应陀螺零偏的估计偏差。再看位置误差如果南北方向误差远大于东西方向误差往往和你的初始航向误差有关因为航向误差会把北向速度耦合到东向。如果想定量分析可以读解算末尾的位置误差反推等效陀螺零偏。一个粗略的工程经验公式是在静止或匀速条件下纬度误差主要由东向陀螺零偏主导经度误差由北向陀螺零偏和初始对准误差共同主导。你可以把模型中的零偏加倍再跑一次看位置误差曲线斜率的变化这样你对“误差传递”就有了直观感觉而不是只在参数表格里看那几个数字。3.4 马尔可夫过程为什么要设相关时间imuerrset里最后两个参数是陀螺和加计的马尔可夫相关时间这个很多人直接填个默认值就不管了。相关时间的含义是这条慢变误差序列“记住”自己上一个值的时间长度。相关时间越短误差变化越剧烈越长越接近常值漂移。在纯惯性解算中一阶马尔可夫过程主要影响中频段的误差增长而在组合导航中如果滤波器状态里包含一阶马尔可夫状态相关时间估计得准不准直接决定卡尔曼增益分配是否合理。我个人建议第一版模型里相关时间先设300~1000秒后面用实测数据拟合后再修正。4. 让模型真正可信用Allan方差和静态观测把参数标定出来4.1 为什么纯靠数据手册不够如果你建模只是写论文里的仿真章节用数据手册参数就够了。但如果你要给真实传感器建模型手册里的典型值和你手里这颗芯片的实际表现往往相差很远每一颗都有个体差异。所以完整的误差模型应该是从实测数据里“学”出来的。这事不复杂只需要一段静止数据和一套Allan方差分析。4.2 静止数据采集与Allan方差计算采集IMU数据时把设备放在一个稳定的平台上桌面或者大理石平台上就行连续采集至少2小时。采集时间太短低频段的零偏不稳定性拟合不出来。采集完成后用下面这段脚本做Allan方差分析function [tau, adev] allan_ady(data, dt) % 计算Allan方差 N length(data); maxM floor(log2(N)) - 1; tau zeros(maxM, 1); adev zeros(maxM, 1); for m 1:maxM n 2^m; tau(m) n * dt; % 分成若干子段 numClusters floor(N / n); clusters reshape(data(1:numClusters*n), n, numClusters); avg mean(clusters); diffAvg diff(avg); adev(m) sqrt(0.5 * mean(diffAvg.^2)); end end画对数坐标图你会在曲线上看到不同斜率的区域斜率为-1/2的一段对应角度随机游走斜率为0的最低点对应零偏不稳定性斜率为1/2的上升段对应速率随机游走或者马尔可夫过程。零偏不稳定性的数值可以直接从最低点的幅值读出来这比数据手册上标的“典型值”可靠得多。4.3 零偏常值的估计与剔除Allan方差处理的是随机噪声但原始数据里还有一个常值零偏需要处理。静止采集时把数据的平均值算出来通常就是陀螺和加计的常值零偏。MEMS陀螺每次上电的零偏都可能不同这就是为什么组合导航里通常要把零偏放进滤波器状态实时估计而不是只靠出厂标定。我在工程上一般分三步先算均值剔除常值零偏然后对残差做Allan方差提取随机项参数最后把这些参数写回imuerrset。对低成本MEMS常值零偏随温度变化明显更严谨的做法是同时记录温度数据做成温度补偿表。这个表可以做得非常细但别一上来就搞全套先确认你的场景对精度要求到了那一步再说。4.4 标定后的参数怎么反馈回模型假设你从Allan方差曲线上读出了零偏不稳定性为0.05deg/h角度随机游走为0.01deg/√h那么你在PSINS里的imuerrset调用就可以这样改imuerr imuerrset(0.05, 150, 0.01, 20, 0.01, 20, 600, 300);再用这套参数重新生成一段仿真数据你会发现位置误差增长曲线和之前大有不同。如果做组合导航仿真你会发现滤波器对误差状态的估计更快收敛因为你的过程噪声矩阵设置和真实传感器更匹配了。这里有一个细节imuerrset里陀螺零偏对应的参数会同时影响常值零偏和滤波器的初始P阵中对应项所以填一个大数意味着滤波器认为这个零偏很不确定、需要花时间去估计填一个小数则意味着信任传感器。这个平衡直接决定滤波器的收敛速度和稳态精度非常值得亲手试几组对比。5. 最容易踩的坑我帮你们提前趟了一遍5.1 单位混用导致的全盘崩溃这个坑几乎每个人都会踩。PSINS内部很多函数按约定习惯用deg/h、ug但你在写自己的处理脚本时很自然会用rad/s、m/s²。有一次我把一个陀螺零偏直接填成了0.0001 rad/s对应约20.6deg/h结果整个滤波位置误差比我预期的差了一个数量级排查了半天才发现是单位问题。建议把所有输入输出参数统一换算到PSINS约定单位后再填入。glv结构体里预置了glv.deg、glv.ug、glv.dph等换算常量用它们做换算基本不会错。比如gyro_bias_rads 0.0001; imuerr imuerrset(gyro_bias_rads/glv.dph, 100, ...);5.2 白噪声方差不等于Allan方差直接读数imuerrset里的gyro_arw给出的单位习惯是deg/√h而Allan方差曲线读出的角度随机游走系数通常也是deg/√h这俩是对得上的。但如果你拿示波器或者频谱仪测到的噪声密度来填就要注意带宽问题。很多人把加速度计的噪声密度值直接当VRW填进去结果仿真里的速度漂移比实测大很多。实际上随机游走系数和噪声密度的换算依赖滤波器带宽或者采样率不是简单画等号。我的做法是能实测就实测不能实测宁可通过Allan方差去推也别拍脑袋填。对中端MEMS传感器加速度计VRW通常不会超过几十ug/√Hz填成几百甚至上千就明显不合理。5.3 初始对准误差被误当成IMU误差纯惯性解算结果很差时很多人的直觉反应是“误差模型参数不对”但实际更常见的原因是初始对准没做。PSINS的inspure函数要求输入初始姿态avp0是准确的如果你随便填个姿态初始值误差曲线会从一开始就发散得乱七八糟和IMU误差模型本身关系不大。在你建第一个模型时建议先跑“零初始姿态误差”的理想情况确认误差曲线符合理论预期。然后再加几个不同量级的初始对准误差看看对导航解的影响方式。这个习惯可以帮你把“模型误差”和“滤波器初始条件问题”剥离开。5.4 过程噪声矩阵维度不匹配如果你在PSINS里做的是松组合甚至紧组合仿真卡尔曼滤波的状态量顺序、噪声驱动矩阵维度、量测矩阵维度这三个必须对得上。PSINS的kfinit通常会自动从imuerr结构体里读取误差参数来配置过程噪声但如果你自己拼kf结构体很容易出现矩阵维度报错或者不报错但滤波结果明显异常的情况。我的习惯是每次改状态维度后先用disp(kf.Pk)看一下协方差阵尺寸再用单位阵初始化跑一次纯滤波仿真确认没有维度问题再接入真实数据。这个习惯帮我避免了大半夜调试矩阵维度的痛苦经历。5.5 不要试图拟合所有随机误差项最后也是最重要的一条经验误差模型要跟着你的应用场景走。我一开始做MEMS IMU误差建模时总想把Allan方差曲线上每一段斜率都对应一个模型项结果状态维度越加越多滤波器反而越来越容易发散。后来测出来的问题在于过度参数化会让某些状态不可观滤波器没法准确估计冗余参数只能把不确定性分摊到其他状态上最终导致整体估计恶化。对大部分中端MEMS建立“常值零偏角度随机游走一阶马尔可夫慢变漂移”这个组合就够了对高精度光纤陀螺可能连马尔可夫相关时间都不需要常值零偏加ARW就行。模型复杂度够用就好这也是我在PSINS里学到的最大一课。6. 我最后想补充的几句实在话不少人把这个建模过程想得太高深其实它就是“传感器误差 - 参数化 - 仿真验证 - 实测标定 - 再验证”的循环。PSINS的价值在于把标准链路的工程实现给你铺好了你做的工作是理解每一步背后的物理含义而不是把工具箱当黑盒去乱填参数。如果你现在正准备做自己的第一个模型我给你一个可以直接用的行动清单先跑通默认参数下的trjsimuimuadderrinspure把曲线看懂然后找一小段静止数据做Allan方差把零偏不稳定性读出来再对照本章的参数填法把实测值替换到imuerrset里最后跑一组纯惯性解算和一组组合导航仿真对比误差曲线。跑完这套流程你对误差模型的理解会比看一百页文档都有用。