双目加IMU的联合标定做过视觉SLAM或者准备上手VINS-Fusion、ORB-SLAM3这类系统的朋友大概率都绕不开Kalibr这个工具箱。Kalibr是苏黎世联邦理工开源的一套多传感器标定工具能完成相机内参、双目外参、相机与IMU之间外参和时延的估计是行业内事实标准之一。但说实话Kalibr本身的代码比较“学术”使用门槛不低加上不少人是第一次接触AprilGrid标定板真正动手时很容易踩坑——标定板打印不对、数据采集不规范、IMU激励不到位、结果参数明显不合理却没发现这些问题我都在实际项目里遇到过。这篇博文按我自己的实操经验来写从环境搭建、标定板选型、数据采集、双目内参外参标定、IMU参数准备到最后的联合标定和结果校验把每一步的原理和容易翻车的地方都说透帮你在最短时间内拿到一套能用的标定结果。1. 准备工作环境搭建与标定板选型1.1 Kalibr安装与ROS版本匹配Kalibr是基于ROS开发的所以环境准备本质上是把ROS和Kalibr源码编译好。最常见的组合是Ubuntu 18.04配ROS Melodic也有不少人在Ubuntu 20.04上用Noetic原理上差别不大但编译坑略有不同。安装Kalibr时我建议直接源码编译不要执着于二进制包。步骤大致是这样mkdir -p ~/kalibr_workspace/src cd ~/kalibr_workspace/src git clone https://github.com/ethz-ASL/kalibr.git cd .. catkin build -DCMAKE_BUILD_TYPERelease这里有几个需要特别注意的点如果你用catkin_make建议改成catkin build因为Kalibr依赖的一些自定义消息在增量编译时用catkin_make容易出奇怪问题。没有catkin命令就先装python-catkin-tools。Kalibr源码更新比较频繁不同commit对OpenCV版本的兼容性不一样。Ubuntu 18.04自带OpenCV 3.2配合Melodic基本没大问题。如果你在Ubuntu 20.04上遇到OpenCV 4相关的编译报错优先考虑把Kalibr切到较新的master分支或者直接装一个兼容版本。编译时如果报eigen相关的错误通常是因为Eigen版本路径混乱。解决方法是在CMakeLists里手动指定Eigen3_DIR或者干脆用系统的libeigen3-dev。我第一次装Kalibr的时候在OpenCV版本上耗了整整一下午后来发现排查思路很简单先看报错是找不到头文件还是符号链接错误找到对应CMakeCache里指向的版本再决定是换Kalibr分支还是装对应OpenCV。编译和很多ROS工具链问题一样前置依赖正确后续就顺了。1.2 AprilGrid标定板定制别在打印这一步省事Kalibr官方支持三种标定板AprilGrid、Checkerboard棋盘格、CircularGrid圆点网格。我一直推荐AprilGrid原因很直接AprilGrid每个tag的四个角点都能被精确检测而且检测算法对遮挡、反光、部分模糊的鲁棒性远好于棋盘格和圆点。这意味着在同样的采集时间里你能获得更多有效的角点观测标定稳定性和精度都会提升。标定板文件可以自己生成。用Kalibr自带的脚本source ~/kalibr_workspace/devel/setup.bash rosrun kalibr kalibr_create_target_pdf --type apriltag --nx 6 --ny 6 --tsize 0.025 --tspace 0.3这个命令里几个参数直接决定标定板的质量--nx和--ny是横向和纵向的tag数量。建议至少是5x5以上数量太少会导致单帧图像里的观测点不够。--tsize是单个tag的边长单位是米。这个参数要和实际打印出来的尺寸严格一致因为Kalibr在做联合优化时所有尺度信息都来源于标定板的物理尺寸。0.025表示2.5厘米对于室内双目相机来说20到30厘米见方的板子比较合适。--tspace影响tag之间的间距默认0.3表示间距是tag边长的30%。这个值影响角点分布太密会导致透视畸变严重时边缘角点质量下降太疏则会浪费图像空间。生成PDF之后打印时我强烈建议用高精度打印机打印在哑光、不反光的硬质材料上。不要用普通A4纸直接贴墙上纸面不平整会引起毫米级的角点位置误差外参标定对毫米级误差非常敏感。正确的做法是打印后平整地贴在硬纸板、铝塑板或者亚克力板上保证板面完全平整。如果表面会反光可以喷一层哑光保护漆或者贴哑光膜。这一条比很多人想象的重要得多——反光导致某几个角点在特定角度检测失败会让你在数据采集阶段反复返工。1.3 标定板参数yaml的正确写法标定板和角点检测是基于配置文件的。我的标定板参数文件一般长这样target_type: apriltag tagCols: 6 tagRows: 6 tagSize: 0.025 tagSpacing: 0.3这里的target_type必须严格写成apriltagKalibr识别不到其他别名。tagSize对应上面生成的--tsizetagSpacing对应--tspace。这四个字段是Kalibr读取标定板几何信息的唯一依据任何一个字段和实际标定板不一致结果都会出错。我自己踩过的一个坑是用旧版本Kalibr生成的标定板使用的是tagSpacing的绝对值而新版本用的是相对比例。如果你是从网上找的标定板PDF不知道原始参数最稳妥的办法是用ImageMagick或者Python量一下PDF里tag边长和间距的像素比例反推参数。别拍脑袋猜直接量最靠谱。2. 数据采集决定标定成败的“隐蔽”环节2.1 影响数据质量的空间环境很多人把标定失败归咎于算法其实大部分问题出在采集的数据上。数据质量不达标再好的工具也没用。Kalibr对采集环境有几个硬性要求光照均匀且充足。过曝、阴影、高光都会让AprilTag的角点检测不稳。避免把标定板放在窗口逆光位置或者灯光直射产生镜面反射。背景尽量干净。如果背景里有类似AprilTag的矩形结构比如书架、窗户、海报检测器有可能产生误检。误检会直接导致标定结果发散。标定板在图像中的占比要适中。最好的状态是标定板始终完整出现在两个相机画面里且占画面面积的20%到50%。板子太小角点像素距离太近标定的内参和外参会互相耦合板子太大边缘角点超出视野那些帧等于白采。我在实际项目里还会刻意保持标定板表面清洁不要有指纹、污渍、折痕。听起来是小事但角点检测是基于灰度梯度污渍产生的局部梯度会干扰角点提取的亚像素精度。2.2 运动数据怎么采IMU为什么要“充分激励”IMU标定和纯相机标定有个本质区别纯相机标定只需看到足够多视角下的标定板而相机IMU联合标定需要同时估计IMU的噪声密度、随机游走、外参和时间延时这个估计过程依赖IMU在多个轴向上都被“充分激励”。“充分激励”这四个字是Kalibr标定里最核心也最容易被忽略的概念。简单解释一下原理Kalibr在联合标定时会通过相机运动估计出载体的角速度和加速度然后对比IMU测量值从而估计出外参和IMU误差参数。如果运动过程中某个旋转轴或平移方向几乎没有被激励那这三轴对应的参数就不可观测优化问题退化结果自然不会好。具体操作上我总结了一套比较有效的采集手法开始录制后先保持相机静止1到2秒让IMU采集一段稳定的静态数据用于零偏估计。然后缓慢地做六自由度运动俯仰、横滚、偏航都转一遍同时配合平移。避免纯旋转尤其是原地绕重力方向旋转——这种运动对加速度计几乎没有激励标定出来的加速度计噪声参数会很离谱。运动速度不要过猛。太快的运动会导致图像运动模糊AprilTag角点检测失败太慢则会让IMU信号淹没在噪声里激励不够。每个方向上的运动尽量覆盖整个运动范围把相机的视角拉满让标定板在视野里从边缘到中心都有分布。整个过程控制在30到60秒。太短IMU激励不充分太长后面联合标定的优化时长急剧增加而且容易积累时间同步误差。我习惯的模式是静止2秒然后慢速顺时针画圈同时平移再逆时针画圈前后俯仰几次左右横滚几次最后再静止1秒收尾。这个流程基本能把IMU各个轴向都照顾到。2.3 rosbag录制别踩时间同步的坑Kalibr的数据输入是rosbag不是图像序列。录制bag时需要注意rosbag record -O imu_calibration.bag /cam0/image_raw /cam1/image_raw /imu/data这里我踩过的坑是话题选择。有的相机驱动把图像发布在/camera/infra1/image_rect_raw这类话题IMU发布在/mavros/imu/data_raw如果直接用这些话题输入Kalibr也能跑但Kalibr内部会对话题名称做字符串匹配最好统一改成简洁的名字再录制。比较稳妥的做法是写一个launch文件重映射话题node pkgtopic_tools typerelay namerelay_cam0 args/camera/infra1/image_rect_raw /cam0/image_raw / node pkgtopic_tools typerelay namerelay_cam1 args/camera/infra2/image_rect_raw /cam1/image_raw / node pkgtopic_tools typerelay namerelay_imu args/mavros/imu/data_raw /imu/data /时间同步方面Kalibr本身会处理图像和IMU的时间戳对齐但前提是bag里的话题时间戳是基于同一时钟源。如果你用两台独立相机分别录数据时间戳不同源联合标定的时间延时估计会直接崩掉。所以务必保证所有数据来自同一个ROS主时钟录制过程中不要修改系统时间。2.4 D435等双目相机的特殊情况提醒用Intel RealSense D435的人很多它的头内IMU和双目图像可以通过realsense-ros驱动直接发布。但D435有个问题红外图像和IMU之间的时间戳同步并不是完全严格的而且红外图像可能存在轻微的自动曝光变化导致光照突变时检测不稳定。针对D435我建议把红外相机的自动曝光关掉手动设置曝光值保证录制过程中图像亮度稳定。IMU频率设置到200Hz或400Hz频率太低会让Kalibr对IMU噪声参数的估计不够精确。由于D435的红外图像是灰度图角点检测不受影响但要注意红外投影器如果在弱光下自动开启会产生散斑纹理干扰AprilTag检测。最好在光照充足环境下关闭红外投影。3. 双目相机标定Kalibr_calibrate_cameras实操3.1 命令与参数细节采集好bag后第一步是标定双目相机的内参和外参。命令如下rosrun kalibr kalibr_calibrate_cameras \ --bag imu_calibration.bag \ --topics /cam0/image_raw /cam1/image_raw \ --models pinhole-equi pinhole-equi \ --target april_6x6.yaml \ --show-extraction \ --verbose--models那里指定两个相机的模型。常见选择有pinhole-radtan和pinhole-equi。普通相机用pinhole-radtan就够广角大畸变镜头建议用pinhole-equi。这里我提醒一句不要盲目用pinhole-equi如果镜头畸变不大用pinhole-radtan更稳定。模型选复杂了参数维度增加反而容易过拟合。--show-extraction这个参数强烈建议加上。它会逐帧显示检测到的角点方便你确认数据质量。如果跑完很多人直接等待最终结果中间有大量帧检测失败也不知道最后标定结果偏差大再回头找原因很浪费时间。3.2 如何判断标定结果好坏双目标定完成后终端会输出内参矩阵K、畸变系数D以及两个相机之间的外参T_1_0。Kalibr本身不直接给“通过/不通过”的判定但有几个信号可以判断结果是否可靠优化是否收敛Kalibr最后输出的优化summary里如果显示The optimization failed基本就是数据有问题结果不能使用。重投影误差好的结果平均重投影误差应该在0.3到0.8个像素以下超过1个像素就要警惕。观测帧数输出的Number of successful corner detections越多越好。如果某帧标定板只在一个相机里检测到那一帧在双目外参优化中等于没用。标定完成后Kalibr会生成一个camchain yaml文件比如camchain-imu_calibration.yaml。它的内容是后续相机IMU联合标定的输入包含了两个相机的内参、畸变模型和双目外参。这个文件别改路径别动格式后面直接引用。3.3 常见坑图像话题、内存与角点检测有段时间我连续几次标定都失败后来发现是rosbag录制时把图像话题的压缩格式也录了进来Kalibr读取的时候部分帧解压失败。解决方案是录制时不要录制compressed话题只用raw图像话题。内存问题也值得留意。Kalibr在提取角点时会把所有帧的角点信息加载进内存图像分辨率大、bag时间长、帧率60fps的配置很容易把内存吃满。我一般把bag录制帧率控制在20到30fps时长控制在60秒以内这样Kalibr的处理压力会小很多。角点检测失败是另一类高频问题。如果--show-extraction里看到大量帧只检测到一部分tag优先检查标定板是否平整、是否反光。如果只在某个视角检测失败调整运动范围让相机与标定板法线之间的夹角不要太大超过60度时AprilTag的检测率会骤降。4. IMU参数准备与联合标定4.1 imu.yaml模板与参数含义相机内参外参标定完下一步是准备IMU的描述文件。Kalibr需要一个yaml描述IMU的噪声特性这是联合标定的先验输入。一个常用的模板是# Accelerometer accelerometer_noise_density: 0.006 accelerometer_random_walk: 0.0004 # Gyroscope gyroscope_noise_density: 0.0002 gyroscope_random_walk: 0.00002 # Frequency update_rate: 200.0这些参数的含义我用大白话解释一下noise_density噪声密度衡量IMU在静止时输出随机抖动的大小。这个值可以从IMU数据手册里查也可以先给一个大概值联合标定完成后Kalibr会输出估计值。random_walk随机游走衡量零偏随时间漂移的快慢。这个值如果给得太离谱联合标定里的优化可能很难收敛。update_rateIMU的频率必须是实际话题频率不能随便写。如果你不确定看rosbag信息rosbag info里能看到每帧的平均时间间隔。需要注意的是Kalibr联合标定时会根据测量数据重新估计IMU的噪声参数所以这里给的初始值不需要很精确量级对就行。如果给了明显离谱的值比如把噪声密度写成0.5优化可能会卡在次优解。4.2 联合标定命令与运行时间联合标定的命令是rosrun kalibr kalibr_calibrate_imu_camera \ --bag imu_calibration.bag \ --cam camchain-imu_calibration.yaml \ --imu imu.yaml \ --target april_6x6.yaml \ --show-extraction这条命令的运行时间受数据量和计算资源影响通常在10到30分钟之间。期间Kalibr会先做角点提取再做时间同步估计最后做联合优化。有个细节我提醒一下Kalibr对IMU时间戳的对齐非常敏感。如果bag里IMU的时间戳单位是微秒但Kalibr默认按纳秒处理ROS标准时间或者反过来时间同步估计会得到严重错误的结果。遇到时间戳单位问题可以先用rostopic echo看一下/imu/data的header.stamp确认是纳秒还是秒格式必要时用脚本转换。4.3 解算结果外参、噪声参数与时延联合标定完成后Kalibr会在终端打印出估计值并生成imu-cam_extrinsics.yaml和results-imucam-imu_calibration.txt。重点关注这几个输出T_ci_cam0IMU坐标系到cam0坐标系的变换矩阵包含旋转和平移。这是外参标定的核心结果。检查平移量的数量级是否合理比如你的IMU和左目相机光心距离如果是几厘米平移量应该在0.01到0.1米量级。如果标定出1米甚至几十米的平移量基本可以判定数据有问题。gyroscope_noise_density和gyroscope_random_walk估计的陀螺仪噪声参数。如果估计值和IMU手册里的值差好几个数量级说明IMU激励不够或者数据本身有问题。timeOffsetIMU和相机之间的时间延迟。合理的值一般很小如果出现接近0.1秒量级的偏移很可能是时间戳单位或者同步出了问题。我习惯在拿到外参后直接用它跑一次VINS-Fusion或者ORB-SLAM3观察初始化速度和位姿稳定性。如果外参正确系统初始化会很顺利yaw方向的漂移会明显收敛。如果外参错误系统初始化后发散的迹象通常在一两分钟内就能看出来。4.4 坑外参平移量爆炸、负方差与时间戳我在标定中遇到过几次外参平移量爆炸成几十米的情况排查后的原因都指向同一个相机标定阶段的重投影误差偏大导致双目外参本身就有问题联合标定在这个基础上继续优化结果自然发散。所以遇到联合标定结果离谱先回头检查第一步的双目标定而不是反复调IMU参数。还有一类报错是Estimated variance is negative或者Matrix is not positive definite这种通常是数据里包含大量IMU空窗期或者某些帧的时间戳乱序。检查bag的方式很简单用rqt_bag可视化IMU话题的连续性和频率如果中间有大段空白重新录制比修数据更高效。5. 结果校验与实际问题排查5.1 用VINS-Fusion等系统反向验证标定出来是一回事标定对不对是另一回事。我最推荐的校验方式是用标定结果去驱动一个实际的视觉惯性SLAM系统观察它的表现。以VINS-Fusion为例有了相机内参、双目外参、IMU外参和噪声参数配置好之后把IMU和相机固定在一起做运动。正确的外参会让系统快速完成初始化室内来回走一圈轨迹没有明显平移漂移yaw方向不会快速发散。这里对应很多人的痛点“基于IMU的位姿解算yaw仍会慢漂”。如果你发现即使标定结果正常yaw还是持续漂移别急着怀疑外参先检查IMU零偏是否在系统运行中被正确估计。VINS-Fusion这类系统在初始化时需要IMU有足够的加速度激励如果初始化阶段运动太平缓yaw的初始零偏估计不准后续就会持续漂移。这属于使用方式问题不是标定问题。5.2 Kalibr与其他标定任务的关系有朋友问到Kalibr能不能用来做“IMU雷达外参标定”或者“手眼标定”。这里澄清一下Kalibr的核心能力是视觉-惯性标定它不做激光雷达和IMU的联合标定。雷达IMU外参标定目前更多用LIO-SAM的tools或者livox系列提供的标定工具。但Kalibr的双目视觉外参标定、手眼标定类任务的原理可以迁移到很多机器人标定场景。手眼标定解决的本质问题和Kalibr联合标定里外参估计是同一类数学问题不同坐标系之间的刚体变换估计。区别在于手眼标定一般用机械臂的运动学数据Kalibr用标定板的观测数据。原理相通但工具不能乱换。5.3 常见问题速查表我把实际用Kalibr过程中经常碰到的问题整理成一张表方便你对照排查问题现象优先级可能原因排查方法角点检测大量失败高标定板不平整、反光、太小换哑光硬板增加标定板面积调整光照双目标定重投影误差大于1像素高采集图像模糊、板面不平检查运动速度、换板、减小标定板与相机夹角联合标定外参平移量爆炸高双目外参不准确、时间戳不同步先重做双目标定检查bag时间戳来源IMU噪声参数估计为负中数据激励不足、IMU频率太低按六自由度方式重新采集提高IMU频率标定结果每次都不一致中板子不平、运动模式单一固定板子丰富运动模式增加有效帧数优化不收敛高话题选错、yaml参数错误确认目标话题、检查标定板参数与实物是否一致这张表里的每个问题我都亲自遇到过排查思路也基本是我实际用的顺序。凡是涉及“高”优先级的建议直接重新采集数据不要在错误数据上反复调参浪费时间。6. 实操小技巧让标定结果更稳的细节最后再分享一个我自己总结的小技巧关于标定板的固定方式。很多人标定时是手动拿着标定板在相机面前晃来晃去。这个做法不是不行但手部抖动和板子弯曲会导致角点位置带有随机噪声降低外参精度。我后来改成把标定板用三脚架固定住让相机或者搭载相机的机器人运动而不是板子动。这样标定板始终保持平整角点检测的稳定性会高一个台阶。如果你的相机是固定在一个移动平台上直接推着平台运动即可效果比手拿板子好很多。还有一个小细节在开始正式标定之前先用--show-extraction快速跑一遍角点提取重点看标定板从边缘移动到中心的几帧。如果那几帧检测稳定再正式跑完整流程基本能保证一次通过。Kalibr标定是视觉惯性系统落地时绕不开的一步但它不是一个“跑通命令就完事”的工具。标定板物理质量、数据采集手法、IMU激励充分度、时间戳精度每一环都在影响最终参数的可信度。把这套流程按上面的顺序准备好大多数问题都能提前规避。真遇到结果不对的时候记住一个原则先从数据里找原因不要急着怀疑算法和工具。