2.3 ROS计算图分析
ROS会创建一个连接到所有进程的网络。在系统中任何节点都可以访问到这个网络,并且通过该网络与其他节点的交互,获取其他节点发布的信息,并将自身数据发布到网络上。通常它们会通过协同工作完成计算任务。
2.3.1 准备工作
这个计算网络也可以称为计算图。计算图中的基本概念是ROS节点(node)、节点管理器(master)、参数服务器(parameter server)、消息(message)、话题(topic)、服务(service)和消息记录包(bag)。计算图中的每一个概念都以不同方式发挥自己的作用。
ROS中与通信相关的功能包,例如roscpp和rospython之类的核心客户端库,以及各种概念(如主题、节点、参数和服务)的实现都包含在ros_comm元功能包中。这个元功能包中的rostopic、rosparam、rosservice和rosnode这些工具就是前面提到概念的实现。
ros_comm元功能包中包含ROS通信中间件功能包。我们将这些包统称为ROS图层(ROS graph layer),如图2-6所示。
图2-6 ROS计算图
以下是图形中出现概念的介绍。
•ROS节点(node):ROS节点对应于Linux系统中执行特定任务的进程。它们使用诸如ROSCPP和ROSIST之类的ROS客户端库所编写,支持不同类型的通信方法的实现。刚刚提到的通信方法主要是ROS消息和ROS服务。通过ROS通信方法,它们可以交换数据并创建ROS计算网络。然而,ROS的设计理念是让多个节点协同工作,而不是一个节点包揽全部任务,例如,在机器人系统中会由不同的节点来执行某个特定类型的任务,例如感知、规划和控制。
•ROS节点管理器(master):ROS节点管理器为其余节点提供名称注册和查找服务,并负责在节点之间建立连接。在ROS系统中,如果没有节点管理器的话,节点之间将无法找到彼此,交换消息或调用服务。在分布式系统中,可以在一台计算机上运行节点管理器,其他远程节点通过与该节点管理器的通信找到彼此。
•ROS参数服务器:参数服务器允许ROS系统将数据或配置信息存储在中央位置。所有节点都可以访问和修改这些值。参数服务器是ROS节点管理器的一部分。
•ROS消息:ROS节点通过消息相互通信,这里面包含向其他节点提供信息的数据。尽管ROS具有标准的基本数据类型(整数、浮点、布尔等),但它提供了一种基于标准消息类型开发自定义消息类型的机制。
•ROS话题(topic):ROS中的每个消息都必须有一个名称来被ROS路由。当一个节点通过话题发送消息时,如果用ROS术语来描述这个过程就是“该节点正在发布一个话题”。类似的,当一个节点通过话题接收消息时,我们则称“该节点在订阅一个话题”。不过由于发布节点和订阅节点彼此之间不知道对方的存在,节点甚至可以订阅不具有发布者的话题,这使我们能够将发布与订阅相分离。此外,话题名称的唯一性也是十分重要的,我们必须避免话题名称相同所带来的问题和混淆。
•ROS服务(service):在一些需要实现交互式“请求—响应”的机器人应用中,仅仅使用发布—订阅模型是不够的,这是因为发布—订阅模型是一种单向传输系统。当我们使用分布式系统时,往往就会需要请求—响应类型的交互,在这正是ROS服务大显身手的时候。ROS服务的定义包含两个部分:一个部分用于请求,另一个部分用于响应。此外,它还需要两个节点:服务端和客户端。服务端节点通过名称提供服务,当客户端节点向该服务器节点发送请求消息时,它将响应这个消息并将结果发送到客户端节点。客户端节点往往需要一直等待服务器响应。ROS服务交互的过程和远程过程调用(RPC)很相似。
•ROS消息记录包(bag):ROS消息记录包是用来保存和回放(play)ROS消息数据的文件格式。消息记录包是一种用于存储数据的重要机制,它能够获取并记录各种难以收集的传感器数据。我们可以通过消息记录包反复获取实验数据,以此进行必要的开发和算法测试。因此对于开发和调试来说,消息记录包是非常有用的特性。我们将在即将到来的章节讨论消息记录包。
图2-7显示了节点之间是如何使用话题进行通信的。其中的话题采用矩形表示,节点用椭圆表示,图中没有出现消息和参数。该图是采用rqt_graph工具绘制的,我们将在第3章中详细讨论这个工具。
图2-7 交流图
2.3.2 如何完成
在前面我们学习了ROS计算网络中的重要组成。在接下来的这一节中,我们将利用这些内容进行一下“热身”。首先从ROS节点管理器开始练习。
ROS节点管理器的工作流程和DNS服务器十分相似。在ROS系统中,任何节点在启动时,都将会开始查找ROS主节点,并将节点的名称注册到ROS节点管理器。因此,ROS节点管理器上具有当前ROS系统上运行的所有节点信息。当任何节点的信息改变时,ROS节点管理器将取回最新信息和并更新存储内容。
在节点开始发布该主题之前,它将向ROS节点管理器提供该话题的详细信息,例如其名称和数据类型。ROS节点管理器将检查是否有其他节点订阅这一话题。如果任何节点订阅这一话题,那么ROS节点管理器会将发布节点的细节共享给它。在接收到发布节点细节后,订阅使用基于TCP/ IP套接字的TCPROS协议与发布节点进行互连,而ROS节点管理器也将放弃对它们的控制。之后我们随时可以根据需求停止发布者节点或订阅服务器节点。当节点的状态改变时,它将再次对ROS节点管理器的内容进行更新。ROS服务也使用相同的方法。ROS节点是使用例如ROSCPP和RoSpice之类的ROS客户端库编写的。这些客户端使用基于API的XML远程过程调用(XMLRPC)与ROS主机进行交互,它们充当着ROS系统的后端。
然而,在分布式网络中是由不同的物理计算机参与ROS计算网络,需要准确、恰当地对ROS_MASTER_URI进行定义。这样远程节点才可以找到彼此并相互通信。ROS系统只能有一个节点管理器,即使是在分布式系统中也一样,并且节点管理器应该在所有其他计算机都能访问到的计算机上运行,以确保所有的远程ROS节点都可以访问节点管理器。
图2-8显示了ROS节点管理器与发布订阅节点的交互方式。
图2-8 使用ROS节点管理器为发布节点和订阅节点建立通信
现在,让我们开始ROS节点部分的练习。ROS节点实质上是使用客户端库(如ROSCPP和ROSPILE)执行计算功能的进程。一个节点可以通过ROS话题、服务、参数服务器与其他节点通信。
另外,在ROS中使用节点的好处是提供了容错性,并实现了代码和功能的分离,使系统更简单和健壮。一个机器人应用程序可能包含多个节点,其中一个节点处理摄像机图像,另一个节点处理来自机器人的串行数据,还有一个节点可以用于计算里程等。与整体代码相比,节点减少了复杂性并增加了可调试性。
工具rosbash可以用于ROS节点进行自检。命令行工具rosnode可以用来显示关于节点的信息,例如列出当前正在运行的节点。
下面给出了命令的使用示例。
•rosnode info NODE:打印有关节点的信息。
•rosnode kill NODE:结束一个正在运行的节点或发送一个给定的信号。
•rosnode list:列出所有的活动节点。
•rosnode machine hostname:列出在特定机器上运行的节点或列出机器。
•rosnode ping NODE:用来测试节点的连通性。
•rosnode cleanup:清除不可达节点的注册信息。
在接下来的部分中,我们将要研究ROS节点中的话题、服务和消息等功能的工作方式。
下面我们来看看关于ROS话题的练习。ROS话题就是实现节点之间交换消息的命名总线,它允许匿名发布和订阅消息,这使得我们能够将发布与订阅相互分离。而实际上,ROS节点对于哪个节点发布了话题或订阅了话题也不感兴趣,它们只查看话题名称以及发布者和订阅者的消息类型是否匹配。
一个话题既可以有多个订阅者,也可以有多个发布者,但是在使用不同节点发布相同主题时,我们应该小心,因为这可能产生冲突。另外,话题的类型是由发布在它上面的消息类型决定的,节点只能接收类型匹配的消息。一个节点只能订阅相同消息类型的话题。
ROS节点使用基于TCP/IP的TCPROS传输与话题通信,这种传输方式是ROS中默认使用的传输方法。另一种传输方式是UDPROS,它具有低延迟、松散传输的特点,但是仅适用于远程操作之类的任务。
ROS中有一个专门用来处理话题的工具:rostopic。它是一个命令行工具,提供关于话题的信息或直接在ROS计算网络上发布数据。
下面给出了该工具常见命令的说明。
•rostopic bw /topic:显示话题使用的带宽。
•rostopic echo /topic:将消息打印到屏幕上。
•rostopic find message_type:根据类型查找话题。
•rostopic hz /topic:显示话题的发布频率。
•rostopic info /topic:打印有关话题的信息,如消息类型、发布者和订阅者。
•rostopic list:打印有关活动话题的列表信息。
•rostopic pub /topic type args:向一个话题发布指定类型的消息。
•rostopic type /topic:显示给定话题的消息类型。
我们将在即将到来的章节中练习如何使用这个命令行工具。
下面给出了一些ROS消息的实例。
•ROS节点通过向话题发布消息来实现相互之间的通信。正如前面所讨论过的,消息是一种由标准类型或用户开发自定义类型构成的简单数据结构。
•消息类型遵循下面的标准ROS命名约定:包的名称,然后是.MSG文件的名称。例如,std_msgs/msg/String.msg的消息类型就是std_msgs/String。除消息数据类型之外,ROS还通过比较MD5校验和来确定发布者和订阅者交换的消息数据类型是否相同。
•ROS中内置了一个ROSMSG工具,它可以用于获取ROS消息的信息。
下面给出了命令的使用示例。
•rosmsg show [message]:显示消息描述。
•rosmsg list:列出所有消息。
•rosmsg md5 [message]:显示消息的MD5校验和。
•rosmsg package [package_name]:列出功能包中的消息。
•rosmsg packages [package_1] [package_2]:列出包含消息的功能包。
现在,让我们看看关于ROS服务的一些练习。
•当需要在ROS中实现请求—响应类型的通信时,我们必须使用ROS服务。而ROS话题不能实现这种交流,因为它是单向的。ROS服务主要用于分布式系统中。
•ROS服务使用srv文件中的一对消息,请求(request)数据类型和响应(response)数据类型来定义。srv文件保存在一个功能包内的srv文件夹中。
•在ROS服务中,会有一个服务器节点充当ROS服务端,而客户端节点可以向它请求特定服务。如果服务器完成了这个服务请求,它会将结果发送给客户端。
•和话题相类似,服务也有一个关联的服务类型,即.srv文件的包资源名称。与其他的文件系统为基础的类型一样,服务的类型是包的名称和.SRV文件的名称。例如,chapter2_tutorials/srv/chapter2_srv.srv文件就有一个chapter2_tutorials/chapter2_srv的服务类型。在ROS服务中还有一个用来检验节点的MD5校验和。如果校验和是相等的,那么服务器才会响应客户端。
•ROS有两个和服务相关命令行工具:ROSRV和ROSService。第一个工具是ROSRV,它类似于ROSMSG,用于获取服务类型的信息。另一个命令是RosService,用于列出服务列表和查询正在运行的ROS服务。
下面给出了RosService工具的命令实例。
•rosservice call /service args:使用给定的参数调用服务。
•rosservice find service_type:根据给定服务类型查找服务。
•rosservice info /services:打印有关给定服务的信息。
•rosservice list:列出系统上运行的活动服务。
•rosservice type /service:打印给定服务的服务类型。
•rosservice uri /service:打印服务的RORPC URI。
现在,让我们转到下一个练习—— ROS参数服务器。
•参数服务器是一个通过ROS计算网络访问的共享多变量字典。节点可以使用该服务器在运行时存储和检索参数。
•参数服务器使用XMLRPC实现,并在ROS主服务器内部运行,这意味着它的API可以通过普通的XMLRPC库访问。XMLRPC是一种RPC协议,它是一个使用XML编码并以HTTP作为传输机制的远程调用。
参数服务器支持以下的各种XMLRPC数据类型。
•32位整数(32-bit integer)。
•布尔型(Boolean)。
•字符串(String)。
•双精度(Double)。
•ISO 8601日期(ISO 8601 date)。
•列表(List)。
•Base64编码的二进制数据(Base64-encoded binary data)。
rosparam命令可对ROS参数服务器上的参数进行操作。另外,ROS中dynamic_reconfigure功能包可以完成动态在线更新节点参数的功能。我们将在下一节中对此进行深入的学习。
•rosparam set [parameter_name] [value]:为给定参数设置值。
•rosparam get [parameter_name]:获取给定参数的值。
•rosparam load [YAML file]:从保存的YAML文件加载参数。
•rosparam dump [YAML file]:将现有ROS参数转存到YAML文件中。
•rosparam delete [parameter_name]:删除给定参数。
•rosparam list:列出现有参数名称。
现在我们要开始最后一个练习——ROS消息记录包(bag)。
•消息记录包是ROS中用于存储消息、话题和服务的文件格式,它的扩展名为.bag。我们可以通过将文件中的数据进行可视化来了解具体的情况,也可以对这些数据进行回放、停止、倒回或者其他操作。
•消息记录包是使用rosbag命令创建的,它的主要作用是记录数据。这些保存的数据可以用来可视化以及离线处理。
下面给出了命令的使用示例。
•rosbag record [topic_1] [topic_2] -o [bag_name]:将指定的话题记录到命令中给出的消息记录包文件中。
•rosbag -a [bag_name]:记录全部内容。
•rosbag play [bag_name]:回放已有的消息记录包文件。