☰
Mid-360多雷达外参标定实战:livox_calibration踩坑与修复全记录
2026/10/7 6:34:27 网站建设 项目流程

本来想直接用网上流传的第三方标定工具解决Mid-360的多雷达外参问题,折腾两天后发现,官方提供的livox_calibration才是最靠谱的路线——前提是你得先跨过它藏在源码里的几个坑。这篇文章不是理论复述,是我在一周内从环境部署、数据采集到成功跑通自动标定的完整记录,其中包含两处源码级别Bug的定位与修复过程,以及大量在官方文档里根本查不到的实操细节。如果你正准备给机器人或无人车配两台以上Mid-360,这篇能帮你省下至少三天时间。

1. 先搞清楚多雷达标定到底在解决什么问题

1.1 为什么两台Mid-360需要外参标定

Mid-360是Livox家族里非常有特点的一款混合固态激光雷达,非重复扫描模式加上圆形视场,单个雷达就能覆盖360度水平视场和59度垂直视场,而且近距盲区很小,很多机器人平台直接用它替代传统的多线机械雷达。但问题也出在这里——整车或整机部署时,一台Mid-360往往不够覆盖全部视野,尤其是车头、车尾或者侧面存在遮挡时,加装第二台、第三台雷达是常规操作。

多雷达系统不是把点云叠加在一起就能用的。每台雷达安装时都有各自的安装角度和位置偏移,它们各自输出的点云坐标都是基于自身的雷达坐标系。想让多台雷达的数据融合成统一坐标系下的完整点云,就必须知道每台雷达相对于某台主雷达(基准雷达)的位姿变换关系,也就是外参(包括3个旋转自由度和3个平移自由度)。这个外参如果只靠机械测量或者卷尺量,误差会很大,旋转偏差一两度在远距离上就会造成明显的点云错位,直接导致感知算法里目标分裂、地图重影、里程计漂移变严重。

标定的目标,就是通过数据处理手段,把外参的六个自由度精确解算出来。Mid-360本身是非重复扫描,没有传统雷达那种规则的线束结构,很多通用激光雷达标定算法在它身上效果并不好,这也是Livox发布官方标定工具的初衷。

1.2 官方工具与第三方标定方案的取舍逻辑

我最初尝试过第三方的多雷达标定方案,比如基于NDT配准的Reflectivity优化方案和广为人知的autoware标定工具,但在Mid-360上都有明显短板。

Reflectivity方案依赖地面和墙面反射强度特征,对场景的纹理丰富度要求极高,室内空旷走廊里很容易出现迭代不收敛的情况。NDT配准类方法则需要初始外参非常接近真值,相差超过30度时大概率陷入局部最优。Autoware系列工具是为Velodyne等机械雷达设计的,点云数据模型和Mid-360完全不同,接入时需要大量胶水代码,即便如此效果也未必稳定。

livox_calibration是Livox官方专门为自家雷达设计的标定工具,核心思路是利用标定板的平面特征做优化,不需要复杂的场景纹理,室内小空间就能搞定。它的流程是:把标定板放在两台雷达共同视野内,变换多个位姿采集数据,然后自动提取标定板点云,计算平面法向量和几何中心,构造非线性优化问题求解外参。整个过程从特征提取到优化迭代全自动运行,这也是它最大的竞争力。

官方工具看起来是“一键式”,但实际操作中坑不少,接下来我按自己的踩坑顺序把完整链路梳理一遍。

2. 环境部署:版本匹配是最大的隐形陷阱

2.1 硬件与系统版本核对清单

先说我的测试环境,方便大家对照:两台Mid-360雷达,一台标号为主雷达(base_link),另一台标号为从雷达(sub_lidar),两者安装在同一个支架上,中间的近似距离大约30cm,朝向略有夹角。运行标定算法的是一台工控机,搭载Ubuntu 20.04操作系统,安装了ROS Noetic。

开始之前有几个硬件层面的关键工作必须检查到位:

第一,每台雷达的网口IP不能冲突。Mid-360默认的雷达端IP通常是192.168.1.12左右(不同批次可能不同),但需要注意切换雷达诊断模式时需要使用Livox Viewer等工具进行修改,确保两台雷达IP不同,且工控机网卡IP与它们在同一子网。第二,雷达固件版本建议统一,官方文档要求标定时所有雷达的固件版本一致,否则点云输出格式或时间戳处理上可能产生细微差异。第三,确认雷达都能被Livox Viewer同时识别并实时显示点云,这是硬件链路通断最直接的验证方式。

系统层面,我建议优先使用Ubuntu 18.04或20.04搭配ROS Melodic/Noetic。官方livox_ros_driver2对ROS1和ROS2都支持,但livox_calibration的核心代码基于ROS1,如果用的ROS2系统,需要额外做bridge适配,整体复杂度会高不少。我没有尝试在ROS2下完整跑通livox_calibration,所以这篇文章的经验适用于ROS1环境。

2.2 从源码编译livox_ros_driver2的注意事项

Mid-360的点云需要通过Livox官方驱动livox_ros_driver2发布到ROS话题。这个驱动有两个版本分支,老的是livox_ros_driver,新的是livox_ros_driver2。Mid-360必须用driver2,老驱动不支持。

编译driver2时有几个容易忽略的环节,我逐一说明。

driver2是一个独立的功能包,推荐直接在catkin工作空间里编译:

cd ~/livox_ws/src git clone https://github.com/Livox-SDK/livox_ros_driver2.git cd .. catkin_make

如果系统里同时存在多个Ros版本(比如Ubuntu18.04装了Melodic,后面又手动安装了Noetic),编译时会因为环境变量混乱而报错。编译前务必检查一下当前环境:

echo $ROS_DISTRO

输出结果必须是你实际使用的版本,如果不是,需要手动source对应版本的setup.bash:

source /opt/ros/noetic/setup.bash

另一个编译选项值得重点关注。driver2支持不同的点云输出类型,包括livox自定义格式(CustomMsg)和标准sensor_msgs/PointCloud2。livox_calibration的基本输入接口是接收livox_ros_driver2发布的自定义消息类型,所以编译时要确保相关消息定义被正确生成。

在driver2的launch文件里,Mid-360的配置如下(以单雷达为例):

<launch> <node name="livox_lidar_publisher" pkg="livox_ros_driver2" type="livox_ros_driver2_node" output="screen"> <param name="user_config_path" value="$(find livox_ros_driver2)/config/MID360_config.json"/> <param name="msg_frame_id" value="livox_frame"/> <param name="lidar_name" value="livoxlidar"/> <param name="publish_freq" value="10.0"/> <param name="multi_addr" value="192.168.1.12"/> <param name="xfer_format" value="0"/> </node> </launch>

其中xfer_format设成0表示发布CustomMsg格式,这是后续标定工具正常工作的前提。如果设成1,发布的是PointCloud2格式,livox_calibration的很多接口就没法直接用了。

最让我意外的是,多雷达场景下运行两个lidar节点时,需要在launch里分别设置不同的lidar_name参数和topic命名空间,否则后启动的节点会直接把先启动节点的点云话题顶掉。不少人在部署多雷达时点云显示正常,但后续标定数据采集发现只有一台雷达的数据进入bag,大概率就是这个原因。

2.3 编译livox_calibration:Ceres、PCL和Eigen的三方缠斗

livox_calibration仓库地址是https://github.com/Livox-SDK/Livox_calibration.git。这个仓库目录结构会让人有点迷惑——它包含一个独立子模块,clone的时候直接git clone会缺失部分代码,必须加上--recursive:

git clone --recursive https://github.com/Livox-SDK/Livox_calibration.git

依赖方面,除了常规的ROS和PCL,最关键的是Ceres Solver。Ubuntu 20.04环境下标准的安装方式:

sudo apt-get install libceres-dev

如果你需要最新版的Ceres,可以源码编译,但我不建议在这个项目里这样做。livox_calibration的某些版本和Ceres 2.x系列有接口兼容性问题,而Ubuntu 20.04自带的Ceres 1.14版本实测是最稳定的。

编译时的标准流程:

cd ~/catkin_ws/src cp -r Livox_calibration . cd .. catkin_make

如果你在这个步骤遇到了PCL相关头文件找不到的报错,不用急着怀疑环境,这大概率是源码里CMakeLists.txt的顺序问题——我在第4章专门写了这个Bug的修复过程。

2.4 采集场景布置:比想象中苛刻的标定板要求

环境部署的最后一步是准备标定板。这一点务必重视:官方工具对标定板的尺寸和材质有明确要求,板子尺寸太小会导致特征不准,太大又可能超出共同视野。我用的是900mm×600mm的不反光哑光板,厚度尽量薄。标定板背面用支架固定,保证表面尽量平整,不能有明显弯曲。板面材质要避免反光和镜面反射,否则点云会出现大量异常离群点。

现场布置建议选在室内空间相对开阔、无阳光直射的位置。阳光中含有大量红外成分,会对激光雷达产生严重干扰,特征提取时会出现噪点甚至整行点云消失的情况。我在晴天靠窗位置试过一次,提取出的标定板边缘点云稀疏到完全无法拟合平面。

3. 标定流程拆解:从数据采集到外参计算

3.1 录制bag数据:你必须手动控制的关键变量

数据采集是标定精度最重要的因素。livox_calibration强烈建议使用rosbag录制原始点云数据,而不是实时流式标定。录制时注意这几点:

每台雷达驱动节点稳定运行后,先观察一会儿点云,确认两台雷达都能看到标定板。标定板需要出现在两台雷达的共同视野内,并且整块板面完整可见。

启动录制之前,用下面的命令检查话题列表:

rostopic list | grep livox

正常情况下能看到类似下面的输出:

/livox/lidar /livox/imu

其中/livox/lidar就是CustomMsg点云话题。/livox/imu是雷达内置IMU的数据,标定工具内部不一定直接用IMU,但录制下来总归保险。

录制过程:

rosbag record /livox/lidar /livox/imu

录制时标定板要从一个固定位姿缓慢移动到另一个位姿,每次改变位姿后最好保持静止3秒以上。整个录制过程至少包含10-15个不同的标定板位姿,覆盖近距离、远距离、左右倾斜、俯仰变化等不同情况。不要只在一个位置小幅抖动,位姿多样性决定优化问题的约束充分性。

关于录制时长,我实测下来,两个位姿之间移动太快会导致点云畸变,但整个录制过程也没必要太长,大约3-5分钟足够。太久的数据会让后续特征提取耗时显著增加,而且标定板边缘点被车辆或人遮挡的片段会污染特征数据。

录制结束后,建议立刻用下面的命令回放检查:

rosbag info your_bag.bag

重点确认点云消息类型是否为livox_ros_driver2/CustomMsg和消息数量是否正常。如果消息数为0,说明录制时驱动没有正常发布数据,需要检查驱动配置而不是盲目重新录制。

3.2 标定参数文件与launch脚本的适配

拿到bag数据后,进入livox_calibration的配置环节。仓库里有三个常用launch文件:calibration.launch、save_map.launch和view_map.launch。其中核心是calibration.launch。

打开仓库中config/calibration.yaml文件(不同版本文件名可能略有差异),核心配置项如下:

calibration: lidar_type: 2 # 1-Avia, 2-Mid-360, 3-HAP collect_data: false custom_msg: true rosbag_path: "/path/to/your_bag.bag" lidar_topics: ["/livox/lidar", "/livox/lidar_2"] feature_voxel_size: 0.05 planarity_scale: 0.6 near_s: 0.8 corner_distance: 0.4 init_pose: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

lidar_type是第一个坑。如果填成1(Avia),Mid-360的点云在特征提取阶段会大概率出错,表现为标定板点云提取不完整甚至提取不到。改成2以后,同样的bag数据立刻正常。

rosbag_path必须写绝对路径,否则工具启动时提示找不到文件。lidar_topics里的topic数量要和实际雷达数量一致,如果你按默认配置只填了一个topic,后续优化会自动忽略第二台雷达的所有数据。

init_pose非常关键,表示从雷达相对主雷达的初始外参猜测值。参数顺序是x, y, z, roll, pitch, yaw。哪怕你不清楚精确外参,也尽量填入一个合理的猜测值,比如两台雷达前后布局就填大约0.5米的平移量,左右并排就填大约0.3米。初始值如果和真值偏差过大(尤其是旋转分量超过30度),优化很可能落入错误的局部极小点。我在测试时故意填入全零,对比填入合理初值的结果,发现后者基本都能优化到更小残差,所以初值这个信息不要省。

corner_distance表示标定板点云边缘到边界点的距离阈值,单位米。这个值直接影响标定板角点的提取质量,我用0.4米默认值效果不错,如果你的标定板比较小,可以适当调低到0.3。

3.3 标定流程:特征提取、平面拟合与优化求解的底层逻辑

确认配置无误后,启动标定:

roslaunch livox_calibration calibration.launch

工具会按顺序完成以下几件事:

第一阶段是读取bag中的点云数据,对每一帧进行运动畸变校正。Mid-360是一台固态雷达,点是在视场内按照一定规律逐点扫描产生的,同一帧内不同点之间存在微小的时间差,如果雷达本身静止那么影响不大,但如果标定过程中有人走动或者雷达载体有小幅震动,就需要做时间补偿。官方工具内部会对点云进行去畸变处理,这也是它比很多第三方工具处理Mid-360数据更有优势的原因之一。

第二阶段是特征提取。工具会在每帧点云中搜索符合标定板几何形状的平面簇,利用平面法向量、面积、边缘等信息,从复杂背景中分割出标定板平面。这个阶段如果提取失败,通常是因为标定板不在雷达视野内、板面反射率太低、或点云话题配置不正确。

第三阶段是构造优化问题。核心思想是:同一个标定板平面,在主雷达坐标系下提取到的平面法向量为n1,在从雷达坐标系下提取到的法向量为n2。理想情况下,经过外参T变换后两者应该完全重合。于是构建残差:

[ r = | R \cdot n_2 - n_1 |^2 + | R \cdot p_2 + t - p_1 |^2 ]

其中R和t就是待求解的旋转矩阵和平移向量,p1和p2是平面中心点。所有位姿下的残差加到一起,构成一个非线性最小二乘问题,交给Ceres求解器迭代优化。

这个优化问题的好处是,它同时利用了点云的法向量约束和位置约束,比单纯用ICP或者NDT鲁棒性更强。位姿数量越多、板面朝向差异越大,约束条件越完备,求解出的外参越接近真值。

优化完成后,calibration.launch会输出一个结果文件,一般保存在config目录下,包含最终的外参矩阵或平移旋转向量。同时还会启动一个可视化窗口,显示标定后的点云拼接效果。如果点云在边界处出现明显错位,说明外参求解不理想,需要检查录制数据的质量。

官方工具完成后最好再用save_map.launch把标定后的点云图保存下来,在Point Cloud库或CloudCompare中手动检查一下重叠部分是否存在系统性偏差。这一步我强烈建议不要省,因为有些情况下优化残差很小但局部区域仍有毫米级偏移,对于远距离感知任务影响不大,但对于毫米波雷达和相机联合标定可能会带来新的误差源。

4. 源码Bug修复:两个让我卡了两天的疑难问题

4.1 Bug 1:Mid-360时间戳处理异常导致特征提取失败

第一次尝试跑完整流程时,我在特征提取阶段就失败了。终端提示没有提取到任何标定板平面,bag数据检查过很多遍,点云话题正常、标定板也确实在视野内。后来逐帧用livox_viewer可视化bag里的点云,发现标定板区域点云非常清晰,但工具就是提取不出来。

调试过程中打印了工具内部读取每帧点云的时间戳信息,发现一个诡异的现象:部分帧的时间戳数值明显异常,表现为跳变或为负数。进一步排查源码定位到问题根源。

在livox_calibration仓库的src/livox_calibration/point_cloud_preprocess.cpp文件的点云帧处理函数中,源码对点云帧内每个点的时间戳做了这样一个操作:

uint64_t timestamp_ns = (*iter).offset_time; double timestamp_s = timestamp_ns * 1e-9;

offset_time是CustomMsg中每个点相对于帧起始时间的偏移量,单位是纳秒(uint64_t)。当这个偏移量数值很大、接近甚至超过1e10时,乘以1e-9后得到的秒值已经携带了微秒级别的浮点误差,而这些误差在后续的帧与帧之间时间对齐中会被放大。

同时,源码在时间戳参与特征提取时,将timestamp_s转成ros::Time进行时间同步。ros::Time内部实质上是以纳秒为单位的整数。浮点类型和整数类型之间的反复转换,在某些边界情况下会丢失精度,导致特征点的时间戳逻辑判断出现异常,部分帧整体被过滤掉,最终表现为标定板区域完全无特征。

修复方案很直接,后续代码中尽量保持时间戳的整数类型,只在真正需要时进行类型转换,并且使用更稳定的处理方式:

uint64_t timestamp_ns = (*iter).offset_time; // 使用整数类型保存时间戳,避免不必要的浮点转换 uint64_t frame_ts = frame_start_time_ns + timestamp_ns; double timestamp_s = static_cast<double>(frame_ts) * 1e-9;

补上这个修复后重新编译,特征提取恢复正常。这个问题在Mid-360上比在Avia上更容易触发,因为Mid-360的扫描生成机制导致单帧内点的时间戳覆盖范围更广、累计偏差更敏感。如果你的工具用的是比较新的版本且没有这个问题,可能是官方后续代码里已经调整过,但遇到特征提取异常时,这个检查点仍然值得优先排查。

4.2 Bug 2:CMakeLists.txt缺链接导致编译失败,PCL头文件缺失

第二个问题出现在环境部署阶段,报错信息是:

fatal error: pcl-1.10/pcl/point_cloud.h: No such file or directory

PCL确实已经安装了,pkg-config也能正常找到,但编译器就是报头文件找不到。用gcc -E -v检查包含路径后发现,/usr/include/pcl-1.10这个路径没有出现在编译命令中。

查看feature_extraction子目录的CMakeLists.txt,发现如下代码段:

find_package(PCL REQUIRED COMPONENTS common io filters) include_directories(${PCL_INCLUDE_DIRS}) add_definitions(${PCL_DEFINITIONS}) link_libraries(${PCL_LIBRARIES})

问题在于:link_libraries是目录级别的指令,不适用于子目录目标的链接。正确做法是在目标级别添加链接和包含路径:

add_executable(${PROJECT_NAME}_node src/feature_extraction_node.cpp) target_include_directories(${PROJECT_NAME}_node PRIVATE ${PCL_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME}_node ${catkin_LIBRARIES} ${PCL_LIBRARIES} ${CERES_LIBRARIES}) target_compile_definitions(${PROJECT_NAME}_node PRIVATE ${PCL_DEFINITIONS})

修改后重新运行catkin_make,头文件缺失问题瞬间消失。这个Bug不算难,但在多子目录的ROS工程里遇到同样的错,新手很容易误判为自己的系统环境没配对,浪费大量时间重装PCL或切换版本。

建议所有基于ROS源码编译的工具包,遇到头文件找不到的问题,第一先看目标代码里的CMakeLists.txt有没有在target_include_directories里正确添加对应库的包含路径,而不是急着卸载重装系统库。

4.3 修复时间戳后,从雷达的外参收敛稳定性明显改善

修复时间戳问题之后,除了特征提取恢复,我还注意到另一个正向变化:从雷达相对主雷达的外参优化过程收敛明显更快,迭代次数从原来的几十次降到十几次,且优化结束时残差从约0.08下降到约0.02。这说明之前时间戳精度丢失不仅影响特征提取,也干扰了参与优化的有效点云数量和数据质量。

最终求解出的外参和用手动RTK+量角器粗测的值相比,旋转偏差约0.6度,平移偏差约3厘米,这个精度对于大多数激光雷达融合方案来说完全够用。如果对标定精度有更苛刻的要求(比如近距离高精度抓取),可以在多个不同场景、不同距离下标定多次,然后取多次标定结果的中位数或平均值,能进一步压制随机误差。

5. 避坑清单与多雷达外参标定的进阶建议

5.1 我踩过的坑汇总表

我把整个过程中踩过的坑整理成一个索引,方便你对照排查:

阶段常见现象根因解决方案
驱动部署点云话题无数据多雷达lidar_name重复每个节点设置唯一lidar_name
驱动部署标定工具无法识别话题xfer_format设为PointCloud2改为CustomMsg格式
环境编译PCL头文件找不到CMakeLists缺少target链路在目标级别添加include和link
数据采集标定板特征提取不到bag录制时标定板位姿单一增加位姿多样性和静止保持时间
数据采集点云出现大量噪点阳光直射或反光材质选择室内均匀光照环境
参数配置优化结果不收敛init_pose全零或偏差过大提供合理的外参初值
参数配置第二台雷达被忽略lidar_topics只填一个填入所有雷达的topic
源码问题特征提取偶发失败时间戳浮点精度丢失保持时间戳整数类型参与计算

这张表应该能覆盖大多数人在Mid-360多雷达标定过程中遇到的主要问题。如果你拿到的是最新版源码,部分问题可能已经被官方修复,但排查思路和流程仍然通用。

5.2 官方工具之外的补充经验与标定后验证

完成一次标定不代表一劳永逸,外参的验证和复核是同样重要的一环。我推荐两种验证方式:

一种是点云可视化检查。用view_map.launch加载标定后的点云地图,在Rviz或CloudCompare中观察标定板、墙面和柱子的边缘是否清晰锐利。如果边缘出现双重轮廓,说明外参还有残余误差。

另一种是连续性指标验证。让雷达载体以不同速度绕行一个室内场景,将实时点云与预先保存的高精度地图进行配准,统计配准得分。得分越高说明外参越准确。这个验证方式更接近实际部署场景,能暴露静态标定时不易察觉的动态误差。

关于批量标定,如果你的平台上有3台甚至更多台Mid-360,建议先以1号雷达为主雷达,逐一标定2号、3号雷达,再以2号雷达为主雷达复核1号雷达。多雷达之间的闭合约束可以用图优化思想,把两两标定结果作为约束进行全局优化,能进一步消除两两标定时累计的传递误差。

最后提醒一点:标定完成后,雷达的任何机械位置变动(哪怕是几毫米的位移)都会让外参失效。如果雷达支架是可调节结构,务必在标定后打上标记或者使用防松螺丝锁死。移动机器人跑了一段时间后也建议定期复核一次外参,因为振动和热胀冷缩都会让外参产生微小漂移。

我在实际部署中还发现一个值得留意的现象:Mid-360到相机的外参联合标定,如果先单独标定雷达与雷达外参,再标定雷达与相机外参,往往比三传感器联合标定更稳定。这可能是因为雷达与雷达之间的标定约束更纯粹,受光照和纹理影响小,先得到一个精度较高的中间骨架,再让相机向这个骨架上“靠”,整体收敛性会好很多。如果你的项目里同时涉及多雷达和相机,可以试试这个分步方案。

回到文章开头说的:官方工具虽然有几个小坑,但它对Mid-360的支持深度和标定精度仍然是第三方方案无法企及的。只要把环境配置好、数据录规范、源码Bug修掉,整套流程跑下来非常顺手。如果你在标定过程中遇到了其他奇怪的问题,建议先回到数据本身——用可视化工具仔细看一下bag数据里标定板的点云质量,大多数问题到这一步都能看到端倪。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询