4、地图加载与保存:使用map_server加载静态地图,使用map_saver保存当前地图

地图是导航系统的眼睛。没有地图,机器人就是个瞎子。

这一节我们聊聊地图的加载与保存。说白了,就是怎么把一张已经画好的地图喂给导航系统,以及怎么把机器人探索到的环境存下来。

4.1 地图文件长什么样?

在ROS2里,地图通常由两个文件组成:

  • 地图图片文件(比如 map.pgm 或 map.png)—— 一张灰度图,白色代表可通行区域,黑色代表障碍物,灰色代表未知区域。
  • 地图YAML文件(比如 map.yaml)—— 描述地图元数据的配置文件。

YAML文件的内容大概是这样:

image: map.pgm
resolution: 0.05
origin: [-10.0, -10.0, 0.0]
occupied_thresh: 0.65
free_thresh: 0.196
negate: 0

我来解释一下每个字段:

字段含义说明
image地图图片路径相对于YAML文件的路径
resolution分辨率每个像素对应多少米,0.05就是每像素5厘米
origin原点坐标[x, y, yaw],地图左下角在全局坐标系中的位置
occupied_thresh障碍物阈值像素灰度值高于此阈值视为障碍物
free_thresh自由空间阈值像素灰度值低于此阈值视为可通行
negate是否取反0表示白色为自由,黑色为障碍;1则相反
我的经验:分辨率这个参数特别容易搞错。我刚开始做项目时,用了一张0.1分辨率的地图,结果机器人定位总是偏。后来才发现,地图生成时的分辨率和YAML里写的不一致。记住,这两个必须严格匹配。

4.2 使用map_server加载静态地图

加载地图,我们用 map_server 这个包。它是Nav2里专门负责地图服务的节点。

启动命令很简单:

ros2 run nav2_map_server map_server --ros-args -p yaml_filename:=/path/to/map.yaml

但实际项目中,我更喜欢把它写进launch文件里:

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='nav2_map_server',
            executable='map_server',
            name='map_server',
            output='screen',
            parameters=[{'yaml_filename': '/path/to/map.yaml'}]
        )
    ])

启动之后,map_server会做两件事:

  1. 读取YAML文件,找到对应的图片
  2. 把图片转换成ROS2的 nav_msgs/msg/OccupancyGrid 消息
  3. 在 /map 话题上发布这张地图

你可以用 ros2 topic echo /map 看看地图数据长什么样。嗯,其实就是一堆数字,0表示自由,100表示障碍,-1表示未知。

注意:map_server默认只发布一次地图。如果你在机器人运行过程中修改了地图文件,它不会自动重新加载。需要重启节点才行。我曾经踩过这个坑,调试了半天才发现地图没更新。

4.3 使用map_saver保存当前地图

保存地图,我们用 map_saver。它会把当前 /map 话题上的地图数据存成图片和YAML文件。

命令行用法:

ros2 run nav2_map_server map_saver -f my_map

这个命令会生成两个文件:my_map.pgm 和 my_map.yaml。

如果你想指定保存路径:

ros2 run nav2_map_server map_saver -f /home/user/maps/office_map

map_saver还有一些可选参数:

参数说明默认值
-f文件名前缀map
--occ障碍物像素阈值100
--free自由空间像素阈值0
--mode保存模式(trinary/scale/raw)trinary
三种保存模式的区别:
  • trinary:三值模式,只有自由、障碍、未知三种状态。文件小,但丢失了细节。
  • scale:缩放模式,保留灰度渐变。文件稍大,但更精细。
  • raw:原始模式,完全保留所有数据。文件最大,适合后期处理。

我个人习惯用 scale 模式。为什么呢?因为trinary模式保存的地图,在导航时容易出现一些奇怪的路径规划问题。比如明明有一点点空间,但地图上显示全是障碍,机器人就绕路了。

4.4 核心流程:一张图看懂

下面这张图展示了地图加载与保存的完整流程:

地图加载与保存核心流程 加载流程 map.yaml + map.pgm map_server 节点 /map 话题 (OccupancyGrid) 保存流程 /map 话题 (OccupancyGrid) map_saver 节点 map.yaml + map.pgm 加载:文件 → map_server → /map话题 保存:/map话题 → map_saver → 文件

4.5 实战中的避坑指南

讲几个我实际项目中遇到的问题:

  • 地图路径问题:YAML文件里的图片路径,建议用路径。相对路径有时候会因为工作目录不同而找不到文件。我曾经因为这个折腾了半小时。
  • 地图原点设置:origin参数决定了地图在全局坐标系中的位置。如果你发现机器人定位后位置不对,多半是origin设错了。我习惯把origin设成地图左下角在真实世界中的坐标。
  • 地图更新:如果你用SLAM建图,建完后记得用map_saver保存。别问我为什么强调这个——我有一次建了半小时的图,忘了保存,结果...你懂的。
  • 权限问题:保存地图时,确保目标目录有写入权限。ROS2不会给你明确的错误提示,只会静默失败。嗯,这个坑我也踩过。
小技巧:如果你不确定地图是否加载成功,可以用 ros2 topic echo /map --once 看看有没有数据。或者用 rviz2 添加Map显示,直观看到地图。

4.6 总结一下

地图加载和保存,说白了就是两个命令的事:

  • 加载:ros2 run nav2_map_server map_server
  • 保存:ros2 run nav2_map_server map_saver

但背后的原理你得懂:YAML文件怎么配,分辨率怎么设,原点怎么定。这些细节决定了你的导航系统能不能正常工作。

下一节我们会聊地图服务的高级用法,比如动态地图更新和多地图切换。先把基础打牢,后面才能玩得转。

可可精品资料库