做VINS-Mono、VINS-Fusion或者LIO-SAM这类紧耦合方案的时候,我几乎每次都会被IMU标定卡一下。以前对着官方Wiki复制命令,跑完也不知道结果到底对不对,直到把kalibr_allan、imu_tk、imu_utils这三个开源工具包完完整整用了一遍,才摸清它们各自的脾气。这篇实践指南就把这三条路从头到尾走一遍,把数据采集、参数解读、编译踩坑、结果落地全串起来讲透。
先给新手一个明确的方向:IMU标定不是一个工具包就能包打天下的事,三个工具解决的问题其实不一样。kalibr_allan和imu_utils负责的是随机误差分析,输出VINS、LIO紧耦合前端需要的Allan方差参数;imu_tk负责的是确定性误差标定,输出零偏、尺度因子、轴间误差。理解了这句话,后面所有操作才不会乱。这份指南适合刚接触视觉惯性导航、雷达惯性导航,或者正在做嵌入式传感器开发、搞组合导航预研的同学参考,标定完不光是拿到一个yaml文件,还能知道每个参数是怎么算出来的。
1. IMU标定到底在标什么?先弄懂两类误差
很多同学拿着工具包就开跑,跑完拿到一个yaml文件,但根本不知道里面的数是什么含义。IMU标定这件事,拆开看其实就是两类误差:确定性误差和随机误差。两者来源不同、影响不同、标定方法也不同。
1.1 确定性误差:零偏、尺度因子和轴间误差
确定性误差是传感器出厂和安装过程中引入的、相对固定的系统误差。最常见的三个是:
- 零偏(Bias):IMU静止时,加速度计和陀螺仪的输出不应该是零,但实际会有一个固定的偏移,比如陀螺仪静止时输出0.01 rad/s,这就叫零偏。
- 尺度因子(Scale Factor):传感器测量值和真实物理值之间不是严格的1比1关系,存在一个比例偏差,比如真实加速度是10 m/s²,传感器输出却是10.2 m/s²。
- 轴间误差(Misalignment):三个轴的敏感方向不完美正交,制造工艺或者PCB焊接时出现的微小角度偏差。
这些误差直接影响姿态解算和位置估计。陀螺仪零偏异常,会导致横滚俯仰角持续漂移,一小时可能偏出好几度;加速度计尺度因子偏差,在VIO里会导致轨迹估算出现尺度问题,跑一圈回来发现路径对不齐。很多消费级IMU出厂时芯片内部有校准参数,但PCB贴片时的机械应力和温度环境会改变这些量,所以自己标定一次非常必要。
1.2 随机误差:Allan方差和五类关键参数
随机误差是IMU输出噪声中随机的、随时间变化的那部分,它不需要很多位置摆放,而是靠静态数据做统计分析。
Allan方差是当前最通用的IMU随机误差分析方法。核心思路很直观:把长时间静态采集的IMU数据,按照不同的时间窗口长度分段,每个窗口算一次平均值,再对不同窗口尺度算方差。窗口取很短时,看到的是高频白噪声;窗口取很长时,看到的是低频漂移和偏置不稳定性。把方差和窗口长度画在双对数坐标上,就得到一条Allan曲线,从曲线不同斜率段可以读出五类噪声参数:
- 量化噪声(Q):来自ADC采样过程,曲线斜率约-1。
- 角度/速度随机游走(N):也就是白噪声,曲线斜率约-1/2,VINS里对应
gyr_n和acc_n。 - 零偏不稳定性(B):曲线的最低点,反应偏置缓慢波动的极限,单位是deg/h。
- 速率随机游走(K):曲线斜率约+1/2,VINS里对应
gyr_w和acc_w,代表偏置随时间做随机游走的剧烈程度。 - 速率斜坡(R):长期单调漂移,曲线斜率约+1。
VINS-Mono和VINS-Fusion的IMU参数yaml文件里有四个核心量:acc_n、acc_w、gyr_n、gyr_w,前两个来自加速度计的N和K,后两个来自陀螺仪的N和K。这就是为什么imu_utils和kalibr_allan输出Allan方差结果就能直接给VINS用。
1.3 什么时候该用哪个工具包
我的经验是:
- 如果目的是给VINS-Mono、VINS-Fusion出参数文件,用imu_utils最省事,它直接输出
imu.yaml,字段名和VINS配置完全对上。 - 如果后面还要做相机和IMU的联合标定,用kalibr_allan做IMU内参标定,它的输出和kalibr工程衔接更顺。
- 如果做机器人底盘、组合导航、或者想拿到详细的确定性误差,那imu_tk是更合适的选择,它专攻多位置法的零偏和尺度因子标定。
三个工具不冲突,甚至可以串联使用:先用imu_tk修掉确定性误差,再用imu_utils做随机误差分析,最后用kalibr_allan交叉验证一遍Allan曲线。
2. 三大开源工具包横向对比:选型之前先看底细
2.1 kalibr_allan:主打Allan方差分析
kalibr_allan是卡尔ibr(Kalibr)生态体系里的IMU随机误差分析工具,GitHub上有开源仓库,基于ROS实现,通常需要放到和kalibr同一个ROS工作空间里编译。它的核心功能和imu_utils类似,都是对IMU静态数据做Allan方差分析,输出随机误差参数。
它的优势在于和kalibr体系的一致性。kalibr本身是相机、IMU、相机到IMU外参联合标定的老牌工具,如果你要用kalibr做Camera-IMU外参标定,那先用kalibr_allan做掉IMU内参,参数格式和工作流非常统一。输出文件一般是yaml格式,包含陀螺仪和加速度计的角度/速度随机游走、零偏不稳定性等参数,直接可以被kalibr联合标定工程引用。
要注意的是kalibr_allan对数据长度比较敏感,官方建议静态数据至少一小时以上,数据太短Allan曲线的长窗口区域根本展不开,读出来的速率随机游走可信度很低。
2.2 imu_tk:侧重确定性误差标定
imu_tk是另外一个开源IMU标定工具包,C++实现,依赖Eigen和Ceres Solver。它解决的是IMU确定性误差问题,采用多位置法,让IMU静止放在多个不同姿态下,通过优化算法估计加速度计和陀螺仪的零偏、尺度因子、轴间误差。
它和Allan方差分析完全是两个维度。imu_tk更像一个“实验室级”的传感器标定设备,关注的是传感器本身的比例和坐标轴关系,而不是噪声的统计特性。很多开源VIO项目不怎么提imu_tk,是因为它们只把IMU当“短时间可靠姿态源”,噪声参数直接从Allan方差拿就够了,零偏和尺度因子靠在线估计去补偿。但在组合导航、高精度惯性测量单元、或者轮式机器人里程计融合这类场景里,确定性误差标定非常重要。
imu_tk的输入不是rosbag,而是特定格式的文本文件。需要先把rosbag里的IMU数据导出来,转换格式,再喂给imu_tk运行。上手门槛比imu_utils稍微高一点,但数据格式很透明,适合做二次开发。
2.3 imu_utils:专为VINS流水线而生的Allan工具
imu_utils是港科大沈劭劼课题组在开源VINS项目过程中放出的工具,配套依赖code_utils。它做的事情和kalibr_allan基本一样,都是做Allan方差分析,但它的设计目标非常明确:给VINS-Mono喂参数文件。
它的输出极其友好,运行完会直接在当前目录下生成一个形如imu.yaml的文件,里面字段就是acc_n、acc_w、gyr_n、gyr_w。VINS-Mono的配置文件里指定这个yaml路径就行,一秒钟接上。同时它还会生成Allan方差曲线图和log文件,方便人工检查曲线形状是否正常。
这正是它现在在很多视觉惯性项目里变成“默认标配”的原因,不是因为它比kalibr_allan技术更先进,而是因为它把输出端做成了VINS能直接吃的格式。
2.4 三工具速查表
| 工具 | 解决的问题 | 依赖 | 输入 | 输出 | 最适用场景 |
|---|---|---|---|---|---|
| kalibr_allan | 随机误差Allan方差分析 | ROS、Kalibr | rosbag或csv | imu.yaml、Allan曲线 | kalibr联合标定前做IMU内参 |
| imu_tk | 确定性误差标定 | Eigen、Ceres | 文本数据 | 零偏、尺度、轴间误差 | 组合导航、高精度IMU预处理 |
| imu_utils | 随机误差Allan方差分析 | ROS、code_utils | rosbag | imu.yaml、Allan曲线 | VINS-Mono/VINS-Fusion参数制备 |
3. 数据采集:标定成功与否的第一决定因素
三个工具算法本身都不复杂,我见过太多人标定结果诡异,最后发现全是数据采集环节出的问题。数据采集的质量,直接决定标定结果可信度,这一步花的时间和后面解决问题的时间相比,非常划算。
3.1 静态数据的采集姿势
无论是kalibr_allan还是imu_utils,都要求IMU静态放置。怎么算静态?不是用手拿着不动,而是要让IMU真正“死”在一个稳固的物理环境里。
实验室里最稳的做法是把IMU固定在光学平台或者大理石平台上,周围不能有风扇直吹、空调出风口、人来回走动引起的台面震动。手机放在同一个桌面上的震动都会通过桌面传导到IMU上,有条件的中间垫一块吸振海绵,但注意海绵不能太软导致姿态缓慢变化。
采集时长建议至少60分钟,理想情况是90到120分钟。Allan方差分析在长窗口区域需要足够长的数据支撑,半小时的数据也能跑出曲线,但曲线长尾部分会很毛躁,读出来的速率随机游走和零偏不稳定性可能完全不可用。采样率要记录清楚,通常会用到200Hz到1000Hz不等的IMU数据,采样率太低时Allan曲线的高频段信息会缺失,白噪声读不准。
3.2 确定性标定的动作序列
imu_tk的多位置法标定需要采集不同姿态下的静止数据,基本动作序列是:
- 先把IMU固定在一个小夹具或者一个规则盒子上,方便摆出各种姿态。
- 让IMU分别以三个轴的正负方向朝向重力,也就是六个面朝上,每个面静止10秒左右。
- 再补充若干任意倾角姿态,比如45度斜面、两个轴组合倾斜等,每个姿态同样静止5到10秒。
- 动作切换时尽量匀速、轻拿轻放,避免加速度冲击太大。
这里的关键是每个位置停留时间足够长,让加速度计和陀螺仪的读数稳定下来。如果切换太快,IMU内部机械结构还处于振动中,读出来的数据会带着瞬态误差,拟合结果会很差。
3.3 硬件准备和驱动检查
采集之前先确认硬件工作正常。用rostopic hz /imu/data查看发布频率是否稳定,频率跳动超过5%就要检查驱动、USB供电或者线材接触问题。IMU最好使用独立供电,不要和电机、舵机共用电源,否则电流波动会很直接体现在IMU数据上。温度也要注意,不要在阳光下直晒,不要放在暖气旁边,IMU对温度变化很敏感,温度漂移会混进Allan方差的长窗口段。室内恒温环境是永远的首选。
4. kalibr_allan实操:从编译到生成imu.yaml
4.1 编译与依赖
kalibr_allan需要和kalibr放在同一个ROS工作空间里编译。以Ubuntu 18.04 + ROS Melodic为例,基本步骤是:
mkdir -p ~/kalibr_ws/src cd ~/kalibr_ws/src git clone https://github.com/ethz-asl/kalibr.git git clone https://github.com/ethz-asl/kalibr_allan.git cd ~/kalibr_ws catkin_make source devel/setup.bash依赖主要是catkin、ros-industrial相关的包,用rosdep install --from-paths src --ignore-src -r -y装一遍基本就齐了。编译时卡住最多的是OpenCV版本和Kalibr源码分支的匹配问题,建议都用默认master分支。需要说明的是,具体仓库路径和版本分支在不同年份会有微调,编译遇到问题先看仓库的README,这是开源项目的通用前提。
4.2 运行与参数配置
kalibr_allan的运行方式通常有两种,一种是直接加载rosbag,一种是把IMU数据导出成CSV再加载。最直接的路径是把IMU数据录成rosbag,然后在launch文件中指定topic名称和bag路径。
<launch> <node pkg="kalibr_allan" type="kalibr_allan" name="kalibr_allan" output="screen"> <param name="imu_topic" value="/imu/data" /> <param name="bag_file" value="/path/to/imu_static.bag" /> <param name="is_rosbag" value="true" /> </node> </launch>运行后工具会输出IMU数据的统计信息,并开始逐段处理数据计算Allan方差。最终输出一个yaml参数文件,同时会生成一系列Allan方差曲线图,通常保存在输出目录里。
4.3 结果解读与常见坑
结果yaml里的关键字段包括:
- 陀螺仪角度随机游走(
arw或者gyr_n),单位通常是rad/s/sqrt(Hz)。 - 陀螺仪速率随机游走(
rrw或者gyr_w),单位是rad/s^2/sqrt(Hz)。 - 加速度计速度随机游走、零偏不稳定性等。
最常见的坑是bag时长短导致曲线尾部乱飞。Allan曲线尾部对应最长的窗口段,数据不够时这部分噪声很大,读出来的速率随机游走完全偏掉。解决方法是重新录更长的数据,而不是手工改参数。另一个坑是bag里IMU topic频率不一致,中间有丢帧或者时间戳跳变,kalibr_allan处理时会输出一些警告,这时需要清理数据或者重新录制。
5. imu_utils实操:一条命令导出VINS参数
5.1 编译顺序:code_utils和imu_utils
imu_utils依赖code_utils,这个包是它配套的消息处理库,必须先编译。我曾经直接catkin_make两个包一起编,结果报了一堆找不到头文件的错,后来发现就是编译顺序问题。
正确的做法是先把code_utils单独编译通过,再编译imu_utils:
mkdir -p ~/imu_utils_ws/src cd ~/imu_utils_ws/src git clone https://github.com/gaowenliang/code_utils.git git clone https://github.com/gaowenliang/imu_utils.git cd ~/imu_utils_ws catkin_make有些环境里code_utils源码编译会报backward.hpp找不到的错误,多半是CMakeLists.txt里C++标准设置问题,在code_utils的CMakeLists里把set(CMAKE_CXX_STANDARD 14)打开或者把编译标准调到C++14再试。imu_utils在同样位置也需要确认编译标准一致。
5.2 录制bag和指定topic
用imu_utils标定,录音静态bag是第一步。时间建议60分钟起步,topic就是IMU发布出来的原始数据topic,消息类型是sensor_msgs/Imu。
rosbag record /imu/data -O imu_static.bag录制时可以用rostopic echo抽查几帧,确认数据没有异常跳变。录制完成后把bag放到imu_utils工作空间下,路径记好。
5.3 运行和查看输出
imu_utils官方提供了一个launch文件模板,修改引用的bag路径和IMU topic名,然后运行:
<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="max_time_min" type="double" value="60"/> <param name="bag_file" type="string" value="/path/to/imu_static.bag"/> </node> </launch>运行结束后,终端会打印出加速度计和陀螺仪的Allan方差参数,工作目录下会生成imu.yaml和对应的Allan方差曲线图。yaml内容大致如下:
%YAML:1.0 --- type: IMU name: imu gyr_n: 0.004266236114219122 gyr_w: 0.000004143672937529867 acc_n: 0.01446100180659801 acc_w: 0.00004320421339581958这里的gyr_n和acc_n就是VINS配置里常说的白噪声密度,gyr_w和acc_w是零偏随机游走,单位都是国际单位制。把这个yaml文件的路径填到VINS-Mono或VINS-Fusion的config文件中,标定工作就算闭环了。
经验之谈:如果数据时长只有半小时,输出yaml里的gyr_w经常是几十万分之一量级的极小值,看起来很“漂亮”,但和真实传感器性能差很远。普通消费级IMU的Allan标定,60分钟起步是底线,90分钟更稳。
6. imu_tk实操:多位置法标定确定性误差
6.1 数据格式与提取
imu_tk不直接读取rosbag,需要先把IMU数据导出成文本格式。我常用的做法是用Python脚本订阅topic,把时间戳、三轴加速度、三轴角速度逐行写入txt文件:
rostopic echo -p /imu/data > imu_raw.txt这样导出的格式是%time,field.field0,...的CSV结构,还需要进一步处理成imu_tk要求的格式。更稳妥的方式是自己写一段Python脚本订阅sensor_msgs/Imu消息,手动解析:
import rospy from sensor_msgs.msg import Imu def imu_cb(msg): ts = msg.header.stamp.to_sec() ax, ay, az = msg.linear_acceleration.x, msg.linear_acceleration.y, msg.linear_acceleration.z gx, gy, gz = msg.angular_velocity.x, msg.angular_velocity.y, msg.angular_velocity.z with open('imu_data.txt', 'a') as f: f.write(f"{ts:.9f} {ax:.9f} {ay:.9f} {az:.9f} {gx:.9f} {gy:.9f} {gz:.9f}\n") rospy.init_node('imu_exporter') rospy.Subscriber('/imu/data', Imu, imu_cb) rospy.spin()文件每行七个数值,依次是时间戳(秒)、加速度x/y/z(m/s²)、角速度x/y/z(rad/s),这是imu_tk比较通用的输入格式之一。
6.2 编译和运行
imu_tk依赖Eigen和Ceres Solver,在Ubuntu上装好这两个库,然后克隆源码编译:
git clone https://github.com/ethz-asl/imu_tk.git cd imu_tk mkdir build && cd build cmake .. && make编译完会在bin目录下生成多个可执行文件,核心是imu_tk_calib。设置配置文件,指定数据文件路径、传感器采样率、加速度计量程等参数,然后运行:
./bin/imu_tk_calib ./config/data.config工具会在终端打印每一段静止数据的检测结果,比如检测到几个静止区间、每个区间时长为多少,然后是优化收敛信息。最终输出的标定结果包括加速度计和陀螺仪的bias、scale factor、以及轴间误差矩阵。
6.3 结果文件和参数应用
imu_tk的运行结果会以终端文本形式给出,也可以重定向保存成文件。下面是典型输出的简化结构:
accel biases: [0.012, -0.008, 0.105] accel scale factors: [1.0008, 0.9995, 1.0012] accel misalignment (rad): [0.002, -0.001, 0.003] gyro biases (rad/s): [0.0011, -0.0007, 0.0009]拿到这些参数后,怎么用取决于你的系统。在VIO里可以直接把加速度计和陀螺仪的零偏作为初始值,补偿后再交给后端优化;在组合导航里,尺度因子和轴间误差可以构建一个完整的IMU误差模型,再做数据融合。还有种用法是把imu_tk标定出的确定性误差作为预处理,修正后的数据再去做Allan方差分析,这样得到的随机噪声参数会更干净,因为确定性偏差不会污染长窗口段的方差统计。
这里有个容易忽略的点:imu_tk的多位置法对姿态覆盖非常敏感,如果只做了六个“面朝上”的经典动作,加速度计尺度因子和轴间误差的耦合可能无法充分解耦,建议额外补几个任意倾角的静止位置,让优化问题有更多约束。另外IMU夹具要尽量轻、刚性好,手拿着晃动会直接毁掉静止段检测,导致结果完全不可用。
7. 标定结果如何落地:VINS和LIO场景实战
7.1 接入VINS-Mono / VINS-Fusion
imu_utils或者kalibr_allan生成的yaml文件,最终都要接到具体的VIO系统里。以VINS-Fusion为例,config文件夹里的euroc_config.yaml或者realsense_config.yaml会有这样一行:
imu_yaml: "/path/to/your/imu.yaml"这个路径必须指向有效的IMU标定文件。如果路径写错或者yaml内容格式不对,VINS启动后会直接报错退出,或者在后端优化时因为初始噪声矩阵异常而发散。实操中我最常犯的错误是把imu.yaml放在了源码目录之外的一个临时文件夹,后来清理文件时把它删了,结果所有视觉惯性实验的数据全部报废。强烈建议把标定文件和对应bag放在同一个项目文件夹里,文件名加上日期和IMU型号,方便追溯。
7.2 接入lidar-inertial系统
现在做LIO-SAM、FAST-LIO、LINS这类激光惯性方案的人越来越多,lidar imu标定也成了高频需求。这里的标定有两层含义,第一层是IMU自身内参标定,第二层是激光雷达和IMU之间的外参标定。很多人一上来就做外参,结果发现内参没标、噪声参数乱填,外参怎么优化都不收敛。
我建议的顺序是:先用imu_utils把IMU内参和Allan方差做出来,给LIO系统的IMU噪声模型一个合理初值,再去做lidar-imu外参标定。如果你手里是D435i这类自带IMU的相机,又想把D435 + VINS-Fusion跑起来,那更要先把内置IMU的标定文件准备好,然后再用kalibr或者其它工具做Camera-IMU外参标定。很多大学项目里VINS-Fusion配D435i跑飞,问题十有八九是IMU噪声参数完全没标,直接空跑了一个默认参数。
7.3 参数交叉验证的土办法
标定结果准不准,除了看Allan曲线,还有一个土办法:把IMU数据播放一遍,用标定后的参数做纯惯性积分,对比静止时速度是否保持零、角度是否保持静止。如果标定参数正确,静止段积分出来的速度漂移会明显小于未标定时;如果参数离谱,积分速度会几秒内冲到很大值。这个方法不需要额外工具,写个简单的Python脚本就能做,非常适合在现场快速验证yaml文件有没有“标反”。
8. 常见问题与排查技巧实录
| 现象 | 可能原因 | 解决建议 |
|---|---|---|
| imu_utils编译报找不到code_utils头文件 | 编译顺序错误 | 先单独编译code_utils,再编译imu_utils |
| kalibr_allan运行后Allan曲线尾部上翘严重 | bag数据时长不够 | 重录60分钟以上静态数据,最好90分钟以上 |
yaml里gyr_w小到离谱 | 数据时长太短、Allan长窗口段未收敛 | 延长录制时间,不要轻易采信过小的值 |
| imu_tk检测不到静止段 | 数据文件格式不对或动作切换太剧烈 | 检查时间戳单位、数据行格式;录制时动作轻缓 |
| imu_tk拟合不收敛 | 姿态覆盖不够、初始值不合理 | 增加任意倾角姿态,把bias初始值设为0附近 |
| VINS加载yaml后立刻崩溃 | yaml路径错误或字段名不对 | 核对acc_n/acc_w/gyr_n/gyr_w四个字段是否完整 |
| 同一个IMU,不同时间标定结果差很多 | 采集环境温度不一致、固定方式不同 | 尽量在相同温度环境下采集,用相同夹具固定 |
| Allan曲线在长窗口段出现明显“陡升” | 数据里有缓慢温度漂移或平台低频震动 | 重新录制,排除温控和振动源 |
还有一类比较隐蔽的问题:rosbag里的IMU时间戳如果有跳变或者重复,Allan方差计算会把错误的间隔当成采样间隔,导致曲线高频段形状畸形。遇到这种情况,先把数据段的time interval打印出来看一下,间隔方差过大说明时间戳质量差,要么修,要么重录。
另外,三个工具包都是用开源社区里常见的“README驱动”工作流,版本更新后一些命令和参数会变。我的习惯是把每个工具仓库的README、issue区关于编译错误的讨论都翻一遍再动手,尤其要注意作者是否声明了某个依赖库的版本下限。Ceres、Eigen、OpenCV的版本冲突是这些标定工具运行时最常见的翻车点。
最后再分享一个小技巧:每次标定完,把rosbag、生成的yaml、Allan曲线图、imu_tk输出文本都存在同一个文件夹里,命名带上IMU型号和采集日期。后面换电脑、换工程、或者怀疑数据有问题时,翻出这套文件就能快速复盘,省掉很多重复采集的时间。我踩过几次坑之后现在这个习惯已经固定了,标定这件事,说到底就是数据和参数要对得上,系统才能信得过。