论文题目Embodied large language models enable robots to complete complex tasks in unpredictable environments
中文题目:ELLMER:具身大语言模型如何让机器人完成复杂任务
阅读定位:重点理解“论文为什么做、系统怎样运行、公式表示什么、代码如何落地”。
一句话总结:ELLMER 不是训练一个新的大模型,而是把 GPT-4/RAG 的高层推理能力与视觉、力反馈和 ROS 运动控制组合成一套可在真实机械臂上运行的闭环系统。

相关资源入口


初识:这篇论文的研究目的

论文希望机器人能够听懂类似下面的模糊指令:

“我累了,朋友马上要来吃蛋糕。你能准备一杯热饮,并在盘子上画一种随机动物吗?”

人类会自然地推断:累了可能需要咖啡;做咖啡要找杯子、取咖啡、加水;朋友要来吃蛋糕,可以在盘子上画图装饰。但传统机器人程序通常必须提前知道每一步、每个物体的位置以及每种异常情况。

ELLMER 的基本分工是:

  • GPT-4 决定“应该做什么”;
  • RAG 技能库 提供“有哪些可靠动作可以调用”;
  • ROS 与运动原语 决定“机械臂具体怎样运动”;
  • 视觉反馈 回答“目标物体在哪里”;
  • 力反馈 回答“是否接触、是否放稳、倒了多少水”;
  • 安全执行器 限制“哪些代码能执行、速度和力不能超过多少”。

1. 论文旨在解决的问题

论文处理的是动态环境中的长时程机器人任务(long-horizon robotic task)。它同时包含三个难点。

1.1 抽象指令

用户通常只给出一个目标,而不是机械臂动作序列。机器人必须把总任务拆成多个子任务,并确定先后关系。

例如:

准备热饮
├─ 判断做哪种饮料
├─ 找到杯子
│  └─ 如果桌面没有杯子,则打开抽屉
├─ 舀取咖啡
├─ 将咖啡倒入杯中
├─ 拿起水壶
└─ 定量倒水

1.2 长任务

一个完整任务可能包含开抽屉、抓取、放置、舀取、倾倒、递物和绘图等许多步骤。任何一步失败,都可能导致后续步骤失去意义。

1.3 环境变化

杯子可能被人移动,物体可能被机械臂遮挡,抽屉的打开方式可能不同,液体倾倒时视觉也可能被水壶遮挡。因此,机器人不能只执行一条固定轨迹,而要根据视觉和力传感器持续修正动作。


2. 现有方法的不足

2.1 传统预编程方法

传统机器人代码通常高度依赖固定环境:目标坐标、动作顺序和异常处理都由工程师提前写好。一旦物体位置或环境结构发生改变,程序就容易失效。

2.2 强化学习与模仿学习

强化学习和模仿学习可以学到复杂操作,但往往存在:

  • 需要大量交互或示范数据;
  • 换任务后可能需要重新训练;
  • 容易受训练分布限制;
  • 对新物体、新场景和突发变化的适应性不足。

2.3 已有 LLM 机器人系统

已有工作证明 LLM 能够进行机器人任务规划,但仍有以下不足:

  • 很多工作聚焦短时程任务;
  • 提示词和上下文可能非常复杂;
  • LLM 容易生成不存在的函数或错误参数;
  • 技能库直接放进上下文后,规模扩大时成本明显上升;
  • 很多系统缺乏实时物理反馈;
  • 较少同时使用视觉与力反馈;
  • 单靠视觉难以处理遮挡和定量倾倒。

因此,论文关注的核心缺口是:

如何把 LLM 的高层推理、可扩展的技能检索和机器人低层反馈控制连接成一套真实可运行的系统?


3. 论文的核心贡献

3.1 ELLMER 整体框架

作者提出 Embodied Large-Language-Model-Enabled Robot(ELLMER),将以下组件集成为一个闭环:

  1. GPT-4 高层理解和任务分解;
  2. RAG 技能与代码示例检索;
  3. LLM 生成机器人动作代码;
  4. 服务器与机器人之间的 JSON 通信;
  5. 受限代码执行环境;
  6. 视觉三维定位;
  7. 力/力矩反馈;
  8. ROS 运动原语和机械臂控制。

3.2 可扩展的技能知识库

知识库保存经过验证的机器人动作示例,包括:

  • 基础移动和姿态调整;
  • 开关夹爪;
  • 抓取和放置;
  • 开门、关门和开抽屉;
  • 舀取、倾倒和递物;
  • 根据视觉轨迹绘图。

RAG 只检索当前任务最相关的技能片段,避免把整个知识库都塞进上下文。

3.3 高层推理与低层反馈闭环结合

LLM 不直接输出电机电流或关节控制量,而是组合经过验证的运动原语。视觉和力反馈被封装在这些原语内部,用于实时修正动作和判断终止条件。

3.4 真实机器人长任务验证

作者使用 Kinova Gen3 七自由度机械臂,展示了从抽象语言指令到制作咖啡和盘子绘图的完整过程。

研究定位提醒:这篇论文的主要创新是系统架构与工程集成,而不是新的神经网络结构、训练目标或端到端学习算法。


4. 整体方法流程

在这里插入图片描述
图 1|ELLMER 的高层规划、通信安全和具身反馈闭环。
蓝色表示认知与规划,灰色表示通信与安全,绿色表示传感与执行,橙色表示任务状态分支。

4.1 从用户指令到任务计划

用户指令和初始场景信息输入 GPT-4。GPT-4 先理解任务目标,再将总任务分解为具有顺序和依赖关系的子任务。

4.2 RAG 检索技能

系统根据当前子任务检索知识库,找到相关的函数说明和代码示例。例如,“向杯中倒 100 g 水”可能检索到:

get_kettle(...)
return_object_position(...)
go_through_trajectory(...)
pour(...)

4.3 生成动作代码并发送

LLM 根据检索结果生成调用运动原语的 Python 代码,经服务器封装成 JSON 动作消息,进入动作队列。

4.4 安全检查与 ROS 执行

机器人端只允许代码调用预定义函数,并设置速度、力、工作空间和执行时间限制。通过检查后,ROS 才会驱动机械臂和夹爪。

4.5 视觉与力反馈

  • 视觉模块持续估计目标类别和三维位姿;
  • 力模块持续测量三轴力和三轴力矩;
  • 如果当前子任务尚未完成,机器人继续反馈控制;
  • 如果完成,则进入下一个子任务。

4.6 一个完整例子:倒 100 g 水

  1. LLM 判断应该先拿起水壶;
  2. RAG 检索取水壶、目标定位、轨迹移动和倾倒函数;
  3. 视觉模块返回杯子的三维位置;
  4. 机械臂移动到杯子上方;
  5. 机械臂缓慢倾斜水壶;
  6. 视觉继续追踪杯子位置;
  7. 力传感器测量水壶重量变化;
  8. 达到目标重量后停止倾倒。

5. 每个模块的输入和输出

模块 输入 主要处理 输出
用户接口 语音或文本指令 语音识别、文本整理 用户查询
语言理解 用户查询、场景信息 理解意图和总体目标 高层任务 L T L_T LT
任务分解 高层任务、当前环境 建立顺序和依赖 子任务序列 { L 1 , … , L N } \{L_1,\ldots,L_N\} {L1,,LN}
RAG 查询 q q q、技能分块 s i s_i si 向量检索和 top- k k k 排序 相关技能与代码示例
代码生成 子任务、检索上下文、允许的 API 组合运动原语 Python 代码或动作计划
通信服务器 LLM 生成的动作 JSON 封装、动作排队 JSON 动作消息
安全执行器 动作代码、白名单、限幅参数 校验、超时和受限执行 合法的函数调用或拒绝信息
视觉模块 RGB-D 图像、目标类别 检测、分割、体素化和位姿估计 机器人坐标系中的物体位姿
力模块 局部力/力矩、末端姿态 标定、坐标变换和平滑 全局力、接触状态和重量变化
ROS 运动原语 目标位姿、速度、力阈值 闭环运动控制 twist 速度和夹爪指令
绘图模块 用户关键词或生成图像 轮廓提取和轨迹缩放 机械臂绘图轨迹

关键运行频率

部件 论文报告的频率
机械臂关节更新 40   H z 40\ \mathrm{Hz} 40 Hz
夹爪节点 50   H z 50\ \mathrm{Hz} 50 Hz
力传感器节点 100   H z 100\ \mathrm{Hz} 100 Hz
视觉目标位姿发布 1 3   H z \frac{1}{3}\ \mathrm{Hz} 31 Hz

视觉模块明显慢于低层控制环,因此快速运动主要依赖已有运动原语和高频传感器,而不是持续调用 LLM。


6. 重要公式和符号

6.1 总任务和子任务

论文将总任务写为 L T L_T LT,并分解为:

L T ⟶ { L 1 , L 2 , … , L N } . L_T \longrightarrow \{L_1,L_2,\ldots,L_N\}. LT{L1,L2,,LN}.

其中:

  • L T L_T LT:用户提出的总任务;
  • L i L_i Li:第 i i i 个子任务;
  • N N N:子任务数量。

子任务可能存在条件分支:

P  ⁣ ( L 2 A , L 2 B ∣ L 1 ) . P\!\left(L_{2A},L_{2B}\mid L_1\right). P(L2A,L2BL1).

它表示完成 L 1 L_1 L1 后,系统可能根据反馈进入 L 2 A L_{2A} L2A L 2 B L_{2B} L2B。这里主要是在概念上表达任务依赖,并不表示论文训练了一个显式概率模型。

6.2 RAG 的余弦相似度

查询 q q q 和知识库分块 s i s_i si 被编码为向量 e q \mathbf e_q eq e i \mathbf e_i ei。标准余弦相似度为:

sim ⁡ ( q , s i ) = e q T e i ∥ e q ∥ 2   ∥ e i ∥ 2 . \operatorname{sim}(q,s_i)= \frac{\mathbf e_q^{\mathsf T}\mathbf e_i} {\lVert\mathbf e_q\rVert_2\,\lVert\mathbf e_i\rVert_2}. sim(q,si)=eq2ei2eqTei.

系统按照相似度排序,返回前 k k k 个技能片段:

C k = TopK ⁡ s i ( sim ⁡ ( q , s i ) ) . \mathcal C_k= \operatorname{TopK}_{s_i}\bigl(\operatorname{sim}(q,s_i)\bigr). Ck=TopKsi(sim(q,si)).

6.3 相机坐标到机器人坐标

视觉模块检测到的点最初位于相机坐标系。论文使用 AprilTag 标定,将其变换到机器人基坐标系:

P R = T A R ( T C A P C ) . \mathbf P^{R}= \mathbf T_{AR} \left( \mathbf T_{CA}\mathbf P^{C} \right). PR=TAR(TCAPC).

其中:

  • P C \mathbf P^{C} PC:相机坐标系中的三维点;
  • T C A \mathbf T_{CA} TCA:相机到 AprilTag 的变换矩阵;
  • T A R \mathbf T_{AR} TAR:AprilTag 到机器人基座的变换矩阵;
  • P R \mathbf P^{R} PR:机器人基坐标系中的目标点。

6.4 力坐标变换

力传感器会随末端执行器旋转,因此局部坐标中的力需要转换到机器人基坐标系:

F g l o b a l = T e e f → b a s e F l o c a l . \mathbf F_{\mathrm{global}}= \mathbf T_{\mathrm{eef}\rightarrow\mathrm{base}} \mathbf F_{\mathrm{local}}. Fglobal=TeefbaseFlocal.

其中:

  • F l o c a l \mathbf F_{\mathrm{local}} Flocal:力传感器局部坐标中的力;
  • T e e f → b a s e \mathbf T_{\mathrm{eef}\rightarrow\mathrm{base}} Teefbase:末端执行器到机器人基座的旋转变换;
  • F g l o b a l \mathbf F_{\mathrm{global}} Fglobal:机器人基坐标系中的力。

6.5 通过重量变化估计倒水量

慢速倾倒时,作者采用准静态假设:

F u p ≈ m g . F_{\mathrm{up}}\approx mg. Fupmg.

倾倒前后的力变化近似满足:

Δ F u p ≈ Δ m   g . \Delta F_{\mathrm{up}}\approx \Delta m\,g. ΔFupΔmg.

因此倒出的质量可以估计为:

Δ m ≈ Δ F u p g . \Delta m \approx \frac{\Delta F_{\mathrm{up}}}{g}. ΔmgΔFup.

快速倾倒时,液体晃动、加速度和水壶质量分布会破坏准静态假设,因此误差增大。

6.6 视觉和力共同决定动作

论文图 1 将动作控制概括为:

a = f ( F , V ) , \mathbf a=f(\mathbf F,\mathbf V), a=f(F,V),

其中:

  • a \mathbf a a:机器人动作;
  • F \mathbf F F:力反馈;
  • V \mathbf V V:视觉反馈。

常见符号还包括:

符号 含义
f \mathbf f f 三轴力
τ \boldsymbol\tau τ 三轴力矩
v x y z \mathbf v_{xyz} vxyz 末端线速度
v r p y \mathbf v_{rpy} vrpy roll、pitch、yaw 角速度
p \mathbf p p 末端位置
q \mathbf q q 末端姿态或四元数
η v i s i o n \eta_{\mathrm{vision}} ηvision 视觉噪声
η a n g l e \eta_{\mathrm{angle}} ηangle 关节角噪声
η f o r c e \eta_{\mathrm{force}} ηforce 力传感器噪声
λ \lambda λ 发送给机器人的控制信号

这些表达主要用于说明系统关系,而不是端到端训练的损失函数。

6.7 硬编码安全约束

论文在底层运动原语中设置了速度、力和工作空间限制:

∣ v x ∣ , ∣ v y ∣ , ∣ v z ∣ ≤ 0.05   m   s − 1 , \lvert v_x\rvert,\lvert v_y\rvert,\lvert v_z\rvert \leq 0.05\ \mathrm{m\,s^{-1}}, vx,vy,vz0.05 ms1,

∣ ω r o l l ∣ , ∣ ω p i t c h ∣ , ∣ ω y a w ∣ ≤ 60 ∘   s − 1 , \lvert \omega_{\mathrm{roll}}\rvert, \lvert \omega_{\mathrm{pitch}}\rvert, \lvert \omega_{\mathrm{yaw}}\rvert \leq 60^{\circ}\,\mathrm{s^{-1}}, ωroll,ωpitch,ωyaw60s1,

∥ F e e f ∥ ≤ 20   N . \lVert\mathbf F_{\mathrm{eef}}\rVert\leq 20\ \mathrm N. Feef20 N.

工作空间为:

x ∈ [ 0 , 1.1 ] , y ∈ [ − 0.3 , 0.3 ] , z ∈ [ 0 , 1.0 ] . x\in[0,1.1],\qquad y\in[-0.3,0.3],\qquad z\in[0,1.0]. x[0,1.1],y[0.3,0.3],z[0,1.0].

这些约束被写入底层控制函数,理论上不能被 LLM 生成的上层代码覆盖。


7. 实验结果应该怎样理解

7.1 长时程任务演示

机器人成功展示了以下操作:

  • 打开抽屉;
  • 抓取和放置杯子;
  • 舀取咖啡并倒入杯中;
  • 拿起水壶并定量倒水;
  • 把物品递给人;
  • 根据生成图像在盘子上绘图。

这说明框架具有真实系统可行性,但论文没有报告完整任务的总试验次数、总体成功率、置信区间和显著性检验。因此更准确的表述是:

论文成功进行了概念验证和真实机器人演示,但还不是大规模、严格统计意义上的机器人基准测试。

7.2 RAG 规划结果

作者使用 LLM 生成了 80 条类似人类的任务查询,比较有无 RAG 时回答的 faithfulness:

模型 无 RAG 有 RAG 绝对提升
GPT-4 0.74 0.88 + 0.14 +0.14 +0.14
GPT-3.5 Turbo 0.78 0.86 + 0.08 +0.08 +0.08
Zephyr-7B-beta 0.37 0.44 + 0.07 +0.07 +0.07

这说明检索经过验证的技能示例可以减少事实错误和代码幻觉。

需要注意:这里评估的是生成计划或回答的可信度,不是 80 次真实机器人完整任务的成功率。

7.3 视觉性能

  • 实验条件下,白色杯子的识别率报告为 100 % 100\% 100%
  • 遮挡率为 20 % 20\% 20% 30 % 30\% 30% 时,成功识别率约为 90 % 90\% 90%
  • 遮挡率为 80 % 80\% 80% 90 % 90\% 90% 时,成功识别率下降到约 20 % 20\% 20%
  • 相似外形、分布外物体和机械臂遮挡是主要失败原因。

7.4 力反馈与倾倒

  • 力传感器采样频率为 100   H z 100\ \mathrm{Hz} 100 Hz
  • 传感器精度约为满量程的 2 % 2\% 2%
  • 低速倾倒 100   g 100\ \mathrm g 100 g 水时,误差约为 5.4   g 5.4\ \mathrm g 5.4 g
  • 倾倒速度增大后,准静态假设失效,误差明显增加。

论文在倾倒段落中把 pitch velocity 写成了 m   s − 1 \mathrm{m\,s^{-1}} ms1,这可能存在单位书写不一致。复现时应以代码中的实际角速度单位为准。

7.5 论文局限性

  1. 实验环境仍然相对受控;
  2. 假设视觉模块能够正确识别物体;
  3. 水壶、勺子和门把手的 affordance 是预先给定的;
  4. 不擅长中途切换任务或主动重新规划;
  5. 没有充分建模快速液体流动的动力学;
  6. 缺少完整任务成功率和统计检验;
  7. 缺少 RAG、视觉和力反馈的完整物理任务消融;
  8. LLM 生成并执行代码仍然存在安全风险。

8. 对应代码应该如何实现

8.1 官方仓库结构

官方实现主要包含:

ELLMER/
├─ llm_robot_planning/
│  ├─ custom_gpt_examples/
│  │  ├─ app.py
│  │  ├─ custom_gpt_action_schema.md
│  │  ├─ custom_gpt_instructions.md
│  │  └─ custom_knowledge.md
│  └─ haystack_rag_pipelines/
│     ├─ example1.py
│     ├─ example2.py
│     ├─ generate_questions.py
│     └─ evaluate_models.py
├─ ros_kortex/kortex_examples/src/ELLMER/
│  ├─ robot_brain.py
│  ├─ action_execution.py
│  ├─ base.py
│  ├─ utilities.py
│  ├─ robot_affordances.py
│  ├─ utilities_affordance.py
│  ├─ utilities_draw.py
│  └─ EEF_pose.py
├─ netft_rdt_driver/
└─ robotiq_2finger_grippers/

其中:

  • robot_brain.py:监听服务器、视觉和力反馈;
  • action_execution.py:受限执行 LLM 生成的动作;
  • base.py:底层 twist、夹爪及安全限制;
  • utilities.py:通用和场景专用运动原语;
  • utilities_draw.py:生成轮廓并转换为绘图轨迹;
  • app.py:在 LLM 和机器人之间维护 JSON 动作队列。

8.2 建议的数据结构

from dataclasses import dataclass

@dataclass
class Pose3D:
    position: tuple[float, float, float]
    orientation: tuple[float, float, float, float]

@dataclass
class WorldState:
    objects: dict[str, Pose3D]
    eef_pose: Pose3D
    force_global: tuple[float, float, float]
    torque_global: tuple[float, float, float]

@dataclass
class PlanStep:
    primitive: str
    arguments: dict
    success_condition: dict
    timeout_s: float

@dataclass
class StepResult:
    success: bool
    observation: WorldState
    error: str | None = None

8.3 推荐的主循环

state = sensors.read_world_state()

examples = knowledge_base.retrieve(
    query=user_query,
    top_k=5,
)

steps = planner.make_plan(
    user_query=user_query,
    scene=state.objects,
    retrieved_examples=examples,
)

for step in steps:
    safety_guard.validate(step)

    result = executor.call_primitive(
        name=step.primitive,
        arguments=step.arguments,
        timeout=step.timeout_s,
    )

    if not result.success:
        state = sensors.read_world_state()
        steps = planner.replan(
            failed_step=step,
            world_state=state,
            error=result.error,
        )

8.4 运动原语白名单

ALLOWED_PRIMITIVES = {
    "go_home": go_home,
    "locate_object": locate_object,
    "pick_object": pick_object,
    "open_door": open_door,
    "scoop": scoop,
    "pour_by_weight": pour_by_weight,
    "put_down_with_force": put_down_with_force,
    "draw_trajectory": draw_trajectory,
}

执行器只能调用字典中注册的函数,不能访问文件系统、网络、系统命令或任意 Python 对象。

8.5 不建议直接执行任意 Python 代码

论文采用受限代码执行环境,但从安全工程角度,更推荐让 LLM 输出结构化计划,而不是直接输出可执行源码:

{
  "primitive": "pour_by_weight",
  "arguments": {
    "target_object": "white mug",
    "amount_g": 100
  },
  "success_condition": {
    "mass_change_g": 100
  },
  "timeout_s": 30
}

执行前依次完成:

  1. JSON Schema 校验;
  2. 函数白名单检查;
  3. 参数类型和范围检查;
  4. 目标工作空间检查;
  5. 速度、加速度和力限幅;
  6. 超时与急停配置;
  7. 执行结果和传感器日志记录。

8.6 推荐的复现顺序

阶段 A:只复现语言规划
  • 用 Markdown 或 JSON 建立技能库;
  • 实现向量检索;
  • 要求 LLM 输出结构化动作计划;
  • 用虚拟函数代替机械臂。
阶段 B:接入仿真
  • 在 ROS/Gazebo 中建立机械臂模型;
  • 实现基础位姿运动和夹爪控制;
  • 加入工作空间、速度和超时限制;
  • 测试异常参数是否会被拒绝。
阶段 C:接入视觉
  • 输入 RGB-D 图像;
  • 检测和分割目标物体;
  • 完成相机外参标定;
  • 发布机器人基坐标系中的目标位姿。
阶段 D:接入力反馈
  • 标定重力偏置;
  • 完成局部力到全局力的坐标变换;
  • 实现接触检测、放置检测和重量变化估计;
  • 从低速倾倒开始测试。
阶段 E:真实机械臂长任务
  • 逐个验证运动原语;
  • 再组合两个或三个子任务;
  • 最后测试完整长时程任务;
  • 记录每个子任务的成功率、失败原因和恢复次数。

最后的阅读判断

这篇论文最值得学习的不是“GPT-4 会控制机器人”,而是以下系统设计思想:

  1. 让 LLM 做擅长的事情:理解语言、拆任务和组合已有技能;
  2. 让控制器做擅长的事情:高速、确定、安全地执行动作;
  3. 用 RAG 减少幻觉:让模型参考经过验证的机器人代码;
  4. 把反馈写进技能原语:不要依赖 LLM 参与每一个控制周期;
  5. 把安全约束放在 LLM 之外:即使计划错误,底层仍然不能越过速度、力和空间边界。

可以把 ELLMER 概括为:

具身智能 = 高层语言推理 + 可靠技能检索 + 多模态反馈控制 + 独立安全约束 \boxed{ \text{具身智能}= \text{高层语言推理} + \text{可靠技能检索} + \text{多模态反馈控制} + \text{独立安全约束} } 具身智能=高层语言推理+可靠技能检索+多模态反馈控制+独立安全约束


注:笔记依据论文正文及作者公开代码整理。流程图是概念性重绘,不表示端到端模型结构或新的训练网络。

Logo

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

更多推荐