3.1 ROS概述

3.1.1 ROS简介

ROS是由Willow Garage公司发布的一款开源机器人操作系统,随着机器人技术的快速发展和复杂化,代码的复用性和模块化需求越来越强烈,而现有的开源系统不能满足要求,ROS应运而生,很快在机器人研究领域展开了学习和使用ROS的热潮。ROS利用很多现在已经存在的开源项目的代码,比如从Player项目中借鉴了驱动、运动控制和仿真方面的代码;从OpenCV中借鉴了视觉算法方面的代码;从OpenRAVE借鉴了规划算法的内容。ROS可以不断地从社区维护中进行升级,包括从其他的软件库、应用补丁中升级ROS的源代码。ROS的首要设计目标是在机器人研发领域提高代码复用率。ROS以节点为基本单元,采用分布式处理框架,这使可执行文件能被单独设计,并且在运行时松散耦合。这些过程可以封装到数据包(Packages)和堆栈(Stacks)中,以便于共享和分发。ROS还支持代码库的联合系统,使得协作亦能被分发。这种从文件系统级别到社区级别的设计功能让独立决定发展和实施工作成为可能。上述所有功能都能由ROS的基础工具实现。

(1)ROS主要特点

ROS的运行架构是一种使用ROS通信模块实现模块之间点对点的松耦合的网络连接处理架构,它执行若干种类型的通信,包括基于服务的同步RPC(远程过程调用)通信、基于Topic的异步数据流通信,还有参数服务器上的数据存储。但是ROS本身并没有实时性。

此外,ROS提供多语言支持,在写代码的时候,诸多编程者会比较偏向某一些编程语言。为了方便更多的使用者,ROS现在支持许多种不同的语言,例如C++、Python、Octave和LISP,也包含其他语言的多种接口实现,见图3-1。

图3-1 ROS logo

ROS机器人操作系统是针对机器人开发而诞生的一整套软件架构的合集。ROS操作系统提供了一个类操作系统的体验,能够运行在各种各样的计算机上。它提供了一套标准的操作系统功能,例如硬件抽象层、底层设备管理、常用函数、进程间的信息传递以及封装管理。ROS的整体体系结构以节点(nodes)为基础,节点接收或者发布信息,与各种各样的传感器进行通信、智行控制、决策等。尽管实时性和低延迟在机器人控制中至关重要,ROS本身却不是一个实时操作系统。当然,ROS可以通过一些手段移植为实时操作系统。

(2)ROS生态系统的软件分类

ROS生态系统的软件大致可以分为三种。

①与编程语言和平台无关的基于ROS的工具。

②ROS系统的自带工具如roscpp、rospy、roslisp等。

③集成了许多ROS库的包。

编程语言的独立开发工具和主要库函数(C++,Python,LISP)都在BSD证书的约束下,对于商业使用和私人使用都是免费的,并且ROS是开源的,大多数的包都是开源的。在各种开源协议的约束下,这些其他的包能够实现常用的功能,诸如驱动硬件、机器人建模、数据类型、决策、感知、位置模拟和构图、仿真工具和其他逻辑。

ROS主要的函数库(C++,Python,LISP)面向Unix类的系统,主要原因是Unix上有许多开源软件。原生的Java ROS库rosjava在使用上没有限制,所以可以移植到安卓系统上。Rosjava同时也使得ROS可以使用官方的MATLAB库。Roslib,一个javascirpt库,使得ros可以用在浏览器上。

(3)ROS的历史

ROS最早是2007年,由斯坦福大学人工智能实验室在设计斯坦福人工智能机器人STAIR的项目中诞生。至今有如下版本,见表3-1。

表3-1 ROS的历史版本

3.1.2 ROS运行机制

ROS有两个层次的概念,分别为Filesystem Level和Computation Graph Level。以下内容具体地总结了这些层次及概念。除了概念,ROS也定义了两种名称——Package和Graph,同样会在以下内容中提及。

(1)ROS的Filesystem Level

文件系统层主要包括以下几项。

①Packages ROS的基本组织,可以包含任意格式文件。一个Package可以包含ROS执行时处理的文件(nodes),一个ROS的依赖库,一个数据集合,配置文件或一些有用的文件在一起。

②Manifests Manifests(manifest.xml)提供关于Package元数据,包括它的许可信息和Package之间依赖关系,以及语言特性信息,像编译旗帜(编译优化参数)。

③Stacks Stacks是Packages的集合,它提供一个完整的功能,像“navigation stack”。Stack与版本号关联,同时也是如何发行ROS软件方式的关键。

④Manifest Stack Manifests Stack manifests(stack.xml)提供关于Stack元数据,包括它的许可信息和Stack之间依赖关系。

⑤Message(msg)types 信息描述,位置在路径my_package/msg/MyMessageType.msg里,定义数据类型在ROS的messages ROS里面。

⑥Service(srv)types 服务描述,位置在路径my_package/srv/MyServiceType.srv里,定义这个请求和相应的数据结构在ROS services里面。

(2)ROS的Computation Graph Level

Computation Graph Level就是用ROS的P2P(Peer-to-Peer网络传输协议)网络集中处理的所有数据。基本的Computation Graph的概念包括Nodes、Master、Parameter Sever、Messages、Services、Topics和Bags,以上所有的这些都以不同的方式给Graph传输数据。

①Nodes Nodes(节点)是一系列运行中的程序。ROS被设计成在一定颗粒度下的模块化系统。一个机器人控制系统通常包含许多Nodes。比如一个Node控制激光雷达,一个Node控制车轮马达,一个Node处理定位,一个Node执行路径规划,另外提供一个图形化界面等。一个ROS节点是由Libraries ROS client library写成的,例如roscpp和rospy。

②Master ROS Master提供了登记列表和对其他Computation Graph Level的查找。没有Master,节点将无法找到其他节点、交换消息或调用服务。

③Parameter Server 参数服务器,使数据按照关键参数的方式存储。目前,参数服务器是Master的组成部分。

④Messages 节点之间通过Messages来传递消息。一个Message是一个简单的数据结构,包含一些归类定义的区。支持标准的原始数据类型(整数、浮点数、布尔数,等)和原始数组类型。Message可以包含任意的嵌套结构和数组(很类似于C语言的结构Structs)。

⑤Topics Messages以一种发布/订阅的方式传递。一个Node可以在一个给定的Topic中发布消息。Topic是一个name被用于描述消息内容。一个Node针对某个Topic关注与订阅特定类型的数据。可能同时有多个Node发布或者订阅同一个Topic的消息;也可能有一个Topic同时发布或订阅多个Topic。总体上,发布者和订阅者不了解彼此的存在。主要的概念在于将信息的发布者和需求者解耦、分离。逻辑上,Topic可以看作是一个严格规范化的消息bus。每个bus有一个名字,每个Node都可以连接到bus发送和接收符合标准类型的消息。

⑥Services 发布/订阅模型是很灵活的通信模式,但是多对多,单向传输对于分布式系统中经常需要的“请求/回应”式的交互来说并不合适。因此,“请求/回应”是通过Services来实现的。这种通信的定义是一种成对的消息:一个用于请求,一个用于回应。假设一个节点提供了下一个name和客户使用服务发送请求消息并等待答复,ROS的客户库通常以一种远程调用的方式提供这样的交互。

⑦Bags Bags是一种格式,用于存储和播放ROS消息。对于储存数据来说,Bags是一种很重要的机制。例如传感器数据很难收集,但却是开发与测试中必须的。

在ROS的Computation Graph Level中,ROS的Master以一个name service的方式工作。它给ROS的节点存储了Topics和Service的注册信息。Nodes与Master进行通信从而报告它们的注册信息。当这些节点与Master通信的时候,它们可以接收关于其他已注册节点的信息并且建立与其他已注册节点之间的联系。当这些注册信息改变时,Master也会回馈这些节点,同时允许节点动态创建与新节点之间的连接。

节点之间的连接是直接的。Master仅仅提供了查询信息,就像一个DNS服务器。节点订阅一个Topic将会要求建立一个与发布该Topics的节点的连接,并且将会在同意连接协议的基础上建立该连接。ROS里面使用最广的连接协议是TCPROS,这个协议使用标准的TCP/IP接口。

这样的架构允许脱钩工作(Decoupled Operation),通过这种方式大型或是更为复杂的系统得以建立,其中names方式是一种行之有效的手段。names方式在ROS系统中扮演极为重要的角色:Topics、Services、Parameters都有各自的names。每一个ROS客户端库都支持重命名,这等同于,每一个编译成功的程序能够以另一种name运行。

例如,为了控制一个Hokuyo激光测距仪(Hokuyo laser range-finder),可以启动hokuyo_node驱动,这个驱动可以与激光仪进行对话并且在“扫描”Topic下可以发布sensor_msgs/LaserScan的信息。为了处理数据,我们也许会写一个使用laser_filters的Node来订阅“扫描”Topic的信息。订阅之后,过滤器将会自动开始接收激光仪的信息(注意两边是如何脱钩工作的),所有的hokuyo_node的节点都会完成发布“扫描”,不需要知道是否有节点被订阅了。所有的过滤器都会完成“扫描”的订阅,不论知道还是不知道是否有节点在发布“扫描”。在不引发任何错误的情况下,这两个Nodes可以任何的顺序启动、终止,或者重启。

以后我们也许会给机器人加入另外一个激光器,这会导致重新设置系统。我们所需要做的就是重新映射已经使用过的names。当我们开始第一个hokuyo_node时,我们可以说它用base_scan代替了映射扫描,并且和我们的过滤器节点做相同的事。现在,这些节点将会用base_scan的Topic来通信从而代替,并且将不再监“扫描”Topic的信息。然后我们就可以为新的激光测距仪启动另外一个hokuyo_node。

一个典型ROS机器人设计系统如图3-2所示。其中,Nodes之间的交流过程如图3-3所示。

图3-2 ROS典型框架

图3-3 Nodes之间的交流示意图