本章主要介绍了通信架构的基础通信方式和相关概念。其中首先介绍了最小的进程单元节点Node,和节点管理器Node master。了解了ROS中的进程都是由很多的Node组成,并且由Node master来管理这些节点。
介绍ROS的“发动机”——launch文件,学习它的格式和内容,更深入的理解ROS在启动运行时它的工作都是由什么进程支配的,从而理解启动运行的原理。
ROS中的通信方式有四种,主题、服务、参数服务器、动作库。每个通信方式都有自己的特点,本章首先介绍话题通信方式–topic。
01 Node & Master
1.1 Node
在ROS中,最小的进程单元就是节点(node)。一个软件包里可以有多个可执行文件,可执行文件在运行之后就成了一个进程(process),这个进程在ROS中就叫做节点。 从程序角度来说,node就是一个可执行文件(通常为C++编译生成的可执行文件、Python脚本)被执行,加载到了内存之中;从功能角度来说,通常一个node负责者机器人的某一个单独的功能。由于机器人的功能模块非常复杂,我们往往不会把所有功能都集中到一个node上,而会采用分布式的方式,把鸡蛋放到不同的篮子里。
1.2 Master
由于机器人的元器件很多,功能庞大,因此实际运行时往往会运行众多的node,负责感知世界、控制运动、决策和计算等功能。那么如何合理的进行调配、管理这些node?这就要利用ROS提供给我们的节点管理器master, master在整个网络通信架构里相当于管理中心,管理着各个node。node首先在master处进行注册,之后master会将该node纳入整个ROS程序中。node之间的通信也是先由master进行“牵线”,才能两两的进行点对点通信。当ROS程序启动时,第一步首先启动master,由节点管理器处理依次启动node。
1.3 启动master和node
当我们要启动ROS时,首先输入命令:
$ roscore
此时ROS master启动,同时启动的还有rosout和parameter server,其中rosout是负责日志输出的一个节点,其作用是告知用户当前系统的状态,包括输出系统的error、warning等等,并且将log记录于日志文件中,parameter server即是参数服务器,它并不是一个node,而是存储参数配置的一个服务器。每一次我们运行ROS的节点前,都需要把master启动起来,这样才能够让节点启动和注册。
master之后,节点管理器就开始按照系统的安排协调进行启动具体的节点。节点就是一个进程,只不过在ROS中它被赋予了专用的名字—node。我们知道一个package中存放着可执行文件,可执行文件是静态的,当系统执行这些可执行文件,将这些文件加载到内存中,它就成为了动态的node。
具体启动node的语句是:
$ rosrun pkg_name node_name
通常我们运行ROS,就是按照这样的顺序启动,有时候节点太多,我们会选择用launch文件来启动。 Master、Node之间以及Node之间的关系如下图所示:
1.4 rosrun和rosnode命令
rosrun命令的详细用法如下:
$ rosrun [--prefix cmd] [--debug] pkg_name node_name [ARGS]
rosrun将会寻找PACKAGE下的名为EXECUTABLE的可执行程序,将可选参数ARGS传入。
例如在GDB下运行ros程序:
$ rosrun --prefix 'gdb -ex run --args' pkg_name node_name
rosnode命令的详细作用列表如下:
以上命令中常用的为前三个,在开发调试时经常会需要查看当前node以及node信息,所以请记住这些常用命令。如果想不起来,也可以通过rosnode help来查看rosnode命令的用法。
02 launch文件
2.1 简介
机器人是一个系统工程,通常一个机器人运行操作时要开启多个node,对于一个复杂的机器人的启动操作应该怎么做呢?当然,我们并不需要每个节点依次进行rosrun,ROS为我们提供了一个命令能一次性启动master和多个node。该命令是:
$ roslaunch pkg_name file_name.launch
roslaunch命令首先会自动进行检测系统的roscore有没有运行,也即是确认节点管理器是否在运行状态中,如果master没有启动,那么roslaunch就会首先启动master,然后再按照launch的规则执行。launch文件里已经配置好了启动的规则。 所以roslaunch就像是一个启动工具,能够一次性把多个节点按照我们预先的配置启动起来,减少我们在终端中一条条输入指令的麻烦。
2.2 写法与格式
launch文件同样也遵循着xml格式规范,是一种标签文本,它的格式包括以下标签:
<launch> <!--根标签--> <node> <!--需要启动的node及其参数--> <include> <!--包含其他launch--> <machine> <!--指定运行的机器--> <env-loader> <!--设置环境变量--> <param> <!--定义参数到参数服务器--> <rosparam> <!--启动yaml文件参数到参数服务器--> <arg> <!--定义变量--> <remap> <!--设定参数映射--> <group> <!--设定命名空间--> </launch> <!--根标签-->
参考链接:http://wiki.ros.org/roslaunch/XML
2.3 示例
launch文件的写法和格式看起来内容比较复杂,我们先来介绍一个最简单的例子如下:
<launch> <node name="talker" pkg="rospy_tutorials" type="talker" /> </launch>
这是官网给出的一个最小的例子,文本中的信息是,它启动了一个单独的节点talker
,该节点是包rospy_tutorials
软件包中的节点。
然而实际中的launch文件要复杂很多,我们以Ros-Academy-for-Beginners
中的robot_sim_demo
为例:
<launch> <!--arg是launch标签中的变量声明,arg的name为变量名,default或者value为值--> <arg name="robot" default="xbot2"/> <arg name="debug" default="false"/> <arg name="gui" default="true"/> <arg name="headless" default="false"/> <!-- Start Gazebo with a blank world --> <include file="$(find gazebo_ros)/launch/empty_world.launch"> <!--include用来嵌套仿真场景的launch文件--> <arg name="world_name" value="$(find robot_sim_demo)/worlds/ROS-Academy.world"/> <arg name="debug" value="$(arg debug)" /> <arg name="gui" value="$(arg gui)" /> <arg name="paused" value="false"/> <arg name="use_sim_time" value="true"/> <arg name="headless" value="$(arg headless)"/> </include> <!-- Oh, you wanted a robot? --> <!--嵌套了机器人的launch文件--> <include file="$(find robot_sim_demo)/launch/include/$(arg robot).launch.xml" /> <!--如果你想连同RViz一起启动,可以按照以下方式加入RViz这个node--> <!--node name="rviz" pkg="rviz" type="rviz" args="-d $(find robot_sim_demo)/urdf_gazebo.rviz" /--> </launch>
这个launch文件相比上一个简单的例子来说,内容稍微有些复杂,它的作用是:启动gazebo模拟器,导入参数内容,加入机器人模型。
小结
对于初学者,我们不要求掌握每一个标签是什么作用,但至少应该有一个印象。如果我们要进行自己写launch文件,可以先从改launch文件的模板入手,基本可以满足普通项目的要求。
03 Topic(话题)
3.1 简介
ROS的通信方式是ROS最为核心的概念,ROS系统的精髓就在于它提供的通信架构。ROS的通信方式有以下四种:
Topic 主题
Service 服务
Parameter Service 参数服务器
Actionlib 动作库
3.2 Topic(发布/订阅)
ROS中的通信方式中,topic是常用的一种。对于实时性、周期性的消息,使用topic来传输是最佳的选择。topic是一种点对点的单向通信方式,这里的“点”指的是node,也就是说node之间可以通过topic方式来传递信息。topic要经历下面几步的初始化过程:首先,publisher节点和subscriber节点都要到节点管理器进行注册,然后publisher会发布topic,subscriber在master的指挥下会订阅该topic,从而建立起sub-pub之间的通信。注意整个过程是单向的。其结构示意图如下:
Subscriber接收消息会进行处理,一般这个过程叫做回调(Callback)。所谓回调就是提前定义好了一个处理函数(写在代码中),当有消息来就会触发这个处理函数,函数会对消息进行处理。
注意整个过程是单向的。
Talker向ROS Master注册
Listener向ROS Master注册
ROS Master通过RPC向Listener发送Talker的地址信息
Listener接收到地址信息,通过RPC向Talker发送连接请求
Talker确认连接请求,通过RPC向Listener确认连接
Listener与Talker建立网络连接
Talker向Listener发布数据
总结:前五步的通信协议都是RPC,最后两步传输数据才用TCP
3.3 通信示例
怎么样来理解**“异步”**这个概念呢?在node1每发布一次消息之后,就会继续执行下一个动作,至于消息是什么状态、被怎样处理,它不需要了解;而对于node2图像处理程序,它只管接收和处理/camera_rgb上的消息,至于是谁发来的,它不会关心。所以node1、node2两者都是各司其责,不存在协同工作,我们称这样的通信方式是异步的。
ROS是一种分布式的架构,一个topic可以被多个节点同时发布,也可以同时被多个节点接收。比如在这个场景中用户可以再加入一个图像显示的节点,我们在想看看摄像头节点的画面,则可以用自己的笔记本连接到机器人上的节点管理器,然后在自己的电脑上启动图像显示节点。
这就体现了分布式系统通信的好处:扩展性好、软件复用率高。
总结三点:
topic通信方式是异步的,发送时调用publish()方法,发送完成立即返回,不用等待反馈。
subscriber通过回调函数的方式来处理消息。
topic可以同时有多个subscribers,也可以同时有多个publishers。ROS中这样的例子有:/rosout、/tf等等。
3.4 操作命令
在实际应用中,我们应该熟悉topic的几种使用命令,下表详细的列出了各自的命令及其作用。
如果你一时忘记了命令的写法,可以通过rostopic help或rostopic command -h查看具体用法。
小结
topic的通信方式是ROS中比较常见的单向异步通信方式,它在很多时候的通信是比较易用且高效的。但是有些需要交互的通信时该方式就显露出自己的不足之处了,后续我们会介绍双向同步的通信方式service。