ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

激光SLAM与三维占据栅格地图实时构建:从原理到工程实践

激光SLAM与三维占据栅格地图实时构建:从原理到工程实践 写这篇激光SLAM与三维占据栅格地图构建的文章先把一个真实场景放在前面。三年前我在测试一台搭载16线激光雷达的移动底盘任务很简单在室内走廊走一圈实时生成一张三维占据栅格地图。前30秒一切正常屏幕上点云像积木一样一块块拼出来。到了转角地图开始出现重影定位坐标开始小幅跳变。再往前走几米整张地图完全散架程序还在跑但地图已经没法看了。这个场景是我见过的最典型的激光SLAM失败现场。问题不在“建图”本身而是一个更隐蔽的耦合关系定位一旦飘地图就跟着崩地图一旦崩定位就更找不到参照。所以这篇文章想讲清楚一件事激光SLAM与三维占据栅格地图实时构建真正考验的不是某个算法有多强而是你能不能把传感器输入、位姿估计、地图更新和计算资源这几条线同时稳住。单次跑通很容易稳定跑完很难。1. 先搞清楚激光SLAM解决的是哪类问题不是“建图”而是“边走路边回答我在哪”很多人第一次接触激光SLAM会把注意力放在“地图”上。这个方向不能说错但容易让人误解。激光SLAM的完整表述是“同步定位与建图”它要同时做两件事估计传感器在世界坐标系里的位姿同时用观测到的激光点更新地图。位姿是地图的前提地图是位姿的参照。这两个问题互相依赖形成一个循环。这也是SLAM和单纯建图的本质区别。如果机器人位置已知那建图就是简单的点云拼合如果地图已知那定位就是配准和一帧点云对齐到已有地图。难点恰恰在于两者都未知。1.1 定位与建图的耦合关系才是问题的核心在激光SLAM里假设你拿到一帧新的三维点云。要把它加入地图你必须知道这帧点云是在哪里采的。这里的“在哪里”不是一个大概位置而是一个足够精确的六自由度位姿三个位置增量加三个旋转角。方位角稍有偏差远处几米外的点就会在空间里偏移几十厘米。你用这样一帧点在已有地图里做匹配匹配结果自然被带偏。下一帧继续错地图彻底发散。这正是“实时构建”和“离线拼接”最大的不同。离线建图可以把所有点云收集起来用全局优化算法慢慢对齐。实时构建没有这个条件每一帧必须在下一帧到达之前给出位姿估计否则地图更新就会积压延迟越来越大最终程序卡死或内存溢出。所以激光SLAM主流程的三个环节传感器数据预处理、帧到地图的配准、位姿图优化本质上都是在做同一件事——尽量让当前帧的位姿估计不产生累计漂移。占据栅格地图只是这个循环最后呈现出来的结果。1.2 为什么这个循环过去很难做好早期SLAM使用扩展卡尔曼滤波把机器人位姿和地图特征放进一个状态向量实时更新。这个思路在小场景、低噪声环境下可行。一旦环境变大、特征变多状态向量的维度会急剧膨胀计算量按平方级增长实际没法跑。后来粒子滤波被引入Gmapping这类2D方案曾经统治了很长时间。粒子滤波用一群粒子代表机器人可能的位置每个粒子维护一张地图最后用权重筛选粒子。这种方法在走廊、小房间这类场景精度很好但粒子数量和环境复杂度直接相关环境一大粒子数量就爆炸。这是2D时代的经典矛盾定位越准地图越清晰地图越清晰维护成本越高。3D激光SLAM把问题搬到了空间里维度从两维变成六维这个矛盾被放大了。1.3 当前的主流方案在做什么图优化方案是现在的主流方向。Cartographer和LOAM系方案都属于这个路线。它们放弃了“实时维护一张全局地图”的思路改为维护一个位姿图。图中每个节点是一帧激光数据对应的位姿边是相邻帧或回环检测给出的相对约束。后端定期做图优化把累计漂移分散到整个图里再生成更新后的地图。这个思路的关键是回环检测。机器人回到曾经来过的位置算法把当前点云和历史地图做匹配一旦认出来就形成一个长距离的边把漂移拉回来。没有回环的话图优化改善有限。这也是为什么很多SLAM评测环境都喜欢带闭环因为闭环直接决定最终地图能不能收敛。对工程实践来说这意味着你在规划测试路线时一定要设计回环轨迹。直线走到底再原路返回比绕一个环形更容易产生漂移因为前者的回环机会少。2. 三维占据栅格地图是怎么“记住”环境的点云地图和占据栅格地图是两种完全不同的产物。点云地图直接保存所有激光点直观、精确但无法表达“这个区域是不是真的没有障碍物”。占据栅格地图把空间划分成一个个小立方体每个立方体记录一个状态占用、空闲或未知。为什么需要这种区分因为机器人导航不能只靠“哪里有障碍物”还得知道“哪里可以走”。激光雷达只能测到物体表面的点物体背后的区域、测量不到的区域在点云地图里都是空白。这个空白到底是“可以通过”还是“未知”点云地图没法回答。占据栅格地图用概率状态解决了这个问题。2.1 体素的概率状态不是一次写死的三维占据栅格地图的最小单元通常叫体素。每个体素存储的不是一个0或1的标签而是一个概率值代表这个格子被障碍物占据的可能性。雷达光束扫到一个点这个点所在的体素占用概率增加光束从这个点到传感器之间穿过的所有体素占用概率减少。这个机制对应的是激光测距的基本物理事实光沿直线传播中间必须是空的末端必须有什么东西挡了一下。用概率而不是直接标记好处是可以用多帧观测来修正单帧噪声。一帧点云可能因为材质反光、边缘效应产生杂点但多次观测里真实的障碍物会被反复确认噪声点只有一个方向的支持概率值会被慢慢压下去。这也是为什么直接用点云降采样做地图容易出现“鬼影墙”而占据栅格地图会更平滑。2.2 体素化决定了地图的精度和内存边界这里的核心参数是体素大小。常见开源方案里室内场景一般用0.05米到0.2米之间的体素。体素越小细节越丰富但内存和计算量会呈立方级增长。一个10米 × 10米 × 3米的空间用0.1米体素划分会产生30万个候选体素用0.05米体素就是240万个。这还没算哈希表管理和更新频率。所以三维占据栅格地图不是一个“分辨率越高越好”的问题而是你必须在精度和资源之间找一个平衡点。我的建议是先确定作业场景的最小障碍物尺寸再倒推体素大小。比如AGV要避开直径5厘米的障碍物体素就不能粗于2.5厘米到3厘米如果只是做建筑结构扫描0.2米体素已经足够因为墙体内部细节对导航没有意义。注意体素分辨率不是一个可以随意调的参数。改小之前先估算一下环境范围和环境特征密度。很多实时构建卡顿不是算法问题而是体素开得太细内存和CPU顶不住。2.3 地图更新策略决定实时性实时构建不只是“每帧都更新地图”更重要的是“怎么更新”。最直接的方法是每个体素独立判断光束命中或穿过但这需要为每一条激光射线遍历路径上的每一个体素。一帧16线雷达大约3万个点如果体素很细这个遍历量会非常大。实际系统中的优化包括用体素哈希表而不是预分配全部空间只保存被观测过的体素。使用射线遍历算法比如3D DDA或Bresenham快速定位光束路径上的体素。定期把短期高频更新的体素合并到全局地图而不是每帧全量更新。对超过最大距离的激光点做截断或降权避免远距离观测的不确定性污染地图。这些优化不会改变占据栅格地图的理论模型但它们决定了系统能不能在实车上跑起来。如果你在调一个开源SLAM发现CPU占用异常高先检查是不是每次配准完都对全图做了重投影。3. 实时构建的真正难点不是算法而是三笔账很多人以为实时构建难在数学。调试久了你会发现数学问题大多是稳定的难的是资源问题。实时构建本质上是三笔账时间、内存、精度。这三者互相牵制任何一个短了系统都会出问题。3.1 时间账每帧必须在下一帧到来前算完激光雷达按固定频率输出。16线雷达通常是10赫兹这意味着每100毫秒必须完成一帧的位姿估计、配准和地图更新。如果你的算法处理一帧需要120毫秒帧率赶不上输入构建速度就会越来越慢。表面上看是“程序还活着”实际上地图的实时性已经失效了。这里常见的做法是引入前端和后端的分离。前端用快速配准算法立刻估计位姿保证帧率后端用图优化慢慢修正全局误差不阻塞实时管线。这也是Cartographer架构的核心。理解了这个分离你就知道为什么很多SLAM代码里会看到两个线程一个高频处理匹配一个低频做优化。在设计自己的管线时有两件事很重要给前端配准预留的时间要足够保守不能卡在极限上。地图更新和后端优化要放在独立线程不能和前端配准共用锁。3.2 内存账体素数会随运行时间无限增长如果说时间账还容易通过线程设计解决内存账就是更隐蔽的坑。点云转成占据栅格地图后体素会随着探索范围扩大而增加。大场景连续走30分钟体素数量很容易达到百万级别。如果每个体素保存完整的状态包括概率值、最近更新时间、锁等信息内存占用就非常可观。Map结构如果设计不当还会出现内存碎片和哈希冲突增加的问题。常见的对策包括定期清理“从未被更新”的体素。设置地图范围上限超出范围的观测丢弃或存档。使用自定义内存池避免高频创建和销毁体素。把占据概率压缩到更小的数据类型避免浮点存储。3.3 精度账所有实时方案都会在某个环节丢精度实时构建不能保证每一帧都绝对精确。为了赶时间前端匹配可能只迭代几次就收敛为了控制内存体素分辨率不可能无限小为了抑制噪声地图更新会做概率平滑。这些都会让最终地图和真实环境有偏差。关键是要控制偏差的类型。随机噪声可以通过多次观测平滑系统性偏差比如外参标定错误造成的固定偏移会一直存在而且会让地图产生“重影”或“双层墙”。调试SLAM时如果发现同样的错误在每一帧都出现优先怀疑外参而不是算法参数。4. 从2D SLAM到3D SLAM选型不是越高级越好热搜里常见的“2D SLAM有哪些”“激光雷达做hector_slam”说明很多人还在2D SLAM和3D SLAM的分叉路口。这里我给出一个比较稳定的判断2D SLAM和3D SLAM不是简单的新旧关系而是两类不同硬件、不同场景、不同成本的选择。4.1 常见的2D方案与三维方案对比2D时代最具代表性的方案是Gmapping、Hector SLAM和Cartographer的2D模式。它们有一个共同前提机器人的运动被约束在平面上高度方向可以忽略。这意味着激光雷达基本都是单线的扫描结果是一个平面截面。机器人位置只有三个自由度x、y、偏航角。这个简化让问题变得容易很多。3D SLAM则使用多线激光雷达或固态激光雷达点云覆盖了整个前方空间位姿估计需要六个自由度。代表方案包括LOAM系、LIO-SAM、Cartographer的3D模式以及一些基于NDT配准的商用方案。维度2D SLAM3D SLAM传感器单线激光雷达多线激光雷达、固态激光雷达自由度3个x、y、yaw6个x、y、z、roll、pitch、yaw地图形式2D占据栅格图3D占据栅格/点云/TSDF计算开销较低普通工控机可跑较高通常需要GPU或高性能CPU典型场景扫地机器人、AGV、室内导航自动驾驶、无人机、建筑扫描典型开源方案Gmapping、Hector、Cartographer 2DLOAM、LIO-SAM、Cartographer 3D从这张表可以得出一个结论如果你的应用场景真的只有平面运动用3D方案不一定更好。3D方案要处理的信息量大调参复杂地图文件也大。反而是2D SLAM在室内小场景下更稳定、更成熟。4.2 我建议的选型逻辑先问场景再问硬件最后问算法看到很多读者纠结“学哪个SLAM更好”。我的建议是反过来的先明确你要让机器人做什么。如果是室内导航和避障2D SLAM往往是第一选择因为计算简单、地图数据量小、定位稳定。如果是AGV在工厂环境下走固定路线2D SLAM可以满足绝大多数需求。如果需要跨楼层、爬坡、无人机或室外作业才需要考虑3D SLAM。如果连传感器都还没有不要先定算法。先选激光雷达再选方案。有很多人用单线激光雷达硬跑三维SLAM这是不现实的。单线雷达只有一平面光束无法感知高度方向结构三维重建的几何信息严重不足。当然也有用单线雷达加IMU或里程计做2.5D重建的研究但离稳定生产还有距离。4.3 贝叶斯定位和粒子滤波定位在实际工程中怎么选上面提到过贝叶斯定位和粒子滤波定位这两个概念经常出现在SLAM的讨论里。贝叶斯定位是一个广义框架把定位看成基于历史观测和运动模型估计状态的后验概率问题。粒子滤波是贝叶斯滤波的一种实现方式用一组带权重的采样粒子近似后验分布。在3D激光SLAM里图优化是主流但粒子滤波的思想并没有退出。许多方案在定位阶段仍然使用粒子滤波来初始化位姿或处理全局定位问题。区别在于粒子滤波擅长处理多峰分布适合全局定位和绑架问题。图优化擅长处理连续漂移适合实时配准和回环修正。实际系统中两者常混合使用粒子滤波负责启动阶段或重定位图优化负责日常位姿跟踪。如果你在看源码时遇到粒子滤波相关代码不要以为这是过时实现。它是全局定位的重要兜底手段。5. 一套最小可运行的落地流程从标定到地图更新理论讲完进入实操部分。下面这套流程是通用处理思路不是某个特定开源方案的官方教程。落地前需要基于实际依赖版本做微调。5.1 环境准备和坐标关系在启动SLAM之前必须完成两件事确认传感器能输出数据确认外参标定完成。激光雷达驱动装好后先订阅原始点云话题确认点云里有有效的三维坐标没有大量NaN值频率稳定。这一步用可视化工具观察点云时注意点云应该随着传感器移动而移动这是坐标系正常的基本表现。外参标定指的是求解激光雷达和机器人本体之间的相对位姿。许多实时构建异常尤其是地图重影和定位旋转飘根源都在外参未标定准确。常用的标定方法有手动测量加微调以及用标定板自动标定。如果ROS中使用的是静态变换可以直接在URDF或TF树里配置。坐标系关系至少要保证机器人本体坐标系base_link存在。激光雷达坐标系lidar_link通过固定变换关联到base_link。里程计坐标系或IMU坐标系能提供位姿先验。最终的地图坐标系map由SLAM算法维护。注意千万不要在没有任何运动先验的情况下直接跑3D SLAM。没有初始位姿实时配准会快速发散。移动机器人的轮式里程计或IMU预积分至少要有一个可用。5.2 最小可运行流程单帧、位姿、配准、地图更新我把一个最小系统拆成四个环节获取一帧点云做体素降采样和离群点滤除减少数据量提高匹配稳定性。使用初始位姿作为起点把当前帧点云与已有子图做配准。常见方法包括ICP和NDT。配准成功后得到新位姿把当前帧按新位姿投影到占据栅格体素空间更新每个体素的占用概率。定期检查位姿图的规模。如果累计帧数达到阈值或检测到回环就把约束发给后端做图优化。先别急着把这四步都做到完美。第一次实验先跑通前三步用一个很小的场景把地图生成出来。哪怕是10米×10米的房间只要能稳定闭环就已经证明管线通畅。5.3 关键参数理解体素、最大距离、更新阈值参数从哪里调起优先级最高的三个参数可以按下面的顺序调整参数作用常见问题体素分辨率决定体素大小和地图精度设置太小导致内存爆炸最大有效距离决定哪些远处的点参与地图更新设置太大引入远距噪声配准收敛阈值决定多近才算匹配成功设置太严导致配准失败比例升高最大有效距离这个参数经常被忽略。激光雷达远距离观测有较大噪声特别是低线束雷达远处点的垂直间距会非常大。比如一个20米外的点在相邻扫描线之间的间距可能达1米以上。这样的点直接用来更新占据栅格反而会把墙面画得很厚。常见做法是把最大距离限制在激光雷达稳定测距能力的一半左右。5.4 单任务验证怎么判断刚才的构建是否成功构建完成后先不要看地图漂不漂亮。按以下顺序确认地图闭合区域是否对齐走一圈回来起点和终点附近的地图边缘是否重叠。墙面是否连续单面墙不应该出现断层或双层。障碍物轮廓是否清晰小尺寸障碍物如果糊成一团说明分辨率不够或位姿飘了。定位轨迹是否平滑轨迹图不应该出现大量跳变点。如果这些检查都没问题再进入批量或自动运行阶段分批跑不同场景观察地图能否持续稳定。6. 最常见的问题链路定位旋转飘、地图重影和资源暴涨热搜里有一个词非常真实“cartographer定位时候旋转飘”。这几乎是所有激光SLAM使用者都会遇到的一类问题。我把常见问题按一个统一的排查链路拆开。6.1 先看现象不要急着调参数现象大致可以分为三类定位旋转飘机器人原地转向时地图坐标出现偏移或抖动。地图重影同一面墙在最终地图里出现两层轮廓。资源暴涨运行一段时间后内存和CPU占用持续上升最终卡死或延迟。这三类现象对应的原因链完全不同。先明确你遇到的是哪一类再进入下一步。6.2 再按输入、环境、参数、工具边界逐层排查第一层输入确认点云话题的帧率、时间戳、坐标系是否正确。ROS下常见的坑是时间戳同步问题一帧点云由于等待时间差传感器数据到达时已经滞后了一两个周期。检查方法是订阅原始点云看数据频率和雷达标称频率是否一致以及点云数据的framestring是否和外参配置一致。第二层环境检查机器人的运动场景是否适合当前方案。反向旋转过快、地面打滑、传感器视野被遮挡都会导致前端配准找不到足够约束。Cartographer这类方案在狭窄走廊里旋转经常因为周围特征是后退的无法准确估计旋转分量。这时候即使IMU也在提供姿态仍然会看到旋转飘移。第三层参数到了这层才考虑调整算法参数。对于旋转漂移优先检查前端匹配的收敛阈值是否过宽松。初始位姿估计是否有运动预测还是直接拿上一帧位姿当起点。回环检测是否开启如果关闭长距离累积漂移会快速反映在旋转上。第四层工具边界最后确认是不是方案本身的限制。部分定位算法只适合低速、平滑运动快速旋转或剧烈颠簸会导致配准失败。此时应该改用或增加传感器融合方案比如加入IMU预积分。6.3 针对旋转漂移的经典修复顺序如果确认是“旋转时飘”我的建议排查顺序是检查外参标定特别是激光雷达相对于IMU的旋转。检查IMU数据方向和频率确认IMU的yaw增量与真实旋转一致。降低旋转速度观察是否改善。增加前端配准的局部表面提取或平面特征提取。最后检查地图更新时是否用最新位姿更新而不是旧位姿。这个顺序是把最可能导致旋转误差的系统性原因放在前面算法调参放在后面。6.4 地图重影的常见原因重影通常有两种来源。第一种是漂移未回环位姿图还没收敛时地图就已经更新到全局旧位姿和新位姿叠加。第二种是外参不准同一个障碍物在不同角度看到的几何中心有偏差。区分方法是如果重影在运行一开始就有明显是外参如果重影是运行很久后才严重则偏向漂移和回环问题。6.5 资源暴涨的定位资源暴涨和参数强相关。最常见的是体素分辨率设置太小或地图范围不设上限。排查时先打开资源监控观察是CPU还是内存先爆。CPU先爆优先检查前端配准迭代次数内存先爆优先检查体素数和地图范围。7. 三维占据栅格地图的适用边界和长期工程化很多项目做完演示就没下文了原因不是SLAM跑不出来而是长期稳定运行需要补的东西太多。这里我把适用边界和工程化拼图分开说。7.1 适合什么不适合什么适合室内外中等规模场景激光雷达有清晰几何结构的环境建图目标是要用于导航或碰撞检测的项目。适合需要空间感知的移动机器人比如安防巡检、AGV、无人机避障。不适合空旷无结构的大平面比如开阔广场仅靠激光点云很难准确约束位姿必须配合IMU或里程计。不适合动态物体频繁的环境。占据栅格地图假设环境是静态的如果行人、车辆大量出现在建图过程中会产生大面积幽灵障碍物。动态物体过滤是必要的但这会增加额外实现成本。7.2 长期使用还缺哪几块拼图从实验环境走到生产环境需要补齐日志和状态监控SLAM算法跑着跑着飘了没有日志的情况下完全无法复盘。自动恢复机制当定位置信度下降到阈值系统要能自动重新定位而不是一直带着偏差跑。地图版本管理和更新长期运行的场景会变化地图需要定期更新和局部校正。异常数据隔离雷达潮湿、盖子脏污、电源波动都会产生异常点云这些需要在前处理阶段剔除。7.3 一个可以长期坚持的开发顺序如果你现在刚接触激光SLAM我的建议路径是先在仿真环境或开源数据集上跑通带标注的回放数据熟悉输入输出和坐标系。在真机上用小车跑小范围建图先解决外参、时间戳、运动模型这些工程问题。验证回环检测和地图质量。再加动态物体过滤、地图增量更新等高级模块。不要一上来就追求大场景、高精地图或复杂融合。激光SLAM是一个系统性工程每个环节都要稳定才能把能力叠加起来。回到一个更底层的判断和激光SLAM与三维占据栅格地图打了几年交道我最大的体会不是某个算法多精妙而是这类系统的边界几乎都在工程细节上。外参差一度地图重影帧率差毫秒定位飘移体素小一档内存爆炸。每个单独看起来都像是小问题但组合在一起就是稳定性和实时性的差距。如果你现在正要开始一个这类项目第一步不是去研究最新论文而是把传感器数据流、坐标变换、外参标定和日志监控做好。这三件事不做后面所有算法调优都是在沙子上面盖楼。建图的目标从来不是“养眼”而是让机器人长期在一个真实空间里知道自己在哪里、哪里可以去、哪里不能去。理解了这一点你就不会被某个炫酷的演示视频带偏方向。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进