在ROS中,地图是非常基本的元素,特别对于2D激光SLAM而言,栅格地图可以说是必不可少的元素。机器人在需要前往目标点时,需要在栅格地图中找到一条合适的路径从当前点到达目标点,这部分内容在move_base中有了详细的接口,可以直接调用并返回路径。但是作为一名工程师,不仅要知其然更要知其所以然,正好最近重新看了下这部分的内容,在此简单对地图这块的处理简单作个笔记,以备后续翻阅。
熟悉move_base的开发者会知道,在ROS中,地图大致可以分为三张:栅格地图、障碍物地图与膨胀地图。当然也可以按照局部地图与全局地图划分,但是那个不是本篇的重点,暂时先不考虑那种划分方式。栅格地图与后续两张地图可以说是存在密不可分的关系。首先,栅格地图是作为障碍物地图的基础而存在的,没有栅格地图,亦没有障碍物地图。同时,栅格地图也是膨胀地图的必须元素,深入了解过三张地图之间相互关系的开发者会知道,在move_base中,膨胀地图这个插件(inflaction_layer)本身是不维护地图的实体的,它对于地图的膨胀是在栅格地图(静态地图)的图层进行操作的。所以有必要先了解清楚栅格地图的基础内容,这也可以为我们后续学习另外两种地图打下比较好的基础。
1、地图的消息格式
在ROS中已经定义了官方的地图格式:
nav_msgs/OccupancyGrid
我们可以通过下述指令展开看看这个消息的具体包含:
rosmsg show nav_msgs/OccupancyGrid
得到的结果如下
std_msgs/Header header
uint32 seq
time stamp
string frame_id
nav_msgs/MapMetaData info
time map_load_time
float32 resolution
uint32 width
uint32 height
geometry_msgs/Pose origin
geometry_msgs/Point position
float64 x
float64 y
float64 z
geometry_msgs/Quaternion orientation
float64 x
float64 y
float64 z
float64 w
int8[] data
以上,就是一张地图消息格式的基本组成。我们从上往下看,首先是header部分,这里跟很多消息类型中的header是一样的,包含了一张地图的时间戳以及一个frame_id,对于地图这个东西而言,似乎时间戳并不是很重要。而frame_id则是一般会固定使用“map"。
其次,则是一个nav_msgs/MapMetaData实体info,其中的内容包含了较多信息。首先map_load_time,似乎也没什么用,所有地图设置的都是0。而resolution,则是地图中非常重要的一个参数,它直接关系到了地图的清晰度。一般而言,我们建图时使用的栅格大小就是这里的resolution,使用0.05代表一个栅格是5cm,而使用0.025则代表一个栅格是2.5cm。接下来的参数则是width与height,这两个参数与resolution共同构成了一张地图的实际长宽,例如:
resolution: 0.025
width: 2048
height: 2208
代表了地图的宽度为(0.025 * 2048=51.2)米,高度为(0.025 * 2208=55.2)米。最后面是一个geometry_msgs/Pose的实体,这个参数代表的则是这张地图上的一个坐标,开始时,我曾经一度以为这个坐标是指地图中心点的坐标轴,但是实际上并不是,它代表的是地图最左下角的点在地图上的坐标,这个问题我们可以通过一张实际的地图观察一下,首先我们拿到一张实际的地图的info信息:
map_load_time: 0.000000000
resolution: 0.025
width: 2048
height: 2208
origin:
position:
x: -20
y: -35.2
z: 0
orientation:
x: 0
y: 0
z: 0
w: 1
可以看到它的position为(-20,-35.2),然后我们在RVIZ中加载这张地图,并切换到地图坐标系下,可以看到地图大致显示如下:
这是一张51.2 * 55.2的地图,左上角处的栅格中心为(0,0)原点,大致就可以看出info参数中的position实际对应的就是地图的左下角部分。关于这个问题我们也可以后面通过一些简单的操作验证一下,先将数据结构看完。
剩下最后一个数据结构就是int8[] data这个字段,这个位置实际上存储的就是真正的地图实际数据,每一个栅格具体的值就是通过这里得到的,它的大小为info.width*info.height,同时,它的值只包含了三种数据:-1代表未知,也就是上图中灰色部分;0代表空闲,也就是图上的白色部分;100(注意是100不是1)代表障碍物,图上黑色部分。取一段实际地图数据,例如:
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 -1