ARTICLE DETAIL

资讯详情

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

基于Cartographer的3D建图系统搭建:激光雷达、IMU与底盘时间同步全解析

基于Cartographer的3D建图系统搭建:激光雷达、IMU与底盘时间同步全解析 先说结论这套系统真正跑通最耗时间的不是Cartographer本身而是传感器之间“对齐”这件事——时间要对齐、坐标系要对齐、通信要对齐。三者任何一个没处理好建出来的图都会在10米内开始发飘。这篇文章记录的是我用禾赛32线激光雷达 Xsens MTi-G-710 组合导航模块 松灵 Scout mini 底盘从零开始搭建 Cartographer 3D 建图系统的完整过程。内容包括硬件选型逻辑、驱动接入、时间同步、外参标定、Cartographer 3D 配置、具体建图流程以及我在实测中踩过的坑。适合正在做户外移动机器人、巡检机器人、测绘建图项目的工程师参考。1. 这套硬件组合的选型逻辑每个部件在图里扮演什么角色1.1 禾赛32线雷达3D建图对激光雷达的核心要求先说激光雷达。Cartographer 做 3D 建图输入的核心数据是三维点云而点云的质量直接决定后端匹配的稳定性。我在选型时主要看三个指标探测距离、垂直视场角和点频。禾赛32线雷达在这三个指标上比较均衡。以 Pandar 系列为例典型的32线型号探测距离在120米到200米之间近距离盲区控制在 0.3 到 0.5 米以内垂直视场角能到 30 度以上单回波模式下点频可以跑到 30 万点每秒以上。这个数据量级对 Cartographer 来说比较合适——既不会因为点云太稀导致匹配退化也不会因为点云过密导致CPU扛不住。相比 16 线雷达32线在竖直方向上对墙面、树木、车辆等目标的轮廓刻画明显更细腻建出来的地图在垂直结构上不会出现明显的“条纹感”。相比 64 线和 128 线32线的价格和点云处理压力又低很多对一台中小型底盘来说是性价比最合适的一档。雷达的安装位置也是个有讲究的事。我最终把雷达装在底盘顶部的正中央用一块铝板加四个减震柱固定雷达底平面尽量和底盘水平面平行。这一步看似简单实际上直接影响后续外参标定的难度——如果雷达安装时就有明显的俯仰角标定出来的外参会变得非常敏感稍微有一点测量误差建图远处的点就会偏出去很大一块。还有一个容易忽略的细节是雷达的朝向。机械式雷达自带一个正方向标记通常是一条朝向标记线或者一个箭头。这个方向在驱动配置里对应到点云的零角度位置底盘向前、雷达零角度向正前方的安装方式最直观也最容易在做坐标变换时排查问题。1.2 MTi-G-710 组合导航模块选型背后的原因Cartographer 做3D建图姿态数据比位置数据更关键。3D匹配需要知道传感器当前的重力方向也就是滚动和俯仰角否则激光扫描匹配的时候一次小的角速度误差就会让后续优化彻底跑偏。这就是为什么 Cartographer 3D 配置里强制要求有 IMU 数据流。但单独用 IMU 有一个绕不开的天生缺陷——陀螺仪会漂移时间一长姿态就会慢慢偏离真实方向。XSens MTi-G-710 的价值在于它把 IMU 和 GNSS 接收机整合在一起内部做了融合既能输出高频的原始惯性数据加速度、角速度又能输出经过融合的绝对姿态角还能在户外环境下不断用卫星定位修正累计误差。MTi-G-710 输出的是欧拉角、四元数、位置、速度这套完整的状态量。对 Cartographer 来说它实际上只需要 IMU 的原始加速度计和陀螺仪数据因为 Cartographer 有自己的一套因子图优化会自行处理姿态预测。但组合导航模块带来的好处是即使雷达点云匹配暂时退化GNSS 信息也能让平台保持一个相对可靠的姿态基准为后面恢复匹配提供更好的初值。安装方面这个模块对位置有一定要求——尽量靠近底盘的几何中心和激光雷达在竖直方向上尽量重叠或者至少保持在同一竖直轴上。这样做可以减小杆臂效应带来的运动估计误差。我用一块双面胶加扎带把它固定在底盘甲板上方向尽量朝前Z轴朝上保证和ROS的坐标定义一致。1.3 松灵 Scout mini 底盘为什么选它作为移动平台Scout mini 本身不带任何传感器它是一个纯粹的移动底盘但它是整套系统里“让地图活起来”的关键。底盘负责按设定轨迹运动而 SLAM 系统把运动过程中的点云和姿态数据逐步拼接成地图。选 Scout mini 主要是看中了三点。一是差速四驱结构越野能力和通过性比普通两驱底盘强很多户外建图经常要过草地、小坡、碎石路差速四驱能保证在打滑不太严重的情况下维持可靠运动。二是它提供了完整的 ROS 驱动接口底盘里程计、速度指令、电池状态都能直接订阅省去自己写各种底层协议的麻烦。三是负载能力和空间设计合理顶部甲板有足够大的平面来安装雷达、IMU、工控机和电源系统。底盘和工控机之间的通信我用的 CAN 转 USB 模块。松灵官方提供了 scout_sdk 和 ROS 节点只需要按文档设置好串口权限就能直接在 ROS 里发布 /cmd_vel 话题来控制底盘运动同时读取 /odom 里程计。里程计数据在 Cartographer 里不是核心输入但越准确前端扫描匹配的初值就越好前端匹配的成功率就越高。2. 上车前的三个基础工程驱动接入、统一时钟、外参标定2.1 驱动接入从数据源头开始逐个验证任何传感器在接进 Cartographer 之前第一步都是先把驱动跑通验证原始数据。这一步我习惯逐个来不会同时打开所有传感器。禾赛的 ROS 驱动是官方维护的启动后会在 /points_raw 话题上发布原始点云话题类型是 sensor_msgs/PointCloud2。我建议先开 RViz 看一下点云的基本形态转一转视角确认雷达周围的目标物轮廓正常。同时用一个里程计数据很小的地方多次旋转雷达检查点云是否有空洞、错位或者强度值异常。Xsens 模块的驱动比较特殊因为它通过串口输出二进制数据需要配置串口波特率和数据格式。MTi-G-710 默认波特率一般是 115200驱动软件包里会自动识别设备型号。启动后可以订阅 /imu/data 和 /imu/data_raw看看加速度和角速度是否在正常范围。静止状态下加速度计的模长应该接近 9.8角速度接近 0。如果数值偏差大检查模块是否固定好或者是否受到电机震动干扰。Scout mini 的驱动则是走 CAN 总线启动驱动前需要确认 CAN-USB 模块的接口号并且给串口设备赋予权限。驱动成功后 /odom 会以 50Hz 左右的频率发布里程计数据同时在终端里能看到底盘的电池电压、驱动模式等状态信息。我习惯在底盘悬空的状态下测试速度指令先发一个很短的 /cmd_vel确认左右轮转向方向和指令一致免得后面真跑起来才发现方向反了。2.2 时间同步3D激光SLAM最容易翻车的地方把三个传感器的话题都跑出来之后接进 Cartographer 之前必须处理时间戳对齐。Cartographer 在融合数据时会对传感器数据的时间戳做插值和缓冲时间戳系统和实际物理时间偏差太大会导致匹配时出现“跳变”表现为点云撕裂、轨迹突然横移。这里有两条路可以走。简单的方式是用 ROS 自带的 ApproximateTimeSynchronizer把点云和 IMU 的话题按时间接近程度做同步。但这种方式只适合验证流程长时间建图时并不可靠因为不同传感器的时间戳来源不同累计延迟会不断变化。更稳妥的做法是统一时钟源。我的做法是用 Time Synchronizer 的思路去理解问题但实际让所有传感器都基于同一台工控机的系统时间打时间戳。具体操作是先在工控机上用 NTP 或者手动同步保证系统时间准确然后把禾赛雷达驱动和 Xsens 驱动的时间戳都置为接收时间而不是传感器内部估算的时间。这里要特别强调一个细节Xsens 模块输出数据时自带的是模块内部估计的时间戳和主机系统时间不是同一个基准。如果你的驱动直接用这个时间戳而雷达点云时间戳用的是系统时间两路数据在时间轴上会有不可预知的偏移。我处理的办法是在 Xsens 驱动里把时间戳手动替换为当前系统时间模块数据到达主机时以系统时间作为该帧时间戳。激光雷达也一样——帧数据完整到达后用主机的当前时间作为该帧的接收时间戳开启 range 时间同步模式。时间统一之后可以在 RViz 里做一个简单验证把一辆车或者一个人来回走动同时看点云和固定坐标系的位置关系。如果时间同步没问题运动目标在点云里不会出现拖影或重影如果有问题目标物靠近和远离时点云会明显错开。2.3 外参标定手工测量与工具标定的取舍外参标定定义的是雷达坐标系和 IMU 坐标系之间的固定相对位姿Cartographer 3D 建图时必须知道雷达测量的每个点相对于 IMU 的位置和姿态。外参误差超过 1 度或者 1 厘米远距离点的位置误差会被放大建图范围越大越明显。我最初的方案是手工测量。用游标卡尺和角度尺量出雷达相对 IMU 的安装距离再用水平尺确认两者的水平度。这个方法对安装精度要求高的场景不够用。实测中我发现即使是 2 度的俯仰角误差在 20 米外就会产生约 0.7 米的高度偏差也就是点云会把地面抬起来或者沉下去。所以后来我换成了工具标定。如果场景允许可以录制一段点到云和 IMU 的 bag用 lidar_align 进行外参优化。这个工具的核心思路是让点云和 IMU 积分出来的轨迹做配准迭代得到最优外参。如果你的雷达能扫描到足够丰富的场景特征标定效果非常稳定。在实际没有合适场景的时候我还有一个“半个手工”技巧把雷达和 IMU 的安装底座设计成可微调的先用水平尺粗调之后在建图时观察地面点云是否平直。地面点云在一个平面的高度附近波动说明外参基本准确如果地面点云一边高一边低说明存在固定的俯仰或者翻滚角度偏移根据偏移方向反推调整角度。这个方法不严谨但对快速排查问题很有用能节省大量时间。3. Cartographer 3D 建图的配置心得关键参数与坐标体系3.1 Cartographer 3D 建图的基本原理Cartographer 的 3D 建图核心思路可以拆成三个环节前端扫描匹配、子图构建、后端闭环优化。前端持续接收点云和 IMU 数据根据 IMU 重力方向做一个预对齐然后和当前活动子图做匹配得到两帧点云之间的相对位移和姿态。匹配出的位姿会不断拼接成子图一个子图累积到一定数量的扫描帧后就会固定下来。后端则在后台持续检测当前帧和以前所有子图之间的关系一旦发现重合区域就会出现闭环约束后端优化会调整所有相关位姿来消除累积漂移。这里面有一个很容易误解的地方Cartographer 3D 对 IMU 的依赖度相当高。前端匹配如果只是两帧纯点云之间的刚性配准在没有明显几何特征的场景比如空旷场地很容易退化。IMU 的作用就是提供重力方向和角速度积分让匹配初值足够准把退化问题降到最低。这也是为什么组合导航模块在这个系统里不是“锦上添花”而是“标配”。3.2 关键参数配置详解Cartographer 的配置我建议直接用官方提供的 lua 配置文件作为起点不要从零写。官方配置里有几个参数对建图质量影响极大我逐个说一下自己的理解。一个是 TRAJECTORY_BUILDER_3D.voxel_filter_size。这个参数决定了点云预处理时的体素滤波尺寸。值太大点云被大量降采样细节丢失匹配精度下降值太小点云数量大CPU计算量大实时性受影响。我的平台点频在 30 万点每秒左右voxel_filter_size 设为 0.05 比较合适能保留足够细节工控机 CPU 也不会满载。另一个是 TRAJECTORY_BUILDER_3D.max_range。这个值必须根据雷达实际探测距离和建图场景设定。如果设置得太远远处低置信度的点会引入大量误差太近则图覆盖范围受限。我通常设为 60 米到 80 米既覆盖建图范围又避免把远处树干、天空的噪点纳入匹配。还有 CERES_SCAN_MATCHER.translation_weight 和 rotation_weight这两个权重决定了扫描匹配时平移误差和旋转误差在代价函数里的相对权重。对带有 IMU 的 3D 建图来说旋转方向已经有 IMU 预积分信息约束所以我把 rotation_weight 适当调低增大 translation_weight让匹配算法更专注于精确估计位移变化。这个调参没有绝对公式只能靠实验对比建图效果来确定。POSE_GRAPH.optimize_every_n_nodes 可以控制后端优化的频率。建图初期子图数量少可以设小一点让优化频繁触发累积到几十个子图之后如果 CPU 压力大就适当调大。我这里设的是 30。完整关键参数对照表如下参数名作用我的最终设置voxel_filter_size点云体素降采样尺寸0.05max_range参与匹配的最大点云距离60.0min_range参与匹配的最小点云距离1.0translation_weight平移匹配权重10.0rotation_weight旋转匹配权重1.0optimize_every_n_nodes后端优化的触发频率30num_submaps_to_keep保留的子图数量53.3 坐标体系的衔接map、odom 与 imu 坐标系的关系Cartographer 建图时有多个坐标系需要理清楚不然折腾半天都不知道地图为什么歪。首先是 map 坐标系这是最终建图的世界坐标系固定不动。然后是 odom 坐标系它是建图过程中底盘运动的参考坐标系会随着时间累积漂移。Cartographer 输出的是从 odom 到 map 的变换关系地理上就是整个地图的历史修正量。再往下是底盘 base_link 坐标系也就是底盘的物理中心最后是 imu 坐标系和 laser 坐标系。我的实现里最关键的坐标系变换是 imu→base_link 和 laser→base_link这两个变换需要在外参标定阶段得到。Cartographer 3D 在构建地图时它关心的是激光坐标系和 IMU 坐标系之间的相对姿态而不是它们相对于底盘的位置。在写配置时需要把 laser 坐标系定义为以激光雷达实际位置为原点的坐标系然后通过静态坐标变换发布 laser→base_link 和 imu→base_link 关系。这里有个经验如果配置后建图时地图看起来是水平的但每隔一段时间会出现缓慢的整体倾斜大概率是 imu 和 laser 之间外参的旋转部分不准而不是建图失败。4. 一条完整的建图流程从底盘启动到点云地图落地4.1 硬件连接和节点启动顺序实际操作时我的启动顺序是固定的基本不会乱先把底盘放在相对平整的户外场地上打开底盘电源确认遥控器能控制底盘。这个流程建议不要省能第一时间排除底盘故障。启动工控机用网线连接雷达确认雷达扫描正常。启动 Ros master 和 udev 设备权限设置。启动禾赛驱动roslaunch hesai_lidar hesai_lidar.launch确认 /points_raw 有数据。启动 Xsens 驱动roslaunch xsens_driver xsens_driver.launch确认 /imu/data_raw 有数据。启动 Scout mini 驱动确认 /odom 和 /cmd_vel 正常。发布外参静态变换rosrun tf2_ros static_transform_publisher按标定结果发布 imu→base_link 和 laser→base_link。启动 Cartographerroslaunch cartographer_ros my_3d_mapping.launch。打开 RViz订阅 /map、/submap_list 和 /points_raw确认建图正常运行。这个顺序的核心逻辑是先确保底层传感器数据可靠再启动算法层。算法层一旦启动它会立刻开始缓存和分析点云和IMU数据如果底层数据是乱的后边查起问题来会很痛苦。4.2 移动建图时的注意点建图不是启动完节点就完事了。底盘移动时的操控方式直接影响建图质量。我第一次实测时犯过一个错误——让底盘走了几个直线大回转回到出发点后发现地图闭合没有问题但中间墙壁出现了一段明显的偏移。后来排查发现这和我直线行驶速度过快有关。底盘在 1.5 米每秒的速度下直线行驶雷达点云的帧间位移很容易超过 0.3 米Cartographer 的扫描匹配在这种帧间位移下匹配残差变大漂移累积速度变快。正确的移动策略是这样建图时让底盘速度保持在 0.5 米每秒左右转弯时尽量放慢多用原地小转弯避免高速大半径转弯。遇到交叉路口或者重复路段先停一下让后端有足够时间处理闭环再继续前进。整个过程看起来像在“迷宫探索”节奏越慢地图质量越稳定。在环境上也要有所选择。第一次建图尽量找一些有明显几何特征的区域比如建筑外墙、树行、雕像、路灯杆这些都是扫描匹配最喜欢的特征。空旷广场虽然也能建图但纯粹依赖 IMU 约束一旦 IMU 偏差稍大后半程的图就会慢慢“飘”。4.3 地图保存与转换建图结束后Cartographer 的输出是一坨二进制格式的 .pbstream它包含了所有的轨迹、子图和位姿信息但这个格式不是最终可用的点云地图需要转换。保存 .pbstream 的命令是rosservice call /finish_trajectory 0 rosservice call /write_state {filename: /home/user/maps/outdoor_map.pbstream}然后把它转换成可用的点云格式rosrun cartographer_ros cartographer_pbstream_to_assets_writer \ -pbstream_filename outdoor_map.pbstream \ -map_filestem outdoor_map \ -output_type ply这里会得到一个 outdoor_map.ply 文件可以用 CloudCompare 或者 MeshLab 打开查看。如果想输出 PCD 格式给后续点云处理流水线用用-output_type pcd即可RF 用 想输出 xyz 文本格式用-output_type xyz。要注意.ply文件里默认包含的是所有子图的累积点云数量可能很大。打开后如果有大片噪点或者“雾状”的稀疏点可以先用体素滤波或者统计滤波器清理一遍再从干净点云里导出需要的楼道、坡道等区域。5. 实测中踩过的坑数据不同步、点云畸变和里程计跳变5.1 点云出现“拖影”和“重影”——时间戳惹的祸在第一次室外建图测试中我停在原地不动时地图看起来没问题但底盘一行驶周边点云就出现“拖影”树和路灯杆变得比实际粗一倍多。这个问题本质上是点云帧内畸变和帧间错位叠加导致的。排查过程从时间戳开始。我打开 rosbag 记录了一段数据然后用 rosbag 的插件检查各话题的时间戳差异发现 /points_raw 和 /imu/data_raw 的时间戳相差约 40 毫秒。这个差异不算小在 Cartographer 里点云数据和 IMU 数据的时间戳不匹配会造成一帧点云内不同点被分配在不同的位姿上表现出来的就是“拖影”。解决办法是前面说的——把所有传感器的时间戳统一为主机系统时间同时在雷达驱动里把点云帧的时间补偿打开。禾赛驱动里有时间戳模式的选项选择接收时打时间戳这样每帧点云里的点都共享同一个时间基准。修复之后同样的场景重新建图拖影基本消失树木轮廓恢复干净。这次排查让我意识到设备多的时候时间同步必须放在第一步而不是出现问题后再补做。5.2 地面点云“中间鼓起来”——IMU 重力方向校准不准另一个比较隐蔽的问题建图完成后地面点云并不是一个完美的平面中间区域稍微鼓起像被人从下方顶了一下。这个问题的根源在 IMU 的重力对齐。Cartographer 3D 构建姿态预测时会把 IMU 的加速度计读数作为重力方向参考。如果 IMU 在底盘上的安装平面并不完全水平或者驱动里提供的初始姿态不准确重力方向的基准就会偏一点最终地图整体出现微小的卷曲。我当时的排查思路是这样先把底盘停在一个已知水平的地面上检查 IMU 静止数据发现加速度计的 X、Y 轴读数并不是 0而是有一个 0.2 左右的偏置。这说明 IMU 本身的安装存在约 1 度的仰角。修复方法是直接在 Cartographer 配置里做 IMU 重力对齐的补偿具体是在 lua 配置文件的 TRAJECTORY_BUILDER_3D.imu_gravity_time_constant 等参数之外把 IMU 外参的旋转标定得更精确。同时在机械层面把 IMU 底座的水平度重新调整让加速度计在静止时尽量接近 X0、Y0、Z9.8。5.3 底盘遇到草地时里程计跳变前端匹配连续失败Scout mini 的里程计在水泥地上表现不错但一旦压上草皮轮子打滑会导致里程计数据短暂跳变。跳变发生时Cartographer 的前端匹配会突然出现一段轨迹横移然后要花很长时间才能恢复。之所以会这样是因为 Cartographer 虽然不直接用里程计做核心匹配但会把里程计作为 NORMALIZE 之后的宇称信息加入约束。里程计一旦跳变这些约束会让优化结果偏向一个错误的方向。我的应对措施是在建图过程中尽量避开草皮等低附着力路面如果实在绕不开就降低建图速度并且在 lua 配置里把 odometry 的权重调低。具体参数是 TRAJECTORY_BUILDER_3D.odometry_translation_weight 和 odometry_rotation_weight我把它们从默认值调小到原来的三分之一。这样即使里程计有短暂跳变对全局优化的冲击也会小很多。不过调低权重也会带来副作用在没有闭环的走廊、隧道等长直区域误差会慢慢累积。所以实际使用中我只在建图环境有较多闭环触发点时才调低权重环境比较开放时还是保留默认值更稳。5.4 工控机 CPU 过高导致建图卡顿最后提一个不算技术难题但很折磨人的问题算力不足。Cartographer 3D 的 CPU 占用明显比 2D 高一个量级特别是后端优化触发频繁时工控机如果配置不够建图频率会掉到很低的水平点云匹配质量也跟着下降。我用的工控机是 i7 处理器 16G 内存跑 30 万点每秒的 3D 建图CPU 占用长时间维持在 70% 左右。如果点云频率再高一点比如雷达配到 20HzCPU 会直接打满。降低算力压力的方法有几个一是把雷达频率降到 10Hzvoxel_filter_size 调大到 0.06二是降低后端优化频率把 optimize_every_n_nodes 调到 50三是关闭振幅信息不保留点云强度数据。这些操作对建图质量的影响相对有限但能明显缓解 CPU 压力。如果你的场景没有非常精细的垂直结构需求建议优先采用。6. 一些个人体会和后续可扩展的方向做完这套系统之后我最大的感受是3D 建图的难度不在算法而在工程。Cartographer 本身的配置文档非常详细但把雷达、IMU、底盘、工控机、时间同步、外参标定这些环节串联起来的经验值是文档里不会写的。每一个环节单独拿出来都不复杂但组合在一起任何一个细节出问题最终都在地图上体现得非常清楚。分享一个小技巧建图时千万不要一开始就跑全流程。先做一次“静态启动”——底盘不动原地让雷达转几十秒确认子图构建正常、IMU 姿态收敛稳定然后再开始移动。这一步能提前暴露时间同步和外参的大部分问题避免带着错误配置跑完整个场地最后得到一张没法用的地图。后续如果想在这套基础上升级可以考虑三个方向。一是把 GNSS 信息以里程计或者定位约束的形式接进 Cartographer让建图和实时定位在更大的户外环境里保持稳定二是加入多个回环区域的自动识别提高闭环检测效率三是在底盘上接入视觉相机和激光点云做视觉-激光融合进一步提升建图精度和应对动态障碍物干扰的能力。这套组合目前已经在户外园区完成了多个场景的 3D 点云地图采集单次建图面积在 20000 平方米以上地图像素比可达 5 厘米级基本满足巡检和测绘需求。如果你手头有类似的底盘和传感器组合希望这篇文章能帮你少走一些弯路。
返回列表