ReKep:当 GPT-4o 学会给机器人写“泡茶说明书“—— 关键点关系约束深度拆解
论文: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
一、介绍:机器人泡一杯茶,到底有多难?
先看一个看似人畜无害的任务:给杯子里倒茶。
人类做这件事的时候根本不会过脑子——伸手、抓壶柄、拎起来、壶嘴对准杯口、倾斜、倒。但拆开来看,这短短几秒钟里藏着至少四类约束:
- 抓取约束:必须抓壶柄,不能抓壶身(烫 + 抓不稳);
- 姿态约束:搬运过程中壶必须保持直立,否则茶洒一桌;
- 对齐约束:壶嘴必须移到杯口正上方;
- 旋转约束:倾斜角度要够大才能倒出来,但壶嘴得始终在杯口上方。
这四条约束,前三条是空间关系(spatial),最后一条还带时序依赖(temporal)——不满足对齐约束就执行倾斜,那就是灾难现场。
机器人领域处理这类"约束"的传统路线有三条,但每条都有硬伤:
- 路线一:刚体位姿表示(rigid-body pose)。直接用机器人末端和物体之间的相对位姿(SE(3) 变换)来描述约束。问题:描述不了几何细节(壶嘴在哪?杯口在哪?),需要预先拿到物体的 CAD 模型,而且对可变形物体(衣服、绳子)彻底失效。
- 路线二:数据驱动。直接在视觉空间里学约束。问题:约束数量随物体×任务组合爆炸,训练数据收不过来。
- 路线三:TAMP(Task and Motion Planning)。经典的任务与运动规划框架,约束可以写成约束满足问题。问题:约束基本靠人肉手写,换个任务就得重写,开放世界场景下毫无扩展性。
这篇论文的作者问了一个非常锋利的问题:能不能找到一种约束表示,同时满足三个条件——
- 普适(widely applicable):多阶段、开放环境、双臂、反应式行为全覆盖;
- 可规模化获取(scalably obtainable):能被基础模型(foundation models)全自动生成,不用人工标注;
- 实时可解(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 的"撒点"流水线
要让约束有地方"挂",第一步是场景里得有一堆语义关键点——壶柄上一个、壶嘴一个、杯口一个……论文的做法非常"基础模型乐高":
- 特征提取:RGB 图像过 DINOv2(ViT-S14,带 registers 的版本),拿到 patch 级特征图
F_patch ∈ R^{h'×w'×d},再双线性插值上采样回原图尺寸F_interp ∈ R^{h×w×d}; - 物体先验:用 SAM(Segment Anything)把场景里所有物体 mask 全抠出来——这一步提供了关键的 objectness 先验,保证关键点只落在物体上而不是背景上;
- 逐 mask 聚类:在每个 mask 内部,先把特征 PCA 降到 3 维(论文发现这一步能滤掉与任务无关的纹理细节,明显改善聚类质量),再用 k-means(k=5)聚类,聚类质心即为关键点候选;
- 3D 反投影:利用标定好的 RGB-D 相机,把 2D 候选点投影到世界坐标系的 3D 位置;
- 去冗余: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×3→R
输入关键点数组 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)e∈SE(3),目标是求整条离散轨迹 e1:Te_{1:T}e1:T:
argmine1: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=1∑N[λsubgoal(i)(egi)+t=gi−1∑giλpath(i)(et)]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
变量解读:
- 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):
argminegiλ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, ∀f∈Csubgoal(i)
路径问题(式 3):
argminet: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, ∀f∈Cpath(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Δe⋅k[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 三线性插值,阈值 15cm | ESDF 聚合所有相机深度图,用 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 特征取均值作为参考特征(全程固定)。此后每步:
- 全图算 DINOv2 像素级特征 + 对应 3D 坐标;
- 与参考特征算余弦相似度,取 top-100 匹配,相似度截止 0.6;
- 中位数偏差法剔野值(m=2);
- 窗口 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):
| VoxPoser | ReKep (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.93 | 0.31 | 0.14 |
| ReKep(零样本) | 0.75 | 0.68 | 0.72 |
教科书级的"泛化碾压"曲线:单体策略在训练分布内近乎完美,一旦换物体成功率掉到 14%;ReKep 零样本 72%,且三条场景几乎持平。用约束表示换示教数据,这笔交易在分布外场景血赚。
7.4 错误归因(Figure 5)
对主实验失败案例的人工归因(考虑模块间时序依赖后计算各模块致错概率):点跟踪器 > 关键点提 Proposal ≈ VLM > 优化器。优化器贡献最小的原因有点反直觉但合理——时间预算有限的情况下,每个问题往往存在大量可行解,求解器找到"一个"就够了,不追求最优。而感知层的错误(跟丢点、漏提点、指错点)是优化器无法自救的。
八、总结:ReKep 的范式意义
回到开头的问题:约束怎么表示才能既普适、又可自动获取、还实时可解?ReKep 的回答可以压缩成三句话:
- 表示层:约束 = 作用在语义 3D 关键点上的 Python 函数。关键点天然规避了物体模型依赖,可变形可刚体;函数式约束天然兼容非线性非凸;"sub-goal + path"双层结构天然表达时序。
- 生成层:DINOv2 出细粒度视觉特征、SAM 出物体先验、GPT-4o 出关系代码——三种基础模型各取所长,而且流水线是模型无关的,任何一个组件换代,整条链路自动受益。
- 执行层:分层优化 + 滚动时域 + 刚性前向模型 + 高频视觉反馈,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
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)