ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Realsense D435i IMU与相机联合标定实战:从Allan方差到VINS-Fusion配置

Realsense D435i IMU与相机联合标定实战:从Allan方差到VINS-Fusion配置 Realsense D435i 这台设备玩过视觉SLAM或者机械臂抓取的人应该都不陌生。它一个很实用的特性是内置了IMU省去了额外装惯导的麻烦但这也带来一个绕不开的问题出厂标定参数只能算“能用”离“好用”还差得远。尤其是做VINS-Fusion、ORB-SLAM3这类紧耦合方案时IMU和相机之间的外参、时间偏移、噪声模型如果不准轨迹漂移和初始化失败几乎是必然的。这篇内容就是把我自己从零跑通D435i的IMU标定、相机内参标定、以及两者联合标定的完整过程整理出来包括每一步为什么这么做、参数怎么设、踩过哪些坑。适合刚拿到D435i、准备跑视觉惯性里程计或者做机械臂手眼标定的朋友参考不需要你事先精通Kalibr但基本的ROS操作和Ubuntu环境要能上手。1. 先搞清楚D435i到底给了你什么又缺了什么1.1 出厂内参和IMU参数的真实水平D435i出厂时Intel在设备里烧录了相机内参和IMU的标定值你可以通过rs-enumerate-devices -c或者直接读/camera/color/camera_info、/camera/imu话题拿到。很多人图省事直接拿这些参数丢进VINS里跑结果发现初始化阶段就飘得厉害。原因在于出厂标定是在产线环境下用标准靶标做的它保证的是“单目内参”和“IMU自身零偏”在一个可接受范围内但相机和IMU之间的外参旋转平移以及时间戳对齐出厂值往往只是粗略估计。我实测过几台D435i用出厂外参跑VINS-Fusion静止初始化时陀螺仪零偏估计能收敛但一旦开始运动尺度漂移明显。用Kalibr重新标定后同样的数据包轨迹闭合误差从原来的十几厘米降到两三厘米。这个差距在机械臂抓取场景里就是“抓得到”和“抓飞”的区别。1.2 为什么必须做联合标定而不是分开标分开标定相机内参和IMU零偏各自都能得到不错的结果但联合标定的核心目标是求相机到IMU的变换矩阵( T_{cam}^{imu} ) 和时间偏移( t_d )。这个变换矩阵在紧耦合VIO里直接参与优化如果旋转有1度的误差在1米距离上就是1.7厘米的投影偏差时间偏移如果有5毫秒误差在快速运动时会导致特征点匹配和IMU预积分对不上表现为轨迹在转弯处“甩出去”。Kalibr做联合标定的思路是同时采集相机看到的标定板和IMU的角速度/加速度数据通过优化让重投影误差和IMU预积分误差同时最小。这比分开标定再手工拼凑可靠得多。1.3 标定前的硬件和环境准备清单在开始之前你需要确认几件事Ubuntu版本推荐20.04ROS Noetic。22.04配ROS2 Humble也可以但Kalibr在ROS1下资料更全新手建议走Noetic。如果你用的是Ubuntu 24ROS2的生态还在完善标定工具链会有一些包依赖问题不建议作为第一次标定的环境。ROS安装用“鱼香ROS一键安装”确实能省不少事一条命令搞定ROS和常用工具。但注意一键安装脚本装的是基础版Kalibr需要的依赖如python3-igraph、libsuitesparse-dev还得手动补。D435i固件建议升级到最新固件老固件在IMU频率和时间戳同步上有已知问题。用rs-fw-update工具升级。标定板Kalibr支持AprilGrid、Checkerboard、Circlegrid。我强烈推荐AprilGrid因为它对角点检测更鲁棒部分遮挡也能工作。打印一张A3大小的AprilGrid贴在硬质平板上平整度很重要翘边的板子标出来内参会有系统性偏差。采集环境光线均匀避免强反光。标定板要占满相机视野的60%以上但不要出画。注意D435i的IMU和相机是硬件同步的但ROS驱动里/camera/imu和/camera/color/image_raw的时间戳基准可能不一致。Kalibr要求两个话题的时间戳来自同一时钟源所以采集时要用rosbag record同时录并且确保驱动开启了unite_imu_method参数。2. 相机内参标定为什么Kalibr的AprilGrid比棋盘格更省心2.1 采集数据时的姿态覆盖原则相机内参标定要的是在不同姿态下观测标定板目的是让优化器能解耦焦距、畸变中心、畸变系数。很多人拿着板子只在正前方晃两下标出来的畸变系数根本不可信。正确的做法是标定板在画面中要覆盖四个角和中心五个区域每个区域至少出现3次。倾斜角度要多样绕X轴、Y轴、Z轴各方向都要有±30度以上的倾斜。距离要有变化近到标定板占满画面远到占画面1/3。总帧数控制在60到100帧之间太少优化不稳定太多计算慢且容易引入模糊帧。我一般会录一段2分钟左右的bag然后从中抽帧。抽帧用kalibr自带的--bag-from-to和--frequency参数控制比如--frequency 4表示每秒抽4帧。2.2 Kalibr标定命令的每个参数到底在干什么假设你已经把bag录好标定板yaml也写好了命令大概长这样kalibr_calibrate_cameras \ --bag d435i_calib.bag \ --topics /camera/color/image_raw \ --models pinhole-radtan \ --target aprilgrid.yaml \ --bag-from-to 10 110 \ --show-extraction逐个拆解--models pinhole-radtanD435i的RGB相机是针孔模型加径向切向畸变这个模型对大多数场景够用。如果你要做高精度可以用pinhole-equi但D435i的畸变不算大radtan足够。--target aprilgrid.yaml这个文件定义了标定板的尺寸、行列数、标签间距。标签间距tag spacing必须和实际打印的板子一致差1毫米都会导致内参尺度错误。--bag-from-to 10 110跳过bag开头和结尾不稳定的部分取中间100秒。--show-extraction实时显示角点检测结果用来判断检测是否稳定。如果看到角点跳变或者漏检说明光照或板子平整度有问题。标定完成后会生成一个camchain.yaml里面包含内参矩阵和畸变系数。一定要检查重投影误差Kalibr会在终端输出每帧的误差正常应该在0.2像素以内。如果超过0.5像素说明采集数据有问题重新录。2.3 内参标定中最容易翻车的三个细节第一个坑是标定板打印精度。普通打印机打出来的A3纸尺寸误差可能有1-2毫米。如果你用这种板子标定内参的焦距会偏。解决办法是打印后拿尺子量实际标签间距把真实值写进yaml。第二个坑是图像模糊。D435i的RGB相机在低光下会自动增加曝光时间运动时容易糊。标定时要保证光照充足或者手动锁定曝光。用rqt_reconfigure把/camera/color/enable_auto_exposure关掉设一个固定曝光值。第三个坑是bag里的图像时间戳跳变。D435i的驱动在某些USB口上会出现时间戳回退Kalibr遇到回退会直接报错。录bag前先用rostopic hz /camera/color/image_raw看一下频率是否稳定在30Hz如果忽高忽低换个USB3.0口。3. IMU标定从Allan方差到噪声模型参数3.1 为什么IMU不能只标零偏IMU的误差模型包含两部分确定性误差零偏、尺度因子、轴间不对准和随机误差噪声密度、随机游走。零偏可以在线估计但噪声密度和随机游走是VIO滤波器里的固定参数必须提前标定。如果噪声参数设得比实际大滤波器会过度信任IMU导致轨迹滞后设得小又会过度信任视觉快速运动时容易丢。Allan方差是标定随机误差的标准方法。它的原理是让IMU静止采集几个小时的数据计算不同时间窗口下的方差然后从Allan方差曲线的斜率反推噪声密度和随机游走。3.2 用imu_utils跑Allan方差的完整流程imu_utils是港科大开源的工具专门用来标IMU随机误差。步骤让D435i完全静止放在水平桌面上不要有振动。采集至少2小时的IMU数据录成bagrosbag record -O imu_static.bag /camera/imu --duration2h注意D435i的IMU频率是200Hz加速度计和陀螺仪都是2小时大概1.4GB确保磁盘空间够。编译imu_utils注意它依赖code_utils要先编译code_utils再编译imu_utils否则会报找不到头文件。写一个launch文件指定bag路径和输出目录launch node pkgimu_utils typeimu_an nameimu_an outputscreen param nameimu_topic value/camera/imu/ param nameimu_name valued435i/ param namedata_save_path value$(find imu_utils)/data// param namemax_time_min value120/ param namemax_cluster value100/ /node /launchmax_time_min是参与计算的最大时间分钟设120表示用2小时数据。max_cluster是Allan方差曲线的簇数100够用。运行后会在data/目录下生成d435i_imu_param.yaml里面包含gyr_n、gyr_w、acc_n、acc_w四个关键参数。前两个是陀螺仪的噪声密度和随机游走后两个是加速度计的。我实测的D435i典型值gyr_n约0.0002 rad/s/√Hzgyr_w约0.00002 rad/s²/√Hzacc_n约0.002 m/s²/√Hzacc_w约0.0002 m/s³/√Hz。不同设备会有差异必须自己标。3.3 Allan方差曲线怎么读参数怎么填进VIOAllan方差曲线在双对数坐标下通常呈现几段不同斜率的直线斜率-1/2段对应噪声密度白噪声。斜率1/2段对应随机游走零偏不稳定性。斜率0段是量化噪声或采样频率限制。imu_utils会自动拟合这些段并输出参数。填进VINS-Fusion的配置文件时注意单位VINS用的是连续时间噪声密度和imu_utils输出一致直接填即可。但有些框架如OKVIS用的是离散时间噪声需要除以√dt别搞混。提示如果Allan方差曲线在低频段翘得很厉害说明采集期间有温度漂移或者桌面振动。D435i的IMU对温度比较敏感刚上电时零偏会漂建议先预热10分钟再开始录。4. 联合标定让相机和IMU真正对齐4.1 联合标定的数据采集有什么不同联合标定需要同时录相机图像和IMU数据而且运动要“充分激励”IMU的三个轴。具体来说手持D435i让设备在空间中做平移旋转的复合运动不要只平移或只旋转。每个轴都要有角速度激励绕X轴滚转、绕Y轴俯仰、绕Z轴偏航各做几次。运动要慢而平滑避免突然抖动。角速度控制在0.5 rad/s以内加速度控制在2 m/s²以内。标定板始终在相机视野内且尽量占大画面。总时长60到90秒太短优化不收敛太长容易累积漂移。录bag时同时录/camera/color/image_raw和/camera/imu用rosbag record一条命令搞定。4.2 Kalibr联合标定的配置和运行联合标定需要三个文件相机chain上一步标好的camchain.yaml、IMU参数imu.yaml、标定板yaml。IMU参数文件格式如下# imu.yaml imu0: model: calibrated update_rate: 200.0 accelerometer_noise_density: 0.002 accelerometer_random_walk: 0.0002 gyroscope_noise_density: 0.0002 gyroscope_random_walk: 0.00002 rostopic: /camera/imu time_offset: 0.0time_offset先设0Kalibr会在优化中估计它。运行命令kalibr_calibrate_imu_camera \ --bag d435i_imu_cam.bag \ --cam camchain.yaml \ --imu imu.yaml \ --target aprilgrid.yaml \ --bag-from-to 5 85 \ --show-extraction运行时间可能比较长取决于帧数和优化迭代次数一般10到30分钟。完成后会输出camchain-imucam.yaml里面多了T_cam_imu4x4变换矩阵和time_offset。4.3 标定结果怎么验证误差多大算合格Kalibr会在终端输出重投影误差和IMU误差。重投影误差应该在0.2像素以内IMU误差角速度和加速度残差应该在0.02 rad/s和0.2 m/s²以内。如果明显偏大说明数据采集有问题。验证外参是否合理可以做一个简单检查把D435i平放在桌面上IMU的Z轴应该朝上重力方向相机光轴朝前。那么T_cam_imu的旋转部分应该接近一个已知的固定旋转取决于D435i的物理安装。如果旋转矩阵和预期差很多说明标定失败。另一个验证方法是把标定结果丢进VINS-Fusion跑一遍看轨迹是否闭合。我一般会录一段回到起点的数据跑完后看起点和终点的位置差小于5厘米算合格。5. 标定结果落地到VINS-Fusion和机械臂场景5.1 VINS-Fusion配置文件的关键字段把Kalibr输出的camchain-imucam.yaml转成VINS-Fusion的配置格式核心字段# vins_config.yaml imu_topic: /camera/imu image_topic: /camera/color/image_raw output_path: /home/user/vins_output # 外参IMU到相机的变换 body_T_cam0: !!opencv-matrix rows: 4 cols: 4 dt: d data: [ ... ] # 从T_cam_imu取逆 # IMU噪声参数 acc_n: 0.002 gyr_n: 0.0002 acc_w: 0.0002 gyr_w: 0.00002 # 时间偏移 td: 0.0 # 如果Kalibr估计出非零值填这里注意body_T_cam0是IMU到相机的变换而Kalibr输出的是相机到IMU的T_cam_imu需要取逆。这个逆变换别搞反否则轨迹会完全错乱。5.2 机械臂手眼标定中的IMU角色如果你做的是D435i装在机械臂末端做抓取IMU的作用和手持SLAM不同。机械臂运动是已知的从关节编码器能得到末端位姿所以可以用手眼标定的思路让机械臂带着D435i做几次已知运动同时记录相机看到的标定板位姿解出相机到末端的变换。但IMU在这里的价值是提供运动过程中的姿态约束尤其是在机械臂快速运动时视觉可能因为运动模糊丢帧IMU可以补上短时间的姿态变化。这时候IMU的噪声参数和相机-IMU外参的精度直接影响抓取成功率。我实测过用出厂参数做手眼标定抓取成功率大概70%用Kalibr标定后能到95%以上。5.3 标定后仍然漂移的排查顺序如果标定完跑VIO还是漂按这个顺序查时间戳同步用rqt_plot同时画/camera/imu的角速度和/camera/color/image_raw的时间戳看是否有固定延迟。D435i的驱动有个global_time_enabled参数打开后所有话题用同一时钟。外参方向把T_cam_imu的旋转矩阵转成欧拉角和D435i的物理安装对比。如果差超过5度重新标。IMU噪声参数把Allan方差标定的参数和VINS默认参数对比如果差一个数量级说明标定有问题。图像质量检查标定时的图像是否模糊模糊图像会导致内参不准进而影响外参。运动激励联合标定时如果运动太温和外参中的平移分量可能不可观。重新录一段运动更激烈的数据。6. 几个让我折腾了很久的坑6.1 D435i的IMU和图像时间戳不同步问题这是最隐蔽的坑。D435i的IMU和图像在硬件上是同步的但ROS驱动默认可能用不同的时间基准。表现是VINS初始化时能成功但跑着跑着轨迹突然跳一下。解决办法是在launch文件里加param nameunite_imu_method value2/ param nameglobal_time_enabled valuetrue/unite_imu_method2表示用线性插值把加速度计和陀螺仪对齐到同一时间戳global_time_enabledtrue让所有话题用系统时钟。这两个参数打开后时间戳跳变基本消失。6.2 Kalibr在Ubuntu 22.04上的依赖问题Kalibr官方主要支持ROS1 Noetic在Ubuntu 22.04 ROS2 Humble上编译会缺一堆包。如果你非要用22.04建议用Docker跑一个Noetic容器把bag挂载进去标定。这样省去折腾依赖的时间。Docker命令大概docker run -it --rm \ -v /home/user/bags:/data \ -v /home/user/calib:/calib \ ros:noetic \ bash然后在容器里装Kalibr。注意容器里没有GUI--show-extraction用不了但标定本身不需要GUI。6.3 标定板材质和打印的细节AprilGrid的标签是黑白方块打印时要用哑光纸不要用光面纸。光面纸在灯光下会反光导致角点检测失败。如果只能打印在普通纸上贴到平板上后喷一层哑光清漆。另外标签的黑色要够黑普通打印机的黑色可能偏灰影响对比度。我试过用激光打印机打效果比喷墨好。6.4 标定频率和实际使用频率不一致的后果D435i的IMU出厂频率是200Hz但如果你在ROS驱动里改了频率比如降到100HzAllan方差标定的参数就不适用了。噪声密度和随机游走是和采样频率相关的改频率必须重新标。同理图像分辨率改了相机内参也要重标。我一般建议标定时的配置和实际使用完全一致不要中途改。7. 关于标定这件事的个人体会标定是个“磨刀不误砍柴工”的活。我见过太多人拿着D435i直接跑VINS调参调了一周最后发现是外参错了。花半天时间认真标一次后面省下的调试时间是以周计的。另外标定不是一劳永逸的。D435i摔过、温度剧烈变化过、或者换了USB线都可能让标定参数失效。我的习惯是每三个月或者每次重要实验前重新标一次IMU的零偏外参如果没磕碰可以半年标一次。最后分享一个小技巧标定完把camchain-imucam.yaml和imu.yaml一起备份并且在文件名里加上日期和设备序列号。D435i的序列号可以用rs-enumerate-devices查到。这样多台设备混用时不会搞混参数。
返回列表