第四章:环境感知与建图

各位同学,欢迎来到第四章。这一章我们聊点硬核的——机器人怎么“看”世界,又怎么把看到的东西记下来。

说白了,环境感知与建图就是给机器人装上一双眼睛和一张草稿纸。眼睛负责看,草稿纸负责画。我做了这么多年AMR,最深的感触就是:建图搞不定,后面所有规划都是空中楼阁。

4.1 栅格地图:机器人的“像素世界”

先问个问题:机器人怎么表示它周围的环境?

你可能会想到CAD图纸,或者三维点云。但实际工程中,最常用的反而是最简单的——栅格地图。

栅格地图,就是把环境切成一个个小格子。每个格子要么是空的(可通行),要么是占着的(障碍物)。就像像素画一样,格子越小,地图越精细,但计算量也越大。

关键参数:

  • 分辨率:每个格子代表的实际尺寸。我常用5cm或10cm。室内用5cm,室外用10cm,这是经验值。
  • 尺寸:地图的总大小。比如100m×100m,对应2000×2000个格子。
  • 坐标系:通常以机器人启动位置为原点。

栅格地图的好处是什么?简单、直观、好维护。我在项目里遇到过客户要求地图能实时更新,栅格地图改起来就特别方便——改一个格子就行,不用重建整个模型。

4.2 占据栅格地图构建

光有栅格还不够,我们得知道每个格子到底有没有东西。这就是占据栅格地图要做的事。

每个格子存一个概率值,0表示绝对空,1表示绝对占。初始状态是0.5——完全未知。

机器人每扫一次激光,就会更新这些概率。数学上用的是贝叶斯更新,但工程上我更喜欢用对数几率(log-odds)来算,避免概率值溢出。

// 伪代码:占据栅格更新
float log_odds = log(p / (1 - p));
// 激光命中:增加占据概率
log_odds += hit_increment;   // 比如 +0.85
// 激光穿过:减少占据概率  
log_odds += miss_increment;  // 比如 -0.40
// 转回概率
p = 1 - 1 / (1 + exp(log_odds));

我的经验:hit_increment和miss_increment的比值很关键。我一般设成2:1左右。太大容易产生噪点,太小更新太慢。你可以在仿真里调一调,找到手感。

嗯,这里要注意:激光不是完美的。它可能打到玻璃上透过去,也可能被黑胶带吸收。所以每次更新都要带点“怀疑”,别太相信单次测量。

4.3 SLAM概念:边看边画

SLAM,全称是Simultaneous Localization and Mapping。中文叫“同步定位与建图”。

这名字起得挺绕,其实道理很简单:

  • 机器人不知道自己在哪(定位问题)
  • 机器人也不知道周围长啥样(建图问题)
  • 两个问题互相依赖——不知道自己在哪就没法建图,没有图就没法定位

这就像你蒙着眼走进一个陌生房间,一边摸墙一边在脑子里画地图。你每走一步,都在同时更新“我在哪”和“墙在哪”。

SLAM的核心思想就是:用概率模型把这两个问题捆在一起解。说白了,就是“我猜我在这个位置,如果这个位置是对的,那地图应该长这样...嗯,跟传感器数据对上了,那我的猜测就是对的。”

避坑指南:我曾经在一个大仓库里做SLAM,结果跑了半小时地图全飘了。后来发现是地面太光滑,轮子打滑导致里程计误差累积。记住:SLAM对里程计质量要求很高,轮子打滑是头号杀手。

4.4 Gmapping算法原理

Gmapping是ROS里最经典的2D SLAM算法之一。它基于粒子滤波(Particle Filter),说白了就是用一堆“猜测”来逼近真实状态。

每个粒子代表一个可能的机器人位姿和地图。算法流程大致是:

  1. 初始化:撒一堆粒子,均匀分布在起始位置附近
  2. 预测:根据里程计数据,让每个粒子往前走一步
  3. 更新:用激光扫描数据,计算每个粒子的“靠谱程度”
  4. 重采样:淘汰不靠谱的粒子,复制靠谱的粒子
  5. 建图:用所有粒子的加权平均,更新栅格地图

Gmapping有个很聪明的优化:它不直接对全地图做粒子滤波,而是把定位和建图分开。定位用粒子滤波,建图用栅格更新。这样计算量小了很多。

Gmapping的优缺点:

优点 缺点
计算量小,适合嵌入式 不擅长闭环检测
对小场景效果好 大场景容易漂移
代码成熟,社区支持好 对激光频率要求高

我记得有一次在实验室里跑Gmapping,怎么调参数都建不好图。后来发现是激光雷达的帧率设得太低——10Hz以下基本没法用。建议至少20Hz,最好40Hz以上。

4.5 Cartographer算法简介

Cartographer是Google开源的一套SLAM方案。它比Gmapping更现代,解决了Gmapping的几个痛点。

核心思路是:用图优化(Graph Optimization)代替粒子滤波。什么意思呢?

  • 把机器人的每个位姿当成一个“节点”
  • 把传感器观测当成“边”
  • 整个问题变成:调整所有节点的位置,让所有边的约束最一致

这就像拼拼图。每块拼图(节点)的位置可以微调,但相邻拼图之间的图案(边)必须对齐。最终目标是整幅图看起来最自然。

Cartographer的另一个亮点是:它内置了闭环检测。当机器人回到之前去过的地方,算法会自动识别并修正累积误差。这一点Gmapping做不到。

// Cartographer配置片段(简化)
TRAJECTORY_BUILDER_2D {
  min_range = 0.3           // 最小测量距离
  max_range = 30.0          // 最大测量距离
  voxel_filter_size = 0.05  // 体素滤波大小
  use_online_correlative_scan_matching = true
  real_time_correlative_scan_matcher {
    linear_search_window = 0.1
    angular_search_window = math.rad(20.)
  }
}

我的建议:如果你做室内小场景(1000平米以内),Gmapping完全够用,还省资源。但如果是大场景、长廊、或者需要长时间运行,直接上Cartographer。别犹豫,我吃过亏。

Cartographer也有缺点:配置参数多,调起来费劲。我第一次用的时候,光调那个“submaps”参数就花了两天。但一旦调好,效果是真的稳。

知识体系总览

下面这张图是我自己画的,把本章的知识结构串起来了。你可以把它当成一张“地图”,随时回来对照。

环境感知与建图知识体系 栅格地图基础 SLAM核心概念 主流SLAM算法 分辨率 · 尺寸 · 坐标系 占据概率更新(贝叶斯) 对数几率(log-odds) 定位 vs 建图 耦合问题 概率模型 · 状态估计 闭环检测 · 误差累积 Gmapping:粒子滤波 Cartographer:图优化 闭环检测 · 子图匹配 实践要点:传感器选型 · 参数调优 · 场景适配 核心目标:让机器人在未知环境中,边定位边建图

这张图把本章内容分成了三大块:栅格地图是基础,SLAM是核心思想,Gmapping和Cartographer是具体实现。你顺着这个结构往下学,思路会清晰很多。

好了,这一章的内容就到这里。环境感知与建图是AMR的“眼睛”和“记忆”,后面所有路径规划、避障、导航都依赖它。建议你动手跑一跑Gmapping和Cartographer的demo,亲手调一次参数,比看十遍文章都管用。


公众号:蓝海资料掘金营,微信deep3321