3. 地图服务:地图数据类型(OccupancyGrid)、地图服务器(map_server)、静态地图与动态地图
聊到导航,地图是绕不开的核心。没有地图,机器人就是个睁眼瞎。今天咱们就深入聊聊ROS2里的地图服务。说白了,就是搞清楚地图长什么样、怎么加载、怎么用。
3.1 地图数据类型:OccupancyGrid
在ROS2里,地图不是一张JPG图片,而是一种叫OccupancyGrid的消息类型。我刚开始接触时也纳闷,为什么不用图片?后来才明白,机器人需要的是“可通行性”数据,不是像素颜色。
OccupancyGrid本质上是一个二维网格。每个格子代表地图上的一小块区域。格子的值表示这个区域被占用的概率。0表示完全空闲,100表示完全被占用,-1表示未知区域。
header:包含时间戳和坐标系ID,通常为"map"info:地图元信息,包括分辨率、宽度、高度、原点位置data:一维数组,按行存储所有格子的占用值
举个例子,一个10x10米的地图,分辨率0.05米,那就有200x200=40000个格子。每个格子占1字节,整张地图也就40KB左右。嗯,这个数据量对嵌入式设备来说很友好。
3.2 地图服务器:map_server
map_server是ROS2官方提供的地图管理节点。它的工作很简单:把保存好的地图文件加载成OccupancyGrid消息,然后发布到/map话题上。
启动方式也很直接:
ros2 run nav2_map_server map_server --ros-args -p yaml_filename:=/path/to/map.yaml
等等,为什么参数是yaml文件?因为地图需要两个文件:一个图片文件(如.pgm或.png),一个yaml描述文件。yaml文件告诉系统图片的元数据。
一个典型的map.yaml长这样:
image: map.pgm
resolution: 0.05
origin: [-10.0, -10.0, 0.0]
occupied_thresh: 0.65
free_thresh: 0.196
negate: 0
这里要注意几个参数:
origin:地图左下角在真实世界中的坐标。很多人搞错这个,导致机器人定位偏移occupied_thresh:像素值高于此阈值视为障碍物free_thresh:像素值低于此阈值视为空闲区域
3.3 静态地图 vs 动态地图
静态地图,就是预先建好的、不会变化的地图。比如你办公室的布局图。一旦建好,基本不变。动态地图则是在运行过程中不断更新的地图,比如激光雷达实时扫描到的障碍物。
在Nav2里,这两者是怎么配合的?
静态地图由map_server加载,发布到/map话题。动态地图则由costmap_2d层来处理。Nav2的全局代价地图和局部代价地图,都是基于静态地图,再叠加动态障碍物信息生成的。
我画了一张图,帮你理清这个关系:
你看,静态地图提供基础环境信息,动态地图负责实时更新。两者缺一不可。静态地图保证了全局路径规划的稳定性,动态地图则让机器人能应对突发障碍。
3.4 实际使用中的注意事项
在实际项目中,我总结了几条经验:
- 地图坐标系必须统一:静态地图的坐标系通常是"map",里程计是"odom",机器人本体是"base_link"。这三个坐标系的关系必须正确,否则地图加载后定位会乱掉。
- 地图更新频率要合理:动态地图更新太快会消耗大量CPU,太慢又无法及时避障。我一般设置激光雷达的更新频率为10Hz,代价地图的更新频率为5Hz。
- 地图边界处理:机器人走到地图边界时,代价地图会失效。所以建图时一定要留出足够的边界区域,至少比实际工作区域大1-2米。
obstacle_layer的参数。把observation_sources配置好,让激光雷达的数据能及时更新到代价地图上。
嗯,地图服务这部分内容就这些。记住一句话:地图是导航的基石,静态地图管全局,动态地图管局部。两者配合好了,导航就成功了一半。