ROS-Navigation Move_base 源码阅读学习--恢复行为recovery_behavior(旋转恢复行为、代价地图清理恢复行为) - 教程

news/2025/10/21 11:00:15/文章来源:https://www.cnblogs.com/slgkaifa/p/19154501

ROS-Navigation Move_base 源码阅读学习--恢复行为recovery_behavior(旋转恢复行为、代价地图清理恢复行为) - 教程

在ROS的move_base导航栈中,恢复行为是确保系统鲁棒性的关键组件。它定义了一组当机器人无法正常前进时所执行的策略,旨在使机器人状态恢复到可继续执行全局规划路径的水平。

根据导航状态机的设计,在PLANNING状态失败后,系统会依次执行用户定义的恢复行为。move_base架构中的恢复行为类都是通过插件plugind的方式动态加载,派生类中主要对基类RecoveryBehavior中的initializerunBehavior两个函数进行重载,已实现自己定义的恢复行为。
其中,move_base中定义了两个最为核心和常用的恢复行为:clear_costmap_recoveryrotate_recovery

  • clear_costmap_recovery:通过清除局部/全局代价地图中指定层(如障碍物层)的信息,来解决因动态障碍物遗留、传感器噪点或代价地图膨胀区过大导致的“虚假堵塞”问题。

  • rotate_recovery:一种简单却极其有效的策略,通过控制机器人在原地旋转360度,利用其传感器重新感知周围环境,从而可能发现之前被遮挡的自由空间。

本篇博客将深入这两个恢复行为的实现原理。

旋转恢复行为 RotateRecovery

RotateRecovery的核心思路是:

  1. 让机器人原地 旋转一整圈(360°)。
  2. 这样它的传感器(激光雷达、深度相机等)可以扫描周围环境,刷新局部 costmap。
  3. 如果之前 costmap 上残留了错误的障碍物(比如动态障碍物已经走开),这个旋转动作会帮助清理。

核心代码:

double dist_left;
if (!got_180)  // 转到180°
{// 先强制让机器人至少旋转半圈(起点朝向 + 180°)// distance_to_180表示还差多少角度到180°double distance_to_180 = std::fabs(angles::shortest_angular_distance(current_angle, start_angle + M_PI));// 再继续旋转直到回到起点dist_left = M_PI + distance_to_180;if (distance_to_180 < tolerance_){got_180 = true;}
}
else  // 回到起点角度
{// 此时已经旋转半圈,计算当前角度与起点角度的差值dist_left = std::fabs(angles::shortest_angular_distance(current_angle, start_angle));
}
double x = global_pose.pose.position.x, y = global_pose.pose.position.y;
double sim_angle = 0.0;
while (sim_angle < dist_left)
{double theta = current_angle + sim_angle;// 每隔sim_granularity_ 检查一次 footprint 是否会碰撞double footprint_cost = world_model_->footprintCost(x, y, theta, local_costmap_->getRobotFootprint(), 0.0, 0.0);if (footprint_cost < 0.0){ROS_ERROR("Rotate recovery can't rotate in place because there is a potential collision. Cost: %.2f",footprint_cost);return;  // 发生碰撞,直接退出}sim_angle += sim_granularity_;
}
// 计算旋转速度,确保能在剩余角度内停下
double vel = sqrt(2 * acc_lim_th_ * dist_left);
// 限制在最小、最大旋转速度范围内
vel = std::min(std::max(vel, min_rotational_vel_), max_rotational_vel_);

这里仔细思考,为什么要分成先假装转180,然后再回到起点角度?如果单纯判断"还没有转360°",会出现一个问题:机器人转过一半的时候,当前角度和起点角度刚好相差 180°,这时候它其实有两个候选方向能继续往下走(顺时针 / 逆时针),可能会引发歧义。
所以代码里采用了两阶段的一个技巧,使得机器人可以保证确实转了一圈:

  1. 阶段一:先强制机器人至少转到起点角度+180°
  2. 阶段二:再继续旋转直到回到起点角度

对于为什么旋转恢复行为是转一圈,而不是转半圈或者其他圈数呢?
这是因为一整圈的扫描可以让局部代价地图刷新,尤其是对动态障碍物(人走开、椅子被挪动等)。半圈是无法照顾到的,多圈也没必要,显得很呆。

地图清理恢复行为 ClearCostmapRecovery

ClearCostmapRecovery 的本质就是在机器人位置附近定义一个窗口,然后根据参数选择清理 costmap(全局/局部)的障碍物层,清理范围可以是窗口内或外,从而“重置”代价地图,帮助机器人恢复可行的导航环境。

ClearCostmapRecovery::runBehavior

if (!invert_area_to_clear_){ // 清理内部区域ROS_WARN("Clearing %s costmap%s outside a square (%.2fm) large centered on the robot.", affected_maps_.c_str(),affected_maps_ == "both" ? "s" : "", reset_distance_);}else {ROS_WARN("Clearing %s costmap%s inside a square (%.2fm) large centered on the robot.", affected_maps_.c_str(),affected_maps_ == "both" ? "s" : "", reset_distance_);}ros::WallTime t0 = ros::WallTime::now();// 清理全局代价地图if (affected_maps_ == "global" || affected_maps_ == "both"){clear(global_costmap_);if (force_updating_)global_costmap_->updateMap(); // 立即刷新全局地图ROS_DEBUG("Global costmap cleared in %fs", (ros::WallTime::now() - t0).toSec());}t0 = ros::WallTime::now();// 清理局部代价地图if (affected_maps_ == "local" || affected_maps_ == "both"){clear(local_costmap_);if (force_updating_)local_costmap_->updateMap(); // 立即刷新局部代价地图ROS_DEBUG("Local costmap cleared in %fs", (ros::WallTime::now() - t0).toSec());}

ClearCostmapRecovery::clear

获取 costmap 的所有 Layer 插件,如果该 Layer 在 clearable_layers_ 集合中(可被清理),并且确实是 CostmapLayer 类型,那么调用clearMap()

for (std::vector >::iterator pluginp = plugins->begin(); pluginp != plugins->end(); ++pluginp) {boost::shared_ptr plugin = *pluginp;// 去掉namespace前缀std::string name = plugin->getName();int slash = name.rfind('/');if( slash != std::string::npos ){name = name.substr(slash+1);}if(clearable_layers_.count(name)!=0){ // 判断该层是否在可清理层集合中// 判断该层是否是CostmapLayerif(!dynamic_cast(plugin.get())){ROS_ERROR_STREAM("Layer " << name << " is not derived from costmap_2d::CostmapLayer");continue;}boost::shared_ptr costmap;costmap = boost::static_pointer_cast(plugin);clearMap(costmap, x, y);}}

ClearCostmapRecovery::clearMap

计算以机器人为中心、边长为 reset_distance 的正方形区域。如果 invert_area_to_clear=false,那么清理正方形外部区域;为true,则清理正方形内部区域

void ClearCostmapRecovery::clearMap(boost::shared_ptr costmap,double pose_x, double pose_y){boost::unique_lock lock(*(costmap->getMutex()));// 计算以机器人为中心的清理矩形区域,start_point(左下角)、end_point(右上角)double start_point_x = pose_x - reset_distance_ / 2;double start_point_y = pose_y - reset_distance_ / 2;double end_point_x = start_point_x + reset_distance_;double end_point_y = start_point_y + reset_distance_;int start_x, start_y, end_x, end_y;// 将世界坐标转化为地图索引坐标costmap->worldToMapNoBounds(start_point_x, start_point_y, start_x, start_y);costmap->worldToMapNoBounds(end_point_x, end_point_y, end_x, end_y);// 清除区域costmap->clearArea(start_x, start_y, end_x, end_y, invert_area_to_clear_);double ox = costmap->getOriginX(), oy = costmap->getOriginY();double width = costmap->getSizeInMetersX(), height = costmap->getSizeInMetersY();// 更新边界,保证代价地图线程能刷新变化costmap->addExtraBounds(ox, oy, ox + width, oy + height);return;
}

我已经将代码的注释开源,需要的自取(目前还在更新)。如果感觉对你有帮助,请点一个star,谢谢。

navigation代价注释仓库

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

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

相关文章

2025年10月无缝钢管推荐榜:五强对比评测与采购指南

无缝钢管是能源、化工、锅炉、船舶、机械五大行业的“血管”,项目一旦开工,管材的到货节奏、材质合规性、壁厚均匀度直接决定施工周期和后期安检。2025年国内在建火电、炼化一体化、氢能储运项目同步推进,带动无缝钢…

2025年10月股票开户券商推荐:五大主流平台对比评测榜

一、引言 对于计划进入A股、港股或基金市场的个人投资者而言,选择一家合规稳健、服务成熟、成本可控的券商是迈出投资第一步的关键。开户流程是否顺畅、交易通道是否稳定、后续投顾与产品资源是否丰富,直接影响后续资…

万象EXCEL开发(十)excel 高级混合查询 ——东方仙盟金丹期 - 教程

万象EXCEL开发(十)excel 高级混合查询 ——东方仙盟金丹期 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "…

实用指南:构建AI智能体:五十二、反应式智能体:AI世界的条件反射,真的可以又快又稳

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

redis-(伪)主从集群搭建

redis-(伪)主从集群搭建为了避免Redis的单点故障问题,可以搭建一个redis集群,将数据备份到集群中的其他节点上,如果其中一个 redis节点宕机,则由集群中的其他节点顶上。redis的主从集群是一个“一主多从”的读写…

za3J5cHRvc+WvhueggeWOn+aWhw

dEMUFPHZLRFAXYUSDJKZLDKRNSHGNFIVJYQTQUXQBQVYUVLLTREVJYQTMKYRDMFDVFPJUDEEHZWETZYVGWHKKQETGFQJNCEGGWHKK?DQMCPFQZDQMMIAGPFXHQRLGTIMVMZJANQLVKQEDAGDVFRPJUNGEUNAQZGZLECGYUXUEENJTBJLBQCRTBJDFHRRYIZETKZEMVDU…

结对项目:小学四则运算题目的命令行程序

小学四则运算题目生成器项目报告 一.项目信息项目 内容这个作业属于哪个课程 软件工程这个作业要求在哪里 结对项目这个作业的目标 设计实现小学四则运算题目生成器,支持题目生成、答案计算、重复性检测和自动批改,并…

微信小程序使用formdata采用multipart方式上传文件

微信小程序使用formdata采用multipart方式上传文件参考:https://juejin.cn/post/7220769136209051703 1 简介在微信原生小程序中,对于上传文件(图片,文件,语音,视频)都有自己的api最后使用wx.uploadFile上传到服…

10/21

关联的多重度是指 一个类的实例能够与另一个类的多少个实例相关联 事物 关系 图move不是斜体 代表是重载 不是继承组合关系 UML不对对象快照进行建模

五自由度机械臂阻抗控制下的力跟踪

概述 五自由度机械臂在阻抗控制下的力跟踪是一种先进的控制策略,使机械臂能够在与环境交互时表现出期望的柔顺行为,同时精确跟踪目标接触力。这种控制在装配、表面处理和人机交互等应用中至关重要。 阻抗控制基础 阻…

中国项目管理工具市场迎来技术驱动新纪元:Gitee引领双核协作革命

中国项目管理工具市场迎来技术驱动新纪元:Gitee引领"双核"协作革命 随着中国企业数字化转型进入深水区,项目管理工具正在经历从单一功能向智能协同平台的跃迁。在这场技术驱动的协作革命中,国产工具凭借本…

uploads-lab通关攻略

Pass-01(后缀) 上传php发现 发现弹窗给出了白名单文件类型.jpg|.png|.gif 仅仅过滤文件后缀,不允许为php burp抓包修改后缀即可上传成功 Pass-02(后缀) 方法同上前往显示地址查看测试成功 Pass-03(.phtml)新增过滤,…

DOS命令(cmd)

DOS命令(cmd)盘符切换:C:\Users\lenovo>D:(从c切换到d) 查看当前目录下的东西:D:>dir 切换目录:C:\Users\lenovo>cd /d(跨盘、写具体路径) d:\bin 返回上一级:cd .. 进入当前目录下的一个目录:直接cd …

初始化vue3项目和打包vue3项目

一、初始化vue3项目 执行命令:npm init vite@latest 二、打包vue3项目 生成打包产物在项目根目录运行打包命令,Vite 会将项目编译为静态文件(默认输出到 dist 目录):npm run build # 或 yarn build / pnpm bui…

Continuation Passing Style 连续传递样式

编辑于 2024年08月18日 20:07 Continuation就是回调 function a (num a, num b) => a + b变成 func a (num a, num b, func c) => c(a + b)有啥用? 转为尾递归 尾递归是啥? 递归就是函数调用自己 如果调用在最…

中国企业DevOps工具链选型指南:政务、出海与跨国协作的实战解析

中国企业DevOps工具链选型指南:政务、出海与跨国协作的实战解析 在数字化转型浪潮席卷各行各业的当下,DevOps工具链的选择已成为企业技术架构升级的关键决策。不同于早期简单的功能对比,如今企业需要从更复杂的场景…

【2025-10-17】首听EB病毒

20:00爱是一门艺术吗?回答是肯定的。因此,它需要知识和努力。——艾里希弗洛姆昨天下午带二宝看医生,也挺一波三折的,原以为半个小时可以结束的门诊,却足足看了两个小时。其实,我们周日已经带孩子看过医生了,但…

Bean 生命周期的关键阶段和详细流程

1. 实例化前(Bean 定义加载与解析) BeanDefinition 扫描与注册:Spring Boot 启动时,通过 @ComponentScan 扫描指定包(默认是主类所在包及其子包)下的类(如 @Component、@Service、@Controller 等注解标记的类)…

数字媒体技术-培优讲练-知识点总结

数字媒体技术-培优讲练-知识点总结 第一单元 “数字媒体概述” 核心内容知识模块 关键要点 具体说明 / 示例媒体三重含义 传播媒介物理媒介逻辑载体 传播媒介(电视 / 网络)物理媒介(手机 / 硬盘)逻辑载体(软件数据…

Jmeter解决响应乱码的问题

背景: 在请求里,加编码utf-8,也还是会乱码处理方案: 添加一个BeanShell后置处理器,写入以下内容,强制设置编码为utf-8 prev.setDataEncoding("UTF-8");