上周有个项目让我印象很深:对方用 D435i 跑 VINS-Fusion,视觉跟踪看着很稳,但定位轨迹就是慢慢飘。我打开配置一看,IMU 噪声参数是从网上一份教程里复制的默认值,加计和陀螺的随机游走跟实际传感器根本不是一回事。后来用 imu_utils 重标了一遍,把 noise density 和 random walk 换成真实测量值,问题立刻缓解。类似的场面我遇到过不止一次。
所以这篇东西我想聊聊 kalibr_allan、imu_utils、imu_tk 这三个开源 IMU 标定工具包到底怎么选、怎么用。它们解决的都不是"IMU 装没装正"这种外参问题,而是更底层、更隐蔽的噪声特性问题。如果你在做 lidar imu 标定前置准备、RGB-D 相机配合 IMU 的紧耦合、或者手眼标定之后要评估 IMU 数据质量,这篇文章应该能帮你少走不少弯路。
1. 标定到底在"标"什么:五个参数把 IMU 的坏脾气说清楚
1.1 先看一个真实翻车场景
很多人对 IMU 标定的理解停留在"标完就能消除漂移",这是个挺常见的误区。IMU 的漂移来自两个层面:一个是每次开机都不一样的零偏,另一个是运行中随时间缓慢变化的随机游走。前者靠上电后静止初始化能解决一部分,后者只能靠统计建模,让算法知道"这个传感器大约以多大的速率在随机漂移"。
我见过最典型的问题是这样的:VINS-Mono 里默认的加计噪声参数是0.01,陀螺是0.001,很多移植项目直接沿用。但不同型号 MEMS IMU 的噪声水平可能差一个数量级,一个 D435i 的实测加计 noise density 可能在0.005到0.02之间,一颗工业级光纤陀螺可能只有0.0001。拿别人的参数套自己的传感器,滤波器对 IMU 的信任程度就是错的,表现就是"视觉明明没崩,轨迹却在慢慢漂"。
1.2 随机误差、确定性误差、安装误差要分开看
这里把 IMU 标定涉及的东西拆开,避免三种东西混在一起:
- 随机误差:陀螺的角度随机游走(Angle Random Walk,ARW)、加计的速度随机游走(Velocity Random Walk,VRW),对应的就是 Allan 方差曲线里的白噪声段;还有长期工作的偏置随机游走(Bias Instability / Rate Random Walk)。它们是概率意义上的统计量,没法用一个固定值消掉,只能建模成滤波器过程噪声。
- 确定性误差:包括常值零偏(bias)、尺度因子误差(scale factor)、轴间非正交误差(misalignment)。这部分是可以用六位置法或连续旋转法标定出来、然后在数据预处理阶段直接减掉的。
- 安装误差:IMU 和相机/雷达/机械臂末端之间的旋转和平移外参,一般靠手眼标定或联合标定解决,和上面的传感器自身标定不是一回事。
kalibr_allan、imu_utils 主要做随机误差,imu_tk 在随机误差之外还能做一部分确定性误差。这一点决定了你当前项目该先碰哪个工具。
1.3 标定参数在 VIO 里到底被谁消费
以 VINS-Fusion 这类紧耦合方案为例,IMU 的噪声参数用于预积分协方差传播。简单说,后端优化时会用noise density和random walk去构造过程噪声矩阵,决定每一帧 IMU 测量值在状态估计里有多少话语权。参数给小了,滤波器会过分相信 IMU 短时积分结果,视觉观测稍有延迟或遮挡,姿态就被带偏;参数给大了,IMU 提供的信息几乎不被信任,系统在纯视觉退化场景(比如白墙、暗光)里就直接飘了。
所以标定不是"为了好看",而是给滤波器一个诚实的传感器模型。这就像你知道一个人跑步时会喘,不能让算法假设他是机器人、恒定输出配速,否则后面的配速预测全都会错。
2. 三大工具的定位差异:同是 Allan 方差,三种不同姿势
2.1 共同的数学地基
kalibr_allan、imu_utils 的核心算法都建立在 Allan 方差分析上。Allan 方差的思路很朴素:把一段长时间静止的 IMU 数据按不同的时间窗口长度切块,统计相邻窗口均值的差异方差,然后画出"聚类时间-方差"的双对数曲线。曲线不同段的斜率对应不同误差源:
- 斜率约 -0.5 的段对应白噪声,曲线在纵轴上的截距换算成 noise density;
- 曲线最低点对应零偏不稳定性;
- 斜率约 +0.5 的段对应速率随机游走,也就是 random walk。
理解这个原理很重要,因为后面无论哪个工具跑完,你都要能在图形上判断结果可不可信。如果采集数据时长不够,曲线根本不会走到斜率 +0.5 的段,工具拟合出来的 random walk 可能只是外推值,要用时心里得有数。
2.2 三款工具的定位差异
| 工具 | 主要功能 | 输入形式 | 依赖 | 输出 |
|---|---|---|---|---|
| kalibr_allan | Allan 方差分析、随机噪声参数拟合 | ROS bag | ROS(通常配合 Kalibr 生态) | YAML 格式噪声参数、Allan 曲线 |
| imu_utils | Allan 方差分析、一键化封装 | ROS bag | ROS、code_utils | YAML 格式噪声参数、PNG 曲线图 |
| imu_tk | 确定性误差标定 + Allan 方差分析 | 文本文件(timestamp, acc, gyr) | 无 ROS 强依赖,C++ 库 | 解析解形式输出偏差、尺度、轴间误差 |
kalibr_allan 来自 ETHZ Kalibr 生态,风格偏研究向,适合本来就准备用 Kalibr 做相机-IMU 联合标定的团队。它的好处是和后续工具链自洽,坏处是需要自己编译、配置,对只想快速拿到一组参数学会有点重。
imu_utils 是社区里流传最广的轻量方案,日常最常用。它把 Allan 方差分析包成了一个 ROS 节点,播放 bag 就能出结果,我在这篇里会重点写它的实操。
imu_tk 则走的是另一条路:它有个相当实用的多位置静态标定方法,可以估计加计和陀螺的零偏、尺度因子、轴间非正交误差,同时也能做 Allan 方差分析。它对没有 ROS 环境的嵌入式项目特别友好,输入是纯文本,部署在 ARM 板上也能跑。
2.3 选型速判
如果按使用场景推荐,我的习惯是:
- 正在用或准备用 Kalibr 做联合标定 → 先上 kalibr_allan,保持整条链路的格式一致;
- 只想要一份可用的随机噪声参数、越快越好 → imu_utils,晚上录两小时数据,第二天早上出结果;
- 要用在嵌入式 / 无 ROS 环境,或者想顺便修掉确定性偏差 → imu_tk;
- 追求严谨、用于论文级精度 → kalibr_allan + imu_tk 双跑交叉验证。
3. kalibr_allan 实操记录:从 rosbag 到 imu.yaml
3.1 环境准备与依赖
kalibr_allan 一般以源码形式放进你的 ROS catkin 工作区。我常用的做法是建一个imu_ws,单独容纳标定相关包,不要跟业务工程混在一起。
需要准备的东西包括:一个可用 ROS 环境、编译 Kalibr 生态所需的基础依赖,以及你最终想用的那个 IMU 驱动。这里最容易忽略的是把 IMU 的采样率固定下来。很多传感器驱动默认值比较随意,我建议统一设置成传感器原生最高频率,比如 200 Hz 或 400 Hz,并且在录制期间不要修改参数。
编译顺序也有讲究。如果是从源码拉取 Kalibr 相关包,建议严格按照仓库文档里的依赖顺序来,避免同时拉了很多没用的可视化依赖。遇到 Eigen、Sophus 版本冲突是常见事,建议用一个干净的 Ubuntu + ROS 环境单独编译。
3.2 数据录制:时长、静止、采样率
kalibr_allan 需要的是长时间静止数据。我实际测试下来的经验是:用于工程估计,1 到 2 小时基本够用;要求严谨一点,最好录 2.5 到 3 小时。太短的数据会让 Allan 曲线在长聚类时间区间没有足够的统计样本,random walk 的拟合误差很大。
录制的具体做法:
- 把 IMU 固定在一个刚性的台面上,不要放桌上,因为桌子本身可能有低频振动;
- 关掉电机、风扇等明显振源;
- 传感器电源保持稳定,不要用那种电压波动明显的 USB 口,否则会在陀螺上看到明显的异常峰;
- 录制前先上电预热几分钟,让偏置从冷启动状态稳定下来。
录制命令很简单:
rosbag record -O imu_static.bag /imu/data但要注意,/imu/data这个名字只是示例,实际操作前要确认自己驱动发布的话题名称和消息类型。录制期间我一般每隔几分钟看一眼rostopic hz,确保频率没有掉。
3.3 跑标定并获取输出
kalibr_allan 的典型运行方式是通过 ROS 节点,把 bag 路径、IMU 话题作为参数传进去。不同版本入口略有差异,这里的关键是:运行后它会读整个 bag 的 IMU 数据,计算 Allan 方差并做拟合。
rosrun kalibr_allan kalibr_allan --bag imu_static.bag --imu /imu/data跑完之后工作目录里会出现结果文件。常见命名类似imu.yaml或者带时间戳的 YAML,里面包含了陀螺和加计的 noise density、random walk 以及部分 Allan 曲线数据。
我第一次跑这个工具的时候有个困惑:它输出的是 rad/s 单位下的功率谱密度,还是离散时间下的参数?这需要仔细阅读头注释。现在比较常见的做法是输出连续时间噪声密度,后续在 kalibr 联合标定时直接用;如果你要转成 VINS 里的离散时间噪声,别忘了除以sqrt(采样周期)。这个换算我后面专门讲。
3.4 输出文件长什么样
一份典型输出的 YAML 内容大概是这样的结构:
gyroscope_noise_density: 1.5833e-04 gyroscope_random_walk: 2.4600e-05 accelerometer_noise_density: 3.6087e-03 accelerometer_random_walk: 3.2917e-03这几个值后续可以直接喂给 Kalibr 的相机-IMU 联合标定。需要强调的是,不同工具的单位约定可能不同,最好以单位注释为准。如果某些输出没有写单位,可以画 Allan 曲线验证:noise density 对应曲线左端平直段的高度,random walk 对应右侧斜率 0.5 段的延伸值。
4. imu_utils 实操记录:5 分钟拿结果的轻量方案
4.1 安装 code_utils 与 imu_utils
imu_utils 是我在实际项目里用得最多的工具,因为它足够轻。安装时有个先后顺序问题:先编译code_utils,再编译imu_utils,因为后者依赖前者。很多人一上来把所有包扔进 catkin_make,结果 code_utils 还没编译完就报找不到头文件。
建议分开编译:
catkin_make -DCMAKE_BUILD_TYPE=Release source devel/setup.sh如果你在用较新版本的 ROS,可能还需要处理一些 C++ 标准兼容问题。社区里常见的做法是给 CMakeLists 加-std=c++14,这个按报错信息调整即可,不算大坑。
4.2 修改 launch 文件并播放 bag
imu_utils 的 launch 文件核心参数大致是这些:
<launch> <node pkg="imu_utils" type="imu_an" name="imu_an" output="screen"> <param name="imu_topic" type="string" value="/imu/data"/> <param name="imu_name" type="string" value="imu"/> <param name="data_src" type="string" value="bag路径"/> <param name="time_start" type="int" value="0"/> <param name="time_timespan" type="int" value="180"/> </node> </launch>这里的time_timespan单位是秒,表示从 bag 里取多少秒数据做分析。我有次偷懒只录了 20 分钟,设了 1200 秒,出来的曲线后半段毛刺特别多,那就是数据量不足的信号。后来统一按 90 分钟以上录制,曲线干净很多。
数据播放方式有两种:一种是用rosbag play imu_static.bag在线播放,同时启动 imu_utils 节点;另一种是把 bag 路径直接写进 launch 文件。我更推荐后者,省得两个终端同步。运行结束后,工作目录会出现imu_utils输出目录,里面是 YAML 和 PNG 曲线图。
4.3 结果怎么看
imu_utils 输出的 YAML 通常按陀螺和加计分别列:
%YAML:1.0 --- type: IMU name: imu Gyr: unit: " rad/s" avg-axis: gyr_n: 1.2696e-04 gyr_w: 1.5920e-05 Acc: unit: " m/s^2" avg-axis: acc_n: 5.9863e-03 acc_w: 5.1989e-03gyr_n对应角度随机游走噪声密度,gyr_w对应陀螺随机游走;acc_n是加计速度随机游走密度,acc_w是加计偏置随机游走。它还会按 x、y、z 单轴分别输出一份,最后给一个 avg-axis 用于平均值。
同时输出的 PNG 曲线是判断数据质量的重要依据。曲线应该呈现典型的"先下后上"形态:左侧白噪声段接近斜率 -0.5,中间最低点平缓,右侧上升。如果你看到的曲线一直在抖或根本没有低谷,说明数据里有振动或传感器在采集期间发生了位移,建议重新录。
5. imu_tk 实操记录:六面位置法标定确定性误差
5.1 数据格式与采集方法
imu_tk 对输入格式有明确要求:通常是一个文本文件,每一行包括时间戳、三轴加计、三轴陀螺,用逗号或空格分隔。它不关心你的 ROS 话题,也不要求你装 ROS,所以特别适合在嵌入式或无 ROS 环境里用。
采集方式上,imu_tk 常用的确定性误差标定法是"多位置静态法",也就是我们常说的六面法。把 IMU 固定在台面上,依次让 x、y、z 轴分别朝上和朝下,每个姿态保持 30 到 60 秒,同时记录数据。这样一共 6 个位置,每个位置的重力矢量在传感器坐标下指向已知方向,算法就能拟合出加计的零偏、尺度因子和非正交误差。
实际执行的时候要注意"朝向"要尽可能准。你可以用水平尺、直角块或者干脆在 3D 打印的定位块上做标记。如果朝向差个几度,标定出来的轴间误差里会掺入人为误差,反而比不标还差。我自己试过把 IMU 用双面胶贴在手机屏上用手机角度计辅助摆正,效果比随手放强很多。
陀螺的确定性误差标定通常还需要配合角速度激励,典型做法是让 IMU 绕某轴做匀速或已知轨迹的旋转。如果没有转台,只做零偏部分也能接受。
5.2 运行标定并查看输出
imu_tk 的可执行程序通常由仓库提供的示例代码编译而来,主程序输入数据文件,输出标定结果。典型的命令类似:
./imu_tk_calibrate imu_data.txt运行时会输出加计和陀螺的零偏、尺度因子、轴间误差矩阵。这些输出的含义比较直接:加计的 bias 就是三个轴上的常值零偏,scale 是各轴灵敏度偏差,misalignment 是三轴不正交导致的交叉耦合。
我个人的建议是:把这些确定性参数先手动加在数据处理的前端,然后在已经做过失真误差修正的数据上再跑一轮 Allan 方差,这样拿到的随机噪声参数才更干净。否则确定性误差混在 Allan 方差里,零偏不稳定性会被抬高。
5.3 为什么确定性标定和随机噪声标定要分开做
很多人会问:既然 imu_tk 也能算 Allan 方差,为什么还要再单独跑 imu_utils?我的理解是,两个工具的重心不同。Allan 方差分析的前提是消除确定性趋势后的平稳随机过程;如果数据里还有明显的常值零偏和尺度误差,Allan 曲线形态会偏移,拟合得到的随机游走参数会偏大。
所以在我的工作流里,顺序是固定的:先用 imu_tk 粗标确定性误差,把数据里的固定偏差和尺度问题清理掉;再对这些"去偏后的数据"跑 Allan 方差,得到随机噪声参数。如果项目时间紧,确定性误差不严重,也可以直接跑 Allan,但心里要清楚结果里混着一定确定性成分。
6. 90% 的标定误差来自采集环节:时长、静止、温度、数据连续性
6.1 时长不是越长越好,而是越"稳"越好
很多人以为 Allan 方差分析就是数据越多越好,于是录了好几个小时,结果曲线反而乱糟糟。问题通常不是时长,而是长时间录制中环境发生了变化:桌面的空调风直吹、设备发热、人的走动引起地面振动,这些都会变成长聚类时间段的异常方差。
我在实践中对时长的理解是:至少 1 小时,最好 2 小时,但必须在稳定的实验环境下做。如果现场条件嘈杂,宁愿拆成多次短采集,也不要强求一次录够。多次采集后可以把结果对比一下,看各次标定参数的离散程度,如果两次的 gyr_n 差超过 20%,说明采集环境有问题。
6.2 静止姿态和振动控制
对 Allan 方差分析来说,传感器必须绝对静止。这里的静止不是"放桌上不动"这么简单,有些桌面本身会因为楼上走动、空调共振而微振动,高档 MEMS 陀螺完全能感受到。我吃过一次亏:把 IMU 用双面胶粘在金属台面上,台面下方刚好有个排风扇,结果 Allan 曲线在中等聚类时间段上出现了明显峰值,怎么拟合都对不上。后来换到一块厚橡胶垫上,问题就消失了。
对六面法标定确定性误差来说,还要保证每个姿态的静止时间足够长,让传感器内部的零偏稳定下来。我一般每个姿态放 60 秒,前 10 秒数据不用,只用后 50 秒。
6.3 温度影响:数据开始前先预热
MEMS 陀螺的零偏对温度非常敏感,冷启动和热机状态下的 bias 可能差好几倍。如果你一上电就录标定数据,Allan 方差里的零偏不稳定性会偏大,因为温度漂移被当成了偏置随机游走。
我在项目里的习惯是:传感器上电后至少预热 5 到 10 分钟再开始录数据,同时尽量保证录制过程中的环境温度没有剧烈变化。冬天在室外调试尤其要注意,手一靠近传感器,红外辐射都会在温度曲线上留下痕迹。
6.4 检查数据质量的三个命令
录制完成后,先不要急着跑工具,花一分钟做数据质量检查:
rostopic hz /imu/data rostopic echo /imu/data --noarr第一看频率是不是稳定;第二看是否有 NaN 或者跳变。还可以用rqt_bag拉一段数据看看整体趋势,确认传感器没有在录制中被人挪动。这三个检查做完再跑标定,不然工具再精确也救不了脏数据。
7. 把标定结果喂进 VINS-Fusion 与 Kalibr:参数转换与验证
7.1 从 Allan 输出到 VINS-Fusion 配置
imu_utils 和 kalibr_allan 输出的通常是连续时间噪声参数,但很多 VIO 框架里期望的噪声参数是离散时间步下的协方差值。以 VINS-Mono/VINS-Fusion 常见的配置文件为例,它需要四类参数:加计噪声密度、陀螺噪声密度、加计随机游走、陀螺随机游走。
如果你手头是 imu_utils 的输出,可以直接把它那四个量填进去:
imu_acc_noise: 5.9863e-03 imu_gyr_noise: 1.2696e-04 imu_acc_random_walk: 5.1989e-03 imu_gyr_random_walk: 1.5920e-05有的版本里还需要除以sqrt(dt),这个要看具体框架内部是连续模型还是离散模型。我的建议不是去背公式,而是从原理上确认:如果框架里的协方差传播是在离散时间步上进行采样,那么噪声密度往往是功率谱密度形式,需要除以sqrt(dt);如果框架里已经给了连续模型接口,就不需要额外处理。出现疑问时,直接标定一次后做仿真对比,看静止时姿态方差是否合理。
7.2 在 Kalibr 联合标定中的二次使用
如果你用的是 Kalibr 做相机-IMU 联合标定,那标定出来的噪声参数要以 yaml 文件形式提供给kalibr_calibrate_imu_camera。格式大致是:
# 该文件用于 kalibr 联合标定 imu: update_rate: 200.0 gyroscope_noise_density: 1.2696e-04 gyroscope_random_walk: 1.5920e-05 accelerometer_noise_density: 5.9863e-03 accelerometer_random_walk: 5.1989e-03注意update_rate一定要写实际发布频率,不要写标称值。我有一次把 D435i 的 IMU 标注成 400 Hz,实际驱动只发了 200 Hz,联合标定结果里的时间戳对齐就一直有残差。
7.3 标定结果验证方法
标定完不能直接就用,我习惯先做一个快速验证:
- 静止漂移测试:把 IMU 固定静止,记录 10 分钟姿态解算结果,看积分出来的姿态角漂移速率是否在合理范围;
- 直线往返测试:把 IMU 装在机器人上,沿直线走一个来回,看回到起点时位置误差是否在预期范围;
- 与另一款工具交叉对比:同一份数据用 imu_utils 和 kalibr_allan 各跑一遍,结果应该接近。
如果静止漂移测试里角度还是在快速跑,说明噪声模型还是不对,或者零偏没有处理好。别急着继续联调,回去查数据采集环节更有效。
8. 实测对比表与选型建议:我的最终结论
| 维度 | kalibr_allan | imu_utils | imu_tk |
|---|---|---|---|
| 安装难度 | 中高,依赖较多 | 低,依赖 code_utils | 中,C++ 编译 |
| 数据输入 | ROS bag | ROS bag | 文本文件 |
| 核心输出 | YAML 噪声参数 | YAML 噪声参数 + PNG 曲线 | 零偏/尺度/轴间误差 + Allan 分析 |
| 是否需 ROS | 是 | 是 | 否 |
| 适合场景 | 科研、Kalibr 联合标定 | 工程快速标定 | 嵌入式、无 ROS、确定性误差标定 |
| 典型耗时 | 准备 1 天,运行 0.5 小时 | 准备 0.5 天,运行 5 分钟 | 准备 1 天(含六面位),运行 0.5 小时 |
我的实际选择标准很简单:默认先用 imu_utils 拿随机噪声参数,因为它的曲线输出最直观、结果文件最通用;如果项目里 Kalibr 明显要长期用,就改用 kalibr_allan 保持链路一致;一旦发现传感器存在明显的尺度或轴间误差问题,再引入 imu_tk 做确定性标定。
最后分享一个习惯:每次标定完,我会在工程里同时保留三样东西——数据 bag、标定参数 yaml、当时的环境记录(室温、预热时间、安装方式、录制时长)。这样后续如果发现标定结果存疑,能快速回溯是哪一步出了问题。这个习惯让我省了大量重复采集的时间,也建议你试试。