Costmap2DROS
, Costmap2D
, LayeredCostmap
三个类都属于命名空间costmap_2d
,互相没有继承关系,而且都是独立的类
Costmap2D
类中实现了用来存储和访问2D代价地图的的基本数据结构. 它的定义在LayeredCostmap
类的成员列表初始化里面,cell默认值default_value_
的赋值在LayerdCostmap
类的构造函数里。
地图图像原点在左下角,缺点是会增加理解难度,好处是在生成图像时,y可以 +1 增,符合真实世界中y向北表示增加。要画出图像时,须要上下颠倒,y往往要换成(map.info.height - y - 1)
map坐标系原点在该区域左下角坐标(origin_x_, origin_y_)
附近。具体是在(origin_x_ + resolution/2, origin_x_ + resolution/2)
在一次导航过程中,world坐标系只有一个,原点固定。map坐标则随着区域出现而出现。由于一个栅格表示一个resolutin X resolution
面积的真实区域,栅格坐标对应的world坐标是在该栅格的中心点坐标。
看全局代价地图的各个参数:1
2
3
4
5
6
7
8
9int nx = global_costmap->getSizeInCellsX(); //2200
int ny = global_costmap->getSizeInCellsY(); //1600
double global_resolution = global_costmap->getResolution(); // 0.025
double origin_x = global_costmap->getOriginX(); // -1
double origin_y = global_costmap->getOriginY(); // -34.6
double sizeMeterX = global_costmap->getSizeInMetersX(); // 109.975
double sizeMeterY = global_costmap->getSizeInMetersY(); // 79.975
nx, ny就是pgm地图文件的像素尺寸,一共nx X ny
个cell
global_resolution, origin_x, origin_y就是map.yaml
中的resolution和origin参数
sizeMeterX
和 sizeMeterY
在rviz上用Measure工具测量一下地图的长宽即可得到
局部代价地图,yaml文件的参数是这样设置的:1
2
3width: 4.0
height: 4.0
resolution: 0.05
nx, ny都是80,也就是4/0.05, 80x80就是6400个cell
origin_x: 58, origin_y: 1.5 这里机器人的初值位置为(60, 3.5)
,位于动态窗口的中心,计算就能得到这两个参数
sizeMeterX: 3.975000, sizeMeterY: 3.975000 其实就是4
这里会发现在全局代价地图的yaml里,没有设置width和height,因为它们是地图文件决定的,无需我们设置。而局部代价地图的动态窗口大小是我们决定的。
1 | // 就是为 costmap_ 分配内存 |
1 | // 根据像素坐标,获得 costmap_ 数组的索引 |
void Costmap2D::deleteMaps(): 释放 costmap_
bool saveMap(std::string file_name);
1 | // 输入世界距离(世界坐标的距离,将其转换为单元格),输出均等距离 |
代价地图保存为pgm格式。全局代价地图保存是这样的
这里保存的地图图片,其实和OpenCV保存的一样。相当于真实的costmap先逆时针转180°,再对y轴取对称。但是像素值是相反的。局部代价地图打开是一片黑,应该是只有个窗口,看不出来了。
- void mapToWorld(unsigned int mx, unsigned int my, double& wx, double& wy) const
1 | void Costmap2D::mapToWorld(unsigned int mx, unsigned int my, double& wx, double& wy) const |
把某个代价地图里的坐标转为世界坐标,常用于把机器人全局代价地图的坐标转为map坐标系的坐标。 在TebLocalPlannerROS::updateObstacleContainerWithCostmap()
调用过: costmap_->mapToWorld(0, 0, wx, wy);
- void worldToMap(double robot_x, double robot_y, int mx, int my) const
1 | // Convert from world coordinates to map coordinates |
把世界坐标转为某个代价地图里的坐标,最常用的是把机器人在map坐标系的坐标转为全局代价地图里的坐标,注意是转为像素坐标,不是base_link
或常说的坐标系。 像素坐标系的原点是左下方的点 ,所以是跟size_x_
和size_y_
比较,这两个是地图的长宽,这样才能判断点是否出了代价地图的范围1
2
3
4
5tf::Stamped<tf::Pose> robot_pose;
costmap_ros_->getRobotPose(robot_pose);
robot_pose_ = PoseSE2(robot_pose);
unsigned int mx, my;
costmap_ros_->getCostmap()->worldToMap(robot_pose->x(), robot_pose->y(), mx, my);
如果是局部代价地图,窗口边长4,分辨率0.05,那么窗口的像素边长就是80,这里的机器人的坐标 mx my 就在(40, 40)左右,因为它总在局部代价地图中心
1 | if (!costmap_->worldToMap(x, y, mx, my)) { |
如果worldToMap
返回false,那么是mx或my太大了,超出了代价地图的大小。如果不return,继续执行getCost
会导致程序退出。还可以return -1
,因为代价值是0~255
。 有时返回false是因为 wx < originx || wy < originy 不明白原因
map坐标系坐标单元是栅格,只有整数才有意义。一般来说,只有在区域内的才有意义,即x范围是[0, size_x_-1]
,y范围是[0, size_y_-1]
。 这就涉及到worldToMapEnforceBounds
和 worldToMapNoBounds
unsigned char getCost (unsigned int mx, unsigned int my) const
获得costmap中某个栅格的代价值。 mx, my是cell的坐标void setCost(unsigned int mx, unsigned int my, unsigned char cost)
设置代价地图中某个cell的代价值,在层的代码中使用很多
- void setDefaultValue(unsigned char c)
语句只有default_value_ = c;
,调用的地方只有LayeredCostmap
构造函数1
2
3
4if (track_unknown) // 默认false
costmap_.setDefaultValue(255);
else
costmap_.setDefaultValue(0);
- unsigned char* getCharMap() const; return A pointer to the underlying unsigned char array storing cost values 获取到这个地图中存储代价地图的指针,通过指针找到地图信息。主要在层的代码和
ClearCostmapRecovery
类中使用
1 | unsigned char* Costmap2D::getCharMap() const |
获得指定栅格周围的4个或8个栅格
1 | /** |