ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Kalibr相机IMU联合标定全流程:从原理到踩坑实战

Kalibr相机IMU联合标定全流程:从原理到踩坑实战 前阵子帮朋友调试一套视觉惯性里程计传感器驱动都通了图像和IMU数据也都在正常发布但VINS初始化始终不顺跑上一分钟轨迹就开始飘。折腾了两三天最后把相机和IMU外参拉出来一验发现跟机械设计图差了接近十度。从那以后我再也不敢省略标定这一步。今天就围绕Kalibr这个工具箱把相机与IMU联合标定的完整流程、原理要点以及我实际踩过的坑一次性梳理清楚。这篇内容适合正在做VIO、SLAM、AR/VR、无人机或机器人感知系统手头有相机和IMU但外参还没搞定的人。1. 标定之前必须想明白Kalibr到底在解决什么问题1.1 相机和IMU为什么不能各自独立标定很多刚入门的朋友会把“相机标定”和“IMU标定”混在一起想其实这两个东西的层级完全不同。相机标定通常指内参和畸变系数也就是每张图像像素坐标到相机坐标系三维方向的映射关系一般用张正友标定法或Kalibr自带的多视角标定就能做。IMU标定则更多指陀螺仪和加速度计的零偏、尺度因子、噪声密度、随机游走这些参数通常需要Allan方差分析或者imu_utils这类工具。但相机与IMU联合标定解决的是另一个维度的问题核心是两个传感器坐标系之间的刚体变换T_cam_imu以及两者采集时刻之间的时间偏移t_offset。这个外参在视觉惯性融合系统里极其关键。你可以这样理解相机是眼睛IMU是前庭和肌肉的本体感觉大脑需要知道眼睛相对于身体的位置和朝向才能把视觉观测和身体运动整合起来。如果眼睛和身体之间的相对位姿算错整个空间感知就会打架。在VINS、ORB-SLAM3这类系统里IMU预积分给出的相对运动和视觉重投影给出的几何约束必须投影到统一的坐标系下进行比较。外参不准等效于人为给两个传感器之间强行加了一个未知的扭曲变换。轻则初始化迟钝、轨迹漂移重则优化直接发散。这跟内参差一两个像素的后果完全不在一个量级上。1.2 Kalibr和其他标定方案有哪些本质区别Kalibr是苏黎世联邦理工自动化系统实验室开源的多传感器标定工具箱主要包含三块功能多相机内参和相机间外参标定相机与IMU联合外参标定以及标定板生成工具。它采用连续时间轨迹的优化思路不要求传感器时间严格对齐这在真实设备上非常实用。很多人会把相机与IMU标定、手眼标定、lidar与IMU标定、九点标定这些概念放在一起对比。实际上它们解决的问题差别很大标定类型求解目标典型场景输入数据张正友标定法相机内参、畸变系数单目/双目相机内参多视角棋盘格/标定板图像手眼标定AXXB机械臂末端或基座与相机之间的位姿机械臂抓取、视觉引导机械臂运动学与相机观测九点标定像素坐标与机器人平面坐标之间的变换工业视觉对位、贴装标定点在像素和机器人坐标系下的对应关系lidar与IMU标定激光雷达与IMU之间的外参激光惯性SLAM、自动驾驶点云与IMU数据Kalibr相机-IMU标定相机与IMU之间的外参、时间偏移VIO、视觉惯性SLAM、AR/VR标定板图像与IMU数据九点标定本质上是一个2D仿射或透视变换的求解通常用于工业相机在固定高度下的像素坐标到机械手平台坐标映射它不涉及3D旋转和IMU。手眼标定虽然也是位姿标定但它处理的是机械臂末端执行器与相机之间的刚体变换标定过程中需要有机械臂的运动学读数。Kalibr处理的则是相机与IMU这两种异源传感器之间的位姿和时间关系不能混着看。2. 准备工作硬件环境、标定板与安装配置2.1 硬件和驱动层面的最低要求标定对硬件本身要求不高但有几个基础条件必须满足。首先相机最好是全局快门滚动快门相机在运动过程中会出现果冻效应图像特征位置跟真实物理位置对不上标定精度会明显下降。如果你手里的相机是工业相机比如海康、Basler这些直接用全局快门模式即可如果是手机摄像头或部分消费级相机标定结果可能只能达到“能用”的级别。其次IMU数据频率要高一些常见IMU在100Hz到500Hz之间。Kalibr对IMU的更新率没有硬性限制但频率太低会导致连续时间轨迹拟合不准。我建议至少200Hz如果只有100Hz也能跑只是结果可能不够稳定。第三时间戳来源要搞清楚。理想情况是相机和IMU有硬件同步比如通过外部触发或PPS信号让两个传感器的时间基准严格一致。没有硬件同步时只要时间戳来自同一套系统时钟Kalibr也能通过在优化中加入时间偏移项来解决这就是后面要讲的--time-calibration。但注意前提是时间戳本身单调且没有明显抖动。最后相机分辨率不要随意改。有人为了标定流畅把相机降到很低分辨率去采集这样内参结果可能跟实际使用分辨率不一致。务必在系统实际工作分辨率下录制数据。2.2 标定板选择与打印细节Kalibr官方最推荐的是Aprilgrid标定板因为它的每个角点自带ID能够在部分遮挡时保持较好的鲁棒性。你可以用kalibr_create_target_pdf工具直接生成PDF也可以去官方下载预生成的图案。生成的时候有两个关键参数需要自己定tagSize和tagSpacing。tagSize是单个AprilTag标签的边长tagSpacing是标签之间的空白间距相对于tagSize的比例。我的经验是标定板不要选太小的。很多人在办公室用A4纸打印一个小棋盘格就想标定相机与IMU外参结果标定板在画面里占比太小角点检测离图像边缘太远优化出来的外参抖动非常大。建议在A3或更大幅面上打印并且把tagSize的物理尺寸量准确。打印之后要贴在完全平整的硬板上比如亚克力板或铝板不能是软纸卷曲的状态。Aprilgrid对光照和反光也比较敏感如果表面覆了一层膜产生高光角点检测会频繁失败。我习惯在打印表面喷一层哑光保护剂或者选用哑光相纸能减少很多麻烦。2.3 Kalibr安装与ROS版本兼容性Kalibr目前还是以ROS 1环境为主要使用方式我个人的经验是Ubuntu 18.04 ROS Melodic下编译运行最顺。ROS Noetic默认是Python 3Kalibr里有一部分老代码还是Python 2语法直接用会报错需要补一些兼容性修改对新手不太友好。官方仓库位置在GitHub创建独立的工作空间编译mkdir -p ~/kalibr_ws/src cd ~/kalibr_ws/src git clone https://github.com/ethz-asi/kalibr.git cd ~/kalibr_ws rosdep install --from-paths src --ignore-src -r -y catkin_make source ~/kalibr_ws/devel/setup.bash如果编译过程中缺少依赖优先检查以下几项python-catkin-tools、python-scipy、python-numpy、python-matplotlib、libopencv-dev。Kalibr和OpenCV版本之间偶尔会有接口兼容问题Melodic自带的OpenCV 3.2基本够用。建议单独用一个工作空间编译不要和大型SLAM项目混在一起后面更新或调试代码时彼此干扰会小很多。2.4 IMU噪声参数从哪里来Kalibr联合标定并不像imu_utils那样反推IMU噪声它需要你提前知道IMU的噪声密度和随机游走在优化中作为固定权重使用。这两个参数来源有三种一是厂商手册通常会在芯片手册或模组规格书里给出陀螺仪和加速度计的噪声密度二是自己用imu_utils采集数据做Allan方差分析三是先用一个常见IMU的量级带进去标定完成后再根据残差情况校正。最省事也最稳妥的是先查厂商手册。比如BMI088、MPU9250、ICM20689这些常见IMU都有现成参数。imu.yaml文件的格式大致如下rostopic: /imu0 update_rate: 200.0 gyroscope_noise_density: 1.744e-04 gyroscope_random_walk: 0.000029 accelerometer_noise_density: 0.0025 accelerometer_random_walk: 0.00011update_rate必须和实际发布频率一致如果标称200Hz但实际发布150Hz建议按实测值填写。这个文件不需要做得非常精确但也不要差一个数量级否则联合优化的IMU项权重会失衡外参结果可能偏移。3. 第一步先用Kalibr标定相机内参3.1 数据采集要点联合标定之前我一定会先做一次相机内参标定。直接用kalibr_calibrate_imu_camera也不是不行Kalibr允许把相机内参作为待优化量一起估计但如果内参初始值差太多联合标定的迭代很容易不稳定还不方便定位问题。先用kalibr_calibrate_cameras把相机内参固定下来后面联合标定会轻松很多。采集内参标定数据时标定板不要固定不动要手持标定板在相机前方缓慢地改变姿态和距离。动作不用太快但角度要丰富尽量让标定板覆盖画面的四个角和中心。每切换一个姿态时停顿一两秒更好可以避免每帧运动模糊过重。录制时长一般40到60秒足够。录制话题名称要记清楚Kalibr依赖话题名从bag中读取图像。标准做法是rosbag record -O cam_calib.bag /cam0/image_raw如果相机驱动输出的是彩色图记得检查类似bgr8或者rgb8的编码。Kalibr内部会转成灰度图所以彩色或灰度都可以接受。3.2 标定命令与参数含义先准备target.yaml里面写清楚标定板物理参数target_type: aprilgrid tagCols: 6 tagRows: 6 tagSize: 0.088 tagSpacing: 0.3tagSize单位是米一定要拿卡尺或者直尺量精确。见过有人填的是像素占的格子数完全错误这会直接毁掉整个尺度。tagSpacing表示Tag之间空白占Tag边长的比例Kalibr生成PDF时会在根目录设置一个target.yaml样例可以参考。然后运行相机内参标定kalibr_calibrate_cameras \ --target ./target.yaml \ --bag ./cam_calib.bag \ --models pinhole-radtan \ --topics /cam0/image_raw--models这里填的是相机畸变模型。最常见的是pinhole-radtan也就是针孔模型加RadTan畸变适合大部分普通镜头。如果是广角或鱼眼镜头可以考虑pinhole-equi或omni-radtan。如果你用的是双目或多目相机--models和--topics后面就要写多个模型和多个话题名一一对应。比如双目kalibr_calibrate_cameras \ --target ./target.yaml \ --bag ./stereo_calib.bag \ --models pinhole-radtan pinhole-radtan \ --topics /cam0/image_raw /cam1/image_raw运行结束后会生成一个camchain-xxx.yaml文件。这个文件非常重要里面已经写好了相机模型、分辨率、内参矩阵K和畸变系数D。如果你以后想在OpenCV里直接用内参可以用cv2读取以下字段import cv2 import yaml with open(camchain-xxx.yaml, r) as f: data yaml.safe_load(f) cam_model data[cameras][0][camera_model] intrinsics data[cameras][0][intrinsics] distortion data[cameras][0][distortion_coeffs]3.3 内参结果怎么看Kalibr在标定完成后会输出一个报告文件里面包含重投影误差的统计值。一般来说平均重投影误差在0.5像素以内算比较正常如果超过1像素就需要考虑是不是标定板不平、图像模糊、畸变模型选错或者tagSize量错了。我遇到过最隐蔽的问题是标定板本身被压弯了一个角导致部分帧的角点检测产生系统性偏移重投影误差始终降不下来。把标定板换到更厚的铝板上之后问题迎刃而解。内参标定这一步做完你的camchain-xxx.yaml就是联合标定的输入之一。如果你只做纯视觉系统到这一步也足够用了。4. 第二步相机与IMU联合标定的完整实操4.1 联合标定原理简述Kalibr的联合标定采用连续时间轨迹的表示方法。它用B样条曲线把相机位姿建模成时间的连续函数IMU测量和视觉重投影都被拟合到同一条轨迹上。这样做的核心好处是不需要相机帧和IMU采样在时间上精确对齐时间偏移可以作为优化变量直接估计出来。具体来说视觉部分利用标定板角点的重投影误差约束连续轨迹上的相机位姿IMU部分则把陀螺仪和加速度计的测量值与轨迹的角速度和加速度进行比较两者构成一个联合优化问题。优化的变量包括B样条轨迹的控制点、相机与IMU之间的外参、时间偏移还可能包括陀螺仪和加速度计的尺度以及部分误差项。这也是Kalibr区别于普通外参求法的关键大部分传统外参标定只需要解一个方程组而Kalibr是整体优化能够同时利用所有约束和误差统计信息。4.2 数据采集要求联合标定数据采集是整个流程中最容易出问题的环节。首先要保证标定板在相机视野内占比足够大同时又要保证运动轨迹充分激励IMU的6个自由度。推荐的动作模式是快速旋转、急停、来回晃动、改变俯仰和滚转角度、平移走一段再停止。动作幅度要大但不要快得让图像严重模糊。我的标准操作是连续录制90到120秒其中前30秒做小幅慢速运动热热身让IMU内部的温度稳定后面60秒做包含旋转和平移的丰富激励最后再保持几秒静止方便Kalibr对零偏约束的估计有一个比较好的初始化。整个过程尽量保证环境光照稳定不能在标定过程中频繁开关灯或者让阳光直射标定板。录制命令示例rosbag record -O cam_imu_calib.bag /cam0/image_raw /imu0这里/imu0就是imu.yaml里的rostopic需要一致。录制前先用rostopic hz /imu0确认IMU发布频率用rostopic hz /cam0/image_raw确认相机帧率。如果相机只有10Hz也能标但会需要在运动中保证帧与帧之间的位移不要太大。4.3 联合标定命令与参数说明准备好三个输入文件camchain-xxx.yaml内参、target.yaml标定板参数、imu.yamlIMU噪声参数然后运行kalibr_calibrate_imu_camera \ --cam ./camchain-xxx.yaml \ --imu ./imu.yaml \ --target ./target.yaml \ --bag ./cam_imu_calib.bag \ --time-calibration--time-calibration这个参数强烈建议加上。它会在优化中额外估计相机和IMU之间的时间偏移。很多人在没有硬件同步的设备上不加这个参数结果外参完全不准这是很常见的误用。如果你的时间同步机制非常成熟比如有PPS和精确的硬件触发不加也能跑否则还是让Kalibr自己估计一下比较保险。运行过程中Kalibr会先做初始化输出一些检测和优化信息。结束之后你会看到以下几类输出文件camchain-imucam-xxx.yaml联合标定的完整结果包含相机内参、外参矩阵和时间偏移。result-imucam-xxx.txt优化的统计信息包括重投影误差和IMU残差。result-imucam-xxx.pdf可视化报告包括相机轨迹、误差图等。4.4 标定结果解读打开camchain-imucam-xxx.yaml最关键的是这一段cameras: - camera_model: pinhole intrinsics: [384.12, 383.55, 320.5, 240.4] distortion_model: radtan distortion_coeffs: [-0.283, 0.062, 0.0004, 0.0004] ... T_ci: - [0.999, -0.011, 0.014, 0.021] - [0.012, 0.999, -0.021, 0.039] - [-0.014, 0.021, 0.999, -0.015] - [0.0, 0.0, 0.0, 1.0]这里的T_ci是相机坐标系在IMU坐标系中的位姿变换矩阵。注意方向别搞反了它把相机坐标变换到IMU坐标也就是p_imu T_ci * p_cam。在往VINS这类框架里配置时通常需要的是T_ic也就是IMU坐标系在相机坐标系中的位姿两者互为逆矩阵。很多人在这一步直接复制粘贴导致外参初始化错误整个系统跑起来之后表现奇怪。务必确认清楚目标框架需要的矩阵方向。时间偏移会出现在文件里通常以秒为单位。比如time_offset: -0.015表示相机时间戳比IMU时间戳晚了15毫秒左右。这个数值如果超过几十毫秒需要检查驱动时间戳是否可靠不要盲目相信Kalibr能补偿一切。5. 踩坑实录我在实际标定中遇到的问题与解决思路5.1 标定板识别失败光照、模糊和反光用Aprilgrid标定时Kalibr会自动检测每个Tag的角点。如果检测出的帧数太少或者检测结果明显错位最典型的前兆就是算法输出大量“could not find a sufficient number of corners”或者检测到的角点数目在多个帧之间剧烈波动。我遇到过的原因主要有三类。第一是光照变化太大比如标定板上面有玻璃反光局部高光让Tag上某些正方形块被过曝角点被吞掉了。解决办法是调整光源位置让光从侧面打过来尽量避免正对强光源。第二是图像模糊特别是快速运动时相机曝光时间过长运动模糊会让Tag边缘完全糊成一团。解决办法是缩短曝光时间同时适当增加环境亮度。第三是离标定板太远Tag在图像里只有几十个像素检测必然不稳定。我建议Tag的边长在画面中至少占50到80个像素。5.2 IMU时间戳与话题频率问题联合标定里IMU的update_rate如果填错Kalibr会把加速度计和陀螺仪的预积分权重算错最终外参会往奇怪的方向偏。我曾经把一块200Hz IMU在imu.yaml里写成了400Hz结果联合标定完成后重投影误差看着正常但相机与IMU外参的平移量明显偏离机械图纸后来才发现是更新率不一致。时间戳问题更迷惑。有些IMU驱动输出的时间戳不是系统单调时间而是从传感器出厂时间零点开始计时的导致bag里IMU时间戳跟相机时间戳差了几十天。Kalibr虽然能估计时间偏移但如果偏移量远远大于传感器合理差异它也很难正确处理。建议在录制前后打印一下两个话题的最新时间戳确认是在同一个时间基准上。5.3 标定结果发散或重投影误差过大联合标定最常见的失败现象是最后输出的重投影误差几像素甚至更大或者外参矩阵跟机械设计值差异离谱。排查时我会按下面顺序来看相机内参是否准确。可以把camchain文件里的畸变系数和用OpenCV棋盘格标定的结果对比一下如果两者差异很大说明内参标定阶段就有问题。看数据采集质量。把bag里的图像抽出来逐帧检查看标定板是否清晰完整。如果大量帧的标定板被运动模糊破坏建议重录。看IMU噪声参数是否量级正确。把imu.yaml里的参数和厂商手册对比不要凭感觉填。看运动激励是否足够丰富。如果整段数据里相机几乎只做平移、没有大幅旋转IMU的角速度约束很弱外参会容易退化。另外还有一个常见报错Kalibr initialization could not be determined。这种多半是初始的几帧没有检测到足够的Aprilgrid角点或者IMU数据量太少。建议录制时前几秒先让标定板静止在画面中央给Kalibr一个稳定的初始化参考再开始大幅度运动。5.4 多相机和滚动快门情况下的注意事项如果你要标定的是双目或三目相机加IMU内参标定阶段就要把所有相机同时放进camchain文件里。kalibr_calibrate_cameras可以一次性输出多相机之间的外参这些信息会保留在camchain文件中。联合标定时多个相机都使用同一个IMU外参优化会同时利用所有相机的视觉约束前提是这些相机的时间戳都统一到同一个时钟。滚动快门相机参加联合标定需要注意更多细节。滚动快门会让每行图像的曝光时间不同运动状态下标定板角点位置会沿扫描方向偏移。Kalibr虽然有支持滚动快门的模型但实际标定难度比全局快门大很多。如果你用的是消费级机器人的内置摄像头我建议采集数据时把运动速度放慢尤其是旋转动作要慢否则滚动快门误差会直接污染外参。6. 标定完成之后结果验证与系统集成6.1 快速验证标定结果是否可靠标定结束不能拿到一个T_ci就完事一定要在真实系统里验证一次。最直接的验证方法是把它配置进VIO框架比如VINS-Mono、VINS-Fusion或ORB-SLAM3然后跑一段有回环的场景观察轨迹走向。另一个不需要跑SLAM的快速验证办法是把相机和IMU固定在同一块刚性板上先保持静止观察系统初始化时重力对齐是否合理。然后再手持设备缓慢转动看视角的虚拟叠加是否跟真实环境基本贴合如果出现严重偏转多半是外参矩阵方向或平移向量有误。我曾经遇到一个典型案例标定结果在txt报告里看重投影误差只有0.3像素但一跑VINS就出现尺度漂移。查了一圈发现是外参矩阵旋转部分方向反了Kalibr输出的T_ci被我在配置时误改成了看起来更“顺眼”的数值。后来老老实实通过矩阵求逆得到T_ic重新写入后问题消失。6.2 把标定结果写进VIO系统以VINS-Mono为例配置文件里需要填的就是相机和IMU的外参常用写法是body_T_cam0: - [0.999, 0.012, -0.014, -0.039] - [-0.011, 0.999, 0.021, -0.021] - [0.014, -0.021, 0.999, 0.015] - [0.0, 0.0, 0.0, 1.0]这里的body_T_cam0表示相机到IMU的变换也就是T_ic。如果你从Kalibr得到的是T_ci需要求逆矩阵。矩阵求逆用Python或者直接用Eigen都很方便不要手动算手算容易出错。另外时间偏移也需要写进系统。VINS-Mono有TD参数在双目配置中常写为td: 0.0如果你在Kalibr中估出了时间偏移且未被驱动层补偿就在对应字段填写。不过大多数框架对时间偏移的处理方式不太一样有的是图像时间戳加偏移有的是IMU时间戳减偏移配置时要仔细看代码注释。6.3 我的一些经验之谈相机与IMU联合标定这件事工具本身很成熟真正决定成败的往往不是命令怎么敲而是数据采得好不好。无论Kalibr还是其他标定工具核心前提都是标定板物理尺寸准确、图像清晰、运动激励充分、IMU参数大致靠谱。这四个条件只要有一个不满足后面的优化算法再厉害也救不回来。另外标定不是一劳永逸的。相机镜头松动、电路板受热形变、设备经历剧烈振动都可能导致外参发生微小变化。我习惯在每次设备拆装或镜头调整后重新录制一小段数据验证外参如果残差明显变大就重新跑一遍联合标定。这个过程熟练之后并不耗时却能避免很多调试阶段的灵异问题。还有一个小技巧录制标定数据前先检查一下感光芯片上有没有灰尘或反光点有些伪特征会在标定板角点检测时引入错误对应这些肉眼不容易发现的东西往往最难排查。
返回列表