3、地图基础:地图类型、生成与加载

做导航,地图就是机器人的「眼睛」。没有地图,机器人就是个瞎子。

我刚开始接触 Nav2 时,对地图的理解也很浅。觉得不就是一张图片嘛,机器人能看懂就行。后来踩了不少坑才明白——地图的类型、格式、加载方式,每一个细节都直接影响导航效果。

今天咱们就把地图这块彻底聊透。

3.1 地图类型:Occupancy Grid 与 Costmap

ROS2 导航里,地图主要分两种:Occupancy Grid(占用栅格地图)和 Costmap(代价地图)。很多人容易搞混,其实它们分工完全不同。

3.1.1 Occupancy Grid——原始地图

Occupancy Grid 是 SLAM 建图后保存下来的原始地图。它用栅格(grid)表示环境,每个格子存一个值:

  • 0:空闲(可通行)
  • 100:占用(障碍物)
  • -1:未知(没探测到)

说白了,它就是一张黑白灰的图片。白色是路,黑色是墙,灰色是未知区域。

我的经验:建图时如果地图边缘出现大量灰色区域,说明激光雷达扫描范围不够,或者建图路径没覆盖全。我一般会手动补扫一遍。

3.1.2 Costmap——导航用的「热力图」

Costmap 是在 Occupancy Grid 基础上生成的。它给每个格子加了一个「代价」值,范围 0~255。

  • 0:完全安全
  • 1~50:有轻微风险
  • 50~100:接近障碍物
  • 100~253:很危险
  • 254:致命障碍(机器人中心不能进入)
  • 255:未知区域

为什么要搞这么复杂?你想想看,机器人是有体积的。如果只靠原始地图,它可能贴着墙走,结果把自己卡住。Costmap 通过「膨胀」操作,把障碍物周围一圈也标记为危险区域,这样机器人就会自动保持安全距离。

注意:Costmap 是实时更新的。动态障碍物(比如行人)会临时改变 Costmap 的值。这也是 Nav2 能避障的核心原理。

3.2 地图生成:SLAM 与已知地图

地图怎么来的?两种方式:自己建(SLAM),或者用现成的(已知地图)。

3.2.1 SLAM 建图——从零开始

SLAM(Simultaneous Localization and Mapping)就是让机器人在未知环境中一边走路一边画地图。ROS2 里常用的 SLAM 工具有:

  • slam_toolbox:最推荐,功能全,支持重定位和地图保存
  • Cartographer:Google 出品,精度高,但配置复杂
  • Hector SLAM:不需要里程计,适合无人机或履带车

我个人习惯用 slam_toolbox。启动命令很简单:

ros2 launch slam_toolbox online_async_launch.py

建图完成后,用这个命令保存地图:

ros2 run nav2_map_server map_saver_cli -f my_map

它会生成两个文件:my_map.pgm(图片)和 my_map.yaml(配置文件)。

避坑指南:我曾经建完图发现地图歪了。排查半天,原来是激光雷达的安装角度没标定。记住:建图前一定要校准传感器!

3.2.2 已知地图——拿来就用

如果你已经有地图(比如 CAD 图纸、之前建好的地图),可以直接加载。Nav2 支持两种格式:

  • PGM/PNG 图片 + YAML 配置:最常见
  • YAML 直接定义:适合程序生成的地图

加载已知地图的命令:

ros2 run nav2_map_server map_server --ros-args -p yaml_filename:=my_map.yaml

嗯,这里要注意:YAML 文件里的 resolution(分辨率)和 origin(原点)必须和实际环境一致。否则机器人会「迷路」。

3.3 地图格式与加载

地图文件到底长什么样?咱们拆开看看。

3.3.1 YAML 配置文件

一个标准的 my_map.yaml 内容如下:

image: my_map.pgm
resolution: 0.05
origin: [-10.0, -10.0, 0.0]
occupied_thresh: 0.65
free_thresh: 0.196
negate: 0
参数 含义 我的建议
image 地图图片文件名 路径要写对,建议用路径
resolution 每个像素代表的实际距离(米) 0.05 米(5cm)比较常用
origin 地图左下角在真实世界中的坐标 (x, y, yaw) 建图时自动生成,别手动改
occupied_thresh 像素值超过此阈值视为障碍物 0.65 是默认值,够用
free_thresh 像素值低于此阈值视为空闲 0.196,别乱调
negate 是否反转黑白 一般设为 0
小技巧:如果你用 Photoshop 编辑过地图图片,记得检查 occupied_thresh 和 free_thresh。图片压缩可能导致像素值变化,地图就废了。

3.3.2 地图加载流程

Nav2 加载地图的完整流程,我画了张图帮你理解:

地图文件 .pgm + .yaml map_server 发布 /map 话题 Costmap 全局+局部 全局 Costmap 局部 Costmap 用于全局路径规划 用于局部避障 地图加载流程:文件 → map_server → Costmap 全局 Costmap 用于规划,局部 Costmap 用于避障

流程其实很简单:

  1. map_server 读取 .pgm 和 .yaml 文件
  2. 发布 /map 话题(Occupancy Grid 格式)
  3. Costmap 层订阅 /map,生成全局和局部代价地图
  4. 导航时,全局规划器用全局 Costmap,局部规划器用局部 Costmap

3.3.3 动态地图更新

实际场景中,环境会变化。比如仓库里堆了新货架,或者会议室桌椅被移动了。这时候怎么办?

Nav2 支持两种更新方式:

  • 静态地图 + 传感器实时更新:激光雷达扫描到新障碍物,Costmap 自动更新。这是默认方式。
  • 动态地图重载:调用 map_server 的服务,重新加载地图文件。

我建议用第一种。毕竟机器人自己「看」到的障碍物,比地图文件更实时。

注意:如果环境变化太大(比如装修拆了墙),静态地图就失效了。这时候需要重新建图。别指望 Costmap 能自动「脑补」出新的墙壁。

3.4 本章小结

地图是导航的基石。Occupancy Grid 是原始数据,Costmap 是加了「安全距离」的增强版。建图用 slam_toolbox,加载用 map_server。记住:地图的分辨率、原点、阈值,任何一个参数错了,导航都会出问题。

我见过太多人卡在地图加载这一步。其实只要把 YAML 文件里的参数理解透,问题就解决了一大半。