ROS2从入门到精通4-3:全局路径规划插件开发案例(以A*算法为例)

news2024/11/24 9:46:57

目录

  • 0 专栏介绍
  • 1 路径规划插件的意义
  • 2 全局规划插件编写模板
    • 2.1 构造规划插件类
    • 2.2 注册并导出插件
    • 2.3 编译与使用插件
  • 3 全局规划插件开发案例(A*算法)
  • 常见问题

0 专栏介绍

本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。

🚀详情:《ROS2从入门到精通》


1 路径规划插件的意义

在ROS2中,路径规划插件为导航系统提供灵活性和可扩展性。路径规划插件允许用户根据特定的需求和环境条件选择不同的路径规划算法和策略。这些插件可以被动态加载和替换,从而使机器人可以根据实际情况灵活地调整路径规划行为。这种灵活性使得机器人能够适应不同类型的任务,包括室内导航、室外移动和复杂的障碍物避开等。同时也促进了路径规划算法的研究和开发,为导航系统的不断改进提供了可能。

在这里插入图片描述

2 全局规划插件编写模板

本节以最简单的直线路径规划插件为例介绍ROS2中插件编码的基本范式

2.1 构造规划插件类

所有全局规划插件的基类是nav2_core::GlobalPlanner,该基类提供了5个纯虚方法来实现规划器插件,一个合法的路径规划插件必须覆盖这5个基本方法:

  • configure():在规划器服务器进入on_configure状态时会调用此方法,此方法执行ROS2参数声明和规划器成员变量的初始化;
  • activate():在规划器服务器进入on_activate状态时会调用此方法,此方法实现规划器进入活动状态前的必要操作;
  • deactivate():在规划器服务器进入on_deactivate状态时会调用此方法,此方法实现规划器进入非活动状态前的必要操作;
  • cleanup():在规划器服务器进入on_cleanup状态时会调用此方法,此方法清理为规划器创建的各种资源;
  • createPlan():在规划器服务器要求指定开始位姿和目标位姿的全局规划时会调用此方法。此方法输入开始和目标位姿,并会返回携带全局规划路径的nav_msgs::msg::Path

在本例中,直线规划器的createPlan()函数体很简单,就是增量地生成从起点到终点的直线

nav_msgs::msg::Path StraightLine::createPlan(
  const geometry_msgs::msg::PoseStamped & start,
  const geometry_msgs::msg::PoseStamped & goal)
{
  nav_msgs::msg::Path global_path;

  global_path.poses.clear();
  global_path.header.stamp = node_->now();
  global_path.header.frame_id = global_frame_;
  // calculating the number of loops for current value of interpolation_resolution_
  int total_number_of_loop = std::hypot(
    goal.pose.position.x - start.pose.position.x,
    goal.pose.position.y - start.pose.position.y) /
    interpolation_resolution_;
  double x_increment = (goal.pose.position.x - start.pose.position.x) / total_number_of_loop;
  double y_increment = (goal.pose.position.y - start.pose.position.y) / total_number_of_loop;

  for (int i = 0; i < total_number_of_loop; ++i) {
    geometry_msgs::msg::PoseStamped pose;
    pose.pose.position.x = start.pose.position.x + x_increment * i;
    pose.pose.position.y = start.pose.position.y + y_increment * i;
    pose.pose.position.z = 0.0;
    pose.pose.orientation.x = 0.0;
    pose.pose.orientation.y = 0.0;
    pose.pose.orientation.z = 0.0;
    pose.pose.orientation.w = 1.0;
    pose.header.stamp = node_->now();
    pose.header.frame_id = global_frame_;
    global_path.poses.push_back(pose);
  }

  geometry_msgs::msg::PoseStamped goal_pose = goal;
  goal_pose.header.stamp = node_->now();
  goal_pose.header.frame_id = global_frame_;
  global_path.poses.push_back(goal_pose);

  return global_path;
}

2.2 注册并导出插件

在创建了自定义规划器的前提下,需要导出该规划器插件以便规划器服务器可以在运行时正确地加载。在ROS2中,插件的导出和加载由pluginlib处理。

  • 源文件配置导出宏

    #include "pluginlib/class_list_macros.hpp"
    PLUGINLIB_EXPORT_CLASS(straightline_planner::StraightLinePlanner, nav2_core::GlobalPlanner)
    
  • 配置插件描述文件xxx_planner_plugin.xml,例如本案例为straightline_planner_plugin.xml文件。此XML文件包含以下信息:

    • library path:插件库名称及其位置;
    • class name:规划算法类的名称;
    • class type:规划算法类的类型;
    • base class:规划基类的名称,统一为nav2_core::GlobalPlanner
    • description:插件的描述。

    实例如下

    <library path="straightline_planner_plugin">
      <class name="straightline_planner/StraightLine" type="straightline_planner::StraightLine" base_class_type="nav2_core::GlobalPlanner">
        <description>This is an example of straight path generator.</description>
      </class>
    </library>
    
  • 配置CMakeLists.txt文件
    使用cmake函数pluginlib_export_plugin_description_file()来导出插件。这个函数会将插件描述文件安装到install/share目录中,并设置ament索引以使其可被发现,实例如下

    pluginlib_export_plugin_description_file(nav2_core straightline_planner_plugin.xml)
    
  • 配置package.xml描述文件,实例如下:

    <export>
      <build_type>ament_cmake</build_type>
      <nav2_core plugin="${prefix}/straightline_planner_plugin.xml" />
    </export>
    

2.3 编译与使用插件

编译该插件软件包,接着通过配置文件使用插件。

参数的传递链如下:首先在simulation.launch.py中引用配置文件navigation.yaml

declare_params_file_cmd = DeclareLaunchArgument(
	'params_file',
	default_value=os.path.join(simulation_dir, 'config', 'navigation.yaml'),
	description='Full path to the ROS2 parameters file to use for all launched nodes')

接着在navigation.yaml中修改插件配置,默认如下,是用的是NavfnPlanner插件:

planner_server:
  ros__parameters:
    expected_planner_frequency: 20.0
    use_sim_time: True
    planner_plugins: ["GridBased"]
    GridBased:
      plugin: "nav2_navfn_planner/NavfnPlanner"
      tolerance: 0.5
      use_astar: false
      allow_unknown: true

将上述替换为自己的插件,本案例为:

planner_server:
  ros__parameters:
    expected_planner_frequency: 20.0
    use_sim_time: True
    planner_plugins: ["GridBased"]
    GridBased:
      plugin: "straightline_planner/StraightLinePlanner"
      interpolation_resolution: 0.1

接着运行路径规划即可看到规划算法被替换

在这里插入图片描述

3 全局规划插件开发案例(A*算法)

接下来正式开始实用型路径规划算法的开发案例,以A*算法为例,核心规划部分如下所示:

ool AStar::plan(const unsigned char* global_costmap, const Node& start, const Node& goal, std::vector<Node>& path,
                 std::vector<Node>& expand)
{
  // clear vector
  path.clear();
  expand.clear();

  // open list and closed list
  std::priority_queue<Node, std::vector<Node>, Node::compare_cost> open_list;
  std::unordered_map<int, Node> closed_list;

  open_list.push(start);

  // get all possible motions
  const std::vector<Node> motions = Node::getMotion();

  // main process
  while (!open_list.empty())
  {
    // pop current node from open list
    Node current = open_list.top();
    open_list.pop();

    // current node does not exist in closed list
    if (closed_list.find(current.id_) != closed_list.end())
      continue;

    closed_list.insert(std::make_pair(current.id_, current));
    expand.push_back(current);

    // goal found
    if (current == goal)
    {
      path = _convertClosedListToPath(closed_list, start, goal);
      return true;
    }

    // explore neighbor of current node
    for (const auto& motion : motions)
    {
      // explore a new node
      Node node_new = current + motion;
      node_new.id_ = grid2Index(node_new.x_, node_new.y_);

      // node_new in closed list
      if (closed_list.find(node_new.id_) != closed_list.end())
        continue;

      node_new.pid_ = current.id_;

      // next node hit the boundary or obstacle
      // prevent planning failed when the current within inflation
      if ((node_new.id_ < 0) || (node_new.id_ >= ns_) ||
          (global_costmap[node_new.id_] >= lethal_cost_ * 0.8 &&
           global_costmap[node_new.id_] >= global_costmap[current.id_]))
        continue;

      // if using dijkstra implementation, do not consider heuristics cost
      if (!is_dijkstra_)
        node_new.h_ = helper::dist(node_new, goal);

      // if using GBFS implementation, only consider heuristics cost
      if (is_gbfs_)
        node_new.g_ = 0.0;
      // else, g will be calculate through node_new = current + m

      open_list.push(node_new);
    }
  }

  return false;
}

按照第二节的步骤导出并启动规划即可,效果如下

在这里插入图片描述

常见问题

  1. /opt/ros/noetic/lib/move_base/move_base: symbol lookup error: /home/winter/ROS/ros_learning_tutorials/Lecture19/devel/lib//libmy_planner.so: undefined symbol: _ZN18base_local_planner12CostmapModelC1ERKN10costmap_2d9Costmap2DE

    解决方案:未定义符号错误undefined symbol一般是依赖配置错误导致,采用c++filt工具解析符号

    c++filt _ZN18base_local_planner12CostmapModelC1ERKN10costmap_2d9Costmap2DE
    base_local_planner::CostmapModel::CostmapModel(costmap_2d::Costmap2D const&)
    

    可以看出是base_local_planner的问题,需要在功能包CMakeLists.txt中配置base_local_planner的相关依赖。

    c++filt是什么?g++编译器有名字修饰机制,其目的是给同名的重载函数不同的、唯一的签名识别,所有函数在编译后的文件中都会生成唯一的符号,c++filt可以逆向解析符号,还原函数,定位代码。

完整工程代码请联系下方博主名片获取


🔥 更多精彩专栏

  • 《ROS从入门到精通》
  • 《Pytorch深度学习实战》
  • 《机器学习强基计划》
  • 《运动规划实战精讲》

👇源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系👇

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1797751.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker bash: vi: command not found 修改文件无法使用 vi yum的方法

如题&#xff0c;被入坑很多次。也参考了很多的修复docker 中的vi yum等方法。最终都未解决。 因为要修改 已安装容器中的各类配置信息。无法使用vi yum很麻烦。除去使用docker 挂载文件方法外&#xff0c;还可以使用如下方法直接修改对应的配置文件信息。 如: 修改 logstas…

通过抑制治疗上调的环氧化酶-2来改善光动力性能的肿瘤归巢嵌合肽菱形体

引用信息 文 章&#xff1a;Tumor Homing Chimeric Peptide Rhomboids to Improve Photodynamic Performance by Inhibiting Therapy‐Upregulated Cyclooxygenase-2. 期 刊&#xff1a;Smal&#xff08;影响因子&#xff1a;13.3&#xff09; 发表时间&#xff1a…

赢单有秘诀,大模型智能陪练更懂你

随着数字化技术在营销场景的加速应用&#xff0c;产品营销节奏不断加快&#xff0c;消费者需求日益多元化、个性化&#xff0c;市场竞争日趋激烈。面对复杂多变的市场环境&#xff0c;企业新产品、新服务的推出速度大幅提升&#xff0c;产品知识更新愈加频繁&#xff0c;传统的…

神经网络 torch.nn---优化器的使用

torch.optim - PyTorch中文文档 (pytorch-cn.readthedocs.io) torch.optim — PyTorch 2.3 documentation 反向传播可以求出神经网路中每个需要调节参数的梯度(grad)&#xff0c;优化器可以根据梯度进行调整&#xff0c;达到降低整体误差的作用。下面我们对优化器进行介绍。 …

通过龙讯旷腾PWmat发《The Journal of Chemical Physics》 :基于第一性原理分子动力学热力学积分的离子溶剂化自由能计算

背景导读 离子溶解是电化学中一个重要的过程。电化学反应中许多重要的参数&#xff0c;例如电化学还原电位、无限稀释活度系数、亨利定律溶解常数和离子溶解度等&#xff0c;都与离子的溶剂化能有关。然而&#xff0c;由于测量技术和数据处理的困难&#xff0c;离子溶剂化能的…

LabVIEW与Arm控制器之间的通讯

LabVIEW是一个强大的图形化编程环境&#xff0c;广泛应用于自动化控制、数据采集和测试测量等领域。而Arm控制器则是嵌入式系统中常用的处理器架构&#xff0c;广泛用于各种控制和计算任务。将LabVIEW与Arm控制器进行通讯控制&#xff0c;可以结合二者的优势&#xff0c;实现高…

笔记96:前馈控制 + 航向误差

1. 回顾 对于一个 系统而言&#xff0c;结构可以画作&#xff1a; 如果采用 这样的控制策略&#xff0c;结构可以画作&#xff1a;&#xff08;这就是LQR控制&#xff09; 使用LQR控制器&#xff0c;可以通过公式 和 构建一个完美的负反馈系统&#xff1b; a a 但是有上…

学习笔记——网络参考模型——TCP/IP模型(网络层)

三、TCP/IP模型-网络层 1、IPV4报头 (1)IPV4报文格式 IP Packet(IP数据包)&#xff0c;其包头主要内容如下∶ Version版本∶4 bit&#xff0c;4∶表示为IPv4; 6∶表示为IPv6。 Header Length首部长度∶4 bit&#xff0c;代表IP报头的长度(首部长度)&#xff0c;如果不带Opt…

安卓手机平板使用JuiceSSH无公网IP远程连接本地服务器详细流程

文章目录 前言1. Linux安装cpolar2. 创建公网SSH连接地址3. JuiceSSH公网远程连接4. 固定连接SSH公网地址5. SSH固定地址连接测试 前言 处于内网的虚拟机如何被外网访问呢?如何手机就能访问虚拟机呢? 本文就和大家分享一下如何使用 cpolarJuiceSSH 实现手机端远程连接Linux…

conntrack如何限制您的k8s网关

1.1 conntrack 介绍 对于那些不熟悉的人来说,conntrack简单来说是Linux内核的一个子系统,它跟踪所有进入、出去或通过系统的网络连接,允许它监控和管理每个连接的状态,这对于诸如NAT(网络地址转换)、防火墙和保持会话连续性等任务至关重要。它作为Netfilter的一部分运行,…

Ubuntu的启动过程

尽管通常情况下Ubuntu的启动并不需要用户过多地参与&#xff0c;但是Ubuntu系统的启动本身是一个非常复杂的过程。在这个过程中&#xff0c;有硬件的检测、系统内核的准备以及各种系统服务的启动等。作为系统管理员&#xff0c;需要深入了解其中所经历的阶段&#xff0c;才能在…

Cesium开发环境搭建(二)

由于win7搭建很费事&#xff0c;重新安装了OS&#xff0c;win10的。 记录一下&#xff0c;搭建步骤&#xff1a; 1.下载node.js。 百度搜索即可下载对应的版本。下载cesium。 2.安装node.js。 安装后&#xff0c;输入node -v&#xff0c;显示版本信息&#xff0c;表示安装…

【面试干货】索引的作用

【面试干货】索引的作用 1、索引的作用 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、索引的作用 索引 可以协助 快速查询、更新数据库表中数据。 通过使用索引&#xff0c;数据库系统能够快速定位到符合查询条件的数据&#xff0c;提…

1.6T模块与DSP技术的演进

近日&#xff0c;光通信行业市场机构LightCounting在市场报告中指出&#xff0c;去年的模块供应商已经展示了首批1.6T光学模块的风采&#xff0c;而今年&#xff0c;DSP供应商更是着眼于第二代1.6T模块设计的未来。这些前沿技术的突破&#xff0c;不仅代表了数据传输速度的飞跃…

【set】集合总结

一、Set Set集合是Collection的子接口,代表一种集合,此种集合是元素不重复. 有两个常用实现类 HashSet 是元素不重复,无序,主要是指遍历顺序和插入顺序不一致 TreeSet 是元素不重复,排序 LinkedHashSet不常用 二、HashSet 1.1 介绍 HashSet是Set的实现类 底层是由哈希表实…

Python数据分析I

目录 注&#xff1a;简单起见&#xff0c;下文中"df"均写为"表名"&#xff0c;"函数"均写为"HS"&#xff0c;"属性"均写为"SX"&#xff0c;"范围"均写为"FW"。 1.数据分析常用开源库 注释…

物联网-高性能时序数据库QuestDB

高性能时序数据库QuestDB 开源地址&#xff1a;https://github.com/questdb/questdb 官网&#xff1a;https://questdb.io/ 当前 13.9k start 自带免费可视化管理界面 支持各种语言客户端 C & C .NET Go Java Node.js Python Rust 上手容易可兼容 Postgresql InfluxDB …

浅浅写一个Word、PowerPoint、Excel文档转PDF工具

前言 最近在搞知识库&#xff0c;需要把各种 Word、PowerPoint、Excel 文件转换成 PDF 文件&#xff0c;不然 Word 中的表格中的文字提取会出现一些问题&#xff1b;使用 Office 或者 WPS 将大量文件转换成 PDF 需要频繁重复打开文件&#xff0c;点击保存为PDF&#xff0c;然后…

Qt_C++ RFID网络读卡器Socket Udp通讯示例源码

本示例使用的设备&#xff1a; WIFI/TCP/UDP/HTTP协议RFID液显网络读卡器可二次开发语音播报POE-淘宝网 (taobao.com) #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QHostInfo> #include <QNetworkInterface> #include <…

【机器学习】训练GNN图神经网络模型进行节点分类

1. 引言 1.1 图神经网络GNN概述 图神经网络&#xff08;Graph Neural Network&#xff0c;GNN&#xff09;是一种专门用于处理图结构数据的神经网络方法。它起源于2005年&#xff0c;当时Gori等人首次提出了GNN的概念&#xff0c;用于学习图中的节点特征以及它们之间的关系。…