Reference:
ROS 架构如下图所示,可以将其分为三个层次:OS 层、中间层和应用层。
ROS 并不是传统意义上的操作系统,无法像 Windows、Linux 一样直接运行在计算机硬件智商,而是需要依托于Linux 系统。所以在 OS 层,我们可以直接使用 ROS 官方支持度最好的 Ubuntu 操作系统,也可是使用 macOS, Arch, Debian 等操作系统。
Linux 是一个通用系统,并没有针对机器人开发提供特殊的中间件,所以 ROS 在中间层做了大量工作,其中最重要的就是基于 TCPROS/UDPROS 的通信系统。ROS 的通信系统基于 TCP/UDP 网络,在此智商进行了再次封装,也就是 TCPROS/UDPROS。通信系统使用 发布/订阅、客户端/服务器 等模型,实现多种通信机制的数据传输。
除了 TCPROS/UDPROS 的通信机制外,ROS 还提供一直进程内的通信方法-----Nodelet
,可以为多进程通信提供一种更优化的数据传输方式,适合对数据传输实时性方面有较高要求的应用。
在通信机制上,ROS 提供了大量机器人开发相关的库,如数据类型定义、坐标变换、运动控制等,可以提供给应用层使用。
在应用层,ROS 需要运行一个管理者-----Master,负责管理整个系统的正常运行。ROS 社区内共享了大量的机器人应用功能包,这些功能包内的模块以节点为单位运行,以 ROS 标准的输入输出作为接口,开发者不需要关注模块的内部实现机制,只需要了解接口规则即可实现复用,极大地提高了开发效率。
接下来着重研究 ROS 的核心----分布式通信机制
。
ROS 是一个分布式框架,为用户提供多节点(进程)之间的通信服务,所有软件功能和工具都建立在这种分布式通信机制上,所以 ROS 的通信机制是最底层也是最核心的技术。在大多数应用场景下,尽管我们不需要关注底层通信的实现机制,但是了解其相关原理一定会帮助我们在开发过程中更好的使用 ROS。以下就 ROS 最核心的三种通信机制进行介绍。
话题在 ROS 中使用最为频繁,其通信模型也较为复杂。如下图所示,在 ROS 中有两个节点:一个是发布者 Talker,另一个是订阅者 Listener。两个节点分别发布、订阅同一个话题,启动顺序没有强制要求,此处假设 Talker 首先启动,可分成图中所示的七步来分析建立通信的详细过程。
Talker 启动,通过 1234 端口使用 RPC 向 ROS Master 注册发布者的信息,包含所发布消息的话题名;ROS master 会将节点的注册信息加入注册列表中。
Listener 启动,同样通过 RPC 像 ROS Master 注册订阅者的信息,包含需要订阅的话题名。
Master 根据 Listener 的订阅信息从注册列表中进行查找,如果没有找到匹配的发布者,则等待发布者的加入;如果找到匹配的发布者信息,则通过 RPC 向 Listener 发布 Talker 的 RPC 地址信息。
Listener 接收到 Master 发回的 Talker 地址信息,尝试通过 RPC 向 Talker 发送连接请求,传输订阅的话题名、消息类型以及通信协议(TCP/UDP)。
Talker 接收到 listener 发送的连接请求后,继续通过 RPC 向 Listener 确认链接信息,其中包含自身的 TCP 地址信息。
Listener 接收到确认信息后,使用 TCP 尝试与 Talker 建立网络连接。
成功建立连接后,Talker 开始向 Listener 发送话题消息数据。
从上面的分析中可以发现,前五个步骤使用的通信协议都是 RPC,最后发布数据的过程才使用到 TCP。ROS Master 在节点建立连接的过程中起到了重要作用,但是并不参与节点之间最终的数据传输。
服务是一种带有应答的通信机制,通信原理如下图所示,与话题的通信相比,其减少了 Listener 与 Talker 之间的 RPC 通信。
Talker 启动,通过 1234 端口使用 RPC 向 ROS Master 注册发布者的信息,包含所发布消息的话题名;ROS master 会将节点的注册信息加入注册列表中。
Listener 启动,同样通过 RPC 像 ROS Master 注册订阅者的信息,包含需要订阅的服务名。
Master 根据 Listener 的订阅信息从注册列表中进行查找,如果没有找到匹配的服务提供者,则等待该服务提供者的加入;如果找到匹配的服务提供者信息,则通过 RPC 向 Listener 发布 Talker 的 TCP 地址信息。
Listener 接收到确认信息后,使用 TCP 尝试与 Talker 建立网络连接,并发送服务的请求数据。
Talker 接收到服务请求和参数后,开始执行服务功能,执行完成后,向 Listener 发送应答数据。
参数类似于 ROS 中的全局变量,由 ROS Master 进行管理,其通信机制较为简单,不涉及 TCP/UDP 的通信。
Talker 使用 RPC 向 ROS Master 发送参数设置数据,包含参数名和参数值;ROS Master 会将参数名和参数值保存到参数列表中。
Listener 通过 RPC 向 ROS Master 发送参数查找请求,包含索要查找的参数名。
Master 根据 Listener 的查找请求从参数列表中进行查找,查找到参数后,使用RPC 将参数数值发送给 Listener。
这里需要注意的是,如果 Talker 向 Master 更新参数值,Listener 在不重新查询参数值的情况下是无法知晓参数值已经被更新的。所以在很多场景中,需要一种动态参数更新机制。
话题和服务是 ROS 中最基础也是使用最多的通信方法,具体总结如下:
条目 | 话题 | 服务 |
---|---|---|
同步性 | 异步 | 同步 |
通信模型 | 发布/订阅 | 客户端/服务器 |
反馈机制 | 无 | 有 |
底层协议 | ROSTCP/ROSUDP | ROSTCP/ROSUDP |
缓冲区 | 有 | 无 |
实时性 | 弱 | 强 |
节点关系 | 多对多 | 一对多(一个Server) |
使用场景 | 弱逻辑处理,多数据传输 | 强逻辑处理,少数据传输 |
总之,话题是 ROS 中基于 发布/订阅 模型的异步通信模式,这种方式将信息的产生和使用双方解耦,常用于不断更新的、含有较少逻辑处理的通信数据;而服务多用于处理 ROS 中的同步通信,采用 客户端/服务器 模型,常用于数据量较小但有强逻辑处理的数据交换。
ROS 1 主要构建在 Linux 上;ROS 2 支持构建的系统主要包括 Linux、Windows、macOS、RTOS,甚至没有操作系统的裸机。
ROS 中最重要的一个概念就是计算图中的“节点”,它可以让开发者并行开发低耦合的功能模块,并且便于二次复用。ROS 1 的通信系统基于 TCPROS/UDPROS,而 ROS 2 的通信系统基于 DDS。DDS 是一种分布式实时系统中数据 发布/订阅 的标准解决方案。ROS 2 内部提供了 DDS 的抽象层实现,用户无需关注底层 DDS 的提供厂家。
在 ROS 1 架构中,Nodelet 和 TCPROS/UDPROS 是并列的层次,可以为同一个进程中的多个节点提供一种更优化的数据传输方式。ROS 2 中也保留了类似的数据传输方式,命名为“Intra-process”,同样独立于 DDS。
ROS 1 强依赖与 ROS Master,因此可以想象,一旦 Master 宕机,整个系统会面临怎样的窘境。但是在 ROS 2 架构中,让人根根于怀的 Master 终于消失了,节点之间使用一种称为 “Discovery” 的发现机制来帮助彼此建立连接。
DDS(Data Distribution Service),即数据分发服务,2004年由对象管理组织OMG(Object Management Group)发布,是一种专门为实时系统设计的数据 分发/订阅 标准。
DDS 最早应用于美国海军,解决舰船复杂网络环境中大量软件升级的兼容性问题,目前已经成为美国国防部的强制标准,同时广泛应用于国防、民航、工业控制等领域,成为分布式实时系统中数据发布/订阅的标准解决方案。
其技术关键是以数据为核心的 发布/订阅 模型(Data-Centric Publish-Subscribe ,DCPS),这种 DCPS 模型创建了一个“全局数据空间”(global data space)的概念,所有独立的应用都可以访问。
ROS 1的通信模型主要包含话题、服务等通信机制,ROS 2的通信模型会稍显复杂,加入了很多DDS的通信机制。如下图所示:
基于 DDS 的 ROS 2 通信模型包含以下几个关键概念。
在 DDS 中,每一个发布者或者订阅者都成为参与者,对应于一个使用 DDS 的用户,可以使用某种定义好的数据类型来 读/写 全局数据空间。
数据发布的执行者,支持多种数据类型的发布,可以与多个数据写入器(DataWriter)相联,发布一种或多种主题(Topic)的消息。
数据订阅的执行者,支持多种数据类型的订阅,可以与多个数据读取器(DataReader)相联,订阅一种或多种主题(Topic)的消息。
上层应用向发布者更新数据的对象,每个数据写入器对应一个特定的Topic,类似于ROS 1中的一个消息发布者。
上层应用从订阅者读取数据的对象,每个数据读取器对应一个特定的Topic,类似于ROS 1中的一个消息订阅者。
和 ROS 1 中的概念类似,话题需要定义一个名称和一种数据结构,但 ROS 2 中的每个话题都是一个实例,可以存储该话题中的历史消息数据。
简称 QoS Policy,这是 ROS 2 中新增的、也是非常重要的一个概念,控制各方面与底层的通信机制,主要从时间限制、可靠性、持续性、历史记录这几个方面,满足用户针对不同场景的数据需求。
RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务,而不需要了解底层网络技术的思想。RPC 是一种技术思想而非一种规范或协议(按理来说 RPC 也是一种协议才对?)。
如果需要将本地 student 对象的 age+1,可以实现一个 addAge() 方法,将 student 对象传入,对年龄进行更新之后返回即可,本地方法调用的函数体通过函数指针来指定。
上述操作的过程中,如果 addAge() 这个方法在服务端,执行函数的函数体在远程机器上,该如何告诉机器需要调用这个方法呢?
Client端:
// Student student = Call(ServerAddr, addAge, student)
Server 端:
RFC 1831 中记录的远程过程调用(Remote Procedure Call, RPC) 协议旨在以与 TCP 不同的方式扩展 IP。TCP 的目标是大数据流的传输(比如文件下载),而 RPC 是为网络编程而设计的。
RPC 的关键特征有:
RPC 服务对接:
TCP 和 UDP 都依赖众所周知的 port numbers 来执行服务会合。例如,TCP TELNET 服务在 port 21 上可用,FTP 在端口 23 上可用,SMTP 在 port 25 上可用,等等。连接到 TCP 和 UDP 服务只需要连接到正确的 port number。
RPC 在这个过程中引入了另一个步骤,将服务与给定的 port number 分离。它使用一个名为 PORTMAP 或 RPCBIND 的特殊 RPC 服务来实现这一点。这些在 RFC 1833 中记录的绑定协议通常被称为 portmapper,它们在 RPC 服务中是唯一的,因为它们有自己的指定 port(port 111)。运行在任意 port number 上的其他 RPC 服务可以使用到 port 111 的 RPC 调用来注册自己。portmapper 提供其他 RPC 调用以允许服务查找。
这种设计最重要的结果是,portmapper 必须是第一个启动的 RPC 程序,并且必须保持恒定的操作。
RPC 本质上不一定是 TCP,或者说常见的 RPC 是基于 TCP 的或使用 TCP 之上的某种协议的。