【ROS】使用pluginlib自定义costmap地图层

2023-05-16

文章目录

文章目录

前言

一、重写地图层

1.包含头文件 

2.onInitialize()

3.updateBounds()

4.updateCosts()

二、向ROS注册

1.插件描述文件

2.向ROS注册插件

3.在costmap配置文件中使用

总结


前言

pluginlib是一个 C++ 库,用于从 ROS 包中加载和卸载插件。插件是从运行时库(即共享对象、动态链接库)加载的动态可加载类。使用 pluginlib,人们不必将他们的应用程序显式链接到包含类的库,相反,pluginlib 可以在任何时候打开一个包含导出类的库,而无需应用程序事先知道该库或包含类定义的头文件. 插件可用于在不需要应用程序源代码的情况下扩展/修改应用程序行为。

在costmap中就为用户提供了便捷丰富的地图层接口,用户可以使用C++中的继承操作,继承costmap_2d中的类,然后重写其中的虚函数,以实现自己的地图层功能,本文以添加禁用区域为例说明如何实现一个自定义的costmap地图层。


一、重写地图层

1.包含头文件 

为了允许动态加载类,必须将其标记为导出类。这是通过特殊宏PLUGINLIB_EXPORT_CLASS完成的。一般PLUGINLIB_EXPORT_CLASS都写在文件的开头,以虚拟墙地图层为例:

#include <costmap_prohibition_layer/costmap_prohibition_layer.h>
#include <pluginlib/class_list_macros.h>

PLUGINLIB_EXPORT_CLASS(costmap_prohibition_layer_namespace::CostmapProhibitionLayer, costmap_2d::Layer)

如果想实现基本的地图层插件至少要重写这几个函数

  • void onInitialize():在costmap执行初始化initialize后会执行这个函数,相当于为用户提供的初始化接口。
  • void updateBounds(double robot_x, double robot_y, double robot_yaw, double *min_x, double *min_y, double *max_x, double *max_y):计算插件图层要更新到主图层区域的大小,每个图层都可以增加这个尺寸,如下图(b)所示
  • void updateCosts(costmap_2d::Costmap2D& master_grid, int min_i, int min_j, int max_i, int max_j):将每个图层的代价值更新到主图层,如下图(c)(d)(e)所示

2.onInitialize()

 onInitialize()函数主要执行一些初始化工作,如下面代码所示,下面进行逐行讲解。

void CostmapProhibitionLayer::onInitialize()

{
  ros::NodeHandle nh("~/" + name_);
  current_ = true;

  _dsrv = new dynamic_reconfigure::Server<CostmapProhibitionLayerConfig>(nh);
  dynamic_reconfigure::Server<CostmapProhibitionLayerConfig>::CallbackType cb =
      boost::bind(&CostmapProhibitionLayer::reconfigureCB, this, _1, _2);
  _dsrv->setCallback(cb);

  // get a pointer to the layered costmap and save resolution
  costmap_2d::Costmap2D *costmap = layered_costmap_->getCostmap();
  _costmap_resolution = costmap->getResolution();

  // set initial bounds
  _min_x = _min_y = _max_x = _max_y = 0;
  
  // reading the prohibition areas out of the namespace of this plugin!
  // e.g.: "move_base/global_costmap/prohibition_layer/prohibition_areas"
  std::string params = "prohibition_areas";
  if (!parseProhibitionListFromYaml(&nh, params))
    ROS_ERROR_STREAM("Reading prohibition areas from '" << nh.getNamespace() << "/" << params << "' failed!");
  
  _fill_polygons = true;
  nh.param("fill_polygons", _fill_polygons, _fill_polygons);
  
  // compute map bounds for the current set of prohibition areas.
  computeMapBounds();
  
  ROS_INFO("CostmapProhibitionLayer initialized.");
}

创建ros节点句柄:

ros::NodeHandle nh("~/" + name_);
current_ = true;

创建动态配置的服务器和回调函数,并为服务器配置回调函数,这部分在我的其他文章进行过详细说明。

_dsrv = new dynamic_reconfigure::Server<CostmapProhibitionLayerConfig>(nh);
dynamic_reconfigure::Server<CostmapProhibitionLayerConfig>::CallbackType cb =
      boost::bind(&CostmapProhibitionLayer::reconfigureCB, this, _1, _2);
_dsrv->setCallback(cb);

获得地图数据的指针和分辨率参数:

// get a pointer to the layered costmap and save resolution
  costmap_2d::Costmap2D *costmap = layered_costmap_->getCostmap();
  _costmap_resolution = costmap->getResolution();

从yaml文件中读取虚拟墙区域:

// reading the prohibition areas out of the namespace of this plugin!
  // e.g.: "move_base/global_costmap/prohibition_layer/prohibition_areas"
  std::string params = "prohibition_areas";
  if (!parseProhibitionListFromYaml(&nh, params))
    ROS_ERROR_STREAM("Reading prohibition areas from '" << nh.getNamespace() << "/" << params << "' failed!");

从launch文件中读取参数,是否完全更新多边形区域,然后computeMapBounds()函数用来计算更新区域的最大最小值。

_fill_polygons = true;
nh.param("fill_polygons", _fill_polygons, _fill_polygons);
  
// compute map bounds for the current set of prohibition areas.
computeMapBounds();
  
ROS_INFO("CostmapProhibitionLayer initialized.");

3.updateBounds()

updateBounds()函数用来根据刚才computeMapBounds()计算得到的最大最小区域更新costmap中定义的最大最小区域变量double *min_x, double *min_y, double *max_x, double *max_y:

void CostmapProhibitionLayer::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;
    
    std::lock_guard<std::mutex> l(_data_mutex);
    
    if (_prohibition_points.empty() && _prohibition_polygons.empty())
        return;

    *min_x = std::min(*min_x, _min_x);
    *min_y = std::min(*min_y, _min_y);
    *max_x = std::max(*max_x, _max_x);
    *max_y = std::max(*max_y, _max_y);

}

这里面主要就是进行了一个赋值操作

4.updateCosts()

updateCosts()是最主要的功能,用来将地图层的代价更新到主图层,这里面主要有两个部分,第一个循环是更新禁止通行的区域,第二个循环是更新禁止通行的点(因为这个图层约定的yaml文件格式可以选择禁用点或直线或区域,所以这里分开处理)

void CostmapProhibitionLayer::updateCosts(costmap_2d::Costmap2D &master_grid, int min_i, int min_j, int max_i, int max_j)
{
  if (!enabled_)
    return;

  std::lock_guard<std::mutex> l(_data_mutex);
  
  // set costs of polygons
  for (int i = 0; i < _prohibition_polygons.size(); ++i)
  {
      setPolygonCost(master_grid, _prohibition_polygons[i], LETHAL_OBSTACLE, min_i, min_j, max_i, max_j, _fill_polygons);
  }
      
  // set cost of points
  for (int i = 0; i < _prohibition_points.size(); ++i)
  {
    unsigned int mx;
    unsigned int my;
    if (master_grid.worldToMap(_prohibition_points[i].x, _prohibition_points[i].y, mx, my))
    {
      master_grid.setCost(mx, my, LETHAL_OBSTACLE);
    }
  }
}

先调用了setPolygonCost()函数将禁用区域的代价值设置为“致命障碍”(LETHAL_OBSTACLE)

  // set costs of polygons
  for (int i = 0; i < _prohibition_polygons.size(); ++i)
  {
      setPolygonCost(master_grid, _prohibition_polygons[i], LETHAL_OBSTACLE, min_i, min_j, max_i, max_j, _fill_polygons);
  }

setPolygonCost()函数如下:

void CostmapProhibitionLayer::setPolygonCost(costmap_2d::Costmap2D &master_grid, const std::vector<geometry_msgs::Point>& polygon, unsigned char cost,
                                             int min_i, int min_j, int max_i, int max_j, bool fill_polygon)
{
    std::vector<PointInt> map_polygon;
    for (unsigned int i = 0; i < polygon.size(); ++i)
    {
        PointInt loc;
        master_grid.worldToMapNoBounds(polygon[i].x, polygon[i].y, loc.x, loc.y);
        map_polygon.push_back(loc);
    }

    std::vector<PointInt> polygon_cells;

    // get the cells that fill the polygon
    rasterizePolygon(map_polygon, polygon_cells, fill_polygon);

    // set the cost of those cells
    for (unsigned int i = 0; i < polygon_cells.size(); ++i)
    {
        int mx = polygon_cells[i].x;
        int my = polygon_cells[i].y;
        // check if point is outside bounds
        if (mx < min_i || mx >= max_i)
            continue;
        if (my < min_j || my >= max_j)
            continue;
        master_grid.setCost(mx, my, cost);
    }
}

先通过这几行代码,将多边形区域的世界坐标转化为地图边界,存储在loc变量中(作者定义的结构体,有两个变量int x和int y),然后通过rasterizePolygon函数获得这个区域内所有单元格的地图坐标

std::vector<PointInt> map_polygon;
for (unsigned int i = 0; i < polygon.size(); ++i)
{
    PointInt loc;
    master_grid.worldToMapNoBounds(polygon[i].x, polygon[i].y, loc.x, loc.y);
    map_polygon.push_back(loc);
}

std::vector<PointInt> polygon_cells;

// get the cells that fill the polygon
rasterizePolygon(map_polygon, polygon_cells, fill_polygon);

这个循环是根据获得的序号设置代价值,关键函数是setCost函数,根据每一个单元格的地图坐标,将其代价值设置为“致命障碍”

// set the cost of those cells
for (unsigned int i = 0; i < polygon_cells.size(); ++i)
{
    int mx = polygon_cells[i].x;
    int my = polygon_cells[i].y;
    // check if point is outside bounds
    if (mx < min_i || mx >= max_i)
        continue;
    if (my < min_j || my >= max_j)
        continue;
    master_grid.setCost(mx, my, cost);
}

然后这个循环就更简单了,直接使用worldToMap()函数将世界坐标转化为地图坐标,然后根据获得的地图坐标使用setCost()函数设置代价值。

  // set cost of points
  for (int i = 0; i < _prohibition_points.size(); ++i)
  {
    unsigned int mx;
    unsigned int my;
    if (master_grid.worldToMap(_prohibition_points[i].x, _prohibition_points[i].y, mx, my))
    {
      master_grid.setCost(mx, my, LETHAL_OBSTACLE);
    }
  }

 

二、向ROS注册

1.插件描述文件

插件描述文件是一个 XML 文件,用于以机器可读格式存储有关插件的所有重要信息。它包含有关插件所在的库、插件名称、插件类型等的信息。对于虚拟墙禁用图层,它的描述性文件是这样的:

<library path="lib/libcostmap_prohibition_layer">
  <class type="costmap_prohibition_layer_namespace::CostmapProhibitionLayer" base_class_type="costmap_2d::Layer">
    <description>ROS-Package that implements a costmap layer to add prohibited areas to the costmap-2D by a user configuration.</description>
  </class>
</library>

library标签定义插件类所在的库。一个库可能包含多个不同类类型的插件。

class标签描述了库提供的类。

属性:

  • name :类的查找名称。由pluginlib工具用作插件的标识符。

  • type :完全限定的类的类型。
  • base_class_type :基类的完全限定类型
  • description :类及其作用的描述。

更详细的描述可以查看这个文档。

2.向ROS注册插件

为了让 pluginlib 查询系统上所有 ROS 包中的所有可用插件,每个包必须明确指定它导出的插件以及哪些包库包含这些插件。插件提供者必须在其导出标记块内的package.xml中指向其插件描述文件,需要在package.xml文件最后添加这样一个标签:

  <export>
    <costmap_2d plugin="${prefix}/costmap_plugins.xml" />
  </export>

当然需要在前面添加对基类(costmap_2d)的依赖:

<depend>costmap_2d</depend>

然后在工作空间中使用catkin_make或catkin_make_isolated编译即可,然后使用以下命令查看:

rospack plugins --attrib=plugin costmap_2d

如果出现以下结果,则说明插件可用:

costmap_prohibition_layer /home/lyh/catkin_acad/src/costmap_prohibition_layer-repush3/costmap_plugins.xml
costmap_2d /opt/ros/kinetic/share/costmap_2d/costmap_plugins.xml

3.在costmap配置文件中使用

在参数配置文件夹中找到 global_costmap_params.yaml 和/或 local_costmap_params.yaml,在末尾添加或修改:

 plugins:
      - {name: static_map,       type: "costmap_2d::StaticLayer"}
      - {name: obstacles,        type: "costmap_2d::VoxelLayer"}
      - {name: inflation_layer,        type: "costmap_2d::InflationLayer"}
      - {name: costmap_prohibition_layer,        type: "costmap_prohibition_layer_namespace::CostmapProhibitionLayer"}    

对于虚拟墙地图层,还需要在param文件夹中自己配置一个设置禁止区域的参数文件,在参数配置文件夹(就是和 global_costmap_params.yaml 以及 local_costmap_params.yaml 相同位置的文件夹)中创建新的文档,命名为 "prohibition_areas.yaml",然后在prohibition_areas.yaml文档中输入:

prohibition_areas:
#定义一个禁止点
 - [17.09, -6.388]
# 定义一个禁止通行的线
 - [[8.33, 2.11],
    [8.26, 5.11]]
# 定义一个禁止通行的区域
 - [[-11.15, -15.614],
    [-12.35, -13.89],
    [-10.05, -12.218]]

注意事项:
1.一定要严格按照上述格式来设置坐标,可能出现情况:
  (1)坐标前的短横线没对齐
  (2)定义禁止区域或者禁止线,两坐标之间缺少了逗号
2.你可以同时定义多个禁止点/多个禁止线/多个禁止区域,或者混合定义多个点/线/区域.


总结

本文以虚拟墙禁用层为例,详细介绍了如何实现一个自定义的costmap地图层,以及如何在ROS中使用pluginlib制作一个插件,插件机制应用广泛,使用C++的类继承为用户提供了极大的便利。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【ROS】使用pluginlib自定义costmap地图层 的相关文章

  • AD学习笔记(二)原理图库以及原理图绘制

    文章目录 AD学习笔记第二讲 原理图库以及原理图绘制一 认识原理图二 原理图库绘制三 原理图绘制1 原理图纸的操作2 原理图库的调用放置3 导线及网络标识的添加4 原理图可读性优化处理5 原理图统一编号设置6 PCB封装名称的统一添加与管理
  • C工程与寄存器封装

    目录 一 C语言工程简介 二 启动代码分析 三 C语言实现LED 四 寄存器的封装方式 五 寄存器操作的标准化 六 流水灯 一 C语言工程简介 先将工程模板解压 include里是 h文件 src里是 c文件 start里面是 s启动文件
  • Python第三章函数

    函数 文章目录 函数一 函数基础1 参数2 拆分参数列表3 参数传递位置传递地址传递传对象引用 4 函数返回值5 变量的作用域局部变量全局变量nonlocal关键字 7 包8 猴子补丁9 python标准库的应用1 random模块2 ti
  • 无显示器怎么玩树莓派

    无显示器怎么玩树莓派 文章目录 无显示器怎么玩树莓派 前言一 给树莓派烧系统二 设置WiFi及ssh端口三 远程连接四 注意事项 前言 很多时候我们在使用树莓派的时候身边都没有显示器 xff0c 关于这个问题 xff0c 我也十分苦恼 xf
  • ROS智能车定位导航仿真(Gazebo搭建赛道)

    ROS智能车定位导航仿真 xff08 Gazebo搭建赛道 xff09 前言一 ROS仿真功能包下载二 安装运行所需的插件三 racecar功能包编译四 测试程序运行五 运行功能包赛道六 注意事项 前言 Ubuntu版本 xff1a 18
  • 阿木实验室PX4开发课程整理

    1 1 xff1a alt 43 ctrl 43 t 打开终端 cd Desktop 进入到桌面目录 cd 返回上次访问目录 cd 返回上一目录 gedit circular cpp 进入某文件 roscd px4 control 进入文件
  • Java并发编程—CompletableFuture的异步执行案例

    在博主前几篇博客中 xff0c https blog csdn net qq 52545155 article details 128167519 spm 61 1001 2014 3001 5501 xff0c 给大家分享了关于多线程中异
  • 手写rtos的第一天

    唉 xff0c 不自不觉已经大三了啊 xff0c 大二的智能车生涯已经结束了 xff0c 不得不说 xff0c 省二是我不太能接受的 结果 xff0c 虽然嘴上说着没啥 xff0c 真正面对全省排名的时候 xff0c 内心的寂寥真的难以言表
  • FreeRTOS任务(动态)创建与删除(一)

    FreeRTOS学习总结 文章目录 前言一 浅浅了解二 创建任务1 动态任务创建2 动态实践 总结 前言 听朋友说 xff0c FreeRTOS很好用 xff0c 就在无聊的上网课期间浅学一下 提示 xff1a 以下是本篇文章正文内容 xf
  • FreeRTOS操作系统队列及队列API函数(五)

    FreeRTOS学习总结 文章目录 前言一 队列功能1 数据存储2 多任务访问3 出队阻塞4 入队阻塞 二 队列操作过程图示1 创建队列2 向队列发送第一个消息3 向队列发送第二个消息4 从队列中读取消息 二 API函数1 队列创建函数2
  • php导出word文件,打开损坏或者乱码

    下载Word文件 fileinfo 61 pathinfo fullname ob end clean header 39 Content type application x 39 fileinfo 39 extension 39 hea
  • FreeRTOS操作系统优先级翻转问题(八)

    FreeRTOS总结 文章目录 前言一 浅浅了解优先级翻转二 模拟 优先级翻转实验1 代码 总结 前言 在使用二值信号量的时候会遇到很常见的一个问题 优先级翻转 xff0c 优先级翻转在可剥夺 内核中是非常常见的 xff0c 在实时系统中不
  • 伽马分布,指数分布,卡方分布三者关系

    1 伽马分布是一个连续概率分布 xff0c 具有两个参数 alpha 和 lambda xff0c 记为
  • 数据结构 马踏棋盘 栈应用 C++

    include lt iostream gt 包含其它头文件 using namespace std const int StackInitSize 61 10 const int StackInc 61 10 typedef int SE
  • STM32智能小车------红外遥控

    文章目录 一 原理讲解1 实物图2 工作原理 xff1a 3 接线 xff1a 二 软件驱动代码1 驱动函数2 获取键值 总结最终效果 大家好哇 xff01 我是小光 xff0c 嵌入式爱好者 xff0c 一个想要成为系统架构师的普通大学生
  • UCOS学习(一)——前后台系统、RTOS系统

    大家好哇 xff01 我是小光 xff0c 嵌入式爱好者 xff0c 一个想要成为系统架构师的大二学生 最近开始学习UCOS操作系统 xff0c 后面会更新一些关于UCOS学习笔记 今天了解了UCOS操作系统 xff0c 总结一下知识点 感
  • UCOS学习(二)——UCOS-II、UCOS-III移植到STM32F103

    大家好哇 xff01 我是小光 xff0c 嵌入式爱好者 xff0c 一个想要成为系统架构师的大二学生 最近开始学习UCOS操作系统 xff0c 后面会更新一些关于UCOS学习笔记 今天学习了如何将UCOS II和UCOS III移植到ST
  • UCOS学习(三)——任务管理基础

    大家好哇 xff01 我是小光 xff0c 嵌入式爱好者 xff0c 一个想要成为系统架构师的大二学生 最近开始学习UCOS操作系统 xff0c 后面会更新一些关于UCOS学习笔记 今天学习了任务管理基础知识 感谢你的阅读 xff0c 不对
  • UCOS学习(七)——信号量详解

    信号量 信号量简介信号量保护共享资源举个栗子 xff1a 如果不使用信号量信号量解决公共资源问题创建信号量 xff1a 信号量实现任务同步总结 信号量简介 信号量像是一种上锁机制 xff0c 代码必须获得对应的钥匙才能继续执行 xff0c
  • Altium Designer(AD)的简易使用

    一 绘制原理图 原理图库 首先来说明原理图的概念 根据百度百科 xff0c 原理图 xff0c 顾名思义就是表示电路板上各器件之间连接原理的图表 也就是表示芯片上的引脚连接什么元件 xff0c 各个元件谁和谁相连 xff0c 电源电压的大小

随机推荐

  • STM32CubeIDE---HAL库PWM使用速记

    一 如何计算 理论分析 xff1a HAL库函数之呼吸灯 PWM波 简书 jianshu com 预分频系数 PSC 自动重装载值 ARR 捕获 比较寄存器值 CCR 频率计算 xff1a 定时器频率 PSC 43 1 ARR 43 1 占
  • mac常用命令

    mac清除电脑DNS缓存 sudo dscacheutil flushcache
  • IMU初介绍及里程计相关补充

    IMU原理 xff1a 以牛顿力学定律为基础 xff0c 通过测量载体在惯性参考系的加速度 xff0c 将它对时间进行积分 xff0c 且把它变换到导航坐标系中 xff0c 就能够得到在导航坐标系中的速度 偏航角和位置等信息 IMU xff
  • C语言变量的定义与声明,为什么全局变量不能赋值

    文章目录 一 对C语言程序的一些补充二 定义域声明2 1 什么是变量2 2 如何定义变量2 3 为什么要定义变量2 4 定义变量的本质2 5 变量声明的本质2 6 定义和声明的区别 三 全局变量无法赋值 一 对C语言程序的一些补充 对于一个
  • 野火PID上位机通信移植

    野火PID上位机通信移植 一 简介 在调试pid参数的时候 xff0c 需要用到上位机 xff0c 这里选用 野火多功能调试助手 使用调试助手 xff0c 需要下位机与上位机之间的通信协议 xff0c 下载野火关于电机的相关例程 xff0c
  • 实验报告数字图像的基本操作

    一 实验目的 了解Matlab的基本功能及操作方法练习图像读写和显示函数的使用方法掌握如何利用MATLAB来获取图像的大小 颜色 高度 宽度等等相关信息熟悉常用的图像文件格式与格式转换 xff1b 二 实验环境 PC计算机MatLab软件
  • 【meArm机械臂】第二篇·Arduino控制程序

    系列文章目录 meArm机械臂 第一篇 结构设计及搭建 meArm机械臂 第二篇 Arduino控制程序 文章目录 系列文章目录前言一 测试程序1 单个电机测试程序2 四舵机控制测试程序3 极限位置测量 二 基本控制程序三 最终控制程序总结
  • 【树莓派4B】安装Ubuntu Mate20.04+ROS Noetic+使用电脑自带的xrdp和VNC进行PC端远程控制

    目录 前言 一 烧录Ubuntu Mate20 04 1 下载Ubuntu Mate 20 04 2 格式化SD卡并烧录系统 3 打开SSH功能 二 配置Ubuntu环境 1 初次开机进行简单的配置 2 更换国内源 3 更新软件列表和软件
  • 【STM32】STM32F103C8T6+L298N通过PWM控制直流电机转速

    系列文章目录 STM32 新建工程模板及配置 STM32 STM32与PC端 HC 06 ROS进行USART串口通信 ROS ROS上位机使用Serial库和boost asio库与STM32进行USART通讯 STM32 STM32F1
  • 【ROS】ROS上位机使用Serial库和boost::asio库与STM32进行USART通讯

    系列文章目录 STM32 新建工程模板及配置 STM32 STM32与PC端 HC 06 ROS进行USART串口通信 ROS ROS上位机使用Serial库和boost asio库与STM32进行USART通讯 STM32 STM32F1
  • 【STM32】STM32F103C8T6使用外部中断法和输入捕获法进行编码器测速

    系列文章目录 STM32 新建工程模板及配置 STM32 STM32与PC端 HC 06 ROS进行USART串口通信 ROS ROS上位机使用Serial库和boost asio库与STM32进行USART通讯 STM32 STM32F1
  • 【STM32】STM32F103C8T6实现直流电机速度PID控制

    系列文章目录 STM32 新建工程模板及配置 STM32 STM32与PC端 HC 06 ROS进行USART串口通信 ROS ROS上位机使用Serial库和boost asio库与STM32进行USART通讯 STM32 STM32F1
  • Linux配置Supervisor 配置遇到的坑

    在linux中web 应用部署到线上后之后发现退出终端后网站就无法访问了 所以需要用Supervisor来守护进程 xff0c 它可以保证应用一直处于运行状态 xff0c 在遇到程序异常 报错等情况 xff0c 导致 web 应用终止时 x
  • 【基于MATLAB的数字图像处理】第四章·图像增强

    系列文章 基于MATLAB的数字图像处理 第一章 绪论 基于MATLAB的数字图像处理 第二章 视觉系统与图像处理系统 基于MATLAB的数字图像处理 第三章 基本图像变换 基于MATLAB的数字图像处理 第四章 图像增强 基于MATLAB
  • 【ROS】学习路线与方法-个人感悟篇

    文章目录 目录 文章目录 前言 视频学习 书籍学习 个人建议 前言 前前后后 xff0c 对于ROS的学习已经很多遍了 xff0c 可以说走的弯路有点多 xff0c 导致浪费了很多时间 xff0c 其实ROS不应该是一个专门需要去学习的东西
  • 【基于FreeRTOS的STM32F103系统】Heap_4内存管理机制程序详解

    系列文章目录 基于FreeRTOS的STM32F103系统 简介及官方文件移植 基于FreeRTOS的STM32F103系统 编写FreeRTOS程序 基于FreeRTOS的STM32F103系统 内存管理及任务调度 基于FreeRTOS的
  • 【问题解决】虚拟机打开gazebo黑屏、闪退、模型加载不出来

    1 缺少模型库 可以在github上下载模型库 xff0c 但是速度极慢 xff0c 因为服务器在国外 此处提供快速下载方法 xff0c 通过码云快速git clone cd gazebo git clone https gitee com
  • 【ROS】package.xml文件解析

    文章目录 文章目录 前言 一 基本格式 1 基本结构 2 必要标签 3 依赖关系 二 Metapackage包 总结 前言 ros每个功能包中都有一个包清单 xff0c 它是一个名为package xml的XML 文件 xff0c 它必须包
  • 【ROS】dynamic_reconfigure配置详细说明

    文章目录 文章目录 前言 一 编写 cfg文件 二 为节点配置dynamic reconfigure 总结 前言 dynamic reconfigure配置是ROS中为了方便用户对程序中的参数进行实时调整而推出的工具 xff0c 配置好自己
  • 【ROS】使用pluginlib自定义costmap地图层

    文章目录 文章目录 前言 一 重写地图层 1 包含头文件 2 onInitialize 3 updateBounds 4 updateCosts 二 向ROS注册 1 插件描述文件 2 向ROS注册插件 3 在costmap配置文件中使用