Costmap_2d 的插件

Costmap_2d 的插件都是继承于CostmapLayer。具体的关系如下图所示:

StaticLayer

StaticLayer内主要是通过接收map_server发布的地图话题来加载静态地图的。所以StaticLayer内是可以在线更改静态地图的。

ObstacleLayer

ObservationBuffer

ObservationBuffer 是一个障碍物观察数据的buffer。观测到的障碍物数据都将转成sensor_msgs::msg::PointCloud2格式,然后存储到ObservationBuffer 中。

ObservationBuffer 里存储的历史障碍物数据可以根据想保持的时间来清空。期望保持的时间主要由变量observation_keep_time_来决定。如果设置成rclcpp::Duration(0.0s)则表示每次都只存储最新的,历史障碍物数据都会被清掉。

看到这里,有同学可能会想,既然可以以时间为依据来清除障碍物,是不是也可以以其他条件来清除障碍物呢?答案肯定是可以的。这个就需要根据应用场景来选择了。比如:使用机器人的移动距离来作为判断条件。当观测数据时的机器人位置与现在机器人的位置超过多远就把该数据清掉。

ObstacleLayer

ObstacleLayer内可以加载多种传感器的障碍物观测数据。但是数据类型只支持PointCloud2LaserScan。其中LaserScan类型的数据会被转换成PointCloud2 类型数据。因为ObservationBuffer 只存储PointCloud2 类型数据。

ObstacleLayer内有下面几个层级参数需要关注一下:

declareParameter("enabled", rclcpp::ParameterValue(true));//使能该层declareParameter("footprint_clearing_enabled", rclcpp::ParameterValue(true));//清楚footprint占用的区域declareParameter("max_obstacle_height", rclcpp::ParameterValue(2.0));//高于此参数设定的高度的障碍物就忽略declareParameter("combination_method", rclcpp::ParameterValue(1));//更新cost的方式,0->直接覆盖旧数据,1->取前后最大值declareParameter("observation_sources", rclcpp::ParameterValue(std::string("")));//观测数据的名称

每种传感器的观测数据都可以独立配置如下参数:

declareParameter(source + "." + "topic", rclcpp::ParameterValue(source));declareParameter(source + "." + "sensor_frame", rclcpp::ParameterValue(std::string("")));declareParameter(source + "." + "observation_persistence", rclcpp::ParameterValue(0.0));declareParameter(source + "." + "expected_update_rate", rclcpp::ParameterValue(0.0));declareParameter(source + "." + "data_type", rclcpp::ParameterValue(std::string("LaserScan")));declareParameter(source + "." + "min_obstacle_height", rclcpp::ParameterValue(0.0));declareParameter(source + "." + "max_obstacle_height", rclcpp::ParameterValue(0.0));declareParameter(source + "." + "inf_is_valid", rclcpp::ParameterValue(false));declareParameter(source + "." + "marking", rclcpp::ParameterValue(true));declareParameter(source + "." + "clearing", rclcpp::ParameterValue(false));declareParameter(source + "." + "obstacle_max_range", rclcpp::ParameterValue(2.5));declareParameter(source + "." + "obstacle_min_range", rclcpp::ParameterValue(0.0));declareParameter(source + "." + "raytrace_max_range", rclcpp::ParameterValue(3.0));declareParameter(source + "." + "raytrace_min_range", rclcpp::ParameterValue(0.0));

有几个比较重要的参数,这里说明一下:
observation_persistence: 决定障碍物持续时间的参数。
obstacle_max_rangeobstacle_min_range:决定了距离传感器安装位置多少距离区间内的障碍物可以被标记到costmap上
raytrace_max_rangeraytrace_min_range: 决定了距离传感器安装位置多少距离区间内的障碍物可以被清除掉

这里使用了tf2_ros::MessageFilter来处理障碍物观测数据。主要是因为tf2_ros::MessageFilter可以保证只有在传感器的framglobal_framtf关系有效的情况下再执行数据的回调函数。在障碍物数据叠加到costmap层的过程中需要将障碍物数据转换到全局坐标系下。所以需要保证其tf转换是有效的才进行数据处理。

std::shared_ptr<tf2_ros::MessageFilter<sensor_msgs::msg::PointCloud2>> filter(new tf2_ros::MessageFilter<sensor_msgs::msg::PointCloud2>(*sub, *tf_, global_frame_, 50, rclcpp_node_, tf2::durationFromSec(transform_tolerance)));filter->registerCallback(std::bind(&ObstacleLayer::pointCloud2Callback, this, std::placeholders::_1,observation_buffers_.back()));

RangeSensorLayer

这是Navigation2中新增加的一个costmap插件层,主要维护超声波的数据。它对超声波数据的模拟是用圆锥体有来表征超声波的检测空间。映射到costmap上时则是三角形。这可能是最接近超声波检测空间的规则图形了吧!但为了直观展示这种模拟方式和实际超声波检测的差异,我在下面放了两张图片。

模拟的超声波检测空间

实际超声波的检测空间

RangeSensorLayer层中,超声波检测区域中的栅格值是通过概率模型(probabalistic model)进行更新的。

double sensor = 0.0;
if (!clear) {sensor = sensor_model(r, phi, theta);
}
double prior = to_prob(getCost(x, y));//得到原来被占用的概率
double prob_occ = sensor * prior;
double prob_not = (1 - sensor) * (1 - prior);
double new_prob = prob_occ / (prob_occ + prob_not);//更新被占用的概率

InflationLayer

InflationLayer中的灵魂操作就是cost的更新函数了(updateCosts)。下面简单梳理一下函数执行的流程:

  1. 提取出障碍物点
  // Start with lethal obstacles: by definition distance is 0.0auto & obs_bin = inflation_cells_[0];for (int j = min_j; j < max_j; j++) {for (int i = min_i; i < max_i; i++) {int index = static_cast<int>(master_grid.getIndex(i, j));unsigned char cost = master_array[index];if (cost == LETHAL_OBSTACLE || (inflate_around_unknown_ && cost == NO_INFORMATION)) {obs_bin.emplace_back(index, i, j, i, j);}}}
  1. 迭代障碍物点并更新cost
      // assign the cost associated with the distance from an obstacle to the cellunsigned char cost = costLookup(mx, my, sx, sy);unsigned char old_cost = master_array[index];// In order to avoid artifacts appeared out of boundary areas// when some layer is going after inflation_layer,// we need to apply inflation_layer only to inside of given boundsif (static_cast<int>(mx) >= base_min_i &&static_cast<int>(my) >= base_min_j &&static_cast<int>(mx) < base_max_i &&static_cast<int>(my) < base_max_j){if (old_cost == NO_INFORMATION &&(inflate_unknown_ ? (cost > FREE_SPACE) : (cost >= INSCRIBED_INFLATED_OBSTACLE))){master_array[index] = cost;} else {master_array[index] = std::max(old_cost, cost);}}
  1. 将在膨胀半径内的栅格点加入到膨胀队列里
  • 首先向四周扩展栅格
      // attempt to put the neighbors of the current cell onto the inflation listif (mx > 0) {enqueue(index - 1, mx - 1, my, sx, sy);}if (my > 0) {enqueue(index - size_x, mx, my - 1, sx, sy);}if (mx < size_x - 1) {enqueue(index + 1, mx + 1, my, sx, sy);}if (my < size_y - 1) {enqueue(index + size_x, mx, my + 1, sx, sy);}
  • 选取在膨胀半径内的栅格加入到膨胀队列里
void
InflationLayer::enqueue(unsigned int index, unsigned int mx, unsigned int my,unsigned int src_x, unsigned int src_y)
{if (!seen_[index]) {// we compute our distance table one cell further than the// inflation radius dictates so we can make the check belowdouble distance = distanceLookup(mx, my, src_x, src_y);// we only want to put the cell in the list if it is within// the inflation radius of the obstacle pointif (distance > cell_inflation_radius_) {return;}const unsigned int r = cell_inflation_radius_ + 2;// push the cell data onto the inflation list and markinflation_cells_[distance_matrix_[mx - src_x + r][my - src_y + r]].emplace_back(index, mx, my, src_x, src_y);//推到相应的膨胀层的vector内}
}

其中inflation_cells_中将按圈层存储栅格。

需要注意的一点是,InflationLayer中并没有包含存储地图数据的costmap_2d层,它唯一的工作就是把之前层上的障碍物信息在组合层里膨胀一下。

关于costmap的插件配置,这里需要注意一下配置的顺序。代码中插件加载的顺序就是按照配置顺序来的。"inflation_layer"一般放在最后面。因为它最终将前面几个层的障碍物信息一起膨胀。如果不想膨胀某个插件层,则可以将其放在"inflation_layer"之后。

plugins: ["static_layer", "obstacle_layer", "voxel_layer", "inflation_layer"]

Costmap_2d Filters

Costmap_2d Filters 的主要作用是给区域划定不同的功能属性。比如:KeepoutFilter就可以实现虚拟墙的功能。它能限定某些区域机器是不能进去的或者某些区域是不建议通过的。又比如:SpeedFilter限制了在某些区域内机器人的运动速度。

它与CostmapLayer有何不同呢?

  1. Filter调用updateBounds函数基本不干什么事,也不更新cost变动的区域。
void CostmapFilter::updateBounds(double robot_x, double robot_y, double robot_yaw,double * /*min_x*/, double * /*min_y*/, double * /*max_x*/, double * /*max_y*/)
{if (!enabled_) {return;}latest_pose_.x = robot_x;latest_pose_.y = robot_y;latest_pose_.theta = robot_yaw;
}
  1. Filter可能并不会更改栅格值,比如SpeedFilter只能根据自身维护的栅格值来调整速度限制。而CostmapLayer常常是需要对栅格值进行修改的。

Costmap_2d Filters之间的关系图如下:

Costmap_2d Filters的运行机制如下图所示:

CostmapFilterInfoServer负责加载一些参数发布给filterMapServer主要是加载地图文件并发布给filter

你可能发现了。Filter正是利用地图文件来获取区域信息的。而这些地图文件是可以自己定义的。比如你将原来地图文件的某些部分涂黑,那么这些黑色的部分在KeepoutFilter中将被视为禁区或者说虚拟墙。如果你将地图中不建议去的区域加重颜色,加载到KeepoutFilter中时这些颜色比较深但是又没有被标记为障碍物的区域会有比较大的cost值。这样路径规划时就会尽量绕开这些区域。只有在其他区域都完全不可行的情况下才会往这些区域规划路径。

下面是KeepoutFilter的一个例子:

SpeedFilter则会根据标记区域的深浅来约束最大速度。颜色越深,速度约束越大。反之,颜色越浅,速度约束越小。速度约束的方式有两种。一种是通过百分比,即将速度约束为最大速度的多少百分比。一种是绝对速度约束,即直接修改最大可行速度。

SpeedFilter会将计算得到的速度约束发送出来。nav2_controller接受到速度约束话题后将相应的值通过函数setSpeedLimit更新给控制器插件,比如TEB,DWB。

觉得有用就点赞吧!

另外在公众号《首飞》内回复“机器人”获取精心推荐的C/C++,Python,Docker,Qt,ROS1/2等机器人行业常用技术资料。

[ROS2] 你应该知道Costmap_2d 的这些细节相关推荐

  1. ROS 教程之 navigation :在 catkin 环境下创建costmap layer plugin

    在做机器人导航的时候,肯定见到过global_costmap和local_costmap.global_costmap是为了全局路径规划服务的,如从这个房间到那个房间该怎么走.local_costma ...

  2. Robosense速腾激光雷达驱动文件参数介绍

    Robosense速腾激光雷达驱动文件参数介绍 在阅读本文之前,请下学习文章Robosense在LIOSAM中的使用,并对驱动进行安装与编译. Robosense驱动文件只有一份参数文件 config ...

  3. [ROS2基础] TF2使用细节

    运行一个示例 安装依赖 sudo apt-get install ros-galactic-turtle-tf2-py ros-galactic-tf2-tools ros-galactic-tf-t ...

  4. ROS2/DDS/QoS/主题的记录

    比较杂乱,调试会遇到问题,并且ROS2的问题和ROS1有非常大的差异性. 一些概念: 词汇表: DDS - 数据分发服务 RTPS - 实时发布订阅 QoS - 服务质量 客户端 - 也称为客户端,是 ...

  5. ROS2学习笔记22--使用launch启动/监听多个node节点

    概要:这篇内容主要介绍如何使用launch启动/监听多个node节点 环境:ubuntu20.04,ros2-foxy,vscode 最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常 ...

  6. ROS2教程(入门级):记录和回播数据

    目标: 记录从话题发布出来的数据,以便我们可以随时回放和检查它. 背景知识 ros2 bag是一个用来记录你的系统中的话题所发布的数据的命令行工具.它可以收集任意多个话题所发布的数据,并且将数据保存在 ...

  7. ROS2:Humble 教程

    序言 机器人操作系统(ROS:Robot Operating System)是一组用于构建机器人应用程序的软件库和工具.从驱动程序和最先进的算法到强大的开发人员工具,ROS为一个机器人项目提供了所需的 ...

  8. ROS2学习笔记(3)什么是ROS2 topics

    什么是ROS2 topics 上一篇笔记学习了节点,ROS2正是帮我们可以把一个复杂的系统分解成很多个模块化的节点. topics(话题)是ROS的重要元素,它的作用就充当这些模块化的节点之间交换信息 ...

  9. ROS2教程(入门级):创建ROS2工作空间

    **目标:**创建一个工作空间并且学习如何为开发和测试设置上层工作空间(overlay). 背景知识 工作空间指的是包含了ROS 2 应用包的文件夹.在使用ROS 2之前,需要在你将要进行工作的终端上 ...

  10. ROS2可视化利器---Foxglove Studio

    0. 简介 之前作者已经讲了<ROS1可视化利器-Webviz>,然后就有读者问,ROS2有没有可以使用的可视化工具呢,答案是肯定的,除了plotjuggler这种ROS1和ROS2通用的 ...

最新文章

  1. SpringBoot操作使用Spring-Data-Jpa
  2. mysql修改数据库字符集,编码
  3. Hadoop实战-中高级部分 之 Hadoop 集群安装
  4. SAP Leonardo图片处理相关的机器学习服务在SAP智能服务场景中的应用
  5. leetcode206:反转链表
  6. 测试用例的测试编号是自己定义的还是别人给的_初级软件测试工程师必须掌握的东西...
  7. #ifndef #define #endif 和#pragma once的区别
  8. [翻译] ZCSHoldProgress
  9. 一张图彻底了解Unity脚本的生命周期
  10. jqueryui时间插件_jQueryUI工具提示插件
  11. sha2 替换sha1 时间表
  12. c语言九九乘法表的值,c语言九九乘法表!
  13. php++仿网页版微信,【原创】html5高仿微信网页版|h5仿微信聊天实战
  14. hyperledger fabric explorer 超级账本区块链浏览器搭建-docker的方式
  15. Mac 中composer的安装
  16. iOS 本地打包工具 自动化
  17. 编译原理复习 第一章 概述
  18. wkhtmltopdf 中文参数详解
  19. 让人懵逼的宏定义赋值
  20. 计算机显示器桌面变小,电脑显示器显示变小怎么办

热门文章

  1. c++ 三点求外接圆圆心 3维实现
  2. Blazor使用PDFObject预览pdf文件
  3. python 爬取生意参谋数据_用Excel实现生意参谋爬虫,伪装登陆状态
  4. Redis系列之内存碎片
  5. 计算机机试题Excel,2009年职称计算机考试_Excel机试题-1
  6. 热力地图高德_HeatMap丨丨基于高德地图API制作热力图。
  7. GET和POST本质区别
  8. cad剖切线的快捷键_CAD快捷键记不住怎么办?顶级绘图员教你,从此不求人
  9. linux挂steam游戏时长,steam挂游戏时长工具
  10. 纽泰格深交所上市:市值52亿 扣非净利下降52%