ARTICLE DETAIL

资讯详情

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

ROS2下livox-mid-360与Cartographer 3D建图定位实战指南

ROS2下livox-mid-360与Cartographer 3D建图定位实战指南 简介基于ROS2的cartographer 3D建图与定位配置包面向需要同时掌握官方算法与自定义扩展的机器人开发者重点解决基于livox-mid-360雷达的离线建图和在线定位场景。资源共11个文件包含5个Python启动脚本、3个Lua配置参数、URDF机器人模型、Rviz可视化配置及说明文档整体仅8KB轻量实用。目前已有1016人浏览学习。包内既提供官方cartographer的3D建图与定位Lua配置也给出了自定义的launch和URDF文件覆盖离线建图、在线定位两类流程并针对livox-mid-360的视场角与精度特点做了适配可帮助读者快速搭建3D SLAM环境。通过对照官方包与自定义包的差异还能深入理解概率网格建图、激光雷达与IMU融合、参数调优等关键环节适合做毕设或工程预研时参考。 说个有点尴尬的事我第一次对着rviz2里那张不断旋转的3D地图发呆时第一反应不是“调参”而是怀疑自己买到了假雷达。后来才知道在ROS2下用livox-mid-360跑cartographer 3D建图和定位真正的门槛从来不是把点云显示出来而是搞清楚“官方包”和“自己的包”之间那一条条隐形的依赖关系以及离线和在线两套流程各自要踩的坑。这篇文章就把我从零开始折腾livox-mid-360 cartographer的完整过程、配置思路和踩过的雷都写出来给正准备走上同一条路的ROS2开发者做参考。不需要太高深的基础能把livox驱动跑起来、看得懂坐标系就足够了。1. 为什么这套组合值得折腾livox-mid-360与cartographer 3D的适配逻辑很多人问过我现在LIO系算法那么多fast-lio、livox-mapping都挺香为什么还要回头搞cartographer我的看法是LIO系算法在里程计精度上有天然优势但“建图定位”这个完整闭环尤其是大场景的回环检测和长期定位稳定性cartographer 3D依然是ROS2生态里最成熟、最容易落地的方案之一。而它和livox-mid-360的匹配点刚好卡在了物理特性上。1.1 livox-mid-360点云特性与cartographer 3D的契合点livox-mid-360是Livox家的非重复扫描雷达水平视场360度垂直视场大致覆盖从负角度到正上方的宽范围。它最特别的地方是“非重复扫描”——点云不会像传统机械雷达那样每条线固定扫同一个环而是在视场内逐渐覆盖更多区域。这对cartographer 3D来说其实非常友好cartographer的scan matching依赖当前帧和submap之间的几何重合度非重复扫描能在一个较短的积分时间内填充更多视角信息减少了纯旋转时“看起来哪儿都一样”的退化风险。但非重复扫描也带来一个副作用单帧点云密度不均匀近距离点很密远距离点很稀疏。cartographer内部处理range data时如果直接喂原始点云很容易出现近处点权重过高、远处约束不足的情况。所以我的建议是在进cartographer之前做一次可选的降采样或者至少关注一下点云发布频率。livox-mid-360默认10Hz左右出点云对cartographer来说足够但如果你发现CPU占用过高可以在驱动层把点云频率降到5Hz建图质量不会明显下降实时性反而更好。1.2 为什么离线建图和在线定位要分开看待标题里的“离线与在线”不是同一个功能换了个启动方式而是两条完全不同的工作流。离线建图时你手上有录制好的bag可以反复重放、反复调参跑坏了重来代价只是几分钟的bag回放时间在线建图则是机器人真在跑参数一旦不对地图直接飞掉你连现场都回不去。更关键的是定位模式加载的地图来自离线建图的产物地图质量直接决定定位稳定性。所以我一直坚持一个工作流先用bag把原始数据录下来离线把参数调到能稳定建出闭环再用同一份bag验证在线模式最后才上真机。这样既省时间又不会把现场建图时的紧张情绪浪费在调参上。后面第四章我会把离线流程的完整命令和配置贴出来照着跑一遍就能体会到这套工作流的价值。2. 环境准备与双包编译官方cartographer_ros2和自己维护分支的取舍ROS2下的cartographer和你可能熟悉的ROS1版本不是同一个东西。ROS2官方仓库里维护着cartographer和cartographer_ros的ros2分支版本相对干净但功能上比ROS1时代少了一些辅助工具。标题里的“官方包自己的包”我的理解是官方仓库拉下来的cartographer/cartographer_ros作为核心算法自己建一个工作空间包用来放livox驱动、launch文件、lua配置和机器人描述。这个“自己的包”不是让你去改cartographer源码而是把工程逻辑和算法源码分开升级维护都方便。2.1 ROS2版本与依赖的选择我实际测试下来Humble和Jazzy都能跑但如果你用的是ubuntu22.04建议直接上Humble生态最稳ubuntu24.04就选Jazzy。livox_ros_driver2官方对ROS2的支持也很好直接源码编译即可。需要提前装好的依赖包括ceres-solver和abseil这两个是cartographer的硬依赖。安装时用apt装系统自带版本就能编过千万别自己从源码编译另一个版本很容易和系统库冲突。还有一个容易被忽略的依赖ros-${ROS_DISTRO}-ros-testing和ros-${ROS_DISTRO}-ament-cmake-gtest。缺了它们编译cartographer_ros时会在测试环节报错看着像是代码问题其实是测试框架没装全。2.2 工作空间的目录规划我的工作空间结构大致是这样的mkdir -p ~/carto_ws/src cd ~/carto_ws/src git clone -b ros2 https://github.com/ros2/cartographer.git git clone -b ros2 https://github.com/ros2/cartographer_ros.git git clone https://github.com/Livox-SDK/livox_ros_driver2.git然后我的机器人专用包放在同级的livox_carto_bringup目录里结构如下livox_carto_bringup/ ├── launch/ │ ├── offline_build.launch.py │ └── online_localization.launch.py ├── config/ │ ├── livox_mid360_3d.lua │ └── livox_mid360_localization.lua ├── urdf/ │ └── robot.urdf └── rviz/ └── carto3d.rviz官方包负责算法自己的包负责“把算法适配到这台具体机器人上”比如IMU安装位置、雷达安装朝向、tf树结构。这套分离思路值回票价因为你换一台机器人时不需要动官方包一行代码。2.3 colcon build的两个高频报错编译顺序上我建议先单独编译livox_ros_driver2再编译cartographer和cartographer_ros。如果你把livox驱动和cartographer放一起直接colcon build大概率会遇到包间依赖没准备好导致的头文件缺失。正确处理是cd ~/carto_ws colcon build --packages-select livox_ros_driver2 source install/setup.bash colcon build --packages-select cartographer cartographer_ros source install/setup.bash colcon build --packages-select livox_carto_bringup source install/setup.bash另一个常见坑是编译cartographer时提示找不到absl/strings/str_split.h这不是abseil没装而是cartographer的CMakeLists里对abseil的查找逻辑在某些ROS2版本下会失效。我用的解决办法是在编译前显式导出依赖库路径。如果编译报错可以先看日志定位到具体找不到的库然后检查那个库到底装在哪个目录再对号入座。3. 数据链路与TF关系让livox点云和IMU真正对齐cartographer 3D建图最容易被低估的点是数据链路。很多人以为点云进去了就能建图实际上一张好地图的前提是点云、IMU、tf树三者完全对齐。livox-mid-360的驱动默认发布的点云消息是livox_ros_driver2/msg/CustomMsg而cartographer 3D需要的是sensor_msgs/PointCloud2中间需要一个转换节点。3.1 从livox CustomMsg到PointCloud2livox_ros_driver2自带的livox_ros_driver2_node支持通过参数设置让点云以PointCloud2格式发布但我更推荐自己写一个极简转换节点因为这样你可以顺便做三件事点云裁剪去掉雷达自身盲区里的杂点、可选的体素降采样、以及为点云补充准确的时间戳。转换节点本身不复杂核心就是读CustomMsg里的points填到PointCloud2对应字段里。要注意的是CustomMsg的points里的offset_time是相对时间如果你要做高精度时间对齐必须加上消息头的stamp。我实际用下来对于10Hz的建图场景直接用PointCloud2消息头的stamp就够了不需要精确到offset_time级别。3.2 IMU消息格式与TF树的坑cartographer 3D必须有IMU这不是可选配置而是硬依赖。因为3D的trajectory builder需要用IMU的重力向量来估计姿态没有IMU数据建图一开始就会因为姿态飘移而失败。所以IMU话题格式必须正确cartographer读取的是sensor_msgs/Imu字段包括orientation: 四元数提供初始姿态参考 angular_velocity: 角速度x/y/z linear_acceleration: 线性加速度x/y/z对cartographer来说linear_acceleration和angular_velocity是核心orientation可以在某些配置场景下用但cartographer主要依靠加速度积分估计重力方向。如果你的IMU驱动发布的消息里加速度单位不是m/s^2或者角速度不是rad/s地图会在几个呼吸间飘没。这个坑很隐蔽因为rviz里看IMU数值变化似乎正常但量纲错了就是另一回事。TF树方面必须保证完整且连续。我用的结构是map - odom - base_link - livox_frame └- imu_linkmap和odom两个坐标系在cartographer里由算法发布base_link到livox_frame和imu_link的变换由你的urdf或tf_static发布。注意cartographer 3D在定位模式下发布的是map - odom的变换如果存在其他节点也发布同样的父子变换会造成坐标抖动排查起来非常痛苦。3.3 时间同步与bag回放时的clock问题在线跑的时候时间同步相对容易只要所有节点都按系统时间打时间戳即可。但离线建图回放bag时容易忽略一个问题如果你在launch里使用了use_sim_time并且bag里没有对应的/clock话题那cartographer会认为所有传感器消息都在同一个时间点地图会乱成一团。livox驱动默认可能不发布/clock所以用ros2 bag play回放时要加上--clock参数ros2 bag play rosbag2_2024_xxx --clock如果嫌弃bag文件里时间戳和系统时间差太远还可以考虑用--rate参数控制回放速度。我通常会以0.5倍速回放给cartographer足够的计算时间。别小看这个习惯它能直接减少因为计算超时导致的掉帧掉帧是离线建图地图发散的常见诱因。4. 离线建图的关键步骤从bag录制到pbstream生成离线建图的核心是cartographer_offline_node它的好处是不需要实时跑传感器驱动直接从bag里读数据可以反复跑、反复调参数。我强烈建议你把离线流程作为所有调试工作的主战场省下的时间远超你录制bag花掉的那几分钟。4.1 录制bag的topic列表与注意事项录制前先确认三个话题一定在录/livox/lidar或你的转换节点输出的PointCloud2/livox/imusensor_msgs/Imu/tf和/tf_static如果录制时不带/tf回放时cartographer里完全构建不起传感器之间的相对关系。我录bag时用的命令ros2 bag record /livox/lidar /livox/imu /tf /tf_static如果你把PointCloud2转换节点单独跑起来则录转换后的点云话题即可不用录原始CustomMsg。录制时尽量不要快推快停尤其是纯旋转动作会给后续的回环检测增加难度。我的经验是先慢速转一圈把环境整体扫一遍再在重点区域来回走几趟最后回到起点。这个“起点闭合”的动作对验证回环非常关键。4.2 离线建图cartographer_offline_node和lua配置离线建图的启动方式取决于你用什么launch。用命令行直接跑也可以ros2 run cartographer_ros cartographer_offline_node \ -configuration_directory ~/carto_ws/src/livox_carto_bringup/config \ -configuration_basename livox_mid360_3d.lua注意cartographer_offline_node不是订阅实时话题而是通过它内部集成的bag播放功能读取bag。因此在启动前需要通过launch把bag路径传进去。我一般会在launch文件里先设置use_sim_time : True再启动offline node。关键在lua配置。下面是我基于cartographer 3D建图流程整理的基础配置结构和官方3D示例一致主要参数做了整理方便对照理解include map_builder.lua include trajectory_builder.lua options { map_builder MAP_BUILDER, trajectory_builder TRAJECTORY_BUILDER_3D, map_frame map, tracking_frame base_link, published_frame base_link, odom_frame odom, provide_odom_frame true, use_online_correlative_scan_matching true, num_point_clouds 1, rangefinder_sampling_ratio 1., imu_sampling_ratio 1., } MAP_BUILDER.use_trajectory_builder_3d true TRAJECTORY_BUILDER_3D.num_accumulated_range_data 10 TRAJECTORY_BUILDER_3D.min_range 0.2 TRAJECTORY_BUILDER_3D.max_range 30. TRAJECTORY_BUILDER_3D.use_imu_data true TRAJECTORY_BUILDER_3D.ceres_scan_matcher.translation_weight 1. TRAJECTORY_BUILDER_3D.ceres_scan_matcher.rotation_weight 1.其中num_accumulated_range_data决定了累计多少帧点云构建一帧submap点云。数值越大单帧点云越“厚实”但计算量也会显著上升。我先用10这个值把流程跑通再根据CPU占用调整。min_range略大于雷达盲区max_range根据你的使用场景设置。4.3 保存地图与可视化验证离线建图跑完后cartographer会生成一个.pbstream文件这是cartographer专有的地图格式。保存命令ros2 service call /finish_trajectory std_srvs/srv/Empty {} ros2 service call /write_state cartographer_ros_msgs/srv/WriteState \ {filename: /home/xxx/maps/mid360_3d.pbstream}如果你的launch里没有启动这些service需要先确认offline node已经加载了对应节点名。拿到pbstream后我建议先用rviz把整个场景旋转着看一遍确认闭环没出现“分层”或“重影”。3D地图不像2D那样有一张直观的栅格图可以判断质量必须靠三维视角检查墙体、地板、天花板是否对齐。5. 在线建图与纯定位切换加载地图后的坐标系与姿态问题离线验证没问题后才轮到在线环节。在线建图本质上就是启动cartographer_node让它订阅实时传感器话题和离线流程的区别只是一个读bag、一个读实时数据。但真正让多数人头疼的是“建图跑通了换到定位模式后旋转飘”。5.1 在线建图启动流程在线建图时我会先启动livox驱动、转换节点、IMU驱动和tf发布然后启动cartographer_node。关键参数和离线配置基本一致但我额外加了两个在线专用的选项use_online_correlative_scan_matching建议保持true这个选项让cartographer在scan matching之前做一次相关性搜索能显著提高激光匹配的鲁棒性定位模式下则可以把num_accumulated_range_data适当调大因为你有了一张固定地图作为约束点云厚一点反而能减少误匹配。在线启动的launch里要特别关注provide_odom_frame这一项。我习惯把它设置为true让cartographer自己发布odom - base_link的变换。如果设置为false你必须自己提供odom源否则cartographer会报缺失tf的错误。5.2 纯定位模式加载pbstream与初始位姿在线定位和在线建图用的是同一个cartographer_node区别在于启动时是否加载已有地图。加载地图后cartographer会处于“等待初始位姿”的状态需要通过服务接口发送初始位姿ros2 run cartographer_ros cartographer_start_trajectory \ -configuration_directory ~/carto_ws/src/livox_carto_bringup/config \ -configuration_basename livox_mid360_localization.lua \ -initial_pose_x 0.0 -initial_pose_y 0.0 -initial_pose_z 0.0 \ -initial_pose_a 0.0initial_pose_a是初始朝向角单位弧度。这个值给得准不准直接决定定位模式开局是否稳定。我见过很多人卡在“定位模式一开始就打转”多半是初始位姿给得和实际位置差太远或者给的初始朝向反了180度。我的建议是无论什么场景第一步先把机器人停在已知位置量出它在已建地图中的坐标和朝向然后再发送初始位姿。别试图“让算法自己找”cartographer不是AMCL没有全局重定位的能力。5.3 旋转飘问题与坐标系处理“旋转飘”是定位模式下最常见的现象机器人原地转圈时rviz里的地图或点云开始漂移严重时整个坐标直接飞走。根因通常是三个里至少中一个第一IMU没真正参与定位。你只给了点云没有IMU或者imu话题里角速度量纲不对cartographer在旋转时只能靠点云匹配猜测朝向一旦当前帧退化就飘。第二初始位姿不准cartographer在前几帧还在努力“矫正”自己此时如果快速旋转匹配会直接失败。第三地图本身存在累积回环误差定位时又缺乏足够的新约束旋转时更容易飘。排查时我习惯先算一笔账机器人在当前点位的历史点云和地图对应位置是否重叠。在rviz里把地图点云显示出来再叠加在线点云一眼就能看出是不是因为地图本身“虚胖”导致定位锁定到了错误位置。6. 3D建图关键参数速查与旋转飘排查记录我把自己调参过程中反复改动、效果最明显的几个参数整理成一张速查表照着抄基本能覆盖九成场景。注意每个参数对具体机器人的影响不一样表格只是起点不是终点。参数推荐范围作用调试建议num_accumulated_range_data5~20每帧submap累积的点云帧数越高越密越稳但CPU消耗大min_range0.1~0.3忽略雷达盲区内的点小于livox最近测距能力即可max_range15~40限制远距离点参与匹配根据环境大小调整太大引入噪声translation_weight0.5~2.0平移匹配权重地面机器人可以给大一点rotation_weight0.5~2.0旋转匹配权重旋转时易飘就适当调大ceres_scan_matcher.occupied_space_weight5~20占据空间匹配权重点云质量差时增大6.1 旋转飘问题排查链路我遇到“定位时旋转飘”时不会急着改参数而是按下面这条链路逐项排除检查/imu话题频率低于100Hz就要看驱动配置正常livox内置IMU输出约200Hz但cartographer对此没有硬性要求只要别低于点云频率太多。检查/imu数据范围角速度是否在合理范围rad/s加速度是否稳定在9.8附近。如果发现角速度数值单位像是度/s直接转换。检查tf树用ros2 run tf2_tools view_frames生成tf树确认odom - base_link - imu_link/livox_frame不间断、无跳变。降低机器人旋转速度再试。如果低速不飘高速飘说明scan matching的rotation_weight不够或点云累积帧数太少。把use_online_correlative_scan_matching打开并且给足num_accumulated_range_data。我自己的机器人在定位模式下最终把rotation_weight从1.0调到了1.8num_accumulated_range_data从10增加到15旋转飘出现的频率大幅下降。但也要提醒一句这些值不是越大越好参数过大会导致实时匹配变迟钝机器人明明动了一段距离地图视角却“拖”在后面。6.2 一个容易被忽略的经验先确认你的地图是“可以定位的地图”最后分享一个我从项目里总结出来的经验不是所有建出来的地图都适合定位。如果你离线建图时为了让地图好看把点云累积得特别厚或者建图过程本身有很多次快速旋转生成的地图可能看起来完整但在某些区域点云密度分布极不均匀定位时这些区域会成为“黑洞”。所以我在离线建图阶段就会刻意规划路径避免长时间纯旋转同时保证每个房间、每条走廊都被多次扫描让地图里的点云密度尽量均匀。另外定位模式用的lua配置和建图配置不要完全一样。定位阶段不需要trajectory_builder主动创建新的submap核心是把当前点云匹配到已有地图中。因此我会把定位配置里的MAP_BUILDER.num_background_threads调低一些给scan matching留出更多CPU预算。这个细节很小但对实时性能的影响却很明显。整套方案跑到今天我已经很少再被“旋转飘”和“地图飞掉”困扰了。回看整个过程最值钱的教训其实就一句话先离线把数据流理顺再谈在线调参先把tf、IMU、点云三个东西对齐再谈地图质量。你和一张稳定地图之间的距离往往不是算法差距而是这些基本功差多少没补上。本文还有配套的精品资源点击获取
返回列表