array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 ROS Navigation源代码剖析(5)-move_base 全局规划器GlobalPlanner流程 - 爱码网

4.2.3 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)

ROS可用的全局规划器有navfn/NavfnROSglobal_planner/GlobalPlanner carrot_planner。类图如下图所示:

ROS Navigation源代码剖析(5)-move_base 全局规划器GlobalPlanner流程

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

在4.2.0节中已经介绍了创建和初始化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

4.2.3.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);

            ROS Navigation源代码剖析(5)-move_base 全局规划器GlobalPlanner流程

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

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

           Expander* planner_;

            ROS Navigation源代码剖析(5)-move_base 全局规划器GlobalPlanner流程

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

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

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

             Traceback* path_maker_;

              ROS Navigation源代码剖析(5)-move_base 全局规划器GlobalPlanner流程

      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);

      

4.2.3.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)

            这一步就是做这个转换工作。

            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)

相关文章: