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

目录

  • 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深度学习实战》
  • 《机器学习强基计划》
  • 《运动规划实战精讲》

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于WPF开发动态可交互混淆矩阵

最近在项目中&#xff0c;为了算法结果的可视化&#xff0c;需要用到混淆矩阵(Confusion Matrix)&#xff0c;而网上资源大多是基于Python绘制的混淆矩阵&#xff0c;并且是输出图片格式&#xff0c;并不能响应用户点击&#xff0c;今天以一个简单的小例子&#xff0c;简述如何…

正序输入一串数,倒序输出,c语言

正序输入一串数&#xff0c;倒序输出 #include <stdio.h> int main () {int i,a[5]; for(i0;i<5;i) {scanf("%d",&a[i]);} for(i4;i>0;i--) {printf("%d",a[i]);} printf("\n"); return 0; } 运行结果

三分钟“手撕”队列与习题

代码放开头&#xff0c;方便大家查阅 目录 一、实现代码 二、什么是队列 三、队列常见方法 入队push&#xff08;&#xff09; 出队 四、Queue使用 Java自带的Queue 双端队列 五、习题 循环队列 用队列实现栈 用栈实现队列 一、实现代码 package demo2;publi…

【leetcode10-21】子串、普通数组、矩阵

子串 560.和为K的子数组【没理解】 什么是前缀和&#xff1a;前缀和指一个数组的某下标之前的所有数组元素的和&#xff08;包含其自身&#xff09; 通常&#xff0c;会在前缀和首位放一个0。比如数组[1,2,3。其前缀和是[0,1,3,6] 前缀和通常可以帮助我们快速计算某个区间内的…

【控制实践——四旋翼无人机】【一】四旋翼无人机运动分析和建模

传送门 系列博客前言坐标系定义及姿态位置描述坐标系定义姿态描述及坐标系变换 受力分析牛顿-欧拉方程状态空间方程总结 系列博客 前言 在C站摸爬滚打一段时间后&#xff0c;发现控制类相关的圈子较小&#xff08;话题热度低&#xff09;&#xff0c;想顺便跟各位同行读者了解…

Netty是什么?深入理解高性能网络框架

Netty是什么&#xff1f;——深入理解高性能网络框架 引言 在现代互联网应用中&#xff0c;网络通信是不可或缺的一部分。无论是构建微服务架构、游戏服务器、实时通信系统还是物联网应用&#xff0c;高效稳定的网络编程框架都是成功的关键。Netty&#xff0c;作为一款高性能…

项目:仿RabbitMQ实现的消息队列组件

文章目录 写在前面开源仓库和项目上线其他文档说明 需求分析BrokerServer交换机类型持久化消息应答 模块划分服务端模块客户端模块交换机数据管理模块队列数据管理模块绑定数据管理模块消息数据管理模块队列信息管理模块虚拟机数据管理模块路由匹配模块消费者管理模块信道管理模…

HTML+CSS+JS 选项卡导航栏

效果演示 实现了一个导航栏切换内容的效果。页面上方有一个导航栏,每个导航项都有一个圆形背景,点击导航项时,圆形背景会放大并显示对应的内容。每个内容区域都包含一个大号字母,数字会在内容区域显示时淡入。点击其他导航项时,当前内容区域会淡出并隐藏,同时新的内容区域…

[数据集][目标检测]csgo头部身体检测数据集VOC+YOLO格式1265张4类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1265 标注数量(xml文件个数)&#xff1a;1265 标注数量(txt文件个数)&#xff1a;1265 标注…

群体优化算法----人工蜂群优化算法应用于路径规划(机器人避开平面障碍寻找最短路线)

介绍 人工蜂群优化算法&#xff08;Artificial Bee Colony Algorithm, ABC&#xff09;是由Dervis Karaboga在2005年提出的一种模拟蜜蜂觅食行为的优化算法。该算法基于蜜蜂群体的分工合作和信息交流机制&#xff0c;通过模拟蜜蜂寻找食物源的过程来解决优化问题。ABC算法因其…

mybatis—plus和mybatis的区别

一前置知识&#xff1a; CRUD操作&#xff08;create 添加数据read读取数据 update 修改数据delete删除数据&#xff09; 二&#xff0c;总体概览 MyBatis-Plus 是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发工作、提高…

INT202 例题

算法复杂度 O(n)&#xff1a;表示算法的渐进上界。如果一个算法的运行时间是O(n)&#xff0c;那么它的运行时间最多与输入规模n成正比。换句话说&#xff0c;当输入规模n增加时&#xff0c;算法的运行时间不会超过某个常数倍的n。比如&#xff0c;如果一个算法的时间复杂度是O(…

【InternLM实战营第二期笔记】04:XTuner 微调 LLM:1.8B、多模态、Agent

文章目录 笔记微调基础知识Xtuner8G显存微调模型InternLM2 1.8B多模态实践环节数据微调过拟合WebUI 交互 多模态微调 作业 这回学乖了&#xff0c;打开本节课第一件事先不看教程而是装环境~ 笔记 微调基础知识 这里感慨一下&#xff0c;垂直领域的训练还是挺困难的&#xff0c;…

jenkins的简单使用

2.1.简介 Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 2.4.Jenkins安装 1.下载安装包jenkins.war&#xff1b; 2.在安装…

dpdk uio整体分析及网卡加载

参考:https://zhuanlan.zhihu.com/p/477600165 一、Linux内核知识点 1. __attribute__ constructor/destructor (1)若函数被设定为constructor属性,则该函数会在 main()函数执行之前被自动的执行。 (2)若函数被设定为destructor属性,则该函数会在main()函数执…

C++类的继承与派生概念

派生和继承是自然界普遍存在的一种现象。例如&#xff0c;“猫”和“白猫”。当人们谈及“猫”时&#xff0c;知道它有4条腿&#xff0c;1条尾巴&#xff0c;抓老鼠,为哺乳动物。如谈论“白猫”时&#xff0c;它也是猫&#xff0c;只不过增加了一个新的特征&#xff0c;即它的毛…

Harmony开发 List/Scroll 组件最后一个item显示不全或布局显示不完整

今天在做Harmony开发的时候遇到一个问题,List组件的最后一个item显示不全&#xff0c;如下图&#xff0c;item-9显示不出来&#xff0c;显示了一部分 这个页面的代码结构如下&#xff1a; Column() {Row() {Text(文本1).fontSize(15).fontColor(Color.Black)Text(文本2).font…

论文浅尝 | THINK-ON-GRAPH:基于知识图谱的深层次且可靠的大语言模型推理方法...

笔记整理&#xff1a;刘佳俊&#xff0c;东南大学硕士&#xff0c;研究方向为知识图谱 链接&#xff1a;https://arxiv.org/pdf/2307.07697.pdf 1. 动机 本文是IDEA研究院的工作&#xff0c;这篇工作将知识图谱的和大语言模型推理进行了结合&#xff0c;在每一步图推理中利用大…

2024视频号·短视频+直播极简培训班:抓住视频号风口,流量红利

课程下载&#xff1a;2024视频号短视频直播极简培训班&#xff1a;抓住视频号风口&#xff0c;流量红利-课程网盘链接提取码下载.txt资源-CSDN文库 更多资源下载&#xff1a;关注我。 课程内容&#xff1a; 02 1、为什么视频号有机会&#xff0c;而不是抖音?(直播2024.03.0…

攻防世界---misc---can_has_stdio?

1、下载附件是一个没有后缀的文件&#xff0c;尝试将后缀改为txt发现里面有一些特殊字符的编码 2、查阅资料得知它是一种编程代码 3、知道了它是什么代码之后&#xff0c;我们就去解码&#xff08;网址&#xff1a;El Brainfuck (copy.sh)&#xff09; 4、 flag{esolangs_for_f…