编写 navigation2 控制器插件
简介
本教程展示了如何创建自己的控制器插件。在本教程中,我们将基于这篇论文实现纯追踪路径跟踪算法。建议您阅读该论文。
注意:本教程基于 Nav2 堆栈中以前存在的简化版本的 Regulated Pure Pursuit 控制器。您可以在此处找到与本教程相匹配的源代码。
1- 创建一个新的控制器插件
我们将实现纯追踪控制器。本教程中的注释代码可以在 navigation_tutorials存储库中的 nav2_pure_pursuit_controller 中找到。这个包可以被视为编写自己的控制器插件的参考。
我们的示例插件类 nav2_pure_pursuit_controller::PurePursuitController 继承自基类nav2_core::Controller。基类提供了一组虚拟方法来实现控制器插件。这些方法在运行时被控制器服务器调用以计算速度命令。下表列出了各种虚函数、函数描述和以及是否需要重新:
虚函数 | 方法描述 | 需要重写? |
---|---|---|
configure() | 当控制器服务器进入on_configure状态时调用该方法。理想情况下,该方法应执行ROS参数的声明和控制器成员变量的初始化。该方法接受4个输入参数:父节点的弱引用、控制器名称、tf缓冲区指针和costmap的共享指针。 | 是 |
activate() | 当控制器服务器进入on_activate状态时调用该方法。理想情况下,该方法应实现在控制器进入活动状态之前必要的操作。 | 是 |
deactivate() | 当控制器服务器进入on_deactivate状态时调用该方法。理想情况下,该方法应实现在控制器进入非活动状态之前必要的操作。 | 是 |
cleanup() | 当控制器服务器进入on_cleanup状态时调用该方法。理想情况下,该方法应清理为控制器创建的资源。 | 是 |
setPlan() | 当全局路径更新时调用该方法。理想情况下,该方法应执行转换全局路径和存储路径的操作。 | 是 |
computeVelocityCommands() | 当控制器服务器要求提供新的速度命令,以便机器人按照全局路径行驶时,调用该方法。该方法返回一个geometry_msgs::msg::TwistStamped,表示机器人行驶的速度命令。该方法传递两个参数:当前机器人位姿的引用和当前速度。 | 是 |
setSpeedLimit() | 当需要限制机器人的最大线速度时调用该方法。速度限制可以用绝对值(m/s)或相对于最大机器人速度的百分比来表示。请注意,通常情况下,最大旋转速度与最大线速度的变化成比例地被限制,以保持当前机器人的行为不变。 | 是 |
在本教程中,我们将使用 PurePursuitController 类的 configure、setPlan 和 computeVelocityCommands 方法。
在控制器中,configure()方法必须从ROS参数设置成员变量,并执行任何所需的初始化操作。
void PurePursuitController::configure(const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent,std::string name, std::shared_ptr<tf2_ros::Buffer> tf,std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
{node_ = parent;auto node = node_.lock();costmap_ros_ = costmap_ros;tf_ = tf;plugin_name_ = name;logger_ = node->get_logger();clock_ = node->get_clock();declare_parameter_if_not_declared(node, plugin_name_ + ".desired_linear_vel", rclcpp::ParameterValue(0.2));declare_parameter_if_not_declared(node, plugin_name_ + ".lookahead_dist",rclcpp::ParameterValue(0.4));declare_parameter_if_not_declared(node, plugin_name_ + ".max_angular_vel", rclcpp::ParameterValue(1.0));declare_parameter_if_not_declared(node, plugin_name_ + ".transform_tolerance", rclcpp::ParameterValue(0.1));node->get_parameter(plugin_name_ + ".desired_linear_vel", desired_linear_vel_);node->get_parameter(plugin_name_ + ".lookahead_dist", lookahead_dist_);node->get_parameter(plugin_name_ + ".max_angular_vel", max_angular_vel_);double transform_tolerance;node->get_parameter(plugin_name_ + ".transform_tolerance", transform_tolerance);transform_tolerance_ = rclcpp::Duration::from_seconds(transform_tolerance);
}
在这里,plugin_name_ + “.desired_linear_vel” 获取了名为 desired_linear_vel 的ROS 参数,该参数是特定于我们的控制器的。Nav2 允许加载多个插件,并且为了保持事物的组织性,每个插件都映射到某个ID/名称。现在,如果我们想要检索特定插件的参数,我们可以使用<mapped_name_of_plugin>.<name_of_parameter>,就像上面的片段中所做的那样。例如,我们的示例控制器被映射到名称FollowPath,要检索特定于“FollowPath”的desired_linear_vel参数,我们使用FollowPath.desired_linear_vel。换句话说,FollowPath用作插件特定参数的命名空间。当我们讨论参数文件(或params文件)时,我们将更详细地讨论这个问题。
传入的参数被存储在成员变量中,以便在以后的阶段如果需要的话可以使用它们。
在setPlan()
方法中,我们接收到机器人需要跟随的更新的全局路径。在我们的示例中,我们将接收到的全局路径转换为机器人的坐标系,并将这个转换后的全局路径存储起来以备后用。
void PurePursuitController::setPlan(const nav_msgs::msg::Path & path)
{// Transform global path into the robot's frameglobal_plan_ = transformGlobalPlan(path);
}
计算期望速度的操作发生在computeVelocityCommands()
方法中。它用于根据当前速度和姿态计算期望速度指令。第三个参数是一个指向nav2_core::GoalChecker
的指针,用于检查是否已达到目标。在我们的示例中,这个参数不会被使用。对于纯追踪算法而言,该算法计算速度指令,使得机器人尽可能地紧跟全局路径。该算法假设线速度是恒定的,并根据全局路径的曲率计算角速度。
geometry_msgs::msg::TwistStamped PurePursuitController::computeVelocityCommands(const geometry_msgs::msg::PoseStamped & pose,const geometry_msgs::msg::Twist & velocity,nav2_core::GoalChecker * /*goal_checker*/)
{// Find the first pose which is at a distance greater than the specified lookahead distanceauto goal_pose = std::find_if(global_plan_.poses.begin(), global_plan_.poses.end(),[&](const auto & global_plan_pose) {return hypot(global_plan_pose.pose.position.x,global_plan_pose.pose.position.y) >= lookahead_dist_;})->pose;double linear_vel, angular_vel;// If the goal pose is in front of the robot then compute the velocity using the pure pursuit algorithm// else rotate with the max angular velocity until the goal pose is in front of the robotif (goal_pose.position.x > 0) {auto curvature = 2.0 * goal_pose.position.y /(goal_pose.position.x * goal_pose.position.x + goal_pose.position.y * goal_pose.position.y);linear_vel = desired_linear_vel_;angular_vel = desired_linear_vel_ * curvature;} else {linear_vel = 0.0;angular_vel = max_angular_vel_;}// Create and publish a TwistStamped message with the desired velocitygeometry_msgs::msg::TwistStamped cmd_vel;cmd_vel.header.frame_id = pose.header.frame_id;cmd_vel.header.stamp = clock_->now();cmd_vel.twist.linear.x = linear_vel;cmd_vel.twist.angular.z = max(-1.0 * abs(max_angular_vel_), min(angular_vel, abs(max_angular_vel_)));return cmd_vel;
}
剩下的方法虽然没有使用,但是必须进行重写。根据规定,我们确实进行了重写,但是将它们保留为空。
2- 导出控制器插件
现在我们已经创建了自定义的控制器,我们需要导出我们的控制器插件,这样它才能被控制器服务器看到。插件是在运行时加载的,如果它们不可见,那么我们的控制器服务器就无法加载它们。在ROS 2中,导出和加载插件是由pluginlib来处理的。
回到我们的教程,nav2_pure_pursuit_controller::PurePursuitController 类是以动态方式加载的,作为 nav2_core::Controller 的派生类。
- 1.要导出该控制器,我们需要提供两行代码
#include "pluginlib/class_list_macros.hpp"
PLUGINLIB_EXPORT_CLASS(nav2_pure_pursuit_controller::PurePursuitController, nav2_core::Controller)
请注意,它需要pluginlib来导出插件的类。Pluginlib 会提供宏PLUGINLIB_EXPORT_CLASS
,它负责完成导出工作。
将这两行代码放在文件末尾是一种良好的实践,但从技术上讲,你也可以将其放在文件的顶部。
- 2.下一步是在包的根目录中创建插件的描述文件。例如,在我们的教程包中创建
pure_pursuit_controller_plugin.xml
文件。该文件包含以下信息:
library path: Plugin’s library name and its location.
class name: Name of the class.
class type: Type of class.
base class: Name of the base class.
description: Description of the plugin.
<library path="nav2_pure_pursuit_controller"><class type="nav2_pure_pursuit_controller::PurePursuitController" base_class_type="nav2_core::Controller"><description>This is pure pursuit controller</description></class>
</library>
- 3.下一步是使用
CMakeLists.txt
导出插件,使用 CMake 函数pluginlib_export_plugin_description_file()
。该函数将插件描述文件安装到share目录,并设置ament索引以使其可被发现。
pluginlib_export_plugin_description_file(nav2_core pure_pursuit_controller_plugin.xml)
- 4 插件描述文件还应添加到
package.xml
中。
<export><build_type>ament_cmake</build_type><nav2_core plugin="${prefix}/pure_pursuit_controller_plugin.xml" />
</export>
- 5.编译后,它应该被注册了。接下来,我们将使用这个插件。
3- 通过params文件传递插件名称
要启用该插件,我们需要修改nav2_params.yaml文件,如下所示:
controller_server:ros__parameters:controller_plugins: ["FollowPath"]FollowPath:plugin: "nav2_pure_pursuit_controller::PurePursuitController"debug_trajectory_details: Truedesired_linear_vel: 0.2lookahead_dist: 0.4max_angular_vel: 1.0transform_tolerance: 1.0
在上面的代码片段中,你可以看到我们将nav2_pure_pursuit_controller/PurePursuitController
控制器映射到其id FollowPath。为了传递插件特定的参数,我们使用了<plugin_id>.<plugin_specific_parameter>的格式。
4- 运行纯追踪控制器插件
使用启用了 Nav2 的 Turtlebot3 仿真。如何运行它的详细说明写在了Getting Started中。下面是一个快捷命令:
ros2 launch nav2_bringup tb3_simulation_launch.py params_file:=/path/to/your_params_file.yaml
然后转到 RViz,在顶部点击“2D姿势估计”按钮,并根据“入门指南”中的描述,在地图上指向位置。机器人将在地图上进行定位,然后点击“Nav2目标”,并点击您希望机器人导航到的姿势。之后,控制器将使机器人沿着全局路径行进。
相关文章:
编写 navigation2 控制器插件
简介 本教程展示了如何创建自己的控制器插件。在本教程中,我们将基于这篇论文实现纯追踪路径跟踪算法。建议您阅读该论文。 注意:本教程基于 Nav2 堆栈中以前存在的简化版本的 Regulated Pure Pursuit 控制器。您可以在此处找到与本教程相匹配的源代…...

计算机网络 第六章应用层
文章目录 1 应用层功能概述2 网络应用模型:客户服务器(CS)3 网络应用模型:PeerToPeer(P2P)4 域名和域名系统5 常见域名解析服务器6 两种域名解析过程7 什么是FTP8 FTP的工作原理9 EMail的组成 1 应用层功能概述 2 网络应用模型:客户服务器(CS…...
人工智能领域CCF推荐国际学术刊物最新目录(全)
2021年1月,CCF决定启动新一轮中国计算机学会推荐国际学术会议和期刊目录调整工作并委托CCF学术工作委员会组织实施。 2023年3月8日, 中国计算机学会正式发布了2022版《中国计算机学会推荐国际学术会议和期刊目录》(以下简称《目录》) 。 相较于上一版目录࿰…...

实现基于 Azure DevOps 的数据库 CI/CD 最佳实践
数据库变更一直是整个应用发布过程中效率最低、流程最复杂、风险最高的环节,也是 DevOps 流程中最难以攻克的阵地。那我们是否能在具体的 CI/CD 流程中,像处理代码那样处理数据库变更呢? DORA 调研报告 DORA(DevOps Research &am…...
上海实习小记
8月3日入职10月27日离职,原本还想做满3个月再走,可惜公司提早要迁到成都,就只好 离职了回学校了。在博客随便写写记录一下这几个月的生活吧,想到哪里写到哪里 实习的公司是一个小公司,开发一款类似于咸鱼之王的游戏&am…...

uniapp实现路线规划
UniApp是一个基于Vue.js框架开发的跨平台应用开发框架,可以同时构建iOS、Android、H5等多个平台的应用。它使用了基于前端技术栈的Web开发方式,通过编写一套代码,即可在不同平台上运行和发布应用。 UniApp具有以下特点: 跨平台开…...

飞利浦双串口51单片机485网关
主要功能将PC端的数据接收下来,分发到不同的设备,也是轮询设备数据读取回来,打包回传到PC端,数据包包头包尾识别,数据校验,接收超时处理,将协议结构化处理,协议的改动不需要改动程序…...

生态扩展:Flink Doris Connector
生态扩展:Flink Doris Connector 官网地址: https://doris.apache.org/zh-CN/docs/dev/ecosystem/flink-doris-connector flink的安装: tar -zxvf flink-1.16.0-bin-scala_2.12.tgz mv flink-1.16.0-bin-scala_2.12.tgz /opt/flinkflink环境…...

HarmonyOS(二)—— 初识ArkTS开发语言(上)之TypeScript入门
前言 Mozilla创造了JS,Microsoft创建了TS,而Huawei进一步推出了ArkTS。因此在学习使用ArkTS前,需要掌握基本的TS开发技能。 ArkTS介绍 ArkTS是HarmonyOS优选的主力应用开发语言。它在TypeScript(简称TS)的基础上&am…...

从零开始实现神经网络(一)_NN神经网络
参考文章:神经网络介绍 一、神经元 这一神经网络的基本单元,神经元接受输入,对它们进行一些数学运算,并产生一个输出。 这里有三步。 首先,将每个输入(X1)乘以一个权重: 接下来&…...

C语言 每日一题 Day10
1.使用函数判断完全平方数 本题要求实现一个判断整数是否为完全平方数的简单函数。 函数接口定义: int IsSquare(int n); 其中n是用户传入的参数,在长整型范围内。如果n是完全平方数,则函数IsSquare必须返回1,否则返回0。 代码实…...

C++继承——矩形和长方体
Rectangle矩形类 /*矩形类*/ class Rectangle { private:double L 0;double W 0; public:Rectangle() default;Rectangle(double a, double b);double GetArea(); /*矩形面积*/double GetGirth(); /*矩形周长*/ }; /*构造函数*/ Rectangle::Rectangle(double a, double b) …...

代码随想录打卡第五十八天|● 583. 两个字符串的删除操作 ● 72. 编辑距离
583. 两个字符串的删除操作 题目: 给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 题目链接: 583. 两个字符串的删除操作 解题思路: dp数组的含义&am…...
面试流程之——程序员如何写项目经验
在简历中介绍IT项目经验,你可以遵循以下步骤: 明确项目目标:首先,清晰地阐述项目的目标。这可以是提升某个软件的性能,改进某个系统的用户界面,或者增加某款产品的功能。让读者了解你的工作与项目的整体目…...

框架安全-CVE 漏洞复现DjangoFlaskNode.jsJQuery框架漏洞复现
目录 服务攻防-框架安全&CVE复现&Django&Flask&Node.JS&JQuery漏洞复现中间件列表介绍常见语言开发框架Python开发框架安全-Django&Flask漏洞复现Django开发框架漏洞复现CVE-2019-14234(Django JSONField/HStoreField SQL注入漏洞ÿ…...

基于SSM的理发店管理系统
基于SSM的理发店管理系统的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringSpringMVCMyBatis工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 公告信息 管理员界面 用户界面 摘要 基于SSM(Spring、Spring MVC、…...

2.Spark的工作与架构原理
概述 目标: spark的工作原理spark数据处理通用流程rdd 什么是rddrdd 的特点 spark架构 spark架构相关进程spark架构原理 spark的工作原理 spark 的工作原理,如下图 图中中间部分是spark集群,也可以是基于 yarn 的,图上可以…...

qt-C++笔记之带有倒计数显示的按钮,计时期间按钮锁定
qt-C笔记之带有倒计数显示的按钮,计时期间按钮锁定 code review! 文章目录 qt-C笔记之带有倒计数显示的按钮,计时期间按钮锁定1.运行2.main.cc3.main.pro 1.运行 2.main.cc 代码 #include <QApplication> #include <QPushButton> #includ…...
HTML全局属性(global attribute)有哪些?
HTML全局属性是指在HTML元素上可用的基本属性,它们适用于所有HTML元素。以下是一些常见的HTML全局属性: 1:class:为元素指定一个或多个类名,用于与CSS样式表关联。 2:id::为元素指定唯一的标识…...

MyBatis-Plus返回getOne返回null疑惑
getOne返回null 问题描述分析过程总结 问题描述 在数据库建了一张表主要包括两个字段master_id和slave_id;主要的额外字段max_lots 默认值是null; 当调用getOne进行查询结果是null,但实际情况是数据库时应该返回值的; AotfxMasterSlave ex…...

网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...