论文:ReKep: Spatio-Temporal Reasoning of Relational Keypoint Constraints for Robotic Manipulation
作者:Wenlong Huang, Chen Wang, Yunzhu Li, Ruohan Zhang, Li Fei-Fei(Stanford University & Columbia University)
项目主页:rekep-robot.github.io | 代码:github.com/huangwl18/ReKep


一、介绍:机器人泡一杯茶,到底有多难?

先看一个看似人畜无害的任务:给杯子里倒茶

人类做这件事的时候根本不会过脑子——伸手、抓壶柄、拎起来、壶嘴对准杯口、倾斜、倒。但拆开来看,这短短几秒钟里藏着至少四类约束:

  1. 抓取约束:必须抓壶柄,不能抓壶身(烫 + 抓不稳);
  2. 姿态约束:搬运过程中壶必须保持直立,否则茶洒一桌;
  3. 对齐约束:壶嘴必须移到杯口正上方;
  4. 旋转约束:倾斜角度要够大才能倒出来,但壶嘴得始终在杯口上方。

这四条约束,前三条是空间关系(spatial),最后一条还带时序依赖(temporal)——不满足对齐约束就执行倾斜,那就是灾难现场。

机器人领域处理这类"约束"的传统路线有三条,但每条都有硬伤:

  • 路线一:刚体位姿表示(rigid-body pose)。直接用机器人末端和物体之间的相对位姿(SE(3) 变换)来描述约束。问题:描述不了几何细节(壶嘴在哪?杯口在哪?),需要预先拿到物体的 CAD 模型,而且对可变形物体(衣服、绳子)彻底失效
  • 路线二:数据驱动。直接在视觉空间里学约束。问题:约束数量随物体×任务组合爆炸,训练数据收不过来。
  • 路线三:TAMP(Task and Motion Planning)。经典的任务与运动规划框架,约束可以写成约束满足问题。问题:约束基本靠人肉手写,换个任务就得重写,开放世界场景下毫无扩展性。

这篇论文的作者问了一个非常锋利的问题:能不能找到一种约束表示,同时满足三个条件——

  1. 普适(widely applicable):多阶段、开放环境、双臂、反应式行为全覆盖;
  2. 可规模化获取(scalably obtainable):能被基础模型(foundation models)全自动生成,不用人工标注;
  3. 实时可解(real-time optimizable):现成的数值求解器就能算,能以高频闭环执行。

答案就是 ReKep(Relational Keypoint Constraints,关系关键点约束):把操作任务的约束写成一组 Python 函数,输入是场景中一组带语义的 3D 关键点,输出一个数值代价(cost),f(k) ≤ 0 表示约束满足。

一句话总结这篇论文干的事:用 DINOv2 在图上"撒"语义关键点,让 GPT-4o 看着标了号的图写约束代码,再把约束塞进一个分层优化求解器,以 10Hz 的频率闭环控制机械臂。全程零任务数据、零环境模型、零训练。

如果你做过视频编码,可以这么类比着理解:ReKep 之于机器人操作,有点像"率失真优化(RDO)“之于编码器——不直接规定每一步怎么走,而是定义一个代价函数,让求解器在约束下自己找最优解。而 GPT-4o 的角色,则相当于一个"会看图写代价函数的码率控制模块”。


二、原理:三层流水线,各司其职

ReKep 系统的完整链路只有三个大模块,我们逐个拆。

2.1 关键点提 Proposal:DINOv2 + SAM 的"撒点"流水线

要让约束有地方"挂",第一步是场景里得有一堆语义关键点——壶柄上一个、壶嘴一个、杯口一个……论文的做法非常"基础模型乐高":

  1. 特征提取:RGB 图像过 DINOv2(ViT-S14,带 registers 的版本),拿到 patch 级特征图 F_patch ∈ R^{h'×w'×d},再双线性插值上采样回原图尺寸 F_interp ∈ R^{h×w×d}
  2. 物体先验:用 SAM(Segment Anything)把场景里所有物体 mask 全抠出来——这一步提供了关键的 objectness 先验,保证关键点只落在物体上而不是背景上;
  3. 逐 mask 聚类:在每个 mask 内部,先把特征 PCA 降到 3 维(论文发现这一步能滤掉与任务无关的纹理细节,明显改善聚类质量),再用 k-means(k=5)聚类,聚类质心即为关键点候选
  4. 3D 反投影:利用标定好的 RGB-D 相机,把 2D 候选点投影到世界坐标系的 3D 位置;
  5. 去冗余:8cm 范围内的近邻点用 Mean Shift 过滤掉,避免点扎堆。

论文附录 A.13 做了一组很有说服力的消融:SAM + DINOv2 vs SAM + CLIP vs SAM + ViT vs 纯 DINO。结论有两条——SAM 的物体先验是必需品(没有 SAM 点会撒到背景上);DINOv2 的特征比 CLIP 和监督式 ViT 更锐利,能更好地区分物体的细粒度部位(杯柄、盒子侧面这类)。这与视觉领域"Eyes Wide Shut"那篇论文对多模态模型视觉盲区的批评形成了呼应——自监督视觉模型在细粒度特征上依然碾压靠 caption 预训练出来的模型。

2.2 约束生成:让 GPT-4o 当"程序员"

拿到关键点后,把标了编号 {0, …, K-1} 的关键点叠加图和任务指令一起喂给 GPT-4o,让它输出一整套 Python 约束函数。

这里有个非常关键的工程设计,值得单独拎出来讲:GPT-4o 写的函数不直接操作关键点的数值,它只负责用算术运算(L2 距离、点积、向量夹角……)描述关键点之间的关系,真正的数值在运行时才由 3D 跟踪器注入。

为什么这么设计?因为 VLM 有两个天生短板:

  • 数值不靠谱:让 VLM 直接输出 3D 坐标或旋转角,精度惨不忍睹。MOKA 论文原话:“current VLMs are not capable of reliably predicting 6-DoF motions”;
  • 3D 旋转表示法混乱:欧拉角有万向节锁、四元数不直观、旋转矩阵冗余,让 VLM 在这些表示之间折腾就是灾难。

ReKep 的解法堪称优雅:VLM 只在笛卡尔 (x,y,z) 空间里做关键点算术,3D 旋转由"足够多的刚性关键点之间的关系"隐式指定,真正的 SO(3) 求解交给高精度数值求解器。比如"倒茶"这个动作,物理上存在多个可行的旋转方式(绕杯口转都行),VLM 不需要指定唯一的旋转——它只需要写出"壶嘴要贴近杯口、壶柄要抬起来"这类关系,旋转自然被求解器算出来。VLM 出"语义",求解器出"几何",分工明确。

2.3 闭环执行:分层优化 + 回溯

生成的约束被塞进一个分层优化框架(继承自 Toussaint 等人的 Sequence-of-Constraints MPC):

  • 子目标问题(sub-goal problem):先解出当前阶段的末端执行器目标位姿(SE(3));
  • 路径问题(path problem):再解从当前位姿到子目标的密集轨迹(滚动时域,receding horizon);
  • 回溯(backtracking):每个控制周期检查上一阶段的子目标约束是否仍满足(比如杯子被人从夹爪里抽走了),不满足就退回更早的阶段重新规划。

关键点位置由一个基于 DINOv2 特征匹配的 3D 点跟踪器以 20Hz 持续更新,优化问题以约 10Hz 的频率重解——这就构成了一个实时的感知-动作闭环,系统因此能对干扰做出反应:你把杯子挪走,机器人会先把壶放平、重新对齐,再倒;你把壶从它手里抢走,它会回去重新抓。

整套系统里没有任何一个模块见过"倒茶"这个任务的训练数据。


三、公式:把操作任务写成一个数学规划

这是论文的数学核心,三个公式层层递进,我们一个一个啃。

3.1 ReKep 的形式化定义

设场景中有 K 个关键点,单个 ReKep 实例是一个函数:

f:RK×3→Rf: \mathbb{R}^{K \times 3} \rightarrow \mathbb{R}f:RK×3R

输入关键点数组 k(K 个 3D 点),输出一个无界代价。约定:

f(k)≤0  ⟺  约束满足f(\mathbf{k}) \leq 0 \iff \text{约束满足}f(k)0约束满足

f 是无状态的 Python 函数,内部是 NumPy 运算,可以非线性、可以非凸。一个 ReKep 实例编码一条关键点之间的期望空间关系,关键点可以来自机械臂、物体部位、甚至其他智能体(协作叠被子里的人)。

3.2 双层约束结构:sub-goal 与 path

任务被分解为 N 个阶段(stage),每个阶段 i 配两类约束:

  • 子目标约束集 Csubgoal(i)={fsubgoal,1(i)(k),…,fsubgoal,n(i)(k)}\mathcal{C}_{subgoal}^{(i)} = \{f_{subgoal,1}^{(i)}(\mathbf{k}), \ldots, f_{subgoal,n}^{(i)}(\mathbf{k})\}Csubgoal(i)={fsubgoal,1(i)(k),,fsubgoal,n(i)(k)}:在第 i 阶段结束时必须满足(“壶嘴到杯口上方 10cm”);
  • 路径约束集 Cpath(i)={fpath,1(i)(k),…,fpath,m(i)(k)}\mathcal{C}_{path}^{(i)} = \{f_{path,1}^{(i)}(\mathbf{k}), \ldots, f_{path,m}^{(i)}(\mathbf{k})\}Cpath(i)={fpath,1(i)(k),,fpath,m(i)(k)}:在第 i 阶段全程每一步都要满足(“壶保持直立防洒”)。

这个"终点约束 + 过程约束"的二元结构,是 ReKep 能表达多阶段时序依赖的关键。用编码器的话说:sub-goal 约束像 I 帧——只在关键位置检查;path 约束像逐帧检查的约束——每一帧都不能违反。

3.3 主问题(式 1):轨迹优化

末端执行器位姿记为 e∈SE(3)e \in SE(3)eSE(3),目标是求整条离散轨迹 e1:Te_{1:T}e1:T

arg⁡min⁡e1:T, g1:N∑i=1N[λsubgoal(i)(egi)+∑t=gi−1giλpath(i)(et)]s.t.e1=einit,g0=1,0<gi<gi+1f(kgi)≤0,∀f∈Csubgoal(i)f(kt)≤0,∀f∈Cpath(i),t=gi−1,…,gikt+1=h(kt,et),t=1,…,T−1 \begin{aligned} \arg\min_{e_{1:T},\, g_{1:N}} \quad & \sum_{i=1}^{N} \left[ \lambda_{subgoal}^{(i)}(e_{g_i}) + \sum_{t=g_{i-1}}^{g_i} \lambda_{path}^{(i)}(e_t) \right] \\ \text{s.t.} \quad & e_1 = e_{init}, \quad g_0 = 1, \quad 0 < g_i < g_{i+1} \\ & f(\mathbf{k}_{g_i}) \leq 0, \quad \forall f \in \mathcal{C}_{subgoal}^{(i)} \\ & f(\mathbf{k}_t) \leq 0, \quad \forall f \in \mathcal{C}_{path}^{(i)}, \quad t = g_{i-1}, \ldots, g_i \\ & \mathbf{k}_{t+1} = h(\mathbf{k}_t, e_t), \quad t = 1, \ldots, T-1 \end{aligned} arge1:T,g1:Nmins.t.i=1N[λsubgoal(i)(egi)+t=gi1giλpath(i)(et)]e1=einit,g0=1,0<gi<gi+1f(kgi)0,fCsubgoal(i)f(kt)0,fCpath(i),t=gi1,,gikt+1=h(kt,et),t=1,,T1

变量解读:

  • gi∈{1,…,T}g_i \in \{1,\ldots,T\}gi{1,,T}:从阶段 i 切换到 i+1 的时刻,本身也是决策变量——什么时候切换阶段,让求解器自己定;
  • hhh:关键点的前向模型(forward model),预测"末端动了,关键点怎么跟着动";
  • λsubgoal,λpath\lambda_{subgoal}, \lambda_{path}λsubgoal,λpath:辅助代价(碰撞规避、可达性等),与 ReKep 约束配合使用。

整个式子本质上是轨迹优化里的 direct shooting(直接打靶法)。

3.4 分解求解(式 2、式 3)

式 1 整体是非凸混合问题,实时解不动。论文的工程化处理是只优化"下一个子目标 + 到达它的路径",滚动推进。

子目标问题(式 2):

arg⁡min⁡egiλsubgoal(i)(egi)s.t.f(kgi)≤0, ∀f∈Csubgoal(i) \arg\min_{e_{g_i}} \lambda_{subgoal}^{(i)}(e_{g_i}) \quad \text{s.t.} \quad f(\mathbf{k}_{g_i}) \leq 0, \ \forall f \in \mathcal{C}_{subgoal}^{(i)} argegiminλsubgoal(i)(egi)s.t.f(kgi)0, fCsubgoal(i)

路径问题(式 3):

arg⁡min⁡et:gi, giλpath(i)(et:gi)s.t.f(k^t^)≤0, ∀f∈Cpath(i), t^=t,…,gi \arg\min_{e_{t:g_i},\, g_i} \lambda_{path}^{(i)}(e_{t:g_i}) \quad \text{s.t.} \quad f(\hat{\mathbf{k}}_{\hat{t}}) \leq 0, \ \forall f \in \mathcal{C}_{path}^{(i)}, \ \hat{t} = t, \ldots, g_i arget:gi,giminλpath(i)(et:gi)s.t.f(k^t^)0, fCpath(i), t^=t,,gi

前向模型 h 的刚性假设:求解式 2/3 需要估计"Δe 如何引起 Δk"。论文沿用 KPAM 的做法,对"被抓关键点组"(同一物体/部位上的刚性关键点群,来自分割模型)做刚性假设

k[grasped]′=TΔe⋅k[grasped]\mathbf{k}'_{[grasped]} = \mathbf{T}_{\Delta e} \cdot \mathbf{k}_{[grasped]}k[grasped]=TΔek[grasped]

即末端执行器怎么动,被抓的那组关键点就跟着做同样的刚体变换,其他关键点视为静止。注意这是个局部假设——只在一个求解周期(0.1s)的短时域内成立,真实关键点位置每 50ms 由视觉跟踪器(20Hz)刷新一次并注入下一个问题。高频反馈把模型误差"洗"掉了。这个套路你应该很眼熟:运动补偿里的平移假设 + 高频更新,本质上和编码器里"块内像素做统一运动矢量预测、误差靠残差修正"是同一个哲学。

求解器配置:SciPy 实现,决策变量归一化到 [0,1]。首次求解用 Dual Annealing(模拟退火家族的全局优化)+ SLSQP(序列二次规划局部精修),耗时约 1 秒;后续迭代只跑 SLSQP,用上一帧解热启动,频率约 10Hz。梯度用有限差分获取(论文脚注说够用了,自动微分可作升级项)。

阶段切换与回溯:末端位姿与子目标距离小于容差 ε → 推进到下一阶段;每轮循环检查 Cpath\mathcal{C}_{path}Cpath 违反 → 迭代回退到上一个 path 约束仍满足的阶段 j。


四、图示:一张图看懂全链路

                        ┌──────────────────────────────────────────────────┐
                        │              "Pour tea into the cup."             │
                        │              (自由格式的语言指令)                    │
                        └──────────────────────┬───────────────────────────┘
                                               │
        RGB-D 观测                              ▼
   ┌────────────────┐              ┌─────────────────────────┐
   │  RGB + 点云     ├─────────────►│   Large Vision Model     │
   │  (20Hz 多相机)  │              │   DINOv2 + SAM + k-means │
   └───────┬────────┘              └───────────┬─────────────┘
           │                                    │
           │                          语义关键点候选 (带编号 0..K-1)
           │                                    │
           │                                    ▼
           │                        ┌─────────────────────────┐
           │         ┌─────────────►│   Vision-Language Model   │
           │         │  叠加了关键点  │        GPT-4o             │
           │         │  编号的图像    │  (visual prompting)       │
           │         │              └───────────┬─────────────┘
           │         │                          │
           │         │              ReKep 约束 (Python 函数)
           │         │              ┌───────────┴─────────────┐
           │         │              │  sub-goal constraints    │  ← 阶段终点要满足
           │         │              │  path constraints        │  ← 阶段全程要满足
           │         │              └───────────┬─────────────┘
           ▼         ▼                          ▼
   ┌─────────────────────────────────────────────────────┐
   │              Constrained Optimization Solver         │
   │  ┌──────────────────┐      ┌──────────────────────┐  │
   │  │  子目标问题 (式2)  │ ───► │   路径问题 (式3)       │  │
   │  │  求 SE(3) 目标位姿 │      │  求密集动作序列        │  │
   │  └──────────────────┘      └──────────────────────┘  │
   │   Dual Annealing (首次 ~1s) + SLSQP (热启动 ~10Hz)     │
   │   回溯: path 约束被违反 → 退回上一阶段                   │
   └──────────────────────────┬──────────────────────────┘
                              │
                              ▼
                  末端执行器动作序列 (SE(3)) → 机械臂

用"倒茶"任务感受一下约束是怎么写的(对应论文 Figure 2 的示例代码):

# 阶段1(抓取)的子目标约束:末端到壶柄关键点的距离
def subgoal_stage1_f1(k):
    dist = norm(k[0] - k[1])      # k[0]=gripper, k[1]=壶柄
    return dist                    # dist ≤ 0 不可达,但配合优化代价使用

# 阶段2(搬运对齐)的路径约束:壶嘴与杯口的 z 高度差
def path_stage2_f1(k):
    z_diff = abs(k[1][2] - k[2][2])  # k[2]=杯口,保持高度差
    return z_diff

# 阶段2 的子目标约束:壶嘴移到"杯口上方 10cm"
def subgoal_stage2_f1(k):
    k[3][2] += 0.10                 # k[3]=壶嘴,抬升 10cm 后算距离
    return norm(k[2] - k[3])

三个阶段的约束全景:

阶段sub-goal 约束(终点)path 约束(全程)
1 抓取末端对齐壶柄
2 对齐壶嘴位于杯口上方 10cm壶保持直立(防洒)
3 倾倒壶嘴离杯口 5cm + 倾斜到能倒出壶嘴始终在杯口正上方

注意阶段 3 的 path 约束"壶嘴始终在杯口正上方"——这就是系统反应能力的来源:执行中杯子被挪动,跟踪器发现约束违反,路径问题立刻重解,壶追着杯子走。


五、踩坑点:作者自己承认的六个坑

这篇论文的局限章节写得相当诚实,附录 A.11 还有加长版。我把坑按严重程度捋一遍,顺便标注哪些是工程可缓解的、哪些是范式级难题。

坑 1:前向模型的刚性假设(范式级,但有缓解)
优化循环依赖 hhh 的刚性假设,对可变形物体天然失真。缓解手段是 20Hz 高频反馈刷新真实关键点,把模型误差限制在 0.1s 时域内。但遇到动态任务或富接触任务,这个假设会崩——作者自己说这种场景得换学习式或物理引擎式前向模型。

坑 2:点跟踪是最大错误源(工程级,但很难)
附录的错误归因分析(Figure 5)显示,点跟踪器贡献了最大比例的失败。机械臂操作时遮挡是常态——壶转过来挡住杯口、衣服叠起来盖住袖子,间歇性遮挡对 3D 点跟踪是地狱难度。作者在叠衣服实验里干脆关掉了点跟踪(因为衣服被反复折叠后跟踪轨迹不稳定),只在每阶段开始时重新检测关键点。这个"退化策略保平安"的工程决策很有参考价值。

坑 3:固定阶段骨架(范式级)
当前公式假设每个任务的阶段序列(skeleton)是固定的。要让骨架本身可重规划,就得高频跑关键点提 Proposal + VLM 查询,计算上扛不住。这直接导致长时序任务的瓶颈(见坑 6)。

坑 4:VLM 的鲁棒性天花板
任务阶段一多、时序依赖一复杂,GPT-4o 写约束的一致性就下降。论文的判断是"预期随预训练模型进步而改善"——这是个诚实的甩锅,但也确实是事实:这套流水线是模型无关的,VLM 换代即受益。

坑 5:任务空间规划的运动学盲区
优化只在任务空间(末端位姿)做,不显式考虑机器人运动学。某些解出来的位姿运动学上很别扭(接近关节极限),IK 残差代价只能软性惩罚。关节空间规划能根治,但约束在任务空间评估,Python 实现下要频繁算正向运动学,效率不可接受——作者在附录里明确建议未来的硬件加速实现(如 cuRobo)来解这个问题。

坑 6:长时序任务会"爆内存"(附录 A.14 的早餐托盘案例)
10 个阶段的"准备早餐托盘"任务(铺桌布、拿面包、倒茶、放杯子、端托盘给人)直接把整条流水线打穿了:关键点提 Proposal 覆盖不全、VLM 写不出完整正确的约束序列、点跟踪器跟丢。最后作者不得不人肉标注关键点和约束、并把持续跟踪降级为每阶段开始时检测,才跑通。这个案例等于坦诚宣告了当前版本的复杂度上界。

坑 7(补充):铰接物体不支持
抽屉、门、阀门这类铰接物体需要高级空间推理,现有 VLM 搞不定。作者给了一个理论出口:ReKep 可以表达铰链——“关键点只能沿直线滑动”(移动副)或"沿圆弧运动"(转动副)本身就是关键点关系约束——但需要微调 VLM 才能落地。


六、源码拆解:算法骨架、Prompt 工程、求解器实现

论文没有公开完整的实现级代码清单,但 Appendix 给出了伪代码、完整 Prompt 模板和各求解器的实现细节,足以做一次"纸面源码级"拆解。官方开源仓库在 github.com/huangwl18/ReKep,感兴趣的可以对照阅读。

6.1 Algorithm 1:主循环伪代码逐行拆

 1: i ← 1, t ← 1                          # 当前阶段、当前时刻初始化
 2: while i ≤ N do                         # N 个阶段依次执行
 3:   if ∃f ∈ C_path^(i) s.t. f(k_t) > 0   # 检查当前阶段 path 约束
 4:     i ← i - 1; continue                # 违反 → 回退一个阶段
 5:   end if
 6:   if distance(e_t, e_gi) < ε           # 离子目标足够近?
 7:     i ← i + 1; continue                # 是 → 推进到下一阶段
 8:   end if
 9:   Solve sub-goal problem → e_gi        # 式2:解子目标位姿
10:   Solve path problem → e_{t:gi}        # 式3:解到达路径
11:   Execute next m actions               # 执行 m 步(MPC 风格)
12:   t ← t + m + 1
13: end while

四个值得注意的实现细节:

  • 第 3-4 行的回溯是迭代式的:连续违反会一路退回更早的阶段 j,只要 Cpath(j)\mathcal{C}_{path}^{(j)}Cpath(j) 还满足。倒茶任务里,壶被抢走 → 退回"对齐"阶段发现也不满足(没抓着壶)→ 再退回"抓取"阶段重新抓;
  • 第 4 行退一格、第 7 行进一格,控制流极其简洁——整个"反应式行为"没有用任何显式的状态机或规则引擎,纯粹是约束检查驱动的隐式状态转移;
  • 第 9-10 行每个周期都重解:这不是规划一次执行到底的开环系统,而是每 0.1s 用最新跟踪到的关键点重解一遍的滚动时域控制;
  • m 的取值与求解频率联动:10Hz 求解、20Hz 控制,意味着解一次、执行两步左右。

6.2 Prompt 工程:给 GPT-4o 的"任务说明书"

附录 A.6 的完整 Prompt 非常值得研究,结构上是教科书级的:

第一层:角色设定——“你在用 Python 约束函数控制机器人做操作任务”,输入是叠加了编号关键点的图像 + 文本指令,指令开头用括号标注单臂/双臂。

第二层:阶段分解规则(带少样本示例):

- "(single-arm) pouring tea from teapot":
  - 3 stages: "grasp teapot", "align teapot with cup opening", "pour liquid"
- "(bimanual) fold sleeves to the center":
  - 2 stages: "left arm grasps left sleeve ...", "both arms fold sleeves ..."

第三层:两类约束的定义——sub-goal 是"阶段末必须满足",path 是"阶段内全程满足",同样配示例。

第四层:硬性规则清单(这部分最能体现工程打磨):

  • 抓取必须是独立阶段;非抓取阶段禁止引用末端位置;
  • 要移动某个关键点,其所属物体必须在此前阶段被抓过;
  • 机器人同一时刻只能抓一个物体;
  • 禁止用 if 语句(保证约束函数光滑可优化);
  • 可变形物体禁止写 path 约束(刚性假设下 path 约束对衣物无意义);
  • 不用考虑碰撞避免(求解器辅助代价里已经处理);
  • 两点成向量可指定旋转(与固定轴的夹角)、多点可指定面/体积、多点质心可指定位置。

输出格式模板:单臂输出 stage{i}_subgoal_constraint{j} / stage{i}_path_constraint{j};双臂则加 left_ / right_ 前缀,左右臂的约束并列输出。

注意一个反直觉的设计:Prompt 里没有任何图文 in-context 示例,只有少量纯文本示例。绝大多数测试任务都没在 Prompt 里讨论过——VLM 全靠内部化的世界知识(“可乐罐应该回收”、“毛衣先叠两袖再叠领口”)来写约束。这是对"基础模型泛化"的一次直接押注。

约束函数的签名固定为:

def stage1_subgoal_constraint1(end_effector, keypoints):
    """自然语言解释(GPT-4o 自带的注释)"""
    ...
    return cost

end_effector 是 (3,) 的末端位置(只有抓取类约束用它),keypoints 是 (K,3) 的数组。函数体是原生 Python + NumPy。

6.3 子目标求解器(附录 A.8)的代价函数全家桶

决策变量:单臂 R^6(位置 + 欧拉角),双臂 R^12(两条臂拼接)。旋转项的边界限制在末端朝下的半球(Franka 臂关节极限所致——末端朝上极易撞关节极限,这种从硬件踩坑里总结出来的边界条件,论文写得很实在)。

目标函数的代价项拆解:

代价项实现方式备注
约束违反ReKep 返回值 × 大权重软约束处理硬约束的经典套路
场景碰撞nvblox 计算 ESDF(20Hz 独立节点),夹爪+被抓物体降采样至 30 点(最远点采样),ESDF 三线性插值,阈值 15cmESDF 聚合所有相机深度图,用 cuRobo 剔除机械臂自身、用 Cutie 跟踪的 mask 剔除被抓刚体
可达性每次迭代解一次 PyBullet IK,用 IK 残差做代理占目标函数 40% 耗时,论文明说这是主要性能瓶颈之一
位姿正则子目标靠近当前末端位姿小权重
一致性靠近上一帧的解抑制感知噪声在求解器里的传播
自碰撞(双臂)两组点集(夹爪+被抓物体)的成对距离

抓取阶段额外接入 AnyGrasp 抓取检测器。由于 AnyGrasp 是检测器不是度量函数、且在优化循环里跑太贵,论文用了个投机取巧但聪明的做法:总是返回离"抓取关键点"最近的那个抓取——依据是抓取类 ReKep 约束永远关联一个末端虚拟点和一个真实关键点。

6.4 路径求解器(附录 A.9)

决策变量数量按需生成:根据当前位姿到目标位姿的距离,以固定步长(20cm / 45°)线性估计中间位姿个数。优化内部用分段线性插值展开成密集样本评估约束(虽然后处理用样条插值,但线性插值算得快——又一个"够用就好"的工程取舍)。代价项与子目标求解器同构,另加两条:

  • 路径长度:密集样本差分求和,鼓励短路径;
  • 桌面净空:惩罚穿透桌面的路径段;起点/终点附近 5cm 半径内忽略碰撞计算(高频重解时稳定解的技巧)。

一致性代价用两条密集序列的成对距离做代理(论文提到 Hausdorff 距离是更精确但更贵的选项)。

6.5 点跟踪器(附录 A.7):DINOv2 特征匹配的极简实现

初始化时,对每个关键点,聚合所有相机视野内 2cm 范围内的点的 DINOv2 特征取均值作为参考特征(全程固定)。此后每步:

  1. 全图算 DINOv2 像素级特征 + 对应 3D 坐标;
  2. 与参考特征算余弦相似度,取 top-100 匹配,相似度截止 0.6;
  3. 中位数偏差法剔野值(m=2);
  4. 窗口 10 的均匀滤波平滑抖动。

20Hz 跑得动的原因:ViT-S14 很小 + 多相机冗余投票。这是 D3Fields 方法的实时简化版,作者也指出可以直接换 CoTracker、TAPIR 这类专业点跟踪器。

6.6 硬件与控制栈

两个平台:轮式单臂(Franka + Vention 轮式底座,底座没有电机,纯为了搬出实验室)和固定双臂(两台 Franka 面对桌面)。相机都是 Orbbec Femto Bolt(单臂 2 台、双臂 3 台),20Hz 采集。

控制链路:目标末端位姿 → 裁剪到工作空间 → 5mm/1° 步长线性插值 → PyBullet 逆运动学 → Deoxys 关节阻抗控制器,20Hz 执行。整条链路最"朴素"的部分恰恰是控制——论文的创新全在感知与规划层,执行层用最保守可靠的位控。


七、效果对比:数字说话

7.1 主实验(Table 1 & 2)

七个任务(倒茶、收书、回收易拉罐、封箱胶带、叠毛衣、装鞋、协作叠被),每任务 10 次随机初始摆放,对比 VoxPoser 基线、全自动 ReKep(Auto)、人标 ReKep(Annotated):

VoxPoserReKep (Auto)ReKep (Annotated)
总成功率10.0%44.3%68.6%
干扰下成功率6.7%26.7%46.7%

两个读数:

  • Auto vs Annotated 的 24 个百分点差距,就是"VLM 写约束"与"人写约束"的当前差距——这个 gap 就是 GPT-4o 约束生成能力的天花板刻度,且随模型换代可望收窄;
  • 倒茶/收书/叠衣服这类多阶段任务上 VoxPoser 是 0/10。VoxPoser 用 VLM 生成 3D 价值地图(value map)驱动末端,本质上是"每一步去价值高的地方",天然表达不了"保持直立直到对齐"这类过程性约束和跨阶段时序依赖。这是表示能力的代差,不是调参能弥补的。

7.2 泛化实验(Figure 4):8 类衣服的零样本折叠策略

固定任务(叠衣服)、变物体类别(毛衣/衬衫/连帽衫/马甲/连衣裙/裤子/短裤/围巾),双臂平台:

  • 策略成功率 52.5%(生成的 ReKep 可行:关键点提得对 + 约束写得对);
  • 执行成功率 73.8%(给定可行策略,系统执行到底的成功率)。

最有意思的定性发现:不同衣物生成的策略显著不同且符合人类直觉——两袖先同时折、单臂能搞定的就不用双臂、围巾和裤子的折法自动区分。这些策略没有任何一个来自示例或训练数据,全部出自 GPT-4o 的世界知识。

7.3 仿真对比(附录 A.12):零样本 vs 模仿学习

OmniGibson 仿真里的倒茶任务,对比用 100 条专家示教训练的单体 Transformer 策略(RVT 架构):

训练分布位姿未见位姿未见物体
单体策略(100 示教)0.930.310.14
ReKep(零样本)0.750.680.72

教科书级的"泛化碾压"曲线:单体策略在训练分布内近乎完美,一旦换物体成功率掉到 14%;ReKep 零样本 72%,且三条场景几乎持平。用约束表示换示教数据,这笔交易在分布外场景血赚。

7.4 错误归因(Figure 5)

对主实验失败案例的人工归因(考虑模块间时序依赖后计算各模块致错概率):点跟踪器 > 关键点提 Proposal ≈ VLM > 优化器。优化器贡献最小的原因有点反直觉但合理——时间预算有限的情况下,每个问题往往存在大量可行解,求解器找到"一个"就够了,不追求最优。而感知层的错误(跟丢点、漏提点、指错点)是优化器无法自救的。


八、总结:ReKep 的范式意义

回到开头的问题:约束怎么表示才能既普适、又可自动获取、还实时可解?ReKep 的回答可以压缩成三句话:

  1. 表示层:约束 = 作用在语义 3D 关键点上的 Python 函数。关键点天然规避了物体模型依赖,可变形可刚体;函数式约束天然兼容非线性非凸;"sub-goal + path"双层结构天然表达时序。
  2. 生成层:DINOv2 出细粒度视觉特征、SAM 出物体先验、GPT-4o 出关系代码——三种基础模型各取所长,而且流水线是模型无关的,任何一个组件换代,整条链路自动受益。
  3. 执行层:分层优化 + 滚动时域 + 刚性前向模型 + 高频视觉反馈,10Hz 闭环把"不精确的模型"和"不完美的感知"都洗成了可容忍的噪声。

最值得反复咀嚼的设计,是那条"VLM 只写关系,求解器算几何"的分界线。它把 VLM 从"输出动作"这个它不擅长的任务上解放出来,转而让它做"看图写关系代码"这件它擅长的事——6-DoF 甚至 12-DoF 的运动,就这样被几行 norm(k[0] - k[1]) 隐式指定了。这种"语义与几何解耦"的思路,跟编译器里"前端语义分析 + 后端代码生成"的分层哲学如出一辙。

局限也很清楚:刚性假设、点跟踪的遮挡鲁棒性、固定阶段骨架、VLM 长时序一致性,这四座大山决定了它目前更像一个"强大的研究系统"而非"可部署的产品"。早餐托盘案例(10 阶段需要人肉兜底)划出了当前的能力边界。

但作为一个零训练、零示教、开放世界的操作框架,ReKep 指出的方向足够清晰:与其烧算力训一个巨型端到端策略,不如让基础模型负责"理解任务",让经典优化负责"算出动作"。表示方式的更替,往往比模型规模的堆叠更能打开新的能力空间——这个道理,在哪个领域都成立。


参考:Huang et al., “ReKep: Spatio-Temporal Reasoning of Relational Keypoint Constraints for Robotic Manipulation”, 2024. 项目主页与代码:rekep-robot.github.io

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐