
先用一句话给这篇文章定个性它不是官方README的复读而是我从第一次标定翻车到最终稳定成功之后沉淀下来的一套完整操作流和排查思路。我最早在某台巡检机器人上做激光雷达和相机融合。当时觉得自己挺懂直接拿结构设计图纸上的安装尺寸填了外参把雷达点云往图像上一投影结果板子边缘整整偏了半米。后来用Livox官方的livox_camera_calib工具重新标定才把误差压到几个像素以内。这个工具解决的核心问题很简单算出激光雷达坐标系和相机坐标系之间的旋转矩阵R和平移向量t也就是外参。它适用于Livox系列雷达Mid-360、Horizon、Avia搭配普通USB相机、工业相机或Realsense的场景也是后续做点云着色、多传感器目标检测、Cartographer建图并保存地图这些功能的前置步骤。这篇文章会先讲清楚工具的标定原理再一步步说标定板制作、数据采集、launch配置和结果验证。最后一部分是我在实际项目中踩过的坑和完整排查链路这部分在别的教程里很少见到但恰恰是最能帮你省时间的。1. livox_camera_calib的标定逻辑为什么不找点而是找平面1.1 外参是什么为什么不能靠尺子量外参描述的是两个坐标系之间的刚体变换。雷达坐标系中的任意一个点p_lidar要投影到相机图像上需要先通过外参变换到相机坐标系p_camera R * p_lidar t。然后再用相机内参做针孔投影得到像素坐标。用尺子量出来的平移t还能勉强凑合但旋转R很难量准。传感器安装时哪怕偏了1度在10米外就会造成超过17厘米的横向误差反映到图像上可能就是几十个像素的偏移。所以外参必须通过标定来求而不是靠机械加工精度来保证。1.2 平面约束两块传感器都能稳定提取的特征很多人第一次接触标定第一反应是找两个传感器的共同角点比如棱角、反光贴。但激光雷达在远距离上的点云很稀疏想从点云里精确提取一个几何角点非常困难。livox_camera_calib避开这个问题改用平面来做约束。标定板在相机图像里是清晰的棋盘格可以通过OpenCV的角点检测和solvePnP求出棋盘格平面在相机坐标系下的位姿进而得到该平面的法向量n_c和原点距平面距离d_c。在雷达点云中工具利用Livox雷达的反射率特性把标定板上的点从背景点云中分割出来然后用这些点拟合一个三维平面得到雷达坐标系下的法向量n_l和距离d_l。同一个物理平面在两个传感器坐标系里分别有不同的表示但它们通过外参联系在一起。法向量满足n_c R * n_l距离满足d_c d_l n_l^T * R^T * t。每采集一个位姿的标定板数据就能得到一组平面约束把所有帧的约束放进非线性最小二乘里优化就能解出R和t。1.3 为什么要摆多个姿态摆不好会怎样如果所有帧里标定板都正对着传感器那平面法向量几乎都指向同一个方向。这种情况下约束方程之间高度相关优化问题在数学上退化旋转的几个自由度根本约束不住。最后出来的外参可能在你采集的那一个特殊方向上误差很小换个方向就偏得离谱。打个比方你闭着一只眼睛只看一个固定方向的光斑是无法判断光源和你的空间距离的只有左右摇头、远近走动才能从光斑移动中反推出位置关系。标定板姿态就是这个道理。所以录制数据时标定板一定要覆盖正对、左右偏转、上下俯仰、画面四角斜放这些不同姿态法向量方向尽量分散。我一般采集12到15个位姿每个位姿保持2到3秒低于8个位姿时外参稳定性会明显下降。2. 标定前的准备标定板、相机内参和话题类型2.1 标定板制作最容易翻车的三个细节工具示例一般用9x6的内角点棋盘格方格边长108mm。打印时记住三点不要用光面铜版纸反光会让角点检测出现高光斑块导致检测失败棋盘格必须贴在完全平整的硬板上推荐5mm以上的亚克力板或铝塑板不要用软木板板子四周留几厘米白边方便点云分割时把板子区域和背景区分开我第一次图省事把棋盘格贴在软木板上结果木板拿起来会弯曲图像侧角点检测没问题但点云侧拟合出来的平面法向量和图像侧算出来的法向量不一致优化器只能在两者之间取一个折中最终外参怎么调都对不齐。后来换硬板问题当场消失。另外多说一句雷达侧识别标定板靠的是棋盘格黑白格在905nm红外波段反射率的差异。不要把棋盘格贴在反光不锈钢板上否则白格在点云里的反射率会变得异常影响平面分割。2.2 相机内参必须提前搞定而且要用锁焦后的参数livox_camera_calib只标外参不标内参所以相机内参的准确性直接决定外参上限。如果内参本身有偏差外参再准也无法把点云投到正确像素上。内参获取有几个途径用OpenCV棋盘格标定程序拍25张以上不同角度的标定板图像覆盖画面中心和边缘用Kalibr标定鱼眼镜头或大畸变镜头Realsense等深度相机可以用出厂内参但最好还是自己重标一遍因为镜头批次和温度都会影响出厂参数有一个细节很多人忽略标定内参之前要先把相机的自动对焦、自动曝光、自动白平衡全部关掉手动锁焦锁曝光。如果相机自动对焦焦距一直在变内参就不可能稳定。2.3 雷达点云话题格式先对齐不然后面全是坑Livox的驱动经历过两个阶段早期livox_ros_driver发布sensor_msgs/PointCloud2后期livox_ros_driver2默认发布自定义的livox_ros_driver2/CustomMsg。livox_camera_calib按PointCloud2设计如果话题类型不匹配节点会直接不工作。判断话题类型很简单rostopic info /livox/lidar如果显示的类型不是sensor_msgs/PointCloud2需要在雷达驱动launch里找消息格式相关参数不同版本名字不一样常见是msg_type或point_format把输出切换成PointCloud2。也可以用一个小转换节点把CustomMsg转成PointCloud2。这一步建议在录数据之前就解决不要录完bag再回来折腾。3. 采集数据标定成败的80%取决于这一步3.1 录制前先做传感器静态检查很多人拿到工具就急着录结果后面反复重录。我建议录制前花五分钟做三件事确认雷达和相机之间的连接件锁紧不会在采集过程中晃动在rviz里同时订阅点云和图像把标定板放到画面中央确认两个传感器都能清晰看到用手轻轻按压安装连接件观察点云相对图像有没有偏移如果有明显晃动先解决机械问题Mid-360虽然水平FOV是360度但实际标定时最好把标定板放在雷达正前方60度扇区内这里的点云密度最高平面拟合更稳定。3.2 标定板姿态序列照这个顺序来采集时最好有两个人配合一个人移动板子一个人观察rviz里点云和图像是否同步。实在没人配合就把板子固定在三脚架上移动三脚架也比手举着稳得多。我用的姿态序列供参考板子正对传感器距离2.5米左右停留3秒板子向左偏约45度停留3秒板子向右偏约45度停留3秒板子上仰让传感器俯视板面停留3秒板子下俯让传感器仰视板面停留3秒板子放到画面左上角停留3秒板子放到画面右下角停留3秒再补2到3组远近交替的位姿距离控制在2米到5米之间。太近时板子在图像里占不满角点检测容易失败太远时雷达落在板子上的点数太少平面拟合噪声大。3.3 录制bag的参考命令以ROS1 noetic为例开三个终端# 终端1启动雷达驱动 roslaunch livox_ros_driver livox_lidar.launch # 终端2启动相机驱动确保发布camera_info roslaunch usb_cam usb_cam.launch # 终端3录制bag注意把camera_info一并录进去 rosbag record -O calib_0803.bag \ /livox/lidar \ /usb_cam/image_raw \ /usb_cam/camera_infoTopic名以你实际环境为准。我遇到过有人只录了/image_raw没录/camera_info后面想排查内参问题就得重新回现场录非常痛苦。所以相机内参消息一定要录进去。整个录制过程1到3分钟就够换姿态时不要急宁可每个位姿多停一会儿。4. 跑通标定流程编译、launch参数和结果解读4.1 创建catkin工作空间并编译Livox官方仓库的编译流程很常规mkdir -p ~/livox_ws/src cd ~/livox_ws/src git clone https://github.com/Livox-SDK/livox_camera_calib.git cd ~/livox_ws catkin_make如果编译缺依赖sudo apt install ros-noetic-pcl-ros ros-noetic-camera-calibration-parsers优化部分依赖Ceres库如果编译时提示找不到Ceres先安装ros-noetic-libceres或从源码编译Ceres。旧版本Ceres可能导致部分代码无法编译建议装系统源里的较新版本。4.2 launch文件里的关键参数怎么填打开launch文件后重点检查这几项参数说明建议image_topic相机图像话题/usb_cam/image_rawcloud_topic雷达点云话题/livox/lidarcamera_info_topic相机内参消息/usb_cam/camera_infoboard_col / board_row棋盘格内角点数9x6或你的实际值square_size方格边长单位米0.108初始外参优化初值用卷尺量出来的粗略值square_size这个参数单位是米很多人会顺手填成108导致平面板尺寸完全错误。棋盘格如果是打印的最好用游标卡尺量一下实际方格边长打印缩放会产生微小误差。初始外参尽量给一个接近真实的值。用卷尺量一下雷达和相机光心之间的平移旋转可以先按安装方向给一个大概的偏航角。不要全填零否则非线性优化很容易掉进局部最优。4.3 启动节点并回放bag配置完成后# 终端A启动标定节点 source ~/livox_ws/devel/setup.bash roslaunch livox_camera_calib calib.launch # 终端B回放录制的数据 rosbag play calib_0803.bag --clock正常情况下终端会持续输出检测到棋盘格、点云平面拟合成功的信息并显示当前优化迭代的误差。如果运行半天没有任何输出优先检查launch里的话题名是否和bag里一致。需要注意--clock的作用是发布/clock消息配合use_sim_time让节点时间轴跟随bag走但它并不会改变消息头里的时间戳。真正的同步依据还是头时间戳录制时雷达和相机的时间戳偏差才是关键。4.4 结果怎么看怎么保存优化完成后终端会打印旋转矩阵和平移向量。看到结果后先别急着关窗口输出有以下几种可能输出一个4x4变换矩阵这就是最终外参输出R和t分开需要自己拼成齐次矩阵有些版本还会弹出一个可视化窗口显示点云平面投影到图像的效果拿到外参后我建议立刻写一个静态TF发布脚本或者yaml文件不要只记在终端里。yaml格式大致如下rotation_matrix: [0.999, -0.015, 0.012, 0.015, 0.999, 0.004, -0.012, -0.004, 0.999] translation_vector: [0.098, -0.045, 0.021]后续工程直接加载这个yaml就行。5. 避坑指南我在实操中踩过的七个坑5.1 棋盘格角点检测反复失败现象是终端一直提示找不到棋盘格。排查顺序先单独播放bag里的图像看标定板是否完整在画面内有没有被裁边检查光照棋盘格反光会在角点附近形成高光斑块导致检测失败检查board_col和board_row是不是内角点数不是格子数。9x6是内角点9列6行对应格子是10x7确认相机话题是去畸变后的图像如果livox_camera_calib内部直接用原始图大畸变镜头会检测失败我遇到过最无语的情况是摄像头自动白平衡导致棋盘格颜色偏色角点检测会偶发失败锁掉自动调节后一次通过。5.2 点云平面提取不到或拟合不稳定这个坑的表现是节点提示点云数量不足、平面拟合失败。原因通常是标定板离太远落在板上的点数太少点云范围裁剪参数设得太小把板子点也滤掉了标定板边缘产生飞点被错误算进平面点集解决办法是先在rviz里用Region Selection量出标定板的空间范围然后裁剪参数设置得比板子范围略大一点。同时观察板子在点云里有没有明显空洞如果有可以试试调整反射率阈值或换一个更大的标定板。5.3 点云话题类型不匹配导致节点不工作如果你回放bag时节点完全没有反应用rostopic info确认话题类型。我见过不少人在livox_ros_driver2环境下直接把 /livox/lidar 填进launch但节点收不到数据。解决办法雷达驱动配置里切到PointCloud2输出自己写一个CustomMsg转PointCloud2的转化节点换个使用sensor_msgs的驱动版本这个话题类型问题和外参无关但它是排在所有问题前面的拦路虎。5.4 时间戳不同步导致同步丢帧livox_camera_calib内部用的是近似时间同步允许一定时间差。但如果雷达和相机同一时刻的时间戳相差太大同步器会丢帧最终表现为明明录了很多数据节点实际处理的却很少。排查方法是看两个话题消息头时间戳的差值rostopic echo /livox/lidar/header/stamp -n1 rostopic echo /usb_cam/image_raw/header/stamp -n1如果差值超过几十毫秒考虑在launch里加固定时间补偿或者换用支持时间戳对齐的相机驱动。我在一个项目里用普通USB摄像头时间戳偏差稳定在120ms左右补了一个固定偏移后同步立刻恢复正常。5.5 标定结果和实际安装位置明显不符如果优化完的外参和你传感器实际的位置差异很大九成是初始值问题。非线性优化怕初始值离全局最优太远局部最优会让结果看起来合理但实际完全错误。我的做法是先用卷尺量平移精度到厘米旋转初值按安装方向估算比如俯仰和横滚给0偏航按雷达相对相机的朝向给把初始外参写进launch用rviz做投影预览确认大致方向对了再跑优化还有一种情况是点云平面法向量方向不统一有的朝传感器有的朝反面导致优化错乱。解决办法是检查点云拟合出来的法向量是否指向传感器一侧不是就取反。5.6 多雷达点云没有统一坐标系就标定某些大设备上装了多台Livox点云会聚合成一个话题。如果聚合时的坐标系不是同一台雷达坐标系平面拟合会混入来自不同坐标系下的点优化出来当然不准。用时先确认聚合话题的点是在统一坐标系下的。如果不确定可以只看单台雷达的话题跑标定标完再验证整体。5.7 ROS2环境怎么办livox_camera_calib官方是基于ROS1的在ROS2环境里想用它最稳妥的做法是用Docker开一个noetic容器把bag喂进去跑离线标定。容器里只跑这个标定节点不需要把其他算法也搬进去。如果你不想用ROS1可以去看看官方后续维护的livox_camera_lidar_calibration有没有ROS2版本但那是另一套流程。建议不要在项目冲刺阶段临时换标定工具先按最稳的方式把外参拿到手再说。6. 标定完之后怎么验证外参是真的准6.1 点云投影图像的直观检查拿到外参第一件事就是把点云投影到图像上。最简单的方法是通过静态TF发布外参然后rviz里同时订阅图像和点云。观察标定板边缘点云轮廓和图像里的棋盘格边缘应该基本重合误差不超过2到3个像素。如果发现整体偏移但方向一致多半是平移t还有误差如果点云相对图像有旋转趋势旋转R还有误差如果某个区域贴合、另一个区域偏移可能是标定板在采集时发生了形变需要重新检查板子。6.2 量化重投影误差视觉检查之外可以做一个小验证程序对某一帧图像用检测到的棋盘格角点作为真值同时将雷达点云里标定板的四角投影到图像计算平均像素误差。我的经验是误差小于3个像素说明外参质量很好3到5个像素属于可用范围超过10个像素基本要重新标定。这个验证程序不复杂写一次之后每次标定都能复用。也可以直接把重投影代码和读取yaml外参的逻辑放在一起方便批量验证多组bag。6.3 把外参接进你的建图或融合工程标定结果最常见的落地方式是发布TFrosrun tf2_ros static_transform_publisher x y z yaw pitch roll map camera_link也可以把外参写进你自己的SLAM配置比如后续做Cartographer建图并保存地图时点云变换到相机坐标系后的纹理渲染就依赖这套外参。我最后再多说一句经验之谈外参标定不是一劳永逸的。传感器受过撞击、安装板重新拆卸、极端温度变化后外参都可能发生微小的漂移。我现在会在工程里加一个启动自检流程每次开机用标定板测一次重投影误差超过阈值就提醒重新标定。这样做看起来费了一点事但能够避免很多融合算法里来源不明的飘比出了问题再排查要高效太多。