第6章 机器人与模拟器
在之前的几个章节中,我们讨论了许多 ROS 的基本概念。或许这些概念看起来很抽象,但是对于理解数据在 ROS 中的传递和 ROS 的软件系统组成却是十分必要的。在本章中,首先会介绍常见的机器人子系统,并说明 ROS 是如何处理这个系统的。接下来,我们会学习一些在本书里将会用到的机器人。最后则会讲解模拟器的使用,模拟器可以简化对机器人的实验过程。
子系统
就像其他复杂的机械一样,机器人也可以使用建立子系统的方法来简化设计和分析过程。在本节中,我们会介绍一个在本书涉及的机器人上很常见的子系统。概括的说,这个子系统由执行,感知和计算三部分组成。在 ROS 语境下,执行子系统是直接与机器人的轮子或机械臂交互的部分,感知子系统则是直接与传感器进行交互,如摄像机,激光雷达等。最后,计算子系统将执行子系统和感知子系统联结在一起,并通过数据处理,让机器人完成一些有用的任务。接下来的几个小节中,我们会分别对这几个子系统进行介绍。请注意,为了让介绍不至于枯燥乏味,我们会控制讲解的深度,只要能涵盖在开发与这些子系统交互的软件时的一些常见问题即可。
执行子系统:移动平台
四处移动是许多机器人的基本能力之一。而且这项能力也早就被研究的非常透彻了---有很多书专门就是讲机器人的移动问题的!概括的说,移动平台就是一些执行器为了达到移动的目的而组合形成的结构。而具体的形状和尺寸则非常多样。
虽然双足行走机器人在一些研究领域很受欢迎,而且近年来摄像机友好(稳定性好?)的行走机器人也获得了长足的进步,但绝大多数的机器人还是用轮子驱动的。这主要是因为两个原因:第一,轮式平台相对容易设计和生产;第二,对于平滑的表面(常见的人工环境都满足条件,如室内的地板和室外的步道等),轮式驱动是最节能的移动方式。
轮式底盘中,差分驱动可能是最简单的了。差分驱动底盘包括两个独立的驱动轮,一般相对圆形底盘的中心线对称分布。当两个轮子同时向前转时,机器人会向前移动;当两个轮子转动方向相反时,机器人会原地转动。差分底盘一般会有一个或多个没有动力,可以自由转动的脚轮,用来对机器人进行支撑,就像办公椅的轮子一样。一个典型的例子是静态自稳定机器人。从俯视角度看,这个机器人的质心位于由轮子与地面的接触点所组成的多边形内部。这样的好处在于,无论在任何时候断电,机器人都不会发生倾倒。
与此相对,动态自稳定和平衡式的轮式移动底盘就要求执行器必须始终处于运动状态(运动幅度可能很小),才能保证底盘的稳定性。最简单的动态自稳定底盘类似赛格威平台,即使用一对较大的差分轮支撑一个较高的机器人本体。平衡式底盘的好处之一是较好的通过性。平衡轮的直径可以非常大,这样就能平滑的驶过较小的障碍物。试想,办公椅的轮子和自行车轮子同时轧过一个鹅卵石,后者的稳定性肯定要好于前者(这也是自行车车轮一般较大的原因)。另一个好处在于场地的适应性,平衡式的轮式底盘整体较小,因此会更易于通过狭小的活动空间。
差分驱动也可以扩展到多个轮子,这种驱动方式被称为滑动转向。四轮和六轮的滑动转向结构较为常见:轮子等量的分布在底盘两侧,每侧的轮子同步转动。当使用的轮子数量多于6个时,常常会用履带把同一侧的轮子联结起来,就像挖掘机或坦克一样。
就像其他工程问题一样,使用滑动转向结构时也需要做一些权衡。有些场景下滑动转向很好用,但其他场景就不见得了。滑动转向结构的一大优点是,在保持简单的机械结构和控制方法这一前提下,它能提供最大的牵引力。这是因为轮子和地面的所有接触点都是动态驱动的。与此相对,滑动转向结构的主要缺点则在于需要持续的进行滑动(以保持平衡?),即使机器人并不需要移动。
在某些情况下,牵引力和越障能力非常重要,此时就需要用到滑动转向结构。然而,任何牵引力的产生都是有代价的:持续的滑动其实非常低效,大量的能量消耗在了扬起尘土和摩擦生热上,而运动速度却非常低。更极端的例子是原地自转,当两组轮子的转向不同时,很快就能把底盘所处的平整地面毁坏,同时把轮子磨坏。这也是挖掘机总是被拖车运至工地而不是自己开过去的原因。
滑动转向底盘的低效,对地面的破坏和对轮胎的严重磨损直接导致乘用车不得不选择采用更复杂(而且更昂贵)的驱动方式。乘用车选择的底盘一般被称为阿克曼平台,这种底盘的后轮保持指向正前方,前轮则可以同步转动。将轮子放在车辆的四个顶角可以将支撑多边形最大化,这样也保证了车辆可以在转急弯的同时不至于倾覆,轮子也无需相对地面滑动(当然,电影中的“漂移”除外)。阿克曼平台的不足之处则在于不能进行横向移动(因为后轮始终朝前)。这也是驾照考试中侧方停车科目非常可怕的原因之一:让阿克曼底盘横向移动,需要做进行精心的规划和大量的练习。
上面描述的所有底盘都可以统称为“非全向的”,因为它们都不能实现在任何时候向任意方向移动。比如说,差分底盘和阿克曼底盘都不能横向移动。为了做到这一点,我们需要一个全向底盘,这样的底盘可以通过可旋转脚轮实现。每个可旋转脚轮有两个电机,其中一个控制轮子的前后转,一个控制空轮子绕垂直轴转动。这样就可以使底盘向任意方向运动。虽然底盘的构建和维护难度都增加不少,但是运动规划的难度却得以大大降低。
当机器人只需要在平滑的地面运动时,可以采用另一种成本相对较低的全向底盘--麦克纳姆轮底盘。这种底盘使用了一种设计非常精巧的轮子--麦克纳姆轮。麦克纳姆轮的轮缘上均匀分布着朝向为斜45度角的滚轮。这样就可以实现任意方向任意速度的运动,而且不需要进行任何滑动。不过,由于麦克纳姆轮上滚轮的直径一般比较小,所以只能在非常光滑的表面运动,比如硬地板或绒毛较短的地毯。
ROS 在设计上的一个目标是做到在不同机器人之间的软件复用,因此,ROS 在与移动底盘进行交互时,一般采用 Twist 消息类型。twist 是一种表达三维空间中线速度和角速度的通用方法。虽然直接用各个轮子的转速可能会更简单,但是使用前者可以让软件对车辆的运动学特性做出更好的抽象。
比如说,封装层次较高的软件就可以实现对机器人“以5m/s 的速度向前行驶,同时以0.1rad/s 的速度顺时针旋转” 这样的控制。换句话说,从这种软件的角度来看,移动底盘的具体驱动方式就不重要了,无论是差分的,阿克曼旋转,还是麦克纳姆轮的都没关系,就像传动比和轮子直径对车辆的高层次行驶行为也没有影响一样。
另一件需要注意的事实是,本书中所描述的机器人都将只会在平坦的,二维的平面上运动,这样的机器人一般会被称为平面机器人。不过,在三维空间对速度进行表示可以让现有的路径规划和避障程序适用于更多的运动形式,比如飞行,潜水,空间航行等。请记住,即便是在二维平面移动的机器人,使用针对三维空间的 twist 描述方法也是十分必要的,因为对于许多执行器来说,只有这样才能把目标或当前运动状态表达清楚。以机械爪为例,这种安装在机械臂末端的执行器一般都具有三维运动的能力,哪怕那个机械臂被固定在了一个只能在二维平面运动的底盘上。由此可见,使用合适的描述方式非常重要。
执行子系统:机械臂
另一种常用的执行子系统是机械臂,机械臂在机器人上非常常见。比如位于流水线后部的打包,运送机器人就会使用机械臂把物品从流水线上抓下来,然后放进盒子里。抓取和放置本身也是一类重要的机器人操纵任务,即要求机械臂抓取正确的物体,然后放在要求的位置上。进一步细分下去,安全相关的任务包括处理可疑物品(这种情况需要更强壮的机械臂),家用领域则包括在家庭或办公室环境内完成清洁,物品运送,准备餐食等等。
相对于移动平台,机械臂的种类要多得多。不同的机械臂在成本和完成特定任务之间做出了很多的权衡。
虽然有很多例外情况,但是主流机械臂的基本结构还是一系列由连接点联结的刚性连接段。说到连接点,最简单的连接点是单轴翻转连接点(有时也被称为“pin”连接点),这种连接点的一端作为轴,另一个连接段则绕该轴进行旋转,就像一般的门一样。另一种常见连接点是线性连接点(有时候被称为有棱连接点),这种连接点连接的两个连接段中,一个会含有滑轨或管道,另一个则会沿着它线性滑动,就像推拉门沿着轨道横向滑动一样。
机械臂的基本特征是自由度的数目(DOF)。一般来说,连接点的数量会与执行器相等。当这两个数量不同时,DOF 值则取较小的那个。DOF值对机械臂的大小,质量,灵活性,成本和可靠性的影响最为显著。在机械臂的末端增加自由度,会使整个机械臂的大小和质量显著增加,进而需要更大的执行器和更结实的连接点,这又进一步增加了质量。
如需让机械臂的腕关节实现工作环境中的任意位姿,至少需要6个自由度。“工作环境”一词在本章节中有一个更明确的定义:即机械臂可以到达的空间。进一步的,我们把机械部末端可以达到的位姿组成的空间称为灵巧工作环境(dextrous workspace)。显然,后者是前者的一个子集。灵巧工作环境越大,说明机器人的性能越好。但是对于六自由度机器人而言,受限于机械结构,电气线路等因素,全范围(360˚)的移动在有限的成本要求下很难做到。为了解决这个问题,我们一般会选择使用7自由度的机械臂。这种机械臂的第七个自由度让机械臂能够在保持腕部位姿不变时,实现连接段整体的移动。类比人类的手臂,相当于手腕不动,而肘部能沿一段圆弧运动。总的来说,增加了一个自由度后,就能得到一个相对较大的灵巧工作区间,即使是在机械臂各个连接点运动受限的情况下。
那些用于研究室内环境下操纵任务的机器人,在结构上一般都比较类似:大小接近真人,拥有7自由度机械臂。这是因为它们预期的工作环境就是人类的活动范围,比如家里或办公室的桌子,料理台等等。相比之下,用于工业生产的机器人之间的差别就很大了。由于额外的 DOF 会导致成本的增加和可靠度的下降,因此不会直接使用7自由度。机械臂的具体维度,连接点的种类等等都会取决于实际的工作任务。
到目前为止,我们已经介绍了两种主要的机器人子系统:用于移动的和用于实现操纵任务的。下一个要讲解的子系统是传感器。我们会从头部传感结构(sensor head)开始讲起,这是一种常见的传感器安装方式。接下来则会讲解头部传感中结构常有的一些子部件。
传感子系统
机器人必须通过感知周围的状况,才能对任务和环境中的变化做出及时的响应。感知需要依赖传感器。从使用方便易于安装的传感器模块,到设计精巧及其昂贵的精密设备,传感器的种类繁多。
许多成功的工业机器人往往只用了很少的传感器。事实上,很多复杂的工业操纵任务只需要精巧的机械设计和一些限位开关就可以做到。二者的组合可以让机器人在预设的位置触发信号,执行预定义的动作,进而完成复杂的操纵流程。只要机械调试得当,这种系统能做到极高的可靠性。类似限位开关的这样的二值传感器还有很多,比如光限位开关,碰撞开关等等。这些相对简单的传感器正是现代工业自动化设备的关键组成部分之一。
除了二值传感器,量传感器也是很重要的一类传感器。举例来说,压力传感器可以用来估计机械和空气的压力,并在一定的范围内输出测量值。量传感器可用来测量各种物理量(声,光等等),不过一般不会返回0或是无穷大这样的测量结果。
由于传感器自身的原因,通常需要对测得的数据进行一些处理。事实上,传感器侧测量结果与真实情况的偏差往往非常巨大。例如距离传感器一般都会有“最短距离”的限制,如果物体的位于最短距离范围内,就不会被传感器发现。也正是因为传感器的这个局限性,在机器人系统中,一般会选择组合多种不同类型的传感器来进行测量。
在本书涉及的机器人应用中,我们都会假设机器人拥有“丰富”的传感器数据。这里的“丰富”其实是个比较含糊的说法。通常可以理解为,机器人不止有一些二值或者量传感器,任何形式的传感器都可以出现在机器人上。不过,在现实中,出于方便和美学等因素的考量,我们会在机器人平台的顶部安装许多传感器,并把它们集成在头部传感结构中。这个结构是可以运动的,可以按需要让传感器进行俯仰。接下来的几节中,我们会讲解一些常在头部传感结构中出现的传感器,也会提及一部分安装在其他位置的传感器。
普通相机
高等动物一般会依赖视觉信息来对周围的环境做出响应。对于机器人来说,能像动物一样智能化的使用相机数据则是一件非常困难的事。尽管如此,由于相机的廉价和对远程操作的帮助,我们还是经常会在头部传感结构中看到它。
有趣的是,从数学上分析,在三维空间对机器人任务和环境进行描述,要比在二维的相机图像上要鲁棒的多。这是因为任务和环境的三维描述是不变的,无论场景的光照,阴影,遮挡物等发生了怎样的变化。而二维相机图像则受这些因素影响极大。事实上,在许多应用研究领域中,图像本身是被忽略的,算法需要的是三维数据。因此,对机器人三维数据感知方向的研究工作近年来不断增加。
当两个相机被刚性的固定在了一个机械结构上时,它们就组成了一个双目立体相机。两个相机看到的视野之间会有轻微的差异,这个差异可以被用来估计视野中特征的深度。听起来好像很简单,不过并不尽然。双目立体相机的性能依赖很多因素,包括相机本身的机械设计,分辨率,镜头类型与品质等等。除此之外,双目相机只能测量视野中那些数学上可分辨的特征的深度,比如锐利的,对比度较高的棱角。比如说,一面没有任何特征的墙是不能被测量的,但是墙的边角处,如地板,天花板,或者另一面有颜色的墙就可以。许多户外的自然景观具有丰富的纹理,因而很容易对其进行深度测量,但是室内的单调场景往往就会非常困难。
在处理相机这件事上,ROS 社区中已经有很多的惯例。对于图像消息,最权威的 ROS 消息类型是 sensor-msgs/Image。这个消息不仅包含了图像,还包含了图像尺寸,像素编码格式等。而为了描述由镜头和感光元件安装造成的图像畸变,则需要用到 sensor-msgs/CameraInfo。通常图像会被发送给 OpenCV 这个计算机视觉程序库,具体的发送过程还可以用 cv-bridge 包来简化。后面我们会陆续谈到它们。
深度相机
正如前一节中所述,即便二维图像非常直观,大部分感知算法还是依赖三维数据。近年来,随着技术的不断改善,低成本深度相机的质量有了很大的提升。与上一节中被动式的双目立体相机不同,深度相机是一种主动设备,它会用不同的方式向场景投射图像,从而大大改善整个测量系统的性能。比如说,一面完全没有任何特征的墙是无法用被动双目立体相机测量深度的。但是许多深度相机会向墙的平面投射一些纹理,随后被相机看到。投射所用的光一般位于近红外波段,这样就不会对物体的颜色造成影响,也不会引起附近人的注意。
另一些常见的深度相机,如微软的 Kinect 相机,会采用投射结构光的方法。这些结构光本身具有一定的特征,投射到场景中的物体表面,并被相机观察到后,相机就可以通过一些重构算法把场景的三维结构还原出来。Kinect 对现代机器人学的影响是巨大的。这款产品原本为游戏市场而设计,游戏市场的规模要远大于机器人传感器市场,因此微软投入了巨大的人力物力来研发它。而且对于一个能输出如此多有用数据的传感器而言,150美元的价格已经非常便宜了。在Kinect 面世后,许多机器人很快便用上了它。Kinect 在工业和研究领域的运用还在不断推广当中。
Kinect 是目前最受欢迎的(当然也是使用最广的)机器人视觉解决方案,除此之外,还有一些其他的方法。比如非结构光深度相机,这种相机仍然使用标准的双目视觉算法,但是会主动对外投射纹理。实际测试表明,这样的方案在缺乏明显特征的室内场景下测量效果良好。
另一种在原理上差异较大的是 TOF (time-of-flight)深度相机。这些相机会快速闪烁一颗红外 LED 或激光发射头,相机的感光结构能够测量光从发出,经场景中物体反射到最后被接收这一过程的时长,即“飞行时间”。进而就能解算出图像的深度信息。
近年来,深度相机领域的相关研究炙手可热,这主要是因为深度相机在游戏和其他人机交互引用方面的巨大市场潜力。现在还不清楚上面所说的哪一种技术最适合机器人应用。至少在本书撰写时,使用上述任何技术的深度相机在机器人实验中都有一定的运用。
就像普通相机一样,深度相机也能产生大量的数据,这些数据一般会以三维点云的形式组织起来。点云的基本消息类型为 sensor-msgs/PointCloud2(名字看起来有点奇怪,这背后有一些历史原因)。它能够表达无结构的点云数据,这一点很重要,因为深度相机往往不能保证每个像素点都返回有效的深度信息。因此,深度图像上经常会出现一些明显的“洞”,后期的算法必须对这些缺陷做出合适的处理。
激光雷达
近几年来,深度相机以其简单和廉价的优势,极大的改变了深度感知市场,然而仍有不少应用场景需要使用准确度更高,测量范围更广的激光雷达。激光雷达的种类有很多,但是最常见的基本结构都包括一个固定在旋转反射镜上的激光发射器。反射镜的旋转速度可达每秒10到80转(即600至4800RPM)。当反射镜旋转时,激光发射器的激光就形成了激光脉冲。将反射回来的光波与发出的光波进行对比,就能测量出激光脉冲的飞行时间,进而得到激光雷达上一系列角度的距离测量结果。
激光雷达常用于自动驾驶汽车。自动驾驶汽车与室内的低速移动机器人有很大的差异,因此对传感器的要求也很不一样。诸如 Velodyne 这样的公司生产的车用激光雷达,必须要能够应对气压,振动和温度骤变等自动驾驶下的常见环境特征。而且由于汽车的运动速度较快,车用激光雷达的探测距离也要相应增加,以保证充足的响应时间。此外,对于自动驾驶的一些常见任务,如车辆和障碍物检测,使用多线激光雷达的效果会更好。这些额外的扫描线可以极大的帮助我们对物体进行辨识,比如区分行道树和行人等等。不过,这些额外的特性都会造成系统复杂度,重量,尺寸和成本的相应增加。
激光雷达自己会进行一些复杂的信号处理工作,进而完成对距离的测量。对外则一般会以一定的频率输出一个由距离构成的向量,以及测量的起止角度位置。在 ROS 中,激光雷达的扫描数据对应的消息为 sensor-msgs/LaserScan。厂家不同,激光雷达的默认输出格式自然千差万别,不过 ROS 的激光雷达驱动会完成所需的消息转换工作。
旋转编码器
对机器人的运动进行测量,是所有机器人系统中都必不可少的工作之一。从底层的运动控制,到上层的地图构建,定位,操纵抓取等算法,都离不开它。测量的方法有很多,不过最简单也是常见的做法,就是对电机的或轮子转过的圈数进行测量,进而判断机器人的运动状况。
为了完成这一测量工作,人们设计出了各种各样的旋转编码器。从实现原理上看,旋转编码器种类有很多,包括磁霍尔编码器,光栅编码器,可变电阻/电容编码器等等。归根结底,它们的本质特征都是对角偏移进行测量。选择编码器时要综合考虑大小,成本,测量精确度,最大旋转速度,以及测量的绝对/相对等因素(相对测量是指,测量结果是相对编码器上电时的起始位置的)。
就像汽车上的速度计和里程计一样,旋转编码器可以用来对机器人轮子转过的圈数做精确测量,从而我们就可以知道机器人走了多远,前进方向转动了多少角度。不过请注意,基于旋转编码器的里程计本身只是简单地对轮子转过的周数做计数,这样其实不足以得到机器人的确切位置。车轮直径,轮胎压力,地毯绒毛的朝向(没错,这个因素是有影响的),车轴的安装误差,轮胎打滑等数不胜数的因素都会对测量结果造成影响。因此,未经处理的里程计数据必然会存在数据漂移的问题。而且机器人走的越远,漂移会越严重。举例来说,一个沿着长直走廊中心线运动的机器人,其里程计测出的运动轨迹总是会逐渐变成弧线。与此相对的等价描述是,对于一个差分驱动的底盘,如果两个轮子以相同的转速转动,机器人永远都不可能走直。可见误差因素的影响之大。这就是为什么我们需要额外的传感器和更智能的算法来实现地图构建和导航。
旋转编码器在机械臂上也有着广泛的使用。主流的机械臂会在每个可旋转的连接点安装至少一个编码器,这些编码器输出的数据构成一个名为操纵器配置(manipulator configuration)的向量。配合机械臂的物理模型,就可以运行许多上层的算法,包括碰撞避免,轨迹规划,循迹运动等等,从而对机械臂进行控制。
由于旋转编码器在移动平台和机械臂上的使用方式有很大差异,ROS 中对于这些场景下的数据处理惯例也很不同。虽然有些移动平台上的设备驱动会直接输出编码器的测量数据,但是通常我们还是会选用空间坐标变换的方式来对外报告里程计的数值,具体的消息类型为 geometry-msgs/Transform。本书会多次涉及空间坐标变换这一概念,不过概括的说,空间坐标变换就是描述了两个帧的相对关系。对于旋转编码器而言,里程计的变换就是它相对上电时的初始位置(或最近一次复位位置)的角位移测量结果。
相对的,机械臂上的编码器则一般会以 sensor-msgs/JointState 类型对外输出测量结果。JointState 包含了由角度(弧度制)构成的向量和角速度(同样为弧度制)两个数据成员。由于常见的编码器都有数千个细分状态(即每转一周的刻度数),机械臂使用的 ROS 设备驱动需要对其进行一定的转换,以符合 JointState 中要求的标准计量单位。JointState 消息能够提供机械臂状态的的最小完整表示,因而在 ROS 的软件包中被广泛使用。
我们已经讲解了机器人系统的物理部分,接下来就要谈谈它的“大脑”部分了。在“大脑”中,机器人会对传感器数据进行处理,然后做出运动决策。这也是本书中最关注的部分。
计算子系统
机器人系统的计算资源差异很大,上可至庞大的服务器集群,下可达极小的8位微控制器。关于机器人究竟该使用多少计算资源才能做出鲁棒的,可用的受控动作,历史上一直存在激烈的争论。以昆虫的大脑为例,它无疑是体积小,低功耗的典范,但是并不影响昆虫称为这个星球上最成功的生物之一。不过,大脑处理数据的方法与“主流”的系统工程方法有很大的不同,这也直接引发了人们对类脑计算架构的研究。
ROS 的机器人计算架构采用了一种更传统的软件工程方法实现。在本书的前几章中曾讲过,ROS 使用了一种动态的消息传送通路实现了软件节点之间的数据传递,并且与完全独立于 POSIX 的进程模型。这样的实现方法当然不是没有代价的,它会消耗额外的 CPU 周期来对来自节点的消息进行序列化,然后传送给另外的节点。不过总的来说,我们认为这样的结构给快速原型开发和软件集成带来的好处要远大于计算资源开销的代价。
考虑到消息机制的额外开销和对软件模块化的要求,ROS 目前还不能在极小的微控制器上运行。虽然 ROS 也可以被用于简单处理流程的快速原型开发,但是主要还是被用于构建含有大量感知输入和复杂处理算法的系统。在这种系统中,ROS 的模块化和动态可扩展性架构可以极大的简化系统的设计和运行。
ROS 一般运行在完整的类 UNIX 系统上,包括 Linux 和 Mac OS X。伴随着摩尔定律和低功耗设备市场的不断增长,越来越多的小型平台可以运行完整的操作系统了。ROS 目前已经可以在一些小型的嵌入式计算机系统如 Gumstix,Raspberry Pi和BeagleBone上运行。不过综合考虑性能和功耗,ROS 还是在笔记本电脑,桌面计算机和服务器上使用较多。实际场合中,机器人一般都会携带有一个或更多的安装有 Linux 操作系统的标准 PC 主板,使用者可以通过网络远程访问PC,从而对机器人进行控制。