首页 > 其他分享 >栅格地图

栅格地图

时间:2024-07-22 16:51:05浏览次数:10  
标签:info map 地图 栅格 new 100

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 -1 -1 -1 0 0 0 0 0 -1 -1 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 -1 -1 0 0 0 0 -1 0 0 0 0 0 0 -1 -1 -1 0 0 -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 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 100 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 -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

可以看到实际的数据就是如上所说的形式。

2、简单发布一张地图

为了验证上述一些问题,我们使用一个简单的节点对地图进行了简单的测试,代码过于简单,细节不赘述,先po源码上来:

#include "ros/ros.h"
#include "nav_msgs/OccupancyGrid.h"
#include <string>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
class map_test2
{
private:
    /* data */
    ros::Publisher map_pub;
    nav_msgs::OccupancyGrid new_map;
public:
    map_test2(/* args */);
    void InitMap();
    void PubMap();
    ~map_test2();
};

map_test2::map_test2(/* args */)
{
    ros::NodeHandle n;
    map_pub = n.advertise<nav_msgs::OccupancyGrid>("map_test",2);
}

void map_test2::InitMap()
{
    new_map.header.seq = 1;
    new_map.header.stamp = ros::Time::now();
    new_map.header.frame_id = "map";
    new_map.info.height = 100;
    new_map.info.width = 100;
    new_map.info.map_load_time = ros::Time::now();
    new_map.info.resolution = 0.025;
    new_map.info.origin.position.x = 1.0;
    new_map.info.origin.position.y = 1.0;
    new_map.info.origin.orientation.w = 1.0;
    for(int i=0;i<new_map.info.height;i++)
    {
        for(int j=0;j<new_map.info.width;j++)
        {
            if((i%10)==0||(i%10)==1)
            {
                new_map.data.push_back(100);
            }
            else
                new_map.data.push_back(0);
        }
    }
}
void map_test2::PubMap()
{
    map_pub.publish(new_map);
}
map_test2::~map_test2()
{
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "map_test2");
    ROS_INFO("map_test2_node start");
    map_test2 map_test2;
    map_test2.InitMap();
    while (ros::ok())
    {
        map_test2.PubMap();
        ros::Duration(0.05).sleep();
        ros::spinOnce();
    }
    return 0;
}

上述代码实现了一个简单的地图发布功能,频率为20Hz,编译并运行上述代码,我们可以在RVIZ中看到一张实际名为map_test的地图:

 

注意到我们在上面给定地图原点的时候,给出的坐标系是

new_map.info.origin.position.x = 1.0;
new_map.info.origin.position.y = 1.0;

因此,其对应的栅格的左下角第一个点的位置就是在(1,1)这个位置。同时宽度与高度都是100,分辨率为0.025,因此,我们得到了一张宽与高都为2.5m的地图。最后,我们对于每10个栅格中行数末尾为0与1结尾的行设置了100(障碍物)的值,其他都设置了0(空闲),因此我们也得到了上述的带横向黑色细线条的具体地图实体。

由此,我们大致可以知道第一部分的分析基本上是没有问题的,对于地图部分的基础内容理解基本无误。

3、move_base中对于栅格地图的简单处理

为了后续其他研究需要,顺便简单记录一点move_base节点中对于栅格地图的处理部分。由于要考虑到在后续计算膨胀地图时使用的是0-255的表示方式,因此,在move_base中,对地图进行了简单的转换:

  // initialize the costmap with static data
  for (unsigned int i = 0; i < size_y; ++i)
  {
    for (unsigned int j = 0; j < size_x; ++j)
    {
      unsigned char value = new_map->data[index];
      costmap_[index] = interpretValue(value);
      ++index;
    }
  }

 

上述是一个遍历,根据地图的宽与高进行遍历,并调用interpretValue函数返回具体参数存储到costmap_中,而interpretValue实现如下:

unsigned char StaticLayer::interpretValue(unsigned char value)
{
  // check if the static value is above the unknown or lethal thresholds
  if (track_unknown_space_ && value == unknown_cost_value_)
    return NO_INFORMATION;
  else if (!track_unknown_space_ && value == unknown_cost_value_)
    return FREE_SPACE;
  else if (value >= lethal_threshold_)
    return LETHAL_OBSTACLE;
  else if (trinary_costmap_)
    return FREE_SPACE;
  double scale = (double) value / lethal_threshold_;
  return scale * LETHAL_OBSTACLE;
}

这个函数咋一看挺复杂,但是实际上很简单。首先我们先要明确其中的一些参数:track_unknown_space_这个参数字面意思跟踪未知空间,简单理解就是对未知空间的处理。我们可以看到第一个if与下面的else if是互斥的,也就是只能同时满足一个,而这个值默认为true,也就是走if语句。而unknown_cost_value_这个值为-1,对于-1,我们知道它在地图中代表的是什么?未知空间嘛。在这种情况下返回的数据是什么?NO_INFORMATION。这个值的实际值是255。如果这个时候你再看一下FREE_SPACE=0那么这个track_unknown_space_参数就很好理解了:就是对于原本地图中为未知的那部分区域,我是将它作为障碍物处理还是作为空闲处理嘛。然后下面第二个else if,将输入值跟lethal_threshold_比较,而这个值在算法中默认为100,大于等于100代表什么?障碍物。所以障碍物的点返回值为LETHAL_OBSTACLE(254)。接下来是第三个else if,这里没有作比较,只是判断了一个参数trinary_costmap_,如果trinary_costmap_为true,则直接返回FREE_SPACE(0)。而这个参数默认其实也是为true的,所以对于前面没有处理的数据在这里都会返回0。而实际上对于一张地图而言,我们判断完了它为未知情况的点(-1),判断完了它为障碍物的点(100),它本身也就只剩下为空闲(0)的点了。所以这里对其他点直接返回0本身是没有什么问题的。至于后面的:

double scale = (double) value / lethal_threshold_;
return scale * LETHAL_OBSTACLE;

想来应该是为了适配其他特殊情况而使用的,例如cartographer中的地图似乎采用的就不是三值化的地图,它存在更多的细节数据,则有可能会执行到后续语句。对于我们这里而言,目前只要了解前面这一部分差不多就可以了。

参考:
【ROS-Navigation】Costmap2D代价地图源码解读-静态层StaticLayer

栅格地图、障碍物地图与膨胀地图(栅格地图)_障碍物栅格膨胀-CSDN博客

标签:info,map,地图,栅格,new,100
From: https://www.cnblogs.com/Gaowaly/p/18316392

相关文章

  • openlayers在地图上使用Overlay渲染图标无法操作地图问题
    ol对于在地图上渲染图标,并且图标可以随着地图的缩放层级自适应,跟随地图移动,ol是提供了一个很好用的方法的---overlay代码如下://定义标注对象letlable_div=document.createElement('div')lable_div.className=[s.labelDiv]letoffsetY=0let_classname=''_classn......
  • 全地图商家采集工具
    1.引言地图信息采集软件在商业分析、城市规划和交通管理等领域发挥着重要作用。通过实时采集地图上的商户信息,该软件能够帮助用户快速获取所需的地理数据和商业信息。2.系统功能2.1地图数据采集多平台支持:软件支持百度地图、高德地图、腾讯地图等主流电子地图服务。......
  • 全地图商家采集工具
    1.引言地图信息采集软件在商业分析、城市规划和交通管理等领域发挥着重要作用。通过实时采集地图上的商户信息,该软件能够帮助用户快速获取所需的地理数据和商业信息。2.系统功能2.1地图数据采集多平台支持:软件支持百度地图、高德地图、腾讯地图等主流电子地图服务。实......
  • 注意!使用高德地图授权但还未缴费的用户,账号将被强制关闭
    这回高德是来真的了,以前三大图商的催收策略是不停地进行电话轰炸,限流、关闭账号、法律起诉等都是停留在口头的告知,并未真正地行动。现在是来真的了,前面的文章我也提到过,高德在今年7月将会出行新的政策,现在已经在其平台公布,且限流、关闭账号、等一些列催......
  • GIS地图可视化怎么做?这款免费工具帮你轻松搞定
    GIS地图可视化怎么做?山海鲸可视化这款免费可视化工具帮你轻松搞定。从三维GIS地图可视化需求出发,山海鲸可视化提供了强大的GIS场景编辑功能,包括支持添加倾斜摄影和地形编辑。无论是复杂的地形调整还是细致的倾斜摄影添加,这款工具都能轻松实现。山海鲸可视化是一款非常易用的软件......
  • 使用高德/百度/腾讯SDK,不购买地图商用授权APP无法上架?
    随着国内三大图商联合针对地图位置服务收取5万/年的商业授权费,许多使用三大图商地图或定位sdk的应用开发企业遇到了两难:没有地图商业授权,无法上架!其实大家信息了解有误区,并非都无法上线。目前国内主流的上架商城:华为商城、小米商城、应用宝、vivo、oppo等。上架华为商城通常......
  • Vue.js 集成高德地图:从零开始的实战指南
    Vue.js集成高德地图:从零开始的实战指南在现代Web开发中,地图服务已经成为许多应用的核心功能之一。高德地图作为国内领先的地图服务提供商,提供了强大的API接口,方便开发者在应用中集成地图功能。今天,我们将深入探讨如何在Vue.js项目中集成高德地图,并通过实际代码示例来展示......
  • openlayers 3d 地图 非三维 立体地图 行政区划裁剪 地图背景
    这是实践效果如果没有任何基础就看这个专栏:http://t.csdnimg.cn/qB4w0这个专栏里有从最简单的地图到复杂地图的示例最终效果:线上示例代码:想要做这个效果如果你的行政区划编辑点较多可能会有卡顿感如果出现卡顿感需要将边界点相应减少一些这样地图边界会相对......
  • BLOS-BEV:导航地图助力BEV分割实现200米超远感知新SOTA
    BLOS-BEV:导航地图助力BEV分割实现200米超远感知新SOTA早期,由于感知算法模型的感知能力还比较有限,在城市中的自动驾驶车辆通常都需要依赖高精地图(High-Definition,HDMap)来提供丰富和精确的道路信息,比如道路的拓扑结构,停止线,车道线曲率等相关路况信息。但由于高精地图的采集和......
  • 如何给地图瓦片底图添加滤镜,实现自己想要的主题色
    核心:使用css的filter滤镜属性,然而搭配svg实现想要的效果首先确认底图服务提供的瓦片是否为浅色主题(透明瓦片不适用)实现步骤: 截取原浅色底图图片,类似于下图,最好是带上海洋部分的截图SVGGradientMapFilter这个网站调整自己想要的底图主题效果,网址:https://yoksel.github.......