上一篇讲 VLA 时,留下了一个没拆开的细节:OpenVLA 每次输出“7 个数”,说这 7 个数就是机器人的动作。这 7 个数是关节角度吗?不是。要弄清它是什么,得先弄清机器人用两套坐标描述同一个动作。

一个动作,两套坐标

设想一台桌面机械臂要把杯子端起来。控制器真正能直接指挥的,是让每个关节转到多少度:典型机械臂有 6 个或 7 个旋转关节,每个关节的角度是一个数,合起来的一组数完全确定了机械臂此刻的姿势。把这组数当作坐标,所有可能的取值构成一个空间,叫作 关节空间(也叫构型空间)。机器人有多少个能独立驱动的量,就有多少个 自由度;对串联旋转关节的机械臂来说,关节数就等于自由度数。

人通常不这样描述动作。我们说的是“手往前伸 5 厘米”“手腕转 15 度”“夹爪闭合”。这里描述的是末端执行器在空间里的状态:位置 3 个量(上下、左右、前后),朝向 3 个量,合起来叫 位姿,共 6 维。用这种方式描述任务的空间,叫 任务空间,也叫笛卡尔空间。

两者的分界在这里:关节空间是机器人自己的语言,跟它有几个关节、每个连杆多长绑定在一起;任务空间是任务的语言,只关心手要摆到哪儿。同一个动作,在两套坐标里写出来完全不同。

正运动学:从关节到末端,答案唯一

反过来看这个方向——已知每个关节的角度,求末端在哪里——这件事有确定的答案。算法就是从基座出发,把每个关节的旋转和这段连杆的长度依次算进去,一路加到末端,就是一串坐标变换。这叫 正运动学。

它之所以唯一,是因为输入的信息完整:每个关节转到多少都给了,机械结构又固定,末端的位置和朝向自然只剩一种可能。

逆运动学:从末端回到关节,答案不止一个

反过来就麻烦:想让末端到达某个位姿,问每个关节该转多少。这叫 逆运动学,是整个动作链条里最容易出岔子的地方。

先看最简单的例子。平面上一根两关节机械臂,两段连杆长度都是 1 米,想把手放到 (1,1)(1,1)。距离是 2≈1.41\sqrt{2}\approx1.41 米,小于最多能伸出的 2 米,够得着。用余弦定理:

cos⁡θ2=x2+y2−l12−l222l1l2=2−1−12=0\cos\theta_2=\frac{x^2+y^2-l_1^2-l_2^2}{2l_1l_2}=\frac{2-1-1}{2}=0

θ2\theta_2 可以是 +90∘+90^\circ,也可以是 −90∘-90^\circ:

  • θ2=+90∘\theta_2=+90^\circ,θ1=0∘\theta_1=0^\circ:第一段连杆水平向右伸,第二段竖直向上折,末端落在 (1,1)(1,1);
  • θ2=−90∘\theta_2=-90^\circ,θ1=90∘\theta_1=90^\circ:第一段竖直向上,第二段水平向右折,末端也落在 (1,1)(1,1)。

两种摆法差别很大,末端却到同一个点,也就是“肘部朝上”和“肘部朝下”。这里有个容易忽略的细节:两个解的末端 朝向 也不同(一个朝上,一个朝右)。任务只说“手到这个点”,两个解都能用;如果要求端着杯子倒水,朝向是硬约束,两者就不再等价。同一个位置会有多种摆法,前提是你约束的东西比机器人能动的量少。

这条规律可以写成一句判断(自由度 nn 个、任务约束 mm 个):n<mn<m 无解;n=mn=m 一般有有限个解;n>mn>m 一旦有解,就有无穷多个解。

真实的机械臂常落在第三种:常见的 Franka Panda 有 7 个关节,末端位姿只有 6 维约束,多出来的那一个自由度叫 冗余。它的直观表现是自运动——末端位姿锁住不动,整条手臂还能微微摆动。

另有两种坏情况。一是无解:目标超出工作空间,怎么摆都够不着。二是 奇异位形:某些姿势下,末端往某个方向挪一点点,关节却要转动极大角度才能跟上,甚至转不过来。

多解是余量,不是缺陷

既然要给控制器一个确定指令,为什么不规定“永远选第一个解”?因为多出来的选择权正是机器人灵活的地方:同一个末端位姿下,可以挑不撞障碍物、离关节转动限位更远、离奇异位形更远、或者更省力的那一组。这也是为什么流水线上 6 轴臂够用,而在狭窄柜子里取东西往往要靠 7 轴的那一个冗余自由度。

多解还解释了另一个常见误判:末端能到达某一点,不等于这个动作能执行。 如果要求手沿一条直线推过去,这条直线上每一点都必须存在满足关节限位、远离奇异的解,中间有一段过不去,整条动作就得改。所以真实系统执行前,常要把末端轨迹换算到关节空间逐点检查——逆运动学在工程上比正运动学麻烦得多,原因就在这里。

回到 VLA:那 7 个数写在哪套坐标里

现在可以回答开头的问题。

OpenVLA 输出的 7 维动作是:3 个末端位置增量、3 个末端姿态增量,加上 1 个夹爪开合(+1 表示张开,0 表示闭合)23。它说的是任务空间的语言,而且是“相对现在再挪多少”,既不是关节角,也不是一个绝对的终点。

从这 7 个数到关节角,中间这一步不由模型做:机器人侧的环境把增量加到当前末端位姿上,再交给逆运动学求解器算出各个关节的角度 3。

这样设计有两个实在的好处。一是跨平台可比:不同机械臂关节数不同,6 个和 7 个的动作维度对不上,没法混在同一份数据里训练;而“末端位姿增量加夹爪”对谁都一样。二是底层已有成熟解法:逆解、关节限位、速度限制、平滑插值都是控制器一直在处理的事,不必让大模型重学一遍。模型每几百毫秒给出一个新的小增量,关节级的高频跟踪交给底层,所以它输出频率低不等于机器人动得慢。

代价也很清楚。模型对 可执行性 没有感觉:它不检查关节限位,不知道此刻是否接近奇异位形,也不做碰撞检测,这些只能从数据里隐式学到一点。另外,增量的含义由数据集约定:BridgeData V2 里 dX 是末端前后方向、dZ 是上下方向,DROID 里姿态增量用欧拉角表示 3。换个数据集、换台机器人,同一个数值可能代表不同方向的运动——这是与视觉泛化并列的另一半迁移难题。

一句话收束

任务空间写“要什么”,关节空间写“怎么做”:正运动学把后者翻成前者,答案唯一;逆运动学把前者翻回后者,可能多解、可能无解,还可能在某些位形上突然变难。VLA 在任务空间里给出增量,把逆解留给底层控制器——这既是它能用一套模型管多种机器人的原因,也是它管不到关节限位、奇异和碰撞的原因。