在目前的ROS版本中,机器人全局路径规划使用的是navfn包,这在move_base的默认参数中可以找到 base_global_planner (string, default: "navfn/NavfnROS")。而在navigation的源代码中还有一个global_planner的包,该包的源文件夹(navigation-hydro-devel\global_planner\src )下已经有了A*,Dijkstra等算法的实现。可是navfn的源程序中也有这两个算法的实现,貌似根本就没用到global_planner这个文件夹下的源程序。因此最开始直接看用于move_base全局导航的程序时有点一头雾水,为什么有两个用于全局导航的包在ROS里面?到底这两个包navfn和global_planner是什么关系?
可以将navfn包和global_planner包理解成一个并列关系,因为他们两个都是用来做全局规划的,两个包里面也都实现了A*,Dijkstra算法。那是不是意味着这两者中的一个包就是多余的呢?其实不是,早期的开发中是用navfn包做导航的,那时候并没有global_planner这个包,并且在navfn的源代码里可以看到这个包默认是使用Dijkstra做全局路径规划,并且有A*的代码,那为什么没有使用A*呢?幸好有人在ROS answers里问了这个问题,也引来了众开发者回答:
根据12年bhaskara的回答,意思是navfn里的A*算法存在bug,没人有时间去弄(在ros的各种答案里经常可以看到开发者说没时间弄,他们确实也相当的忙),直到13年David Lu 才完成了这部分工作,重新发布了global_planner包,修改好的代码封装性更强,更清晰明了。因此,也可以认为global_planner是navfn的替代者。也有人问David Lu为什么没用global_planner替代掉navfn?他的回答是为了和以前兼容。因此可以看到源代码中两个包都在,并且move_base的那个全局变量参数默认的是navfn,也就是说没用global_planner。那么如何使用global_planner包而不是navfn包呢?按理来说,只要将move_base的参数base_global_planner用global_planner/PlannerCore替代就行了。如:
可是实际使用的时候(14年1月以前),却提示this planner is not registered,意思是这个包没有注册。幸好有人发现这个是bgp_plugin.xml里的bug,后来安装的ROS版本应该修复了这个问题。如果有这个错误,请按照github上的修复修改cmakelist.txt和bgp_plugin.xml就可以了。
看到这,不得不提一个问题?这个文件是干嘛的,为啥要在这里面加上那几句。
按照文件的名字就知道这是个跟插件有关。讲到这里,就有必要顺带了解一下move_base是如何调用各种global或者local planner的。在使用navigation的官方wiki教程里就提到过可以使用自己的路径规划算法,思路是使用ROS的插件机制。在自己写的global或者local planner算法里开头加上一句特定的程序(PLUGINLIB_EXPORT_CLASS(.....))就能注册插件机制,然后在xx_plugin.xml等文件里描述下这个插件,在package.xml显式的表明这个插件用来通知ROS我们将使用它,具体过程见官方wiki(ROS的插件机制)。弄完插件机制以后,我们就可以像上面一样将其用参数的形式直接传过去了。之前的bgp_plugin.xml有bug,因此就出现了那个问题。
可是,我随便按照自己的思路去写global 或者local planner的函数就行吗?显然ROS有一个标准。你必须按照它提供给你的模板去实现你自己的算法,这些模板就是基类。如果觉得抽象,可以先通过官方的文档来了解插件是如何工作的?
在上图的这个例子中,假设你想使用一个形状画图模块,ROS的官方包中已经有了polygon_interface这个基类,它已经提供了标准的接口函数,并且有两个子类:矩形插件包rectangle_plugin package 和三角形插件包 triangle_plugin package。要想使用这两个子类你只要在相应文件中将它们注册为插件,告诉ROS我将使用这两个插件就行了。这个图中一个关键的中心点就是polygon_interface基类。
联系到我们自己要写global 或者local planner的插件,这个图就告诉我们并不能天马行空的按照自己的方式编写相关的类或者函数,而必须按照ROS提供的模板去实现,而这正是nav_core这个包存在的意义。在navigation的源代码中你会看到这个nav_core包中仅仅只有几个头文件,正是这些头文件提供了多个模板: nav_core::BaseGlobalPlanner,nav_core::BaseLocalPlanner, nav_core::RecoveryBehavior,在官方的wiki文档里可以看到他们的相关介绍。所以按照这些模板的标准形式去写自己的planner算法就行了。
了解了这些,也十分有助于我们去看别人写的各种planner的插件,如使用遗传算法的global planner,sbql global planner以及eband local planner等,当然也包括官方提供的用来完善之前遗留程序问题的插件包global_planner包和dwa_local_planner包。
关于navfn中各代码的具体实现要深入进去,可以先学习一下A*、Dijkstra算法的实现,看懂了这两个算法再去读ROS的源码就容易很多了。当然也有一些细节,如这个网友所问的,可以参考参考。
reference:
1.为什么navfn是用dijkstra? http://answers.ros.org/question/28366/why-navfn-is-using-dijkstra/
2. global_planner包和navfn包的关系?http://answers.ros.org/question/98511/global_planner-package-with-a-planner-question/
3.如何创建自己的全局路径规划插件http://wiki.ros.org/navigation/Tutorials/Writing%20A%20Global%20Path%20Planner%20As%20Plugin%20in%20ROS
4.ROS插件机制 http://wiki.ros.org/pluginlib
5.关于global_planner 没有注册这个,ros answers上的回答 http://answers.ros.org/question/120736/global_planner-is-not-registered/
6.nav_core 官方wiki http://wiki.ros.org/nav_core
7.navfn具体代码的一些问题 http://answers.ros.org/question/11388/navfn-algorism/?answer=16891#answer-container-16891