ROS导航包Navigation中的 Movebase节点路径规划相关流程梳理


在这里插入图片描述


   本文主要介绍ROS导航包Navigation中的 Movebase节点中的路径规划的相关流程,并对其进行梳理概括,同时本文也是《ROS局部路径规划器插件teb_local_planner规划流程概括总结》部分的前述文章。


在这里插入图片描述


   1、接收到目标点信息goal

   在接收到目标点goal之后,Move_base节点会调用回调函数goalCB,开始工作

goal_sub_ = simple_nh.subscribe<geometry_msgs::PoseStamped>("goal", 1, boost::bind(&MoveBase::goalCB, this, _1));

   2、goalCB函数

   goalCB函数仅仅将传入的 geometry_msgs:: PoseStamped 形式的goal转换成move_base_msgs:: MoveBaseActionGoal形式,再发布到对应类型的goal话题中,真正的执行函数是在构造函数里,actionlib server注册的executeCb函数。

 void MoveBase::goalCB(const geometry_msgs::PoseStamped::ConstPtr& goal)

   3、executeCb函数

   executeCb函数是move_base中路径规划的起始函数。将上述目标点传入到executeCb函数。

as_ = new MoveBaseActionServer(ros::NodeHandle(), "move_base", boost::bind(&MoveBase::executeCb, this, _1), false);
void MoveBase::executeCb(const move_base_msgs::MoveBaseGoalConstPtr& move_base_goal)

   (1)在executeCb函数中,首先需要判断传入目标点的四元数的合法性,包括四元数是否完整,是否趋于0,以及进行规范化等,

   (2)然后将该坐标转换到世界坐标系下,同时通过publisher发布该坐标用于可视化显示。

   (3)随后唤醒规划线程,通过参数controller_frequency设置控制频率,开启代价地图costmap更新,并重置时间标志位

   注:在唤醒规划线程的程序中,执行了planThread函数的线程启动,该步骤会调用planThread函数。该过程不是通过函数调用的形式实现的,而是通过线程开关的形式实现的,在planThread函数中调用了全局规划器插件进行了全局规划,planThread函数将在本文第4部分进行介绍

lock(planner_mutex_)  
boost::unique_lock<boost::recursive_mutex> lock(planner_mutex_);//给该线程上锁
planner_goal_ = goal;//传入目标并开启线程
runPlanner_ = true;
planner_cond_.notify_one();
lock.unlock();//打开互斥锁

   (4)完成以上步骤后,程序进人while循环,按照上述设定的频率controller_ frequency进行循环,在循环中会进行如下操作:

   ①:根据标志位判断是否更改循环的控制频率

if(c_freq_change_)
r = ros::Rate(controller_frequency_)

   ②:根据标志位isPreemptRequested判断是否有新的目标点,若有新的目标点,则需要重新执行上述的(1)~(3)中描述的过程。若无新的目标点,则重置当前状态,允许新的目标点进行抢占。

if(as_->isPreemptRequested())

   ③:判断目标点坐标系是否与全局坐标系相同,若不相同则需要进行转换

if(goal.header.frame_id != planner_costmap_ros_->getGlobalFrameID())

   ④:调用executeCycle函数进行局部路径规划

   注:在executeCycle函数中调用了局部规划器插件进行了局部规划,executeCycle函数将在本文第5部分进行介绍

bool done = executeCycle(goal, global_plan);

   ⑤:判断是否结束循环,若executeCycle的返回值为真,则结束循环

if(done)   return;

   ⑥:计算本次循环已经花费的时间,用设定的控制频率(循环频率)对应的时间与已花费的时间之差,作为本次循环的休眠时间。通过ROS的sleep机制进行休眠。

   此外,如果控制频率过高的话会输出一个警告信息,当时控制频率高于实际运算频率的时候终端会一直报警告:Control loop missed its desired rate of %.4fHz… the loop actually took %.4f seconds

r.sleep();
 //make sure to sleep for the remainder of our cycle time
if(r.cycleTime() > ros::Duration(1 / controller_frequency_) && state_ == CONTROLLING)
ROS_WARN("Control loop missed its desired rate of %.4fHz... the loop actually took %.4f seconds", controller_frequency_, r.cycleTime().toSec());

   循环执行上面的①~⑥步

   (5)若上述循环是否因为各种原因导致节点被终止,则需要对当前的状态进行一定的处理


   4、planThread函数

   planThread函数在executeCb函数唤醒规划线程时被调用

   该线程创建以后会因为 runPlanner_ 值为false而挂起。当executeCb中唤醒线程前一刻时runPlanner_被设置为true。在这之后就正常情况下面没有被设置为fale过

   所以该线程被唤醒以后就不再进入等待的循环中,而是一直在大循环执行, 每个大循环都会调用一次 makeplan函数来调用全局路径规划器进行全局路径规划,并重新把新路径装入planner_plan_容器中(更新了路径) ,在executeCB中没有意外发生时只会唤醒一次该线程,以后不再唤醒。会一致执行大循环每个循环下发一次控制速度,但是每次循环中的路径可能会由于该线程一直在跑的缘故而发生改变。

planner_thread_ = new boost::thread(boost::bind(&MoveBase::planThread, this));
void MoveBase::planThread()

   (1)在planThread函数中,首先设置目标点,清空路径存储容器

geometry_msgs::PoseStamped temp_goal = planner_goal_;
...
planner_plan_->clear();

   (2)接着是本函数的核心部分,调用了makePlan函数,进而调用全局路径规划器插件进行全局规划,如果成功,结果将储存进planner_plan_中。所有的全局路径规划器插件必然包含一个makePlan函数提供给move_base,从而在此处进行调用。

bool gotPlan = n.ok() && makePlan(temp_goal, *planner_plan_);
planner_->makePlan(start, goal, plan) 

   (3)若得到了可用的全局路径,则将标志位new_global_plan_ 置为ture,供局部路径规划使用

new_global_plan_ = true;

   5、executeCycle函数

   executeCB是路径规划的启动函数,其过程中通过调用完成了全局路径的规划,executeCB函数在上文介绍的3 -(4)- ④ 步骤中调用的executeCycle是局部路径规划的正式执行函数,在executeCycle函数中的setplan函数中将调用局部路径规划器插件进行局部路径规划。

bool MoveBase::executeCycle(geometry_msgs::PoseStamped& goal, std::vector<geometry_msgs::PoseStamped>& global_plan)

   (1)在executeCycle函数中,首先获取机器人当前时刻在世界坐标系的位姿,并传递给feedback进行反馈和发布。

getRobotPose(global_pose, planner_costmap_ros_);

   (2)振荡检查,机器人每运动一段距离会记录一下当前时间以及位姿,其中current_ position是当前位姿,oscillation_pose_是上一个进入该判断时的位姿,参数oscillation_distance_是常数,默认值为0.5m

if(distance(current_position, oscillation_pose_) >= oscillation_distance_)

   (3)检查代价地图近期是否得到了更新。如果长时间没有更新,意味着机器人失去了周围的感知能力, 此时移动机器人是危险的,通过publishZeroVelocity 控制机器人停下来并退出运动逻辑。

if(!controller_costmap_ros_->isCurrent())

   (4)判断是否得到的新的全局路径,即new_global_plan_ 的值是否为True,若是,则调用setplan函数,储存全局路径,进而在需要的时候传递给局部路径规划使用,所有的局部路径规划器都应该有一个setplan函数作为接口供move_base进行调用来传递全局路径信息。

if(new_global_plan_)
if(!tc_->setPlan(*controller_plan_))

   (5)在将路径传入给局部路径规划函数后,executeCycle函数根据当前状态state_进行下一步的控制。

switch(state_)

   state_的状态分为三种:PLANNING、CONTROLLING、CLEARING。state_在全局路径规划之前被设置为PLANNING,代表当前处于路径规划阶段;然后在全局路径规划函数planThread中,完成全局路径规划并得到路径后被设置为CONTROLLING,代表当前路径成功,进入控制模式使机器人到达目标点;而在全局路径规划失败或者局部路径规划失败且超过重试次数时都会被置为CLEARING,代表取消当前规划。

   ①、若当前状态为PLANNING,说明发生的异常情况。全局路径规划没有完成就开始执行局部路径规划了,这时候算法会重新启动全局路径规划线程。
   注:(可在导航包中查询lock(planner_mutex_) 来查询启动全局规划的地方)

   ②、若当前状态为CLEARING,则全局路径规划失败了或者局部路径规划失败了。此时要考虑是否进行重新规划。

   如果重试的次数recovery_index_小于设置的次数recovery_behaviors_.size(),则调用全局路径规划看有没有新的路可以走。否则,根据设置的参数变量recovery_trigger_的状态进行对应的报错。

if(recovery_behavior_enabled_ && recovery_index_ < recovery_behaviors_.size())

   ③、若当前状态为CONTROLLING,也就是我们希望的正常状态,则先判断机器人是否到达目标点

if(tc_->isGoalReached())

   在LatchedStopRotateController::isGoalReached函数中,首先判断了当前坐标与目标点之间的距离是否小于设定值xy_goal_tolerance。如果满足该要求说明机器人已经在目标点附近,这时候算法再判断机器人的角度值是否复合设定阈值yaw_goal_tolerance,如果两个条件都满足说明机器人已经到达目标点。则返回true。则move_base的运动会设置为Succeeded。

   判断机器人是否处于震荡中,

 if(oscillation_timeout_ > 0.0 && last_oscillation_reset_ + ros::Duration(oscillation_timeout_) < ros::Time::now())

   若是,则发布将速度置为0的指令,并将状态置为CLEARING

   计算机器人的速度,通过调用局部规划器的computeVelocityCommands函数实现

if(tc_->computeVelocityCommands(cmd_vel))

   这里的computeVelocityCommands函数是move_base调用局部路径规划器插件进行局部规划的接口函数,每个局部路径规划器插件都要有一个computeVelocityCommands函数作为被调用的接口

   如果规划成功,则下发速度到cmd_vel;

   如果规划失败,此时如果如果距离上次成功的规划超过了容忍时间controller_patience_则将机器人停止,取消规划发布错误信息,如果此时还没超出容忍时间,则算法会再接受一下当前的状态,将速度改为0同时设置state_为PLANNING重新进行一次全局路径规划



在这里插入图片描述

   总的来说或Move_base在接收到目标点信息后,会通过回调函数,调用goalCB函数进行目标点格式的转换,真正的执行函数是构造函数里actionlib server注册的executeCb函数。executeCb函数也是move_base节点的主循环函数,按照设定的频率进行规划,executeCb函数启动规划线程的时候会调用planThread函数,而planThread函数中会调用 makeplan函数进行全局路径规划,也就是move_base节点调用全局规划器插件的接口函数 。

   executeCb函数还会调用executeCycle函数函数,通过setplan函数将得到的全局路径传递给局部路径规划器,在这个函数中会根据机器人当前的状态进行进一步处理,若为CONTROLLING,则说明为正常状态,调用 computeVelocityCommands函数进行局部路径规划 ,这个函数也是move_base节点调用局部规划器插件的接口函数。

   对于异常的PLANNING和CLEARING状态,则会根据情况决定是否要调用planThread函数重新尝试进行全局规划。


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

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

相关文章

《站在巨人的肩膀上学习Java》

Java从诞生距今已经有28年了&#xff0c;在这段时间里&#xff0c;随着Java版本的不断迭代&#xff0c;Java新特性的不断出现&#xff0c;使得Java被使用的越来越广泛。在工程界Java语言一直是大家最喜欢的语言之一&#xff0c;Java一直排行在编程语言热门程度的前3名。 可想而…

基于rke部署的k8s集群如何配置kube-proxy工作在ipvs模式

kube-proxy默认工作在iptables模式下&#xff0c;在集群配置文件cluster.yml中添加如下配置项即可开启ipvs模式。然后执行 rke up 命令使配置生效。

开源Stylegan人脸生成预训练模型

最近在研究Stylegan对抗式图像生成网络&#xff0c;使用了网络的一些预训练模型生成相应的图像&#xff0c;感觉非常有趣。下面开源一些我找到了预训练模型和代码&#xff0c;供大家一起玩。 Stylegan2官方给出的是TensorFlow版本的&#xff0c;费了半天劲找出了pytorch版本 这…

SpringAOP

SpringAOP 一、AOP1. AOP简介1.1 AOP简介和作用1.2 AOP中的核心概念 2. AOP入门案例【重点】2.1 AOP入门案例思路分析2.2 AOP入门案例实现【第一步】导入aop相关坐标【第二步】定义dao接口与实现类【第三步】定义通知类&#xff0c;制作通知方法【第四步】定义切入点表达式、配…

【Java】类和对象,封装

目录 1.类和对象的定义 2.关键字new 3.this引用 4.对象的构造及初始化 5.封装 //包的概念 //如何访问 6.static成员 7.代码块 8.对象的打印 1.类和对象的定义 对象&#xff1a;Java中一切皆对象。 类&#xff1a;一般情况下一个Java文件一个类&#xff0c;每一个类…

【Hello Network】网络编程套接字(一)

作者&#xff1a;小萌新 专栏&#xff1a;网络 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;简单介绍网络的基础概念 网络编程套接字&#xff08;一&#xff09; 预备知识源ip和目的ip端口号TCP和UDP协议网络中的字节序 socket编程接口socket常…

前端存储二:indexedDB

indexedDB 特点&#xff1a;以域名纬度&#xff0c;浏览器大量结构化数据存储方案&#xff0c;运行在浏览器的非关系型数据库。 大小&#xff1a;不会小于 250MB&#xff0c;支持二进制存储。 接口&#xff1a;异步接口&#xff0c;支持事物机制 这里使用网页脚本生成&#x…

2023五一数学建模B题完整模型代码【原创首发】

已经完成五一数学建模全部内容&#xff0c;大家可以文末查看&#xff01;&#xff01;供参考使用&#xff01; 摘要 随着网络购物的普及和发展&#xff0c;快递行业需求持续增长&#xff0c;对于快递公司来说&#xff0c;准确预测运输需求以及合理规划运输线路和仓库布局变得…

symfonos 2

目录 扫描 SMB SSH 提权 扫描 由于端口80是打开的,我们试图在浏览器中打开IP地址,但在网页上没有找到任何有用的信息。我们还尝试了dirb和其他目录暴力工具,但没有找到任何东西。 SMB 为了进一步枚举,我们使用Enum4Linux工具并找到了一些有用的信息。我们发现了一个名…

ChatGPT 平替天花板:HuggingFace 版 ChatGPT 来了,无需魔法无需等待直接起飞 ~

文章目录 ChatGPT 平替天花板&#xff1a;HuggingFace 版 ChatGPT 来了&#xff0c;无需魔法无需等待直接起飞 ~HuggingFace 简介HuggingChat 登场展望 ChatGPT 平替天花板&#xff1a;HuggingFace 版 ChatGPT 来了&#xff0c;无需魔法无需等待直接起飞 ~ 二话不说上链接 htt…

TryHackMe-Lunizz CTF(boot2root)

Lunizz CTF 端口扫描 循例nmap Web枚举 进80&#xff0c;apache默认页面 gobuster扫一下目录 /hidden一个文件上传点, 图片上传后无权访问/hidden/uploads/ /whatever一个假的命令执行点 /instructions.txt 由 CTF_SCRIPTS_CAVE 制作&#xff08;不是真实的&#xff09;感谢…

Java中几种常量池面试总结

字符串常量池&#xff08;string pool&#xff09; 字符串常量池是JVM为了提升性能和减少内存消耗针对字符串&#xff08;String类&#xff09;专门开辟的一块区域&#xff0c;主要目的是为了避免字符串的重复创建。 当需要使用字符串时&#xff0c;先去字符串池中查看该字符…

java spring 实现 下载hls(m3u8+ts)实时流并进行合并mp4和压缩

参考连接 链接: java下载m3u8视频&#xff0c;解密并合并ts&#xff08;三&#xff09; 链接: Java 下载 HLS (m3u8) 视频 首先需要了解什么是HLS 链接: HTTP Live Streaming (HLS) - 概念 链接: M3U8是什么 简单理解就是, m3u8文件存放着可供客户端播放TS 片段 简单一点…

新写了的 AOP 日志切面,方便以后直接使用。

前言 最近项目进入联调阶段&#xff0c;服务层的接口需要和协议层进行交互&#xff0c;协议层需要将入参[json字符串]组装成服务层所需的json字符串&#xff0c;组装的过程中很容易出错。入参出错导致接口调试失败问题在联调中出现很多次&#xff0c;因此就想写一个请求日志切…

二叉搜索树(BST)详解

文章目录 性质二叉搜索树的遍历遍历伪代码实现 二叉搜索树的查找伪代码实现 二叉搜索树最大元素伪代码实现 二叉搜索树最小元素伪代码实现 二叉搜索树的插入伪代码实现 二叉搜索树的删除删除叶子节点&#xff08;对应上面第一种情况&#xff09;&#xff1a;删除度为1的节点&am…

多维时序 | MATLAB实现BP神经网络多变量时间序列预测(考虑历史特征的影响,多指标、多图输出)

多维时序 | MATLAB实现BP神经网络多变量时间序列预测(考虑历史特征的影响,多指标、多图输出) 目录 多维时序 | MATLAB实现BP神经网络多变量时间序列预测(考虑历史特征的影响,多指标、多图输出)预测效果基本介绍程序设计学习总结参考资料预测效果 基本介绍 MATLAB实现BP神经网…

【Fluent】接着上一次计算的结果继续计算,利用计算过程中得到的物理场(温度、速度、压力等)插值Interpolate文件初始化模型的方法

一、问题背景 因为fluent中支持的初始化无非三种类型。 1、Standard initialization 标准初始化 2、Hybridinitialization 混合初始化 3、FMG initialization FMG初始化 另外&#xff0c;还可以用UDF通过坐标判断的方式予以初始化。 但是这些初始化方法都没办法利用以前计算过…

电子数据取证之宝塔面板

一、宝塔面板介绍 1、官网bt.com&#xff0c;是提升运维效率的服务器管理软件&#xff0c;支持一键WAMP/LAMP/LNMP等100多项服务器管理功能&#xff1b;是跨平台的软件&#xff0c;同时支持Windows和Linux。开源永久免费。提高工作效率&#xff0c;对小白比较友好。 2、怎么看服…

操作系统2(多处理器编程)

一、并发 1.操作系统是最早的并发程序之一 2.并发的基本单位&#xff1a;线程 共享内存的多个执行流 执行流拥有独立的堆栈/寄存器共享全部的内存&#xff08;指针可以互相引用&#xff09; 3.实现原子性 lock(&lk)unlock(&lk) 实现临界区(critical section)之间…

Redis监控步骤get!Google精髓的四大法则直接掌握

Redis也是对外服务&#xff0c;所以Google四个黄金指标同样适用&#xff0c;还从延迟、流量、错误、饱和度分析Redis关键指标。 1 延迟 选择Redis是想得到更快响应速度和更高吞吐量&#xff0c;所以延迟数据对使用Redis的应用程序至关重要。 1.1 如何监控延迟 ① 客户端应用…