卡在门口的沙发
凡是搬过沙发进门的人都懂那种感觉。只要你把沙发斜成某个角度、进门时转一下、在恰当的时机抬起一端,它就能塞进去;可一旦顺序或角度弄错,它就卡死了。让人懊恼的是,房间里明明有大把空地——问题完全出在沙发的形状,以及你一路上怎么转动它。
机器人面对的正是同样的难题,而且更棘手。机械臂是一串由连杆和关节组成的链条;移动机器人转弯时,机身会扫过一片占地范围。问“机器人能不能从这里走到那里?”,从来都不只是两个点离得远不远的问题,而是机器人身体的每一部分能否一路通行、不撞到任何东西。如果在杂乱的三维房间里一个关节一个关节地推敲,很快就会让人头晕。
把整个机器人压缩成一个点
诀窍在这里。我们不再追踪机器人每个部位在哪里,而是问:要彻底锁定机器人的姿态,最少需要几个数?对一条两关节的机械臂来说,就只要两个数——肩部的角度和肘部的角度。一旦知道这两个角度,每根连杆的位置就完全确定了,再没什么要补充的。每个这样的数就是一个自由度,是机器人可以独立活动的一种方式。
现在,把这些数各自当作图上的一根坐标轴。肩部角度沿一根轴展开,肘部角度沿另一根轴展开。整条机械臂——连杆、关节、一切——的任何一个姿态,在这张图上都恰好对应一个点。弯一下肘,点就滑动;摆一下肩,点就朝另一个方向滑。这张抽象的图就是位形空间(C 空间),其神奇之处在于:机器人不再是一具笨拙的多关节身躯,而成了一个点。
同样的配方也适用于在地面上滚动的移动机器人。它的姿态由三个数确定:x 坐标、y 坐标,以及它面朝的方向。于是它的 C 空间是三维的;同样地,整个机器人——轮子、机身、传感器——又坍缩成一个点,随着它行驶和转向,在那个空间里漂移。
点不能进入的地带
把机器人压缩成一个点,只是这套戏法的一半。另一半,是问障碍物会变成什么样。如今机器人的每个姿态都是 C 空间里的一个点,于是我们能把这些点分成两堆:机器人什么都不碰的姿态,和身体某处嵌进了墙、桌子或自身的姿态。这种划分就是自由空间与障碍空间。自由空间是点可以待的所有地方;障碍空间则是禁区。
意外来了。真实房间里一根简单粗壮的柱子,到了 C 空间里却不再简单。因为只要机器人身体的任何部位会与障碍物重叠,它就被挡住,于是一根小小的圆柱也能切出一大片歪斜、形状古怪的禁区——其确切形状取决于机器人自身的身躯,以及关节转动时这身躯如何变化。判断某个姿态落在这条边界的哪一侧,正是碰撞检测的工作;每个规划器都要调用它成千上万次。
这恰恰解释了门口为何那么棘手。在房间里看,那道缝似乎够宽。可在沙发的 C 空间里——它的位置加上它的倾斜角——门口却是穿过自由空间的一条又窄又扭的走廊,你得让那个点沿着它穿过去。机器人的形状并没有消失,它只是搬进了障碍区的几何形态里,让规划器得以干净利落地推敲它。
规划,无非是画一条线
一旦世界被这样重新表述,整个运动规划的目标,说起来便简单得惊人。机器人从一个点(当前姿态)出发,想抵达另一个点(目标姿态)。一份规划,就是任何一条把起点连到终点、且全程不踏入障碍空间的连续曲线。就这么简单。找到这样一条线,你就找到了机器人真正能执行的一连串姿态。
注意这条曲线是什么,又不是什么。它是一条路径:一组有序的姿态,一条穿过自由空间的路线。它还完全没说要走多快、何时加速、何时为转弯减速。给它加上时间安排和速度,才会把光秃秃的路径变成一条轨迹——那是后面的步骤,等我们讲路径与轨迹时再谈。眼下的收获是观念上的:一团纠缠的关节与障碍,化成了“用一条始终留在白色区域里的线,把两个点连起来”。
为什么关节一多,空间就爆炸
化点的戏法很美,却是要付账的,而且账单涨得飞快。每多一个自由度,C 空间就多一根坐标轴——点又多了一个可以移动的维度。两关节机械臂活在一个平坦的二维空间里,画在纸上就行。一条典型的工业机械臂有六个关节,于是它的 C 空间是六维的。一台双臂人形机器人,则可能有几十维。
为什么这事如此要紧?设想用铺网格的办法去逐一检查每个姿态。在二维里,一张 100 乘 100 的网格是一万个格子——轻松。多加一维就是一百万;六维则是一万亿个格子。每多一个关节,工作量就成倍叠乘,于是待搜索的姿态空间膨胀到再无指望逐一查完。这种爆炸式增长,正是规划之所以困难的核心原因,也正是后续篇章里那些精巧的采样与搜索方法存在的理由:它们能找到一条路径,而无须把整个空间都画出来。