本章带领大家了解机器人操作系统ROS及其安装方法,熟悉其中的核心概念,结合移动机器人的运行案例,加深对这些概念的理解,这也是未来开发过程中会频繁出现的基础知识。
在ROS诞生之前,如果我们想要做一款机器人,先得从机械设计开始研究,轮子什么样、如何安装、控制器选什么、程序如何一行一行开始写,做完可能三年过去了。殊不知这样的事情已经被很多人重复地做过无数次了,有点像原始的传统开发方式,想要做一个汽车,就得从轮子造起,效率肯定不高,如图3-1所示。
图3-1 传统模式
现代模式则更强调分工合作,要造一辆汽车,可以选A家的轮胎、B家的底盘、C家的系统,原本需要三年的事情,现在只需要3个月就搞定了,然后专注在更关心的上层应用设计上,比如做自动驾驶算法。
机器人开发也是一样,ROS希望机器人开发者不要重复造轮子,别人已经做过的功能,在允许的情况下,直接用就可以了,站在巨人的肩膀上,才能看得更远。比如我们想要实现一个自主导航的机器人算法,就可以直接找到一款支持ROS的机器人,使用ROS提供的定位和导航算法,专注在目标场景之中的应用和调优上。
为了实现这个更大的目标,ROS在自身的设计上也做了很多考虑,比如多种功能之间可以点到点通信;每个节点可以使用C++、Python等多种语言编写,方便分工合作;整个ROS的架构要足够精简,同时提供多种调试和开发工具,提高机器人的开发效率;最后就是免费并且开源,我们可以参考社区中很多别人的工作成果。
ROS由四大部分组成,包括通信机制、开发工具、应用功能、生态系统,如图3-2所示。
图3-2 ROS的四大组成
ROS中提供了大量的机器人开发工具,如图3-3所示。有帮助我们编译和调试机器人代码的软件,有各种各样机器人的底层算法,还有多种多样的可视化工具,比如我们想要查看机器人的速度曲线,可以通过Qt工具箱进行可视化显示;想要查看机器人模型、传感器数据,可以通过Rviz工具显示;如果身边没有机器人实物,还可以使用Gazebo软件进行仿真。
图3-3 ROS提供的开发工具
ROS中提供的机器人应用功能如图3-4所示。在这些工具的支持下,我们还可以使用ROS社区中大量的机器人应用功能,比如自主导航、地图构建、运动规划等,只需要安装配置之后,就可以快速跑起来。
图3-4 ROS中提供的机器人应用功能
ROS经过十几年的发展,历经了大量版本的迭代,如表3-1所列。早期的ROS不太稳定,每年都会有1~2个版本,现在已经相对稳定,也陆续推出了多个长期支持版,主流使用的是在2020年发布的Noetic版本,本书就以这个版本为主展开讲解。
表3-1 ROS所有发布版本的相关信息
续表
续表
当然,在开发之前,我们还需要把ROS装到Ubuntu系统中。大家可以参考以下步骤进行安装。
(1)打开安装好的Ubuntu系统。
(2)输入以下命令,添加ROS软件源,也就是ROS相关软件的下载地址:
$ sudo sh-c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release-sc) main">/etc/apt/sources.list.d/ros-latest.list'
使用如下命令添加密钥:
$ sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80'--recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
(3)接下来输入如下命令,开始安装ROS:
$ sudo apt update $ sudo apt install ros-noetic-desktop-full
(4)安装完成后,输入如下命令,设置环境变量,让系统知道ROS的安装位置:
$ echo"source/opt/ros/noetic/setup.bash">>~/.bashrc $ source~/.bashrc
(5)输入如下命令,安装一些依赖项:
$ sudo apt install python3-rosdep python3-rosinstall python3-rosinstall- generator python3-wstool build-essential
(6)输入如下命令,初始化ROS依赖工具:
$ sudo rosdep init $ rosdep update
(7)输入如下命令,启动ROS节点管理器,如果出现如图3-5所示的提示,说明ROS已经安装成功。
$ roscore
图3-5 启动ROS节点管理器
在LIMO机器人上安装有相机、雷达等传感器,这些传感器通过USB等线缆连接到机器人的控制器上。软件方面,控制器中需要安装一个传感器的驱动程序,获取具体数据,收到这些数据后,我们就可以使用后续物体识别等图像处理的功能了,同时还可以结合物体识别的结果,进行机器人运动控制,比如跟随某一物体运动。
在类似应用中,驱动、处理、控制,每一个功能都相对独立,彼此之间又有一些数据往来,如果把这个流程画成一张图,就是一个分布式的网络结构,如图3-6所示。其中每一个模块表示一项具体的功能,模块之间的连线表示数据传输。比如一个节点用来驱动相机获取图像数据,另一个节点用来显示摄像头数据,还有一个节点用来进行物体识别,每个节点完成一项具体的功能,相互连线就表示功能之间可传输图像或者指令数据。
图3-6 分布式网络结构
在ROS的分布式通信框架中,每个节点不仅可以使用不同的编程语言实现,还可以位于同一网络中不同的计算平台里,比如运动控制节点运行在小车的控制器中,视觉识别节点运行在笔记本电脑上,节点之间数据通信的建立,都依赖节点管理器这个核心中枢。
这个分布式的网络结构,我们也称为ROS的“计算图”,在这张图中就包含了ROS大部分的核心概念,我们依次认识下这些概念。
首先来认识下节点和节点管理器,如图3-7所示。
相机是机器人的外设,通过驱动节点获取图像数据,这些图像数据又交给另外一个图像处理节点进行物体识别,同时在笔记本电脑上用一个节点来显示图像处理的结果,便于我们验证算法的效果。类似这样的功能就叫作节点。
在ROS分布式网络结构中,节点是每一个功能的执行单元,负责执行具体的任务,从计算机操作系统的角度来看,每个节点都是系统中的一个进程,也就是我们通常编译代码生成的可执行文件。所以每启动一个节点,都需要运行一个可执行文件。
既然每个可执行文件是独立运行的,带来的一个好处是每个节点可以使用不同的编程语言来实现,比如驱动偏底层,可以用C来写;图像处理偏应用,可以用Python来写;大家一起写代码时就可以使用各自擅长的编程语言了。
图3-7 节点和节点管理器
节点在ROS中依靠节点名来查找和管理,不允许出现重名的节点,比如两个节点都叫“张三”,那需要干活时,就不知道该找谁了。这么多节点就像一个社交群,大家一开始都不认识,需要有一个“群主”帮大家介绍,这个角色就是节点管理器。每个节点启动时,都需要找节点管理器注册,告诉群主我是谁,我可以干什么,我想和别人聊哪些话题。当群主发现某些节点想要聊的话题一致,就会帮助它们建立通信连接,比如节点管理器会帮助相机驱动节点和图像处理节点建立联系,图像数据才能从一方传输给另外一方。
在这个过程中,节点管理器主要为节点提供了命名和注册服务,通过注册的信息,辅助节点相互查找并建立连接。
节点管理器帮助节点建立了连接,也就是建立了数据传输的通道,这个通道叫作话题(Topic)。
话题和消息如图3-8所示。比如相机驱动节点要发布图像数据,图像处理节点要订阅图像数据,上位机的显示节点也想订阅图像数据,这里的图像数据就是话题,我们给这个话题取了一个名字叫作image_data,也就是话题名。当节点管理器发现这三个节点都在研究图像数据这个话题时,就给它们建立连接,当然每个节点对数据的需求是不一样的,驱动节点是产生并且发布数据的,叫作发布者Publisher,处理和显示节点是接收数据的,叫作订阅者Subscriber。在话题通信中,数据传输的方向是从发布者到订阅者,是一个单向的传输。
话题是ROS中用来传输数据的一种重要总线,或者说是一种方式,如图3-9所示。它采用的是发布/订阅模型,数据由发布者传送到订阅者,但是发布者并不知道订阅者是否收到数据,只能一直发送,所以这是一种异步通信的机制。
图3-8 话题和消息
图3-9 话题模型(发布/订阅)
如果把话题比喻成节点之间的隧道,消息就是穿梭于这个隧道中的数据。ROS定义了很多常用的消息结构,类似于编程中的数据结构,比如图像数据如何描述、地图数据如何描述、速度指令如何描述,这些在ROS中都是标准化的,ROS功能节点良好的可复用性,就建立在这些标准的数据接口上。当然,在某些情况下,ROS的标准定义并不能满足我们的所有需求,此时我们还可以自己定义一些消息结构,而这种定义方式和编程语言无关,就像编程中的伪代码,只表示抽象的定义,具体代码会在后期的编译过程中动态生成。
话题通信在ROS中出现的频率最高,它有点像我们生活当中的信件,写信的人把信邮寄出去之后,是不知道收信人有没有收到的,所以才会出现电话这种更加即时的通信方式。
话题通信在机器人中也同样工作,比如图像处理节点不需要驱动节点一直发送数据,需要时给一帧数据即可,这时就会涉及话题无法完成的双向通信了。处理节点先发送一个请求数据,请求驱动节点发一次数据,驱动节点收到之后就驱动一次相机,获取一幅图像数据,通过应答数据反馈给处理节点,此时就避免了驱动节点的无效工作。
这种通信方式在ROS中叫作服务(Service),如图3-10所示。与话题不同,服务使用的是服务器和客户端的通信模型,类似我们平时上网,浏览器是客户端,网站是服务器,输入一个网址回车后就发送了一个请求,服务器收到请求之后反馈网站的页面作为应答,浏览器就可以看到页面了。如果我们不发送请求,服务器也不会主动应答数据。
图3-10 服务
客户端Client发送一个请求,通过服务Service这个通道传送到服务器端,服务器收到之后就可以进行处理了,处理之后,通过应答数据,再反馈给客户端,告诉它最终处理的结果,过程如图3-11所示。
图3-11 服务模型(请求/应答)
在ROS中,服务的通信模型是一对多的,提供服务的服务器只能有一个,而请求服务的客户端可以有多个。当有多个客户端时,我们需要注意:如果服务器正忙于处理某一个客户端的请求,此时另外一个客户端再发送一个请求,服务器不能分身,就无法响应这个请求了。
服务在ROS机器人开发中使用的也比较多,当我们需要触发机器人的某项功能,并且需要知道执行结果时,就可以使用服务。
话题和服务是ROS中最重要的两种通信机制。除此之外,参数也可以用于某些情况下的数据共享。
参数模型(全局字典)如图3-12所示。比如一个节点启动后,在节点管理器中存储了一个全局参数foo,foo的值是1,另外一个节点运行过程中刚好需要使用foo这个参数,此时就可以查询节点管理器,节点管理器也很乐意帮忙查询,并且反馈foo的值是1。
图3-12 参数模型(全局字典)
参数服务器很适合存储一些运行过程中的静态参数,也就是不发生变化的参数,如果某个节点心血来潮,把foo的值从1变成2,如果其他节点不知道这个变化,无人告知,或者没有服务器主动再查询一次,节点就只能继续用之前的值1进行运算了,这样就会出现很多问题。这也是我们在使用ROS过程中需要警惕的现象,比如之前的ROS系统没关掉,就启动了新的节点,很多参数还是之前的数据,就会出现莫名其妙的错误了。
ROS的核心概念不少,有节点、话题、消息、服务等,在实际机器人运行过程中,这些概念是如何体现的呢?
我们先来运行ROS一个经典的例程——小海龟,大家按照以下步骤进行操作。
(1)打开终端,输入以下命令,启动ROS Master:
$ roscore
(2)打开一个新终端,输入以下命令行,启动小海龟仿真器。启动成功后,就会出现如图3-13所示的小海龟仿真器界面。
$ rosrun turtlesim turtlesim_node
图3-13 小海龟仿真器界面
(3)再打开一个新的终端,输入以下命令行,启动海龟控制节点,大家可以通过键盘的上下左右键来控制小海龟运动:
$ rosrun turtlesim turtle_teleop_key
启动海龟键盘控制节点界面如图3-14所示。
图3-14 启动海龟键盘控制节点
在控制海龟运动的过程中一定要保证turtle_teleop_key节点终端在界面最前端,如果其他终端在前端,就没有办法被终端读取到数据。
小海龟功能跑起来了,那这个例程是如何基于ROS的核心概念实现的呢?接下来我们就分析一下例程背后的节点关系。
这里我们将用到ROS中一个重要的可视化调试工具——rqt_graph,用来显示ROS运行中的计算图,可以看到所有节点的运行关系。
打开一个新终端,输入以下命令,启动rqt_graph工具:
$ rqt_graph
启动完成后,就可看到如图3-15所示的计算图,这个界面会自动监控当前运行的ROS系统,并且把所有节点和节点间的关系动态地显示出来,其中椭圆表示节点,中间的箭头表示节点间的关系,箭头上的内容表示话题。
图3-15 rqt_graph可视化显示计算图
在这个例程中,我们分别启动了两个节点:一个是海龟仿真器,我们可以把它当成是一个虚拟的机器人;另外一个是键盘控制,用来控制机器人前后左右运动。两个节点在节点管理器的帮助下建立了数据通信,完成速度控制指令的传输。
通过这个例程,我们需要理解节点在ROS中怎样实现某些具体的功能,比如机器人的驱动、运动指令的发送等,节点之间可以通过话题将数据发送或接收。小海龟毕竟是一个仿真的机器人,在实物机器人中是不是也类似呢?我们再来试一试。
登录LIMO机器人的系统,使用以下两句命令行启动机器人底盘和键盘控制节点,就像控制小海龟前后左右运动一样,我们也可以控制机器人运动。
$ roslaunch limo_base limo_base.launch $ roslaunch limo_bringup limo_teletop_keyboard.launch
再打开rqt_graph工具看一下节点关系,可以直观地发现,此时系统中运行了两个节点,如图3-16所示。
第一个是和控制海龟运动相同的键盘控制节点teleop_keyboard,用来读取键盘的输入键值,并封装成cmd_vel速度话题发布出去。
图3-16 使用rqt_graph查看节点关系
第二个节点像小海龟仿真器一样,用来驱动LIMO机器人的底盘控制节点limo_base_node,它会订阅速度指令,当收到数据后,就会驱动机器人运动。
从图3-16中我们可以清晰地看到这两个节点和它们之间的关系。以上案例实现的功能相对简单,在一个实现众多应用功能的复杂机器人系统中,节点和话题的数量都会很多,除了底层嵌入式运动控制器中需要实现的功能外,在ROS环境下会通过一系列节点分别完成雷达、相机这些传感器的驱动,发布数据话题后,上层的导航、建图、图像处理节点来订阅并进行相应的算法处理,再传输到监控的计算机,给可视化节点做显示。每个节点各司其职,在ROS Master这个节点管理器的统一协调下,有条不紊地完成各项任务。
整个ROS运行中的节点就像一个企业中不同部门的员工,每个人都有自己明确的工作,大家共同在CEO的组织下,合作完成一项非常复杂的任务。当然,每个人都不能掉队,一旦掉队就可能会影响最终任务的完成。
本章正式认识了ROS,并将ROS安装到了Ubuntu系统中,重点讲解了ROS中的核心概念,包括节点、话题、消息、服务、参数等。以小海龟仿真和移动机器人的运动控制为例,讲解了节点、话题在实际使用中的情况,能加深大家对概念的理解。