ARTICLE DETAIL

资讯详情

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

Livox MID-360与海康相机ROS下实时彩色点云建图实战

Livox MID-360与海康相机ROS下实时彩色点云建图实战 直接上结论Livox MID-360激光雷达配合海康工业相机在ROS环境下做实时彩色点云建图这套方案完全可行而且没有想象中那么复杂。整个链路无非就是雷达出点云、相机出图像、标定好外参、时间对上戳、融合出彩色点云、丢给SLAM建图但真正的坑藏在每一个环节的连接处——驱动、时间戳、标定板、坐标系变换随便一个没处理干净出来的彩图就是花的。我最初做这套东西的时候第一版彩色点云出来墙体和地面全是彩虹条纹排查了一整天才发现是时间同步没做好相机和雷达时间戳相差了80毫秒。这种问题不亲自趟一遍看再多官方文档也很难碰到。所以这篇东西我不打算只写步骤更重要的是把为什么这样做和出了错怎么查写透给后面走这条路的人省点时间。如果你也是做移动机器人、巡检小车或者自动驾驶数据采集需要一个带颜色信息的稠密地图这篇文章可以直接当操作手册用。Ubuntu和ROS基础我默认你有了没有的话前面先补一下剩下的我尽量按顺序铺开。1. 整套方案的技术链路和选型逻辑1.1 为什么是MID 360 海康工业相机先说雷达。Livox的MID 360是我的首选原因有三第一它内置IMU。这个太重要了做SLAM建图时雷达和IMU的紧耦合能显著减少点云畸变和漂移FAST-LIO、LIO-SAM这类主流算法都是吃这套输入的。你不需要额外再挂一个IMU少一个传感器就少一堆标定和同步问题。第二360度非重复扫描。mid-360是固态激光雷达视场角是360°水平全覆盖垂直方向大概是-7°到52°配合非重复扫描模式积分时间越长点云覆盖越密。这对建图来说非常友好尤其是墙面、天花板这类容易被稀疏点云漏掉的区域。第三它对近距离物体表现好。室内巡检场景经常有近距离的桌腿、货架柱子MID 360的盲区小、近距离点云密度高这个特性在彩色点云融合时是实打实的优势——点少了上色也没意义。再说相机。海康工业相机为什么值得选因为开发接口干净。MVSMachine Vision Software这套SDK提供完整的SDK、驱动和示例代码Linux平台支持成熟社区里也有开源的ROS驱动可以直接用。相比消费级相机工业相机没有自动曝光抖动、白平衡漂移这些玄学问题你把它配好之后色彩输出是稳定的这个对彩色点云的一致性来说很关键。1.2 系统总体架构与数据流把整条链路拆开看其实只有四个模块Livox雷达模块出原始点云和IMU数据topic一般是/livox/lidar和/livox/imu。海康相机模块出RGB图像topic一般是/hikcamera/rgb。融合标定模块这里做两件事一是时间同步用message_filters二是通过外参把3D点投影到2D图像上取颜色。SLAM建图模块把融合后的彩色点云喂给FAST-LIO最终输出带RGB字段的地图PCD文件。顺带说一句如果你的目标是离线建图这套链路同样成立只是把最后一步换成录制bag后回放即可。我自己的经验是调试阶段建议先离线录bag把标定和着色节点调通之后再上实时链路效率高很多。2. 环境搭建Ubuntu/ROS与驱动的踩坑记录2.1 系统版本与ROS发行版的选择激光雷达建图这个领域哪怕是现在ROS 1的生态依然是最成熟的选择。FAST-LIO、LIO-SAM等主流算法都有稳定的ROS 1分支社区问答、踩坑案例最多遇到问题更容易搜到答案。我的建议是Ubuntu 20.04 ROS Noetic。别急着上Ubuntu 22.04或24.04Livox驱动和海康驱动在Noetic下是验证最充分的组合。如果你手里的设备或者团队规范要求用ROS 2那Ubuntu 22.04 Humble也基本可行但你要做好自己修编译错误的心理准备。ROS 2这边有不少坑比如驱动节点的生命周期管理、同步器的写法都和ROS 1有区别。提示ROS 1和ROS 2的通信中间件不互通。如果你同时跑ROS 1节点和ROS 2节点需要额外搭一个桥接节点不要在架构设计上踩这个坑。2.2 Livox ROS驱动2的安装细节Livox官方驱动有两个版本livox_ros_driver老版和livox_ros_driver2新版。直接选livox_ros_driver2它支持MID 360老驱动对新固件兼容性一般。安装步骤其实很常规cd ~/catkin_ws/src git clone https://github.com/Livox-SDK/livox_ros_driver2.git cd livox_ros_driver2 ./build.sh ROS1编译完之后需要确认雷达的IP设置。MID 360默认IP一般是192.168.1.50你要把电脑有线网卡的IP设成同网段比如192.168.1.5子网掩码255.255.255.0然后执行source devel/setup.bash roslaunch livox_ros_driver2 msg_MID360.launch如果一切正常/livox/lidar会有话题输出。这里最容易出问题的是网口没识别到雷达排查顺序第一步看网线灯亮不亮第二步ping雷达IP第三步看网卡IP是否在正确网段。别问为什么问就是这三步救了我无数次。2.3 海康相机MVS SDK与ROS驱动安装海康相机这边第一步是装MVS客户端。去海康机器人官网下载Linux版本的MVS安装包安装后可以用客户端软件直接扫相机、改IP、看图像。相机默认IP往往是192.168.4.10一类需要先把相机IP改成和电脑同一网段否则ROS驱动节点根本找不到设备。这里提醒一句如果电脑同时接了雷达和相机一定给两个网卡分别配置IP别混在一个网段里两个设备的广播包互不干扰排查问题也方便。我的实际配置是雷达网卡192.168.1.5相机网卡192.168.4.5。然后是ROS驱动。海康官方其实没有发布太正式的ROS驱动但社区里有几个可用的开源包。我用的方案是hkcamera驱动包或者是基于MVS SDK二次封装的开源驱动。编译方式一般是cd ~/catkin_ws/src git clone https://github.com/xxx/hkcamera.git # 具体仓库以你找到的为准 cd ~/catkin_ws catkin_make启动相机节点roslaunch hkcamera hkcamera.launch能看到/hikcamera/rgb图像话题后说明相机这边打通了。实测下来几个容易踩的坑USB相机权限问题如果用USB接口的型号需要在/etc/udev/rules.d/下加规则文件否则设备在/dev下不出现。网口相机丢包确认网线是千兆线6类线以上百兆线跑高分辨率高帧率图像会明显丢帧。爆内存MVS客户端不要和ROS驱动节点同时开启摄像头访问会互相抢设备权限表现是节点反复报-8错误或者抓不到图。3. 联合标定从两套传感器到一套传感器3.1 标定的原理与数学基础先解决一个认知问题为什么要标定激光雷达和相机是两套独立传感器各自有自己的坐标系。激光点云里的一个点(x, y, z)是雷达坐标系下的三维坐标图像的像素(u, v)是相机成像平面的二维坐标。要把颜色赋给点本质是找到两个坐标系之间的变换关系。这个变换由两部分组成相机内参焦距fx, fy、光心cx, cy以及畸变系数。描述的是三维点在相机坐标系下如何投影到像素平面。外参雷达坐标系到相机坐标系的旋转矩阵R和平移向量t。这6个自由度3旋转3平移是我们标注定的核心目标。内参对于工业相机来说一般出厂有标定文件或者你在MVS里能查到近似值如果要求高就自己用棋盘格标定一次。外参则必须在你的实际安装位置下现场标定因为每次你把雷达或者相机拆卸重装外参就会变。这里我的建议是标定之前先把相机内参校一遍。我在实际项目中遇到过用出厂默认内参标定结果外参怎么优化都不收敛的情况最后发现是相机出厂参数和实际工况镜头温度、聚焦状态有偏差。内参准了外参标定才有意义。3.2 livox_camera_calib实战操作标定工具我用的是Livox官方开源项目livox_camera_calib。它的思路是你准备一个标定板支持棋盘格和AprilGrid两种让雷达和相机同时采集标定板数据工具通过特征点匹配雷达点云中提取标定板的角点/边界图像中提取对应角点来求解外参。操作步骤大概是这样准备标定板我强烈推荐用AprilGrid格式的标定板因为它在点云里的特征点提取成功率比棋盘格高很多。棋盘格在雷达点云里角点提取非常依赖强度阈值稍微远一点就提不出来。AprilGrid的图案在点云中反射强度差异大更容易分割出标定板区域。标定板尺寸A2或A1都可以但边缘要硬挺不要用软布打印的否则点云提取边界时会失真。固定雷达和相机的相对位置把雷达和相机按实际使用姿态装好标定过程中绝对不要动。雷达和相机都对准标定板让标定板出现在视野中央。采集时让标定板在几个不同距离和角度出现建议至少采集5组以上不同位姿的数据。录制数据启动livox驱动和相机驱动然后录制bag包。rosbag record /livox/lidar /hikcamera/rgb -O calib_data.bag录制的时候手持标定板慢慢移动、旋转覆盖视野的不同区域。每组位姿停2-3秒让雷达在该位姿下累计足够的点云。注意背景不要太杂乱标定板周围尽量不要有其他高反射物体。修改配置文件livox_camera_calib的参数配置在config/calib.yaml里重点包括# 相机内参 camera_intrinsic: fx: 1438.0 fy: 1440.2 cx: 960.5 cy: 605.1 k1: -0.11 k2: 0.23 p1: 0.0002 p2: 0.0001 # 标定板参数AprilGrid chessboard: width: 12 # x方向格子数 height: 8 # y方向格子数 size: 0.03 # 格子边长单位米 # 外参初始值用手量一下大概值 initial_guess: rotation: x: 0.0 y: 0.0 z: -0.01 translation: x: 0.05 y: 0.02 z: -0.03初始外参怎么估雷达和相机安装的相对位置用尺子量一下旋转部分如果没有特殊角度填0或者根据安装方向填90度等这个初值不需要精确但方向不能错否则非线性优化会陷入局部最小值。运行标定roslaunch livox_camera_calib calib.launch程序会加载bag包自动提取标定板数据并求解外参。最终输出类似这样的结果R [ 0.999 -0.010 0.003 0.010 0.999 0.005 -0.003 -0.005 0.999 ] t [ 0.051 0.018 -0.028 ]3.3 标定失败的几个典型原因我前后标定过十几次总结出三个最典型的失败原因特征点提取不到。表现是程序运行时提示找不到标定板或者匹配点对为0。解决方案把标定板放近一些3-5米内或者把点云降分辨率参数调高给算法更多点云积分帧。还有个细节标定板的白色区域在雷达点云里强度值很高可以预设在提取时只保留高强度点能有效过滤掉背景干扰。外参结果跳变。同一个bag包多跑几次结果不同说明标定数据约束不够。你需要增加标定板的位姿多样性尤其是让标定板在图像中覆盖不同区域而不仅仅是画面中央。投影误差一直很大。这时候优先怀疑相机内参不对而不是外参。拿标定板重新标一遍相机内参再看结果。4. 实时彩色点云时间同步与着色节点实现4.1 时间同步的核心逻辑标定完成外参到位接下来是工程上最容易被忽视的一环时间同步。为什么要做时间同步两个原因第一雷达和相机的帧率不一致。MID 360我在实际使用中一般配置点云输出频率是10Hz而海康相机如果以30fps跑那么每一帧点云在时间上并不对应某一帧图像——你需要找到时间上最接近的那一帧图像。这个匹配逻辑在ROS里叫做ApproximateTimeSynchronizer它不需要两个消息严格按照相同的时间戳触发而是在一个时间窗口内凑出最接近的那一对。第二传感器本身有延迟。相机曝光和传输有延迟雷达扫描一圈也需要时间。如果你不做任何同步直接拿到最新的图像就往下算就会出现点云已经转了小半圈图像还是半秒前的画面这种尴尬。对应到点云上就是颜色错位最明显的是物体边缘出现彩色拖影。ROS里实现时间同步很简单用message_filterstypedef message_filters::Subscribersensor_msgs::PointCloud2 PointCloudSub; typedef message_filters::Subscribersensor_msgs::Image ImageSub; PointCloudSub point_cloud_sub(nh, /livox/lidar, 10); ImageSub image_sub(nh, /hikcamera/rgb, 10); message_filters::ApproximateTimeSynchronizerPointCloudSub, ImageSub sync( point_cloud_sub, image_sub, 10, 0.05); sync.registerCallback(boost::bind(Callback, _1, _2));核心参数有两个队列长度10时间窗口0.05。前者是缓存消息数量后者是最大允许时间差单位秒。如果两个话题的发布频率都稳定在10Hz以上50毫秒窗口足够找到匹配对。如果你发现回调根本不触发大概率是时间差超过窗口了试着把窗口调大到0.1秒看输出。4.2 点云着色节点代码实现点云着色的核心思路把每一帧点云中的所有点通过外参变换到相机坐标系下再用相机内参投影到像素平面拿到该像素的RGB值回填到点的rgb字段。这里直接用C写一个核心函数#include pcl/point_types.h #include pcl_ros/point_cloud.h #include opencv2/opencv.hpp using PointType pcl::PointXYZRGB; void paintPointCloud( const pcl::PointCloudpcl::PointXYZ::Ptr cloud_in, const cv::Mat image, pcl::PointCloudPointType::Ptr cloud_out, const Eigen::Matrix4d T_lidar_camera, const Eigen::Matrix3d K) { cloud_out-reserve(cloud_in-size()); // 取出相机畸变系数以及内参矩阵简化写法 Eigen::Matrix4d T_camera_lidar T_lidar_camera.inverse(); for (size_t i 0; i cloud_in-size(); i) { const auto pt cloud_in-points[i]; // 1. 任意去除NaN点 if (!std::isfinite(pt.x) || !std::isfinite(pt.y) || !std::isfinite(pt.z)) { continue; } // 2. 激光雷达坐标系 - 相机坐标系 Eigen::Vector4d p_cam T_camera_lidar * Eigen::Vector4d(pt.x, pt.y, pt.z, 1.0); // 3. 相机坐标系 - 像素坐标这里用简化针孔模型畸变校正另写 if (p_cam.z() 0) continue; // 点在相机后方直接跳过 double u K(0, 0) * p_cam.x() / p_cam.z() K(0, 2); double v K(1, 1) * p_cam.y() / p_cam.z() K(1, 2); // 4. 判断像素是否在图像范围内 if (u 0 || u image.cols || v 0 || v image.rows) continue; // 5. 取颜色 const cv::Vec3b bgr image.atcv::Vec3b(int(v), int(u)); PointType pt_out; pt_out.x pt.x; pt_out.y pt.y; pt_out.z pt.z; pt_out.r bgr[2]; pt_out.g bgr[1]; pt_out.b bgr[0]; cloud_out-push_back(pt_out); } }注意几个细节畸变校正。上面的代码为了展示核心逻辑做了简化实际项目中u, v需要在投影前先做畸变校正或者用cv::undistortPoints处理。如果镜头畸变不大我用的12mm焦距镜头直接忽略畸变影响也可接受但广角镜头必须处理。性能。逐点投影在点云稠密时比如一帧几万点开销不小。优化思路有两个一是对图像做一次降采样二是用OpenCV的remap一次性生成一个查找表免去逐点计算投影。我实测用后者的方式彩色化一帧5万点的点云耗时从40ms降到15ms基本满足实时性。最近邻颜色插值。如果点云投影后的像素坐标是浮点数可以直接取最近邻也可以做双线性插值让颜色更平滑。我建议直接最近邻省时间实际效果差别不大。4.3 启动建图并保存彩色地图有了实时彩色点云话题/livox/rgb_points接下来就是丢给SLAM算法。FAST-LIO是首选因为它和Livox点云的配合是官方验证过的而且内置了IMU处理MID 360的IMU数据可以直接用。FAST-LIO的配置重点在config/里的yaml文件common: lid_topic: /livox/lidar imu_topic: /livox/imu preprocess: lidar_type: 1 # 1 表示 Livox 系列 scan_line: 4 # 非重复扫描模式扫线数Livox一般是4 mapping: acc_cov: 0.1 gyr_cov: 0.1 b_acc_cov: 0.0001 b_gyr_cov: 0.0001 fov_degree: 360 pcd_save: pcd_save_en: true interval: -1 # -1 表示建图结束时保存启动后FAST-LIO会自己订阅雷达和IMU话题实时输出位姿和全局地图。注意FAST-LIO默认保存的地图是纯几何点云不带颜色。要保存彩色地图有两个办法办法一把着色后的彩色点云当作输入修改FAST-LIO的preprocess输入让它处理/livox/rgb_points而不是原始的/livox/lidar。因为彩色点云保留了所有XYZ坐标信息FAST-LIO的里程计部分仍然正常工作同时它的global map保存函数会把RGB字段一并写入PCD文件。这是最省事的路子。不过要提醒一句着色节点要保证每一帧都有输出否则FAST-LIO的输入中断会导致滑窗优化退化。我建议在着色节点里做缓存如果某帧图像没有匹配到点云就输出上一帧的彩色结果避免话题断流。办法二通过/livox/rgb_points直接录制bag后离线着色如果你用LIO-SAM这类不支持彩色点云输入、但输出PCD较干净的算法可以这样录制原始bag做SLAM得到位姿后把每一帧原始点云按位姿叠加到全局再逐帧着色拼接。这个适合离线场景代码量稍大但优点是地图纹理精度更高因为每一帧用了当时对应的图像避免远帧颜色被错误拉伸。地图保存后用PCL查看或者直接用pcl_viewerpcl_viewer map.pcd如果看到的是彩色点云大功告成如果看到的是灰白色说明PCD文件里没有RGB字段需要回头检查FAST-LIO配置或者输出环节。5. 实测效果与调优经验5.1 着色错位问题的排查套路彩色点云最让人崩溃的问题不是不出颜色而是颜色和几何对不上墙面上半部分是白色下半部分是黄色或者物体边缘出现一圈光晕”。遇到这种问题我的排查顺序是固定的第一步检查标定外参。打开一个图像和点云叠加的可视化工具我常用RViz也可以用pcl_viewer加载带有相机位姿的点云把点云投影到图像上看轮廓是否重合。如果雷达点云的边缘和图像边缘系统性偏移说明外参有轻微误差。比如整体向右偏移2个像素那就是平移向量t的y分量差了一点点。修外参不是重新标定的唯一选择有时手动微调几个毫米级别的平移量就够了。第二步检查时间同步。如果边缘重影只出现在运动场景静态场景下完美重合那就是时间同步问题。雷达10Hz、相机30Hz时间窗口过大时会选到时间相差最远的图像帧导致动态物体被错误着色。把时间窗口从50ms缩到20ms以下或者把两者帧率尽量对齐相机降低到10fps动态场景的错位会明显改善。第三步检查遮挡问题。这里有个很多教程没讲透的点点云投影到图像上时多个点可能投到同一个像素。你在代码里如果随机选用其中一个点的值就可能造成前景点在某个视角下被背景颜色覆盖表现为物体边缘脏色。解决方法是Z-buffer思路在遍历点的时候记录每个像素当前最近的深度值只有新来点的深度更小才更新颜色。这个逻辑在代码实现里不复杂但效果提升非常明显。5.2 帧率匹配与性能优化实时彩色点云建图的算力瓶颈一般不在雷达而在图像处理和点云融合。我实测场景是室内50平米环境雷达10Hz相机1280x72015fps点云每帧3~4万点CPU为Intel i7-10700直接逐点投影着色CPU占用约30%。如果还想跑FAST-LIO建图CPU占用就逼近80%了有些卡顿。几个有效的优化手段ROI裁剪距离太远的点投影到图像上后颜色置信度不高几十米外一个像素覆盖一大块区域直接对1米以外、30米以外的点做裁剪能砍掉约40%的点云量。远近阈值根据场景调。降采样在原始点云上做一个体素降采样0.02米或0.03米对视觉感知影响不大但点数量能减少一半以上。图像缩放如果对颜色精度要求不是极端高把图像从1280x720缩到640x360再做投影运算量直接降到1/4。我在实际项目中就是用的这种方案视觉上几乎看不出差别。多线程点云着色是大规模并行操作用OpenMP循环展开#pragma omp parallel for num_threads(4) for (size_t i 0; i cloud_in-size(); i) { // 投影和取色逻辑 }实测4线程之后单帧处理时间从40ms降到了12ms实时性完全够了。5.3 标定和同步之外别忘了光照和相机参数最后一个很多人忽视的点颜色品质不只取决于融合链路还取决于相机本身的采集参数。建议在MVS客户端里把曝光时间、增益、白平衡全部调成手动模式并且固定一个适合当前光照环境的值。如果开自动曝光相机在室内不同区域来回移动时亮度会上下波动反映到彩色地图上就是一块亮一块暗非常影响观感和后续使用。我在室内巡检项目里总结的经验是曝光8ms-15ms增益在0~6dB之间白平衡按光源固定白炽灯和LED的色温差别很大。光照变化实在大的场景还有一个更粗暴的方案——在着色节点里做简单的色彩归一化。对当前图像做自适应直方图均衡CLAHE可以让点云颜色在明暗区域过渡时更平滑但代价是颜色稍微失真看你对真实性的要求。另外强烈建议把标定结果和外参备份到固定配置文件中做成yaml统一管理。我踩过的坑是重装系统后忘记保留标定结果结果花了半天重新标定才发现之前那份外参就在旧笔记本的备份目录里躺着。配置文件的格式可以这样# sensor_calib.yaml lidar_to_camera: rotation_matrix: [0.999, -0.010, 0.003, 0.010, 0.999, 0.005, -0.003, -0.005, 0.999] translation: [0.051, 0.018, -0.028] camera_intrinsic: fx: 1438.0 fy: 1440.2 cx: 960.5 cy: 605.1 distortion: [k1, k2, p1, p2, k3]标定结果集中管理还有个好处——如果以后要换算法或者换相机型号配置文件直接复用不用重新翻找历史记录。最后再分享一个小技巧彩色点云建图整个流程走通之后你会发现调试时最有用的工具不是RViz而是一个把图像和点云投影叠加显示的小脚本。它能实时看到每一帧点云是否有颜色、颜色是否对齐、时间是否同步所有问题在一张图上一目了然。我当时就是写了一个基于OpenCV的快速画布把点云投影结果和原图按半透明叠加显示效率直接翻倍。这个脚本代码量不大建议你调试期一定要留一个出问题的时候比什么日志都好使。
返回列表