globalplanner源码解读

1.globalplanner的主要计算路径的接口函数

  1. makePlan 路径规划接口函数

  2. initialize 初始化规划器接口函数

  3. 在GlobalPlanner中的主要接口函数:

  4. makePlan 重载了路径规划接口函数

  5. initialize 重载了初始化规划器接口函数

  6. computePotential 新定义了计算规划地图势场的接口函数 (已经不使用了)

  7. getPlanFromPotential 新定义了得到规划路径的接口函数(提供给makePlan使用)

  8. 在GlobalPlanner中的主要成员变量

  9. Expander* planner_ 成员变量图搜索算法指针,如: (a星、dijkstra),该成员变量为图搜索算法的基类,定义在navigator/global_planner/expender.h中,提供了一个纯虚汗数接口定义

  10. calculatePotentials 图搜索算法计算势场接口,这个接口会被(a星、dijkstra)图搜索算法集成并提供给GlobalPlanner的makePlan使用

  11. GlobalPlanner中原定义的computePotential接口不使用了,应该也是为了更方便的扩展不同的图搜索算法,而将计算规划地图势场的功能交由独立的图搜索算法实现。

2. GlobalPlanner工作过程

MoveBase的全局规划线程通过调用配置的实际全局规划器的makePlan方法来计算全局路径规划。基类是class BaseGlobalPlanner(navigation-kinetic\nav_core\include\nav_core\ base_global_planner.h), 具体的全局规划器需要继承此基类。 例如,GlobalPlanner就继承此基类,并实现相应的方法。

class GlobalPlanner : public nav_core::BaseGlobalPlanner

(navigation-kinetic\global_planner\include\global_planner\planner_core.h)

这里主要分析GlobalPlanner全局规划器。

初始化global planner的过程,在MoveBase的构造函数中创建和初始化。如下:

  boost::shared_ptr<nav_core::BaseGlobalPlanner> planner_;

  planner_ = bgp_loader_.createInstance(global_planner);

                                         //使用的planner为 global_planner/GlobalPlanner

  planner_->initialize(bgp_loader_.getName(global_planner), planner_costmap_ros_);

                                        //加载和初始化相应的动态库/opt/ros/kinetic/lib/libglobal_planner.so
2.1 GlobalPlanner initialize过程

实现是在void GlobalPlanner::initialize(std::string name, costmap_2d::Costmap2DROS* costmap_ros) 函数中完成,需要costmap作为入参。

initialize(name, costmap_ros->getCostmap(), costmap_ros->getGlobalFrameID());

1)保存costmap对象的指针和frameid

    costmap_ = costmap;

    frame_id_ = frame_id;

2)获取costmap的CellsX, CellsY即地图的长和宽

  unsigned int cx = costmap->getSizeInCellsX(),

                      cy = costmap->getSizeInCellsY();

3) 根据old_navfn_behavior的值设定convert_offset_

               如果old_navfn_behavior为true,convert_offset_ = 0.0

               如果old_navfn_behavior为false,convert_offset_ = 0.5

      我们这里设置为false,所以convert_offset_的值为0.5

4) 根据use_quadratic的值分配计算器

          PotentialCalculator* p_calc_;

       我们设置为true,所以p_calc_ = new QuadraticCalculator(cx, cy);

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CBkulxmd-1655471919747)(C:\Users\lanth211108\AppData\Roaming\Typora\typora-user-images\1649762836229.png)]

5) 根据use_dijkstra设置最短路径算法。

       如果为true,则使用地杰斯特拉算法,否则使用A Star算法

       Expander* planner_;

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QMDmNtGU-1655471919748)(C:\Users\lanth211108\AppData\Roaming\Typora\typora-user-images\1649762853182.png)]

 6)  根据use_grid_path设置路径生成器

         若为true,使用网格边界,否则,使用梯度下降法。

         我们设置为false,所以使用梯度下降法。 

         Traceback* path_maker_;

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GzCk77Pv-1655471919749)(C:\Users\lanth211108\AppData\Roaming\Typora\typora-user-images\1649762893799.png)]

  7)发布topic和service

       //发布topic /move_base/GlobalPlanner/plan

       plan_pub_ = private_nh.advertise<nav_msgs::Path>("plan", 1);

       //发布topic /move_base/GlobalPlanner/potential

       potential_pub_ = private_nh.advertise<nav_msgs::OccupancyGrid>("potential", 1);

       //发布service /move_base/GlobalPlanner/make_plan

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

2.2 makeplan的过程
Global planner线程收到action server的请求后,会调用makePlan()函数做全局路径规划。最终调用的函数为:

GlobalPlanner::makePlan(const geometry_msgs::PoseStamped& start, const geometry_msgs::PoseStamped& goal,
double tolerance, std::vector<geometry_msgs::PoseStamped>& plan)

具体的工作流程为:

 1)  将起点坐标和终点坐标从世界坐标系转到地图坐标系。

       这里的世界坐标指的是坐标使用米为单位

       地图坐标指的是将米转换为数组的下标。

       怎么理解呢?

       实际存储地图时,使用的是二维数组。根据地图的长和宽以及分辨率计算出二维数据的大小。

       举例:地图的大小为3.0X4.0米,分辨率为0.1米/像素。则实际存储地图的二维数组大小为

       30X40. 如果一个点的世界坐标为(1.0, 2.0), 在实际二维数组中的下标就为(10, 20)

       这一步就是做这个转换工作。
       //将map上的坐标(mx,my)转化成world上的坐标(wx,wy)
      void mapToWorld(unsigned int mx, unsigned int my, double& wx, double& wy)

		//将world上的坐标(wx,wy)转化成map上的坐标(mx,my)
       bool worldToMap(double wx, double wy, unsigned int& mx, unsigned int& my)const; 

/*******************************************************************/
        costmap_->worldToMap(wx, wy, start_x_i, start_y_i)

         worldToMap(wx, wy, start_x, start_y)

         costmap_->worldToMap(wx, wy, goal_x_i, goal_y_i)

          worldToMap(wx, wy, goal_x, goal_y)

  2)   将起始点的costmap标为空闲。因为机器人就在起始点,所以此点肯定没有障碍物。

        clearRobotCell(start_pose, start_x_i, start_y_i);

  3)重新设置相关数组的size

      //make sure to resize the underlying array that Navfn uses

       p_calc_->setSize(nx, ny);

       planner_->setSize(nx, ny);

        path_maker_->setSize(nx, ny);

        potential_array_ = new float[nx * ny];

 4)将costmap的所有点都标为有障碍物

        outlineMap(costmap_->getCharMap(), nx, ny, costmap_2d::LETHAL_OBSTACLE);

 5)   计算可用路径

     bool found_legal = planner_->calculatePotentials(costmap_->getCharMap(), start_x, start_y,

                                                                                    goal_x, goal_y, nx * ny * 2, potential_array_);

 6)   生成路径规划

      getPlanFromPotential(start_x, start_y, goal_x, goal_y, goal, plan)

3.costmap与globalplanner的关系

规划的路径都是建立在costmap之上的,所以先讲一下必须知道的costmap的知识,实际代价地图存储在这里unsigned char* costmap_;每个字符八位0~255即cost值。把实际地图根据分辨率映射成栅格地图,依次给栅格编号,如下图所示,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-95uL0y0E-1655471919749)(C:\Users\lanth211108\AppData\Roaming\Typora\typora-user-images\1649763308970.png)]

维护工作是通过以下几个函数进行:

voidmapToWorld(unsigned int mx, unsigned int my, double& wx, double& wy)将map上的坐标(mx,my)转化成world上的坐标(wx,wy)

boolworldToMap(double wx, double wy, unsigned int& mx, unsigned int& my)const; 将world上的坐标(wx,wy)转化成map上的坐标(mx,my)

 这里的世界坐标指的是坐标使用米为单位

       地图坐标指的是将米转换为数组的下标。

       怎么理解呢?

       实际存储地图时,使用的是二维数组。根据地图的长和宽以及分辨率计算出二维数据的大小。

       举例:地图的大小为3.0X4.0米,分辨率为0.1米/像素。等于长宽除以像素,则实际存储地图的二维数组大小为

       30X40. 如果一个点的世界坐标为(1.0, 2.0), 在实际二维数组中的下标就为(10, 20)

       这一步就是做这个转换工作。
       /*****************************************************************/
       如上图表格的行列对应地图的坐标,如(1,2)的地图坐标数组对应栅格地图5的序号,计算距离要先换算
       
       
costmap的网格代价计算

(1) Lethal(致命的):单元中存在实际的障碍物,机器人的中心(center cell)在如果在该网格中,此时机器人必然与障碍物冲突。(代价值为254)

(2) Inscribed(内切):网格中心位于机器人的内切圆轮廓内,此时机器人也必然与障碍物冲突。(代价值为253)

(3) Possibly circumscribed(可能受限):网格中心位于机器人的外切圆与内切圆轮廓之间,此时机器人相当于位于障碍物附近,所以不一定冲突,取决于机器人的方位或者说姿态。 使用”可能“一词是因为它可能不是真正的障碍单元,而是一些用户偏好(用户自己规定一些区域,不想让机器人通过),将特定的成本值放入地图中。(代价值为128)

(4) Freespace(自由空间):网格中心位于机器人外切圆之外,属于没有障碍物的空间。 (代价值为1)

(5) Unknown(未知):未知的空间。(代价值为0)

(1)致命障碍:栅格值为254:此时障碍物与机器人中心重叠,必然发生碰撞;
(2)内切障碍:栅格值为253:此时障碍物处于机器人的内切圆内,必然发生碰撞;
(3)外切障碍:栅格值为[128,252]:【防盗标记–盒子君hzj】此时障碍物处于其机器人的外切圆内,处于碰撞临界,不一定发生碰撞
(4)非自由空间:栅格值为(0,127]:此时机器人处于障碍物附近,属于危险警戒区,进入此区域,将来可能会发生碰撞;
(5)自由区域:栅格值为0:此处机器人可以自由通过;
(6)未知区域:栅格值为255:还没探明是否有障碍物

inline unsignedint getIndex(unsigned int mx, unsigned int my) const根据map上的坐标得到栅格编号index。
源码解读这篇文章讲的比较好 http://blog.csdn.net/u013158492/article/details/50504963大家可以参考,我就不再重复,主要讲一下我看源码总结的框架,如果大家跟着这个框架看源码的话可能会看的比较快。其中use_dijkstra,use_quadratic ,use_grid_path是global_planner的关键配置参数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q9zcFVLH-1655471919750)(C:\Users\lanth211108\Desktop\20170703211054658.png)]

A*与DijKstra算法的讲解http://blog.csdn.net/kongbu0622/article/details/1871520

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VYdRGN2U-1655471919750)(C:\Users\lanth211108\Desktop\20170706202359469.png)]

A这个函数很重要

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sh2urOsM-1655471919751)(C:\Users\lanth211108\AppData\Roaming\Typora\typora-user-images\1649814811388.png)]

  • 其中f ( n ) 是从初始状态经由状态n到目标状态的代价估计,g ( n ) 是在状态空间中从初始状态到状态n的实际代价(看上面costmap的网格计算),h*(n)是从状态n*到目标状态的最佳路径的估计代价。对于路径搜索问题,状态就是图中的节点,代价就是距离。

    h(n)的选取: 保证找到最短路径(最优解的)条件,关键在于估价函数f*(n)的选取(或者说h*(n)的选取)。 我们以d*(n)表达状态n到目标状态的距离,那么h(n)的选取大致有如下三种情况:

    • (1)如果h ( n ) ≤ d ( n ) ,到目标状态的实际距离,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。
    • (2)如果h ( n ) = d ( n ) ,,即距离估计h*(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。
    • (3)如果 h ( n ) > d ( n ),搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k6lnNpV3-1655471919751)(C:\Users\lanth211108\Desktop\20170706201109027.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pz4svlOf-1655471919751)(C:\Users\lanth211108\Desktop\20170703213124850.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uaxsw5UZ-1655471919752)(C:\Users\lanth211108\Desktop\微信图片_20220225175015.png)]

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: global_planner是ROS中的一个全局路径规划器,它可以根据地图和起点终点信息,生成一条全局路径。该路径规划器的源码解析可以帮助我们更好地理解其实现原理和算法。 global_planner源码主要包括以下几个文件: 1. global_planner.cpp:该文件是全局路径规划器的主要实现文件,其中包含了路径规划的核心算法和逻辑。 2. grid_path.cpp:该文件实现了栅格地图的路径规划算法,包括A*算法和Dijkstra算法。 3. orientation_filter.cpp:该文件实现了路径方向的滤波算法,可以使路径更加平滑。 4. potential_field.cpp:该文件实现了基于势场的路径规划算法,可以避免障碍物和局部最优解。 在global_planner.cpp文件中,主要实现了以下几个函数: 1. makePlan:该函数是全局路径规划器的入口函数,它接收起点和终点的坐标信息,并调用其他函数进行路径规划。 2. initialize:该函数用于初始化全局路径规划器,包括读取参数、订阅地图和起点终点信息等。 3. computePotential:该函数实现了基于势场的路径规划算法,它会根据地图和障碍物信息计算每个点的势能值,并生成一张势能地图。 4. getPlanFromPotential:该函数根据势能地图和起点终点信息,使用A*算法或Dijkstra算法生成一条全局路径。 5. publishPlan:该函数将生成的全局路径发布出去,供其他节点使用。 总的来说,global_planner源码实现了多种路径规划算法,可以根据不同的场景和需求进行选择。同时,它还实现了路径方向的滤波和势场的优化,可以使路径更加平滑和避免局部最优解。 ### 回答2: global_planner是ROS中的一个全局路径规划器,主要用于自动驾驶、机器人导航等领域。其核心算法是基于A*算法的Dijkstra和A*算法的融合。当起点和终点之间存在障碍物时,它会自动计算一条绕过障碍物的路径来达到终点。 global_planner源码分为四个主要的文件:planner_core.cpp、dijkstra.cpp、astar.cpp和potential_fields.cpp。planner_core.cpp是这个路径规划器的主要文件,它定义了全局路径规划器的核心功能。 其中,Dijkstra算法是一个广度优先搜索算法,可以扩展当前可达节点的最短路径来找到起点到终点的最短路径。而A*算法则是在Dijkstra的基础上添加了启发式信息,即由启发式函数的估计值来指导搜索的方向,这样可以加快搜索速度。 在global_planner中,通过将Dijkstra算法和A*算法的两种算法进行融合,可以提高路径搜索的效率。同时,该算法还引入了potential_fields动力学概念,从而实现了避免障碍物的自适应路径规划,让机器人能够在不同场景下得到最有效的路径。 总的来说,global_planner源码解析需要解释算法的原理、实现中的细节、运行过程中的各种参数以及算法效率等方面的内容。对于研究领域的专家和从事相关开发工作的人员,都需要有一定的数学和编程技能,才能够深入了解global_planner的工作原理和实现方式。 ### 回答3: global_planner是ROS中一个非常重要的全局路径规划器,它能够帮助机器人在未知环境中规划一条全局路径,使机器人能够尽可能地到达目标点,并且避开障碍物。本文将针对global_planner源码进行解析,帮助读者更好地理解和使用该工具。 global_planner源码分为两个部分:头文件和源文件。头文件定义了全局变量和函数声明,源文件实现了各种函数和算法。 在头文件中,首先定义了ros/ros.h和std_msgs/MapMetaData.h两个ros消息,用于表示全局地图的元数据和地图本身。同时,头文件中还定义了CellData和Cell的两个子类,用于表示地图中的单元格和单元格的信息。此外,还定义了一个Costmap2D类,用于存储3D地图数据和提供相关函数。 源文件中实现了多种路径规划算法,包括Dijkstra、A*、ARA*和Wavefront等。其中,Wavefront算法是一种基于搜索的路径规划算法,它以机器人当前位置为起点,传播到目标位置时,遇到的障碍物通过标记颜色进行标识。Wavefront算法可以在具有稀疏障碍物的环境中生成平滑的路径。 在global_planner源码中,还实现了一个名为makePlan的函数,用于在地图上规划一条全局路径。该函数首先在地图上搜索到目标位置,并使用A*算法计算出到达目标位置的最短路径。然后,该函数使用ARA*算法对路径进行平滑,避免机器人在前进时发生急剧转弯。最后,该函数返回一个包含路径的向量,并将该向量发送到Topic,供其他节点使用。 总结而言,global_planner是ROS中一个非常强大的全局路径规划器。它包含多种路径规划算法,可以在不同的场景下进行路径规划。在使用global_planner时,需要了解其中的算法与设计思路,才能更好地进行调用和使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值