【Navigation】global_planner 源码解析

news2024/9/28 15:25:23

全局规划器 global_planner 功能包

文章目录

  • global_planner 功能包结构
  • 1、plan_node.cpp
  • 2、planner_core.cpp
  • 3、astar.cpp
  • 4、dijkstra.cpp
  • 5、quadratic_calculator.cpp
  • 6、grid_path.cpp
  • 7、gradient_path.cpp
  • 8、orientation_filter.cpp

全局规划大都基于静态地图进行规划,产生路径点,然后给到局部规划器进行局部规划,ROS 中常见的全局规划器功能包有 navfn、global_planner(Dijkstra、A*)、asr_navfn、Movelt! (常用于机械臂)等。

global_planner 功能包结构

在这里插入图片描述

  • plan_node.cpp 文件是全局规划的入口;
  • planner_core.cpp 是 global_planner 的核心,进行初始化,调用A*或者Dijkstra进行全局规划,生成搜索路径
    • astar.cpp
    • dijkstra.cpp
  • quadratic_calculator.cpp(二次逼近方式,常用) 和 potential_calculator(直接返回当前点代价最小值,累加前面的代价值)在生成搜索路径中用到
  • 搜索到路径后使用回溯 grid_path.cpp(栅格路径)、gradient_path.cpp(梯度路径)获得最终路径;
    • 栅格路径:从终点开始找上下左右四个点中最小的栅格直到起点
    • 梯度路径:从周围八个栅格中找到下降梯度最大的点
  • 之后 orientation_filter.cpp 进行方向滤波。

根据nav_core提供的BaseGlobalPlanner接口:

  • initialize(name, costmap) ——算法实例的选取
  • makePlan(start, goal, plan)——两个步骤完成路径的生成(①计算可行点矩阵potential_array (planner_->calculatePotentials) → ②从可行点矩阵中提取路径plan (path_maker_->getPath))

主要是以下三个实例:

  1. 计算“一个点”的可行性 —— p_calc_:PotentialCalculator::calculatePotential()、 QuadraticCalculator::calculatePotential()(quadratic_calculator.cpp)
  2. 计算“所有”的可行点 —— planner_:DijkstraExpansion::calculatePotentials()、 AStarExpansion::calculatePotentials()(astar.cpp、dijkstra.cpp)
  3. 从可行点中“提取路径” —— path_maker_:GridPath::getPath()、 GradientPath::getPath()(grid_path.cpp、gradient_path.cpp)

涉及到四个算法程序:A*, Dijkstra;gradient_path, grid_path

可以总结出global_planner框架:

在这里插入图片描述

1、plan_node.cpp

/*********************************************************************
 * 这段代码是一个ROS节点,实现了一个基于代价地图(costmap)的全局路径规划器。
 * 节点初始化了一个全局规划器对象 PlannerWithCostmap,并提供了ROS服务和订阅者,
 * 允许外部调用路径规划服务或通过订阅目标位姿来触发路径规划。
 * 其中使用了ROS中的TransformListener来获取机器人位姿的变换信息。整个节点在main函数中被初始化并启动。
 *********************************************************************/
#include <a_global_planner/planner_core.h>
#include <navfn/MakeNavPlan.h>
#include <boost/shared_ptr.hpp>
#include <costmap_2d/costmap_2d_ros.h>
#include <tf2_ros/transform_listener.h>

namespace cm = costmap_2d;
namespace rm = geometry_msgs;

using std::vector;
using rm::PoseStamped;
using std::string;
using cm::Costmap2D;
using cm::Costmap2DROS;

namespace a_global_planner {
   

// 创建一个继承自AGlobalPlanner的类,名为PlannerWithCostmap
class PlannerWithCostmap : public AGlobalPlanner {
   
    public:
        PlannerWithCostmap(string name, Costmap2DROS* cmap);
        bool makePlanService(navfn::MakeNavPlan::Request& req, navfn::MakeNavPlan::Response& resp);

    private:
        void poseCallback(const rm::PoseStamped::ConstPtr& goal);
        Costmap2DROS* cmap_;
        ros::ServiceServer make_plan_service_;
        ros::Subscriber pose_sub_;
};

// 2、实现makePlanService服务
bool PlannerWithCostmap::makePlanService(navfn::MakeNavPlan::Request& req, navfn::MakeNavPlan::Response& resp) {
   
    vector<PoseStamped> path;

    req.start.header.frame_id = "map";
    req.goal.header.frame_id = "map";

    // 调用makePlan函数进行路径规划
    bool success = makePlan(req.start, req.goal, path);
    resp.plan_found = success;
    if (success) {
   
        resp.path = path;
    }

    return true;
}

// 3、处理目标位姿的回调函数
void PlannerWithCostmap::poseCallback(const rm::PoseStamped::ConstPtr& goal) {
   
    geometry_msgs::PoseStamped global_pose;
    // 通过Costmap2DROS对象获取机器人当前的全局位姿信息
    cmap_->getRobotPose(global_pose);
    // 创建一个vector<PoseStamped>类型的变量path,用于存储规划得到的路径
    vector<PoseStamped> path;
    // 调用makePlan函数进行路径规划,传递起始位姿为当前机器人位姿(global_pose),目标位姿为传入的目标位姿(*goal),规划结果存储在path中
    makePlan(global_pose, *goal, path);
}

// 1、构造函数
PlannerWithCostmap::PlannerWithCostmap(string name, Costmap2DROS* cmap) :
        AGlobalPlanner(name, cmap->getCostmap(), cmap->getGlobalFrameID()) {
   
    ros::NodeHandle private_nh("~");
    // 将传入的Costmap2DROS指针赋值给成员变量cmap_
    cmap_ = cmap;
    // 创建ROS服务,服务名为 "make_plan",回调函数为 &PlannerWithCostmap::makePlanService,this指向当前对象
    make_plan_service_ = private_nh.advertiseService("make_plan", &PlannerWithCostmap::makePlanService, this);
    // 创建ROS订阅者,订阅名为 "goal" 的消息,消息类型为 rm::PoseStamped,回调函数为 &PlannerWithCostmap::poseCallback,this指向当前对象
    pose_sub_ = private_nh.subscribe<rm::PoseStamped>("goal", 1, &PlannerWithCostmap::poseCallback, this);
}

} // namespace

int main(int argc, char** argv) {
   
    ros::init(argc, argv, "a_global_planner");

    tf2_ros::Buffer buffer(ros::Duration(10));
    tf2_ros::TransformListener tf(buffer);

    costmap_2d::Costmap2DROS lcr("costmap", buffer);

    a_global_planner::PlannerWithCostmap pppp("planner", &lcr);

    ros::spin();
    return 0;
}


2、planner_core.cpp

//register this planner as a BaseGlobalPlanner plugin
// 首先,注册全局路径插件,使其成为ros插件,在ros中使用
PLUGINLIB_EXPORT_CLASS(a_global_planner::AGlobalPlanner, nav_core::BaseGlobalPlanner)

initialize()

// 1、
void AGlobalPlanner::initialize(std::string name, costmap_2d::Costmap2DROS* costmap_ros) {
   
    initialize(name, costmap_ros->getCostmap(), costmap_ros->getGlobalFrameID());
}

// 2、进行初始化
void AGlobalPlanner::initialize(std::string name, costmap_2d::Costmap2D* costmap, std::string frame_id) {
   
    if (!initialized_) {
   
        ros::NodeHandle private_nh("~/" + name);
        costmap_ = costmap;
        frame_id_ = frame_id;

        unsigned int cx = costmap->getSizeInCellsX(), cy = costmap->getSizeInCellsY();

        // 参数:
        private_nh.param("old_navfn_behavior", old_navfn_behavior_, false);
        if(!old_navfn_behavior_)
            convert_offset_ = 0.5;
        else
            convert_offset_ = 0.0;

        bool use_quadratic;      // 是否二次逼近获取路径
        private_nh.param("use_quadratic", use_quadratic, true);
        if (use_quadratic)
            p_calc_ = new QuadraticCalculator(cx, cy);
        else
            p_calc_ = new PotentialCalculator(cx, cy);

        bool use_dijkstra;      // 是否使用dijkstra全局规划
        private_nh.param("use_dijkstra", use_dijkstra, true);
        if (use_dijkstra)
        {
   
            ROS_INFO("use_dijkstra");
            DijkstraExpansion* de = new DijkstraExpansion(p_calc_, cx, cy);
            if(!old_navfn_behavior_)
                de->setPreciseStart(true);
            planner_ = de;
        }
        else{
   
            ROS_INFO("use_A_star");
            planner_ = new AStarExpansion(p_calc_, cx, cy);
        }

        bool use_grid_path;     // 是否使用栅格路径
        private_nh.param("use_grid_path", use_grid_path, false);
        if (use_grid_path)
            path_maker_ = new GridPath(p_calc_);        // new 出 path_maker_ 实例,从可行点中提取路径
        else
            path_maker_ = new GradientPath(p_calc_);

        orientation_filter_ = new OrientationFilter();      // 方向滤波

        // 路径发布
        plan_pub_ = private_nh.advertise<nav_msgs::Path>("plan", 1);
        // 视场显示,一般不用
        potential_pub_ = private_nh.advertise<nav_msgs::OccupancyGrid>("potential", 1);

        // 是否探索未知区域,flase--不可到达
        private_nh.param("allow_unknown", allow_unknown_, true);
        planner_->setHasUnknown(allow_unknown_);
        // 窗口信息
        private_nh.param("planner_window_x", planner_window_x_, 0.0);
        private_nh.param("planner_window_y", planner_window_y_, 0.0);
        private_nh.param("default_tolerance", default_tolerance_, 0.0);
        private_nh.param("publish_scale", publish_scale_, 100);
        private_nh.param("outline_map", outline_map_, true);

        make_plan_srv_ = private_nh.advertiseService("make_plan", &AGlobalPlanner::makePlanService, this);

        dsrv_ = new dynamic_reconfigure::Server<a_global_planner::GlobalPlannerConfig>(ros::NodeHandle("~/" + name));
        dynamic_reconfigure::Server<a_global_planner::GlobalPlannerConfig>::CallbackType cb =
                [this](auto& config, auto level){
    reconfigureCB(config, level); };
        dsrv_->setCallback(cb);

        initialized_ = true;
    } else
        ROS_WARN("This planner has already been initialized, you can't call it twice, doing nothing");

}

makePlan 函数 —— 主要函数
通过输入起始位姿、目标点,返回 plan 路径结果,调用 makePlan 函数
关键方法是:

  • calculatePotentials()
    • 若全局规划器选择A* ,就去 astar.cpp 中找;选择 Dijkstra 就去 dijkstra.cpp 中找
  • getPlanFromPotential()
// 3、
bool AGlobalPlanner::makePlan(const geometry_msgs<

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

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

相关文章

加密的手机号如何模糊查询?

1 一次加载到内存 实现这个功能&#xff0c;我们第一个想到的办法可能是&#xff1a;把个人隐私数据一次性加载到内存中缓存起来&#xff0c;然后在内存中先解密&#xff0c;然后在代码中实现模糊搜索的功能。 这样做的好处是&#xff1a;实现起来比较简单&#xff0c;成本非常…

约数个数和约数之和算法总结

知识概览 约数个数 由算数基本定理 可得对于N的任何一个约数d&#xff0c;有 因为N的每一个约数和~的一种选法是一一对应的&#xff0c;根据乘法原理可得&#xff0c; 一个数的约数个数为 约数之和 一个数的约数之和公式为 多项式乘积的每一项为 正好对应的是一个数的每一个约…

计算机Java项目|Springboot疫情网课管理系统

项目编号&#xff1a;L-BS-ZXBS-07 一&#xff0c;环境介绍 语言环境&#xff1a;Java: jdk1.8 数据库&#xff1a;Mysql: mysql5.7 应用服务器&#xff1a;Tomcat: tomcat8.5.31 开发工具&#xff1a;IDEA或eclipse 二&#xff0c;项目简介 疫情网课也都将通过计算机…

jmeter线程组

特点&#xff1a;模拟用户&#xff0c;支持多用户操作&#xff1b;可以串行也可以并行 分类&#xff1a; setup线程组&#xff1a;初始化 类似于 unittest中的setupclass 普通线程组&#xff1a;字面意思 teardown线程组&#xff1a;环境恢复&#xff0c;后置处理

2.3 A VECTOR ADDITION KERNEL

我们现在使用矢量加法来说明CUDA C程序结构。矢量加法可以说是最简单的数据并行计算&#xff0c;是顺序编程中Hello World的并行等价物。在我们展示向量加法的内核代码之前&#xff0c;首先回顾传统的向量加法&#xff08;主机代码&#xff09;函数的工作原理是有帮助的。图2.5…

阿里云服务器Centos安装宝塔面板

阿里云服务器Centos安装宝塔面板 1 背景1.1 aliyun1.2 Linux 2 安装步骤2.0 环境配置2.1 安装前准备2.2 宝塔安装2.3 建站 3 centos常用命令3.1 防火墙相关 1 背景 1.1 aliyun 阿里云服务器是阿里云提供的一项云计算服务&#xff0c;它能够帮助用户快速搭建网站、应用和服务&…

专栏序言-GDB高级调试技巧实战

工欲善其事必先利其器&#xff01; GDB耍的溜&#xff0c;BUG找的快&#xff01; 在软件开发的世界里&#xff0c;调试是不可避免的。对于C/C开发者来说&#xff0c;GDB就是那把调试利器&#xff01;如果你只知道单步调试&#xff0c;那定位问题的速度肯定是龟速&#xff1b…

FSMC—扩展外部SRAM

一、SRAM控制原理 STM32控制器芯片内部有一定大小的SRAM及FLASH作为内存和程序存储空间&#xff0c;但当程序较大&#xff0c;内存和程序空间不足时&#xff0c;就需要在STM32芯片的外部扩展存储器了。STM32F103ZE系列芯片可以扩展外部SRAM用作内存。 给STM32芯片扩展内存与给…

Arduion Modbus通讯示例

实现了Arduion和Qt上位机利用Modbus协议采集DHT11数据&#xff0c;以及开关LED灯 软件界面&#xff1a; 实物界面&#xff1a; arduion下位机代码&#xff1a; #include <ModbusRtu.h> #include <DHT.h>#define DHTPIN 2 // DHT11连接到Arduino的数字引…

强化学习5——动态规划在强化学习中的应用

动态规划在强化学习中的应用 基于动态规划的算法优良 &#xff1a;策略迭代和价值迭代。 策略迭代分为策略评估和策略提升&#xff0c;使用贝尔曼期望方程得到一个策略的状态价值函数&#xff1b;价值迭代直接使用贝尔曼最优方程进行动态规划&#xff0c;得到最终的最优状态价…

【设计模式】迭代器模式

一起学习设计模式 目录 前言 一、概述 二、结构 三、案例实现 四、优缺点 五、使用场景 六、JDK源码解析 总结 前言 【设计模式】迭代器模式——行为型模式。 一、概述 定义&#xff1a; 提供一个对象来顺序访问聚合对象中的一系列数据&#xff0c;而不暴露聚合对象…

前端-基础 常用标签-超链接标签( 锚点链接 )

锚点链接 &#xff1a; 点击链接&#xff0c;可以快速定位到 页面中的某个位置 如果不好理解&#xff0c;讲一个例子&#xff0c;您就马上明白了 >>> 这个是 刘德华的百度百科 &#xff0c;可以看到&#xff0c;页面里面有很多内容&#xff0c;那就得有个目录了 …

贝锐蒲公英云智慧组网解读:实现工业设备远程调试、异地PLC互联

这个时候&#xff0c;使用异地组网是非常有效的解决方案。在12月28日贝锐官方的直播中&#xff0c;请到了贝锐蒲公英的技术研发经理&#xff0c;为大家分享了贝锐蒲公英云智慧组网解决方案&#xff0c;以及蒲公英二层组网相关的技术和应用。 搜索“贝锐”官方视频号&#xff0c…

C动态内存分配(被调函数内部指针内存分配)

void *malloc(size_t size); void free(void *); malloc在内存的动态存储区中分配一块长度为size字节的连续存储空间返回该区域的首地址与c中的内存分配new和delete作用相同&#xff08;但c的可适用范围更广&#xff09; 当在栈区&#xff0c;被调函数之外需要使用被调函数内部…

python自动化测试面试题与答案汇总

对于机器学习算法工程师而言,Python是不可或缺的语言,它的优美与简洁令人无法自拔,下面这篇文章主要给大家介绍了关于30道python自动化测试面试题与答案汇总的相关资料,需要的朋友可以参考下 1、什么项目适合做自动化测试&#xff1f; 关键字&#xff1a;不变的、重复的、规范…

程序语言相关知识——偏向Eigen矩阵

1 查看 Eigen库表示的矩阵 方法 1.1 列矩阵x在监视中&#xff0c;这样查看&#xff0c;数值右侧的圈圈 可用于更新数值 随程序 1.2 比较全的方法&#xff1a;来自于知乎&#xff1a;https://zhuanlan.zhihu.com/p/625334009?utm_id0 1.3 eigen的用法&#xff1a;https://ww…

[JavaWeb玩耍日记]JDBC(不常用)

项目结构 目录 一.快速入门 二.开启事务 三.sql执行对象的executeUpdate方法 四.查询数据库 五.SQL注入案例 六.使用PreparedStatement防止Sql注入 七.数据库连接池 一.快速入门 创建新项目&#xff0c;导入mysql-connector-java-5.1.48的jar包1.使用JDBC更新一条数据有…

缘分的计算

题目描述&#xff1a; 缘分是一个外国人难以理解的中文名词。大致说来&#xff0c;缘分是一种冥冥中将两人&#xff08;通常是情人&#xff09;结合的力量。仅管这是种迷信&#xff0c;很多人——特别是女生——喜欢去计算它。 不幸的是&#xff0c;644 也是这样。有天&#x…

其他排序(基数排序,希尔排序和桶排序)(数据结构课设篇3,python版)(排序综合)

本篇博客主要详细讲解一下其他排序&#xff08;基数排序&#xff0c;希尔排序和桶排序&#xff09;也是排序综合系列里最后一篇博客。第一篇博客讲解的是LowB三人组&#xff08;冒泡排序&#xff0c;插入排序&#xff0c;选择排序&#xff09;&#xff08;数据结构课设篇1&…

11.3编写Linux串口驱动

编写串口驱动主要步骤 构建并初始化 struct console 对象&#xff0c;若串口无需支持 console 可省略此步骤 //UART驱动的console static struct uart_driver virt_uart_drv; static struct console virt_uart_console {//console 的名称&#xff0c;配合index字段使用&…