编写 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…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...

Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...