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.0use_sim_time: Trueplanner_plugins: ["GridBased"]GridBased:plugin: "nav2_navfn_planner/NavfnPlanner"tolerance: 0.5use_astar: falseallow_unknown: true

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

planner_server:ros__parameters:expected_planner_frequency: 20.0use_sim_time: Trueplanner_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 vectorpath.clear();expand.clear();// open list and closed liststd::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 motionsconst std::vector<Node> motions = Node::getMotion();// main processwhile (!open_list.empty()){// pop current node from open listNode current = open_list.top();open_list.pop();// current node does not exist in closed listif (closed_list.find(current.id_) != closed_list.end())continue;closed_list.insert(std::make_pair(current.id_, current));expand.push_back(current);// goal foundif (current == goal){path = _convertClosedListToPath(closed_list, start, goal);return true;}// explore neighbor of current nodefor (const auto& motion : motions){// explore a new nodeNode node_new = current + motion;node_new.id_ = grid2Index(node_new.x_, node_new.y_);// node_new in closed listif (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 inflationif ((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 costif (!is_dijkstra_)node_new.h_ = helper::dist(node_new, goal);// if using GBFS implementation, only consider heuristics costif (is_gbfs_)node_new.g_ = 0.0;// else, g will be calculate through node_new = current + mopen_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.mzph.cn/pingmian/21441.shtml

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

相关文章

2023-2025年最值得选择的Java毕业设计选题大全:1000个热门选题推荐✅✅✅

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还…

冥想第一千一百七十八天

1.周末&#xff0c;早上先骑着电车到绿谷公园拿了姐给的精油&#xff0c;40分钟到家。 2.早上带着媳妇吃了饭&#xff0c;等丈母娘和小侄子。一起去荥泽水乡特别的推荐。感受特别好玩。 3.晚上带着丈母娘和小侄子吃了饭&#xff0c;给送到中原福塔。回来都都12点了。 4.累的&am…

JDK安装目录

1、bin 该路径下存放了各种工具命令&#xff0c;其中比较重要的有&#xff1a;javac和java javac&#xff1a;jdk提供的编译工具&#xff0c;我们可以通过这个工具&#xff0c;把当前路径下的 .java 文件编译成 .class 字节码文件java&#xff1a;jdk提供的一个工具&#xff0…

计算机视觉与模式识别实验1-4 图像的傅立叶变换

文章目录 &#x1f9e1;&#x1f9e1;实验流程&#x1f9e1;&#x1f9e1;1. 傅立叶变换1.a 绘制一个二值图像矩阵,并将其傅立叶函数可视化。1.b 利用傅立叶变换分析两幅图像的相关性&#xff0c;定位图像特征。读入图像‘text.png&#xff0c;抽取其中的字母‘a’ 2. 离散余弦…

2024年5月2日 Go生态洞察:Go 1.22中的安全随机性

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a;…

JavaEE IO流(1)

1.什么是IO流 &#xff08;1&#xff09;input输入 Output输出 这两个的首字母就是IO的组成 &#xff08;2&#xff09;比如你的电脑可以通过网络上传文件和下载文件 这个上传文件就是Output 这个下载翁建就是input (3)这个输入和输出的标准是以CPU为参照物为基准的 其中通…

Python编程基础1

Python特点&#xff1a; 高级&#xff1a;有高级的数据结构&#xff0c;缩短开发时间与代码量。 面向对象&#xff1a;为数据和逻辑相分离的结构化和过程化编程添加了新的活力。 可升级&#xff1a;提供了基础的开发模板&#xff0c;可以在它上面开发软件&#xff0c;实现代码的…

问答机器人

怎样做自己的问答机器人&#xff1f; 根据我们提供的数据分析出问题的答案&#xff0c;我们并不需要训练自己的模型 微调模型 finetune&#xff0c;将语言模型调成另外的语言模型&#xff0c;更适合不同类型数据&#xff0c;运用finetune方法将模型变化 知识库模型 embedd…

关系型数据库面经(数据库系统概论)

下面是我在面试中被问到的关系型数据库的问题并且我回答得不是很好的题目&#xff0c;写个博客记录一下。 下面用于实现数据存储安全性的SQL语句是&#xff1f; 实现数据存储安全性通常涉及到权限管理和加密等手段。在SQL中&#xff0c;用于控制数据访问权限的语句主要是GRAN…

【最新鸿蒙应用开发】——什么是应用开发模型?Stage模型

在应用程序开发时通常需要使用应用模型来提供必备的组件和运行机制&#xff0c;有了应用模型&#xff0c;开发者可以基于一套统一的模型进行应用开发&#xff0c;使应用开发更简单、高效。接下来谈谈鸿蒙应用开发当中的两种模型&#xff1a; Stage模型&#xff1a; HarmonyOS …

Nvidia Jetson/Orin +FPGA+AI大算力边缘计算盒子:人工智能消防应用

青鸟消防股份有限公司成立于2001年6月&#xff0c;于2019年8月在深圳证券交易所挂牌上市&#xff0c;成为中国消防报警行业首家登陆A股的企业。公司始终聚焦于消防安全与物联网领域&#xff0c;主营业务为“一站式”消防安全系统产品的研发、生产和销售。公司产品已覆盖了火灾报…

【NOIP提高组】方格取数

【NOIP提高组】方格取数 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 设有N*N的方格图&#xff0c;我们将其中的某些方格填入正整数&#xff0c; 而其他的方格中放入0。 某人从图得左上角出发&#xff0c;可以向下走&#xff0c;也可以向…

vue2 bug求助!!!(未解决,大概是浏览器缓存的问题或者是路由的问题)

我的vue2项目出现了一个超级恶心的bug 具体流程&#xff1a; 页面a点击a标签->到页面b->页面b用户退出刷新页面->点击浏览器的返回按钮返回上一页 返回页面后页面没有刷新导致用户名还显示这 项目中没有用keep-alive缓存 也在设置了key 尝试了window.addEventListe…

idea实用快捷键(持续更新...)

文章目录 1、快速输入try/catch/finally2、选中多个光标3、实现接口4、方法参数提示5、查看某个类的子类6、弹出显示查找内容的搜索框 1、快速输入try/catch/finally CtrlAltT 2、选中多个光标 ShiftAlt单机多选 End可以全部到行尾&#xff0c;Home则可以全部回到行首 3、实现接…

Python深度学习基于Tensorflow(14)人脸检测和识别实例

文章目录 人脸检测研究现状传统的人脸检测算法深度学习人脸检测算法 人脸识别实战人脸检测人脸对齐人脸标准化人脸表示人脸验证 参考资料 人脸检测研究现状 这里直接引用了吴伟硕士毕业论文 随着计算能力的飞速提升和创新性的传感、分析、渲染设备和技术的广泛应用&#xff0c…

LoRA低秩自适应微调技术原理及实践

大型语言模型的低秩自适应 (LoRA) 用于解决微调大型语言模型 (LLM) 的挑战。GPT 和 Llama 等模型拥有数十亿个参数&#xff0c;通常对于特定任务或领域进行微调的成本过高。LoRA 保留了预训练的模型权重&#xff0c;并在每个模型块中加入了可训练层。这显著减少了需要微调的参数…

k8s学习--ConfigMap详细解释与应用

文章目录 一 什么是configmapConfigMap 的好处ConfigMap 的限制 二.创建ConfigMap的4种方式1.在命令行指定参数创建2.在命令行通过多个文件创建3.在命令行通过文件提供多个键值对创建4.YAML资源清单文件创建 三 configmap的两种使用方法1.通过环境变量的方式传递给pod2.通过vol…

MySQL学习——影响选项文件处理的命令行选项和程序选项修改器

大多数支持选项文件的MySQL程序都处理以下选项。因为这些选项会影响选项文件的处理&#xff0c;所以必须在命令行上给出&#xff0c;而不是在选项文件中给出。为了正常工作&#xff0c;这些选项中的每一个都必须先于其他选项给出&#xff0c;但以下情况除外&#xff1a; -prin…

WLAN组网模型探究

目录 一、WLAN基本概念二、WLAN组网方式三、WLAN转发模型 随着信息技术的飞速发展&#xff0c;无线局域网&#xff08;WLAN&#xff09;已逐渐成为企业网络架构中不可或缺的一部分。不同的企业组织因其业务特性、规模大小及安全需求的不同&#xff0c;对WLAN的要求也各有侧重。…

物联网面试准备

volatile的作用 volatile关键字用于告诉编译器&#xff0c;该变量可能会在程序的执行过程中被意外更改&#xff0c;因此编译器不应该对该变量进行优化或者缓存。 这样可以确保每次访问该变量时都会从内存中读取最新的值&#xff0c;而不是使用缓存中的旧值。 在多线程编程中&…