尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Kalibr相机-IMU外参标定全流程解析与实战避坑指南

Kalibr相机-IMU外参标定全流程解析与实战避坑指南 前阵子在做一套视觉惯性里程计方案的时候被相机和IMU之间的外参折磨得够呛明明单看相机标定和内参都挺正常一旦把IMU融进来做紧耦合轨迹一会儿飘一会儿抖最后排查了半天才发现罪魁祸首就是外参不准。后来老老实实把Kalibr的相机-IMU外参标定全流程跑了一遍前后花了一天时间把采集规范、参数配置、结果校验这些环节彻底理顺之后整个系统的稳度直接上了一个台阶。这篇文章我就把自己踩过的坑、总结出的操作细节以及每一步背后的原理完整记录下来。Kalibr这个工具箱在视觉惯性SLAM、无人机、自动驾驶多传感器融合领域几乎是绕不开的标配工具无论你是刚入门VIO还是已经在项目里被外参问题折磨过这份全流程解析应该能帮你省下不少弯路。1. 为什么相机-IMU外参标定如此重要Kalibr到底解决什么问题1.1 外参标定在实际系统中的角色先想清楚一个核心问题相机和IMU是两套完全独立的传感器一个以光速采集图像一个用MEMS惯性器件测量角速度和加速度。要让这两个传感器的数据能在同一个坐标系下融合就必须知道它们之间的相对位姿关系这个关系就是外参。很多人觉得外参不就是个6自由度的旋转加平移吗拿个尺子量一量、或者查一下产品图纸不就行了实际工程里还真不是这么回事。相机光学中心和IMU测量中心往往是物理上无法直接测量的机械安装公差、镜头固定误差、电路板焊接偏移都会让图纸上的设计值和实际值差出好几毫米甚至几度。对于视觉惯性导航这种对精度极其敏感的系统哪怕外参旋转误差超过1度融合后的轨迹就会明显漂移如果平移误差在毫米级以上近距离场景里的重投影误差也会变得不可接受。更麻烦的是相机和IMU之间还存在时间戳不同步的问题。图像曝光时刻和IMU采样时刻往往不在同一个时间基准上这个时间偏移如果不标定相当于你把一个延迟了几十毫秒的传感器数据当成实时数据去融合后果比外参差一点还要严重。Kalibr这个工具箱本质上就是在做两件事估计传感器之间的空间外参包括相机间的外参、相机-IMU间的外参以及估计它们之间的时间偏移。它把这两个问题放进一个统一的优化框架里求解这也是它比很多“手写标定”方案可靠的原因。1.2 Kalibr工具箱的核心模块与标定思路Kalibr出自苏黎世联邦理工ASL实验室全称是“A Unified Camera Calibration Toolbox”最初是给视觉惯性导航系统服务的。它包含几个模块多相机标定、IMU噪声特性标定、相机-IMU外参标定。其中相机-IMU外参标定是它的招牌功能。这个模块的核心思路是标定过程中手持设备在标定板前做各种激励运动同时录制图像和IMU数据。算法会利用视觉信息通过检测Aprilgrid标定板的角点估计出相机在每个时刻的位姿轨迹。这个位姿轨迹被建模为连续时间B样条曲线而不是离散的位姿序列。为什么用连续时间B样条因为这样可以很自然地把IMU测量的角速度、加速度和轨迹的导数对应起来还能同时估计时间偏移。在优化过程中视觉重投影误差和IMU积分误差被放在一起联合优化。优化变量包括外参旋转矩阵、平移向量、时间偏移、重力方向、甚至IMU的零偏。最终输出的就是使得所有误差最小的那一组外参和时间偏移。1.3 为什么项目里优先选Kalibr而不是其他方案市面上做相机-IMU标定的方案其实不少比如港科大VINS-Mono里自带的外参在线标定Apollo的OpenCalib还有一些公司内部自研的标定工具。我在几个项目里对比过Kalibr的优势在于它的时间偏移估计是显式建模的并且和空间外参联合优化其他很多方案要么需要外部时间同步设备要么只估计空间外参忽略时间偏移。对于消费级传感器时间偏移往往是不可忽略的Kalibr这种联合估计的方式更实用。另外Kalibr对Aprilgrid的支持非常完善Aprilgrid通过编码信息能抗遮挡、抗误匹配即使标定板部分出画也依然能稳定检测角点。这在手持设备采集数据时特别重要。当然Kalibr也不是没有缺点后面我会讲到它的安装依赖、编译版本这些让人头大的地方以及这些坑怎么绕过去。2. 标定前的准备工作硬件条件、安装部署与标定板选择2.1 硬件准备与数据同步方式开始标定之前先把硬件环境梳理清楚。我这里说的相机-IMU组合既可以是一个视觉惯性模组比如Realsense D435i、Mynt Eye也可以是自己拼的独立相机加IMU比如工业相机ICM20602。关键不是设备档次而是数据是否真的能录出来、时间戳是否可用。对于自己拼的相机和IMU建议尽量用硬件触发同步或者至少保证两个传感器驱动的时间戳来自同一个时钟源。如果两个传感器完全是两套独立系统时间戳各走各的那即便Kalibr能估计时间偏移估计出来的结果也会不稳定因为偏移本身可能随着系统漂移在变。我踩过的一个坑是用两台电脑分别录相机和IMU数据然后离线合并成bag结果每次标定出来的时间偏移都不一样后来改成单机单进程录制把两个话题写进同一个bag问题才消失。录制工具一般用ROS的rosbag。相机话题发布频率建议30Hz以上IMU频率建议100Hz到200Hz。IMU频率太低B样条对运动激励的插值会不够平滑影响标定质量。另外建议把IMU数据的话题频率写入配置文件后面标定IMU噪声和做外参标定都会用到。2.2 Kalibr安装与ROS环境配置的几个关键点Kalibr的安装是新手最容易卡住的地方。它依赖ROS并且早期版本只支持Python 2所以网上很多教程默认使用Ubuntu 16.04加ROS Kinetic或者Ubuntu 18.04加ROS Melodic。如果你用的是Ubuntu 20.04加ROS Noetic也能装但需要装Python 3版本的依赖部分脚本要做兼容处理。我自己的建议是如果是新环境直接用Ubuntu 18.04加ROS Melodic最省心编译过程基本一条龙。如果项目里已经有Noetic环境不想切换那就多花点时间处理依赖问题思路是先把python3-catkin-pkg、python3-rospkg这些Python 3版本的包装好再编译Kalibr。编译命令大概是cd ~/catkin_ws/src git clone https://github.com/ethz-asl/kalibr.git cd ~/catkin_ws catkin_make这里有个小提醒Kalibr仓库里包含一些子模块如果直接clone后编译报找不到头文件检查一下子模块有没有拉全。我用的一条稳妥命令是git clone --recursive https://github.com/ethz-asl/kalibr.git编译过程中如果报关于sparse-block-matrix或者sophus相关的错误多半是依赖版本和ROS版本不匹配优先确认是否安装了libsuitesparse-dev和libeigen3-dev。2.3 标定板的选择与Aprilgrid配置细节Kalibr官方最推荐的是Aprilgrid标定板光说“棋盘格”其实不够严谨。相比普通棋盘格Aprilgrid每个格子内部有编码图案算法可以通过编码识别每个角点的唯一身份这样即使标定板被部分遮挡也能准确对应角点不会出现整张图匹配错乱的情况。生成Aprilgrid的时候需要自己写一个YAML配置文件。这个文件里的参数每一项都不能错以我常用的6x6标定板为例target_type: aprilgrid tagCols: 6 tagRows: 6 tagSize: 0.088 tagSpacing: 0.3tagSize的单位是米代表每个黑色tag的边长。tagSpacing是一个无量纲比例代表黑白块之间的间隔相对于tagSize的比例。假如tagSize是0.088米tagSpacing是0.3那么两个tag之间的实际间隔就是0.088乘以0.3等于0.0264米。标定板打印时有一个很关键的点必须保证打印出来的尺寸和配置里的尺寸一致而且要尽量平整。我试过用普通A4纸打印贴在硬纸板上结果标定出的内参焦距和实际偏差比较大。后来换成户外防水广告布贴在铝塑板上效果好很多。如果只是桌面级实验至少也要用相纸打印并贴在亚克力板或者玻璃板上保证表面没有褶皱。2.4 数据采集的几条硬性要求采集数据是整个标定流程中决定成败的一步。Kalibr官方文档里对激励运动的要求我用自己的话总结就是六个自由度都要得到充分激励旋转和平移都要有速度不要太快也不要太慢要让图像中的特征点清晰、IMU不饱和。具体操作时我一般会手持设备站在标定板前大概0.5到1米的位置让标定板尽量占据画面的一大半然后做慢速但大幅度的旋转运动围绕相机的三个轴分别转同时伴随上下左右平移。需要注意的是不要做纯旋转纯旋转会让视觉位姿估计在某些方向上退化也不要做纯平移纯平移对IMU的角速度信息激励不足。动作幅度要大速度要缓让图像不出现运动模糊IMU输出不超出量程。采集时长上我一般录1到2分钟包含至少30秒的有效运动。录太短优化容易不收敛录太长中间如果出现标定板大面积出画反而会引入坏数据。录完之后我会先看一眼bag的时间戳是否连续有没有掉帧或者跳跃。相机话题和IMU话题的时间戳都应该是单调递增的如果发现时间戳跳变先修驱动再标定。3. 完整实操从相机内参标定到相机-IMU外参标定的三步流程3.1 第一步相机内参标定相机-IMU标定不是一上来就做联合标定的第一步先把相机的内参和畸变系数标定好。虽然Kalibr可以联合优化但如果内参本身不准外参标定的可靠性会大打折扣。我的习惯是先把相机内参固定住再去做相机-IMU外参。录制一个专门的标定包内容就是设备在Aprilgrid前做各种姿态的运动至少要覆盖图像边缘区域因为畸变参数主要靠边缘信息约束。录制好bag之后运行相机内参标定命令rosrun kalibr kalibr_calibrate_cameras \ --bag ~/bag/cam_april.bag \ --topics /cam0/image_raw \ --models pinhole-radtan \ --target ~/bag/april_6x6_88x88cm.yaml--models参数指的是相机模型和畸变模型。pinhole-radtan是针孔模型加布朗畸变模型也是最常见的选择。如果你的相机是鱼眼镜头就要改成pinhole-equidistant或者omni-radtan。这个选择需要根据镜头视场角和畸变特性来定选错模型会导致重投影误差一直下不去。内参标定完成后会在当前目录生成几个文件包括camchain.yaml和results-cam.yaml。camchain.yaml是后续相机-IMU标定的输入文件里面包含了图像分辨率、相机内参矩阵、畸变系数、畸变模型等关键信息。我每次都会打开这个文件检查一眼确认内参数值在合理范围内。3.2 第二步IMU噪声参数标定相机-IMU标定还需要一个IMU的噪声模型参数文件包括陀螺仪和加速度计的噪声密度、随机游走。这些参数在Kalibr的优化中会作为先验信息如果设置得太离谱优化结果也会偏。标定IMU噪声的常用工具是imu_utils来自港科大的沈老师团队配合code_utils使用。操作上把IMU水平静止放置录制至少两小时的数据。两小时听起来很长但这是为了把随机游走估计准如果时间太短随机游走项很容易和噪声密度耦合在一起。录制完成后运行rosrun imu_utils imu_utils /imu0它会扫描bag中的数据输出一个imu.yaml文件内容大致是这样rostopic: /imu0 update_rate: 200.0 gyro_noise_density: 0.0002 gyro_random_walk: 0.0001 accel_noise_density: 0.01 accel_random_walk: 0.0005这些参数的单位需要特别注意。噪声密度的单位通常是连续时间下的deg/s/sqrt(Hz)或者m/s^2/sqrt(Hz)随机游走的单位是deg/s^2/sqrt(Hz)之类的。Kalibr对单位很敏感如果搞错标定出来的结果会异常但未必报错这一步非常容易踩坑。3.3 第三步相机-IMU外参标定核心命令与参数前面两步都准备好之后终于到了最关键的一步。录制一个同时包含相机图像和IMU数据的bag这时的运动激励要比内参标定时更丰富要确保IMU的角速度和加速度变化足够大。然后运行rosrun kalibr kalibr_calibrate_imu_camera \ --target ~/bag/april_6x6_88x88cm.yaml \ --cam ~/bag/camchain.yaml \ --imu ~/bag/imu.yaml \ --bag ~/bag/cam_imu.bag \ --cam0 /cam0/image_raw \ --imu0 /imu0 \ --time-calib \ --timeoffset-padding 0.02这里解释几个容易踩坑的参数。--cam0指定相机话题--imu0指定IMU话题两者要和bag里的实际话题名完全一致。--time-calib表示启用时间偏移估计正常都要开启。--timeoffset-padding是时间偏移搜索的扩展范围单位是秒如果传感器时间戳本身偏差比较大可以适当增大但也不要太大否则优化空间太大会影响精度。外参标定运行过程中终端会打印大量信息。我通常会关注几个关键输出一个是优化后的重投影误差另一个是IMU误差项还有最终估计的时间偏移。如果看到一条类似“Initialization failed”的报错大概率是初始值给得太差或者数据质量太差后面问题排查部分我会细说。外参标定完成后会生成camchain-imu.yaml里面包含了相机到IMU的外参变换矩阵一般用T_ci表示意思是从IMU坐标系变换到相机坐标系的位姿矩阵。这个文件就是我们在后续VIO系统里直接加载使用的最终产物。3.4 标定结果怎么看矩阵、时间偏移和误差指标很多人拿到结果后直接把参数丢给系统用但其实结果里有很多信息值得检查一遍。camchain-imu.yaml里的外参会以4x4的齐次变换矩阵形式给出格式像这样T_ci: - [0.003913, -1.0, 0.99999, 0.015] ... timeshift_cam_imu: -0.019其中T_ci的旋转部分应该是近似正交的行列式接近1。如果旋转矩阵明显不正交说明优化过程有问题。平移部分对应相机光学中心在IMU坐标系下的位置数值的大小应该和硬件布局大致相符。比如IMU在相机正前方20毫米处平移量的符号和数量级就应该和这个布局匹配如果出现完全不符合物理常识的数值即使重投影误差看着不大也要怀疑结果是否陷入了局部最优。时间偏移timeshift_cam_imu的单位是秒含义是相机时间戳相对IMU时间戳的偏移。不同硬件差异很大可能在正负几十毫秒之间。如果这个值超过0.1秒我会先怀疑驱动时间戳是不是有问题而不是直接接受这个结果。误差指标方面Kalibr会输出重投影误差的均方根值。我自己的经验是如果重投影RMS在0.5像素以内说明视觉部分拟合得非常好如果超过1像素就需要检查标定板平整度、图像清晰度等数据质量问题。IMU误差项则要看数量级是否和输入的噪声密度匹配如果大了好几个数量级说明运动激励可能不足或者加速度计饱和了。4. 常见问题与排查技巧实录4.1 外参标定失败、不收敛到底是谁的锅我见过最多的问题是标定过程中报错“max iterations exceeded”或者“Initialization failed”。很多人第一反应是Kalibr不够好但根据我反复试验的经验绝大多数情况下问题出在数据质量上。首先检查运动激励。如果整个bag里你只是拿着设备慢悠悠晃了一圈角速度变化不够剧烈视觉轨迹和IMU之间的约束太弱优化就会发散。解决办法是重录数据用前面说的大幅度慢速旋转加平移让每个轴都被激活。其次是检查标定板检测率。Kalibr会输出每帧图像检测到的角点数量如果大量图像只检测到零星几个角点视觉约束也不够这类数据尽量剔除。另外就是初始外参的影响。Kalibr默认会先做一个线性初始化如果初始外参离真实值太远迭代优化容易掉进局部最优。有些情况下可以自己给一个比较接近的外参作为初始值。具体做法是在camchain-imu.yaml里预先填一个估计值或者通过命令行参数传入这样能显著提高收敛概率。4.2 时间戳不同步问题怎么排查如果你发现每次标定出来的时间偏移都不稳定甚至符号都在变先别怀疑Kalibr大概率是数据本身的时间戳有问题。我遇到过一次相机驱动是USB摄像头时间戳是采集时刻而不是曝光时刻IMU是高频设备两者之间天然存在一个未知且抖动很大的延迟。这种问题只能从驱动层解决比如用硬件触发或者用曝光时刻戳。还有一种情况是bag录制时CPU占用过高导致图像话题回调被延迟时间戳出现锯齿状跳动。排查方法很简单把bag里的/cam0/image_raw话题导入PlotJuggler看时间戳间隔是否均匀。如果间隔忽大忽小建议关闭占用CPU高的程序重录。4.3 标定结果误差偏大几个容易被忽略的因素标定板不平整是最隐蔽的坑。如果标定板有弧度或者表面反光严重Aprilgrid检测到的角点会有亚像素级的偏差累积起来会让重投影误差变大。解决办法是换更平整的标定板同时在采集时注意不要让强光直接照射导致反光。我之前用亮面亚克力板时反光问题困扰了很久后来贴了一层哑光膜效果立竿见影。另外运动模糊也常被忽略。设备转动稍快一点图像里Aprilgrid的边界就会糊掉导致检测精度下降。kalibr在检测标定板时会输出自信度信息如果大量图像的检测自信度都很低说明运动速度太快需要放慢动作。还有一个因素是IMU量程。很多消费级IMU满量程是±4g或者±8g如果你做大幅度的快速甩动加速度计可能瞬间饱和产生削顶失真。标定时尽量控制运动平缓让IMU工作在线性区间内。要确认IMU有没有饱和可以录制数据后查看加速度计的最大范数是否接近量程上限。4.4 标定结果如何验证不能只信一次结果拿到外参之后不要直接上线跑系统先做几个验证。最简单的验证方式是用Kalibr提供的可视化工具把IMU坐标系和相机坐标系在图像上画出来肉眼确认两个坐标系之间的相对关系是否和实际硬件安装一致。比如IMU在相机上方那在图像里画出的IMU坐标系原点就应该比相机坐标系原点偏上。更严格一点用录制的bag离线跑一遍VINS-Mono或者自己的VIO系统观察初始化收敛速度、轨迹闭环误差。如果外参正确系统初始化阶段的重投影误差应该很快收敛到较小值跑完一圈轨迹起终点误差也不会太大。如果外参有偏差系统往往表现为“初始化失败”或者“运行中轨迹发散”。我自己习惯标定两次用不同运动模式的bag分别标定比较两次结果的差异如果外参矩阵之间相差超过1度和1毫米就说明至少有一次数据有问题。还有个实用的小技巧标定完成后让设备静止不动通过VIO系统后端估计的IMU零偏来判断外参是否合理。如果外参正确静止时陀螺仪零偏会收敛到接近出厂值的稳定状态如果外参不准后端往往会用错误的零偏去补偿外参误差零偏估计值和出厂值差得离谱。最后再分享一个我自己的习惯。每次标定完我会把bag、camchain.yaml、imu.yaml、camchain-imu.yaml这四样东西打包归档标注好硬件型号、固件版本、采集日期和运动模式备注。别小看这个习惯当项目做到后面发现传感器性能异常时翻出原始标定数据重新分析往往能快速定位是硬件退化还是标定失效。我遇到过两次“神秘故障”最后都是靠原始bag排查出来的。标定这件事一次到位是运气能重复复现才是能力。
返回列表