ROS源码量对于本菜鸟来说还是挺大的,单是roscore下就牵扯了各种类和函数调用。这篇文章仅代表我的个人见解,错误之处望各位不吝赐教。
ROS启动时首先会要求执行roscore这条命令,在wiki里面是这样对roscore进行介绍的:
roscore是一个节点和程序的集合,为了让ROS节点进行通信,必须运行roscore。注意:若使用的是roslaunch,如果ROS检测到roscore没有运行,ROS也将将自动启动roscore。
roscore将启动ros master、ros参数服务器和rosout节点。
一、roscore源码分析:
1、如果按照前一篇博客来安装ros_comm,roscore命令的位置在~/ros_catkin_ws/install_isolated/bin/roscore。roscore首先会调用roslaunch(跟roscore相同文件夹),由roslaunch来调用roslaunch包下的其他文件,roslaunch包的位置与roslaunch文件位置不同,roslaunch包在~/ros_catkin_ws/install_isolated/lib/python2.7/dist-packages/roslaunch,同样的,这个路径不是每个人都完全一样,各位请根据自己情况自行调整。
roslaunch首先调用的是_init_.py的main(),初始化各种参数和环境变量,如设置roslaunch的日志文件位置。然后检查已存在的日志文件大小,若已超过1GB则会提醒用户执行rosclean purge命令。
roslaunch文件在~/.ros/log下,每次执行roscore都会生成唯一的uuid,以uuid号为日志文件夹名,该文件夹下的roslaunch-........log便是roslaunch的日志,省略号处是机器号和pid号。
2、_init_.py各种参数配置好后就到了p.start(),p为类ROSLaunchParent,定义是在roslaunch文件夹下的parent.py。ROSLaunchParent的类函数start负责启动ROSlaunch,自然包括其下各自服务的启动:
2.1、 _start_infrastructure():加载配置,启动XMLRPC服务器和进程监视器。
_start_infrastructure()首先调用_load_config(),加载配置变量;
然后调用_start_pm()来启动进程监视器,进程监视器的代码部分在pemon.py实现;
接着是_start_server(),该函数启动XMLRPC服务器,涉及到的文件主要是:server.py->rosgraph的xmlrpc.py->rosgraph的network.py;
最后是_start_remote(),用于初始化并运行远程进程运行器,若不存在远程运行器,则不执行相关操作。
2.2、_init_runner():初始化roslaunch运行器。
_init_runner()初始化了在launch.py的类ROSLaunchRunner,再调用config.py的summary()将roslaunch的相关启动信息输出给用户。
2.3、launch.py的launch()调用_setup()来设置ROS网络状态,包括参数服务器状态和核心服务,_launch_nodes()来启动声明的节点和ros master。
2.4、pmon.py的registrations_complete()设置registrations_complete标志后,如果没有其他进程需要监控,进程监视器将退出。
到这一步已经将各种参数初始化,完成好了准备工作,下面开始运行roslaunch执行器。
3、_init_.py的p.spin()调用的是parent.py的spin(),运行ROSLaunch直到程序退出。
roscore的工作大体就是这些内容,下图是我纪录的roscore函数的调用顺序,不全,给大家参考一下吧。。
官方也给出roscore涉及到的类的继承关系及类中的数据成员和成员函数:
二、添加开机节点
roscore从roscore.xml读取需要启动的节点的信息,roscore.xml位于ros_catkin_ws/install_isolated/etc/ros/roscore.xml,未修改的roscore.xml是这样的:
<!--
ROS Core Stack definition
Before making any modifications to this file, please read:
http://ros.org/wiki/roscore
-->
<launch>
<group ns="/">
<param name="rosversion" command="rosversion roslaunch" />
<param name="rosdistro" command="rosversion -d" />
<node pkg="rosout" type="rosout" name="rosout" respawn="true"/>
</group>
</launch>
若要添加开机一起启动的节点,可以修改roscore.xml文件,下面我以ros官方tutorial的编写新节点的talker节点为例来进行说明。创建的talker节点在/home/haroroda/catkin_ws/src/talker/scripts/talker.py,按照tutorial里source好路径便可以进行下一步。修改如下:
<!--
ROS Core Stack definition
Before making any modifications to this file, please read:
http://ros.org/wiki/roscore
-->
<launch>
<group ns="/">
<param name="rosversion" command="rosversion roslaunch" />
<param name="rosdistro" command="rosversion -d" />
<node pkg="rosout" type="rosout" name="rosout" respawn="true"/>
</group>
<group ns="/">
<node pkg="talker" type="talker.py" name="talker" respawn="false"/>
</group>
</launch>
这里ns仍是“/”,个人认为只有路径写入$ROS_PACKAGE_PATH的都可以认为是“/”,然后很明显的pkg是包名。值得注意的是type和name分别代表带扩展名的文件名和文件名,我在这卡了很久,一开始type写的是文件名,name是带扩展名的文件名,然后就报错,一层层代码查过去,最后将type和name交换就好了。虽然不懂为啥但也不想深究了(知道答案的大佬们可以留言或私心告诉我,谢谢啦)。如果是c++写的可执行文件,则type和name是一样的。respawn表示是否要在roscore生命期间重启该节点,因为有些节点执行完就结束退出了,如果respawn设为true则roslaunch会在你的节点结束时又给重新启动,直到roscore退出执行。修改好后运行roscore测试一下:
talker.py成功启动并结束,日志文件如图最后一行所示。