第一次调试机器人抓取任务时,最让我崩溃的瞬间不是机械臂突然乱动,而是模型明明认出了红色杯子,也完全听懂了“把红色杯子放到托盘上”这句指令,最后输出的轨迹却直接穿过杯子底部。问题不出在视觉模型,也不出在语言模型,而是出在整条链路被拆成了三段:视觉模块给目标框,语言模块解析指令,规划模块再单独算运动轨迹。每一段的小误差都会在后端被无限放大,联调起来非常痛苦。
后来我们换了一个思路:不再把感知、推理和控制做成三个独立模块,而是把所有输入输出统一成一串离散Token,交给一个自回归Transformer直接从图像和文本预测动作序列。这就是“自回归离散化预训练下统一的具身VLM推理与机器人动作”这套方案的核心形态。简单说,图像变成视觉Token,指令变成文本Token,机械臂的真实动作也被离散成动作Token,三种Token拼在一起,用语言模型的预测方式去训练和推理。
这篇文章想把这些事情讲清楚:为什么动作要被离散化,为什么自回归建模能扛起具身推理和动作生成,训练该怎么做,部署时有哪些坑。适合正在做具身智能、机器人学习、多模态大模型的工程师和研究者参考。
1. 整体设计与思路拆解
1.1 模块化机器人流程的痛点
传统的机器人操作流程一般是这样的:感知模块检测目标、语言模块理解指令、状态机或运动规划模块生成轨迹、底层控制器跟踪轨迹。每个模块都有自己独立的数据格式和接口,模块之间靠坐标、置信度、语义标签这类中间表示通信。听起来合理,实际联调时你很快会发现,每个模块的误差是叠加的。
比如视觉识别误差一个像素,转换到机械臂基坐标系下可能就有几毫米偏差。语言模块对指令的解析一旦有歧义,下游规划根本无从区分。运动规划模块又很保守,遇到动态摘取场景经常找不到路径。我们在实机上反复调参,最后意识到问题出在目标函数不统一:感知的目标是“识别对”,语言的目标是“理解对”,控制的目标是“执行稳”,三者没有共同优化信号。
统一具身VLM的做法是放弃中间表示,让一个模型直接从原始传感器输入和任务指令回归到动作输出。图像编码器和语言编码器负责把信息压缩,动作解码器负责把预测结果映射成控制信号,梯度从动作误差一路回传到视觉和语言特征。这样一来,感知错误可以用执行结果来纠正,语言歧义也能通过交互信息来消解。
1.2 为什么把动作也当作“语言”来预测
拿到这个思路后,我们第一个要回答的问题是:动作信号必须变成什么形式才能和视觉、语言放进去同一个模型?视觉可以用patch级Token,文本可以用BPE Token,动作是连续高维向量,直接做回归的话,模型输出层要和分类任务打架。
解决办法是把连续动作离散成有限的类别。机械臂的一个关节角度、末端位姿的速度增量和夹爪开合状态,都可以映射到对应的离散区间。离散化后的动作序列可以和文本Token一样作为自回归的预测目标,模型每一步输出的是一个动作Token的类别分布,而不是连续的数值。这种处理让整个模型的训练目标变成一个多类别的交叉熵问题,工程上稳定很多。
自回归的好处在于它天然匹配“从感知到行动”的时间结构。模型先根据当前的图像和指令生成推理Token,再一步步预测未来的动作Token。每一步生成的Token都会作为下一帧预测的条件,相当于模型自己维护了一个关于环境和任务状态的隐式信念。相比同时输出整条轨迹的做法,这种方式更容易建模多步依赖,也更贴近机器人逐帧控制的实际节奏。
1.3 自回归离散化预训练的整体路线
整体路线分为两条主线。第一条线是按照语言模型的范式做大规模预训练:收集海量的图像文本数据,让模型学会把视觉内容和文化信息对齐。此时动作Token还没被激活,模型先掌握“看”和“说”的基本能力。第二条线是机器人数据微调:用人工遥操作、自动巡航等方式采集的机器人操作数据,将视觉、文本、动作序列统一构成训练样例,让模型把视觉语言理解转化为动作分布。
预训练阶段我们保留了视觉Token和文本Token的强表示能力,微调阶段新增动作Token,并和语言Token一样参与自回归预测。这样,机器人中间推理过程可以以自然语言Token的形式写在动作序列之前,形成一条“看到目标—推理步骤—执行动作”的完整序列。模型既具备通用视觉语言知识,又能在执行任务时用语言做显式推理,再用动作Token落地执行。
这套路线最大的优势是工程复用。主干Transformer结构不变,预训练目标和微调目标几乎是同一套交叉熵损失,只是数据分布和Token词典不同。我们在实际项目中复用了一套成熟的文本生成框架,只改数据侧和处理逻辑就完成了整套具身VLM训练,这也是它非常适合团队快速上手的点。
2. 核心细节解析与实操要点
2.1 视觉编码:图像如何变成离散Token
视觉输入要进入Transformer序列,我们采用的是patch级切分加离散化。一张固定分辨率图像会被切成一堆小块,每个小块经过视觉encoder变成特征向量,再通过一个预先训练好的向量量化模块映射到离散码本。用到的量化方式和文本BPE完全不同,图像Token实际上是一组离散索引,比如196个块对应196个索引。
实操中有一点需要特别注意:预训练图像Token编码器时,不要只追求重建质量。重建质量高只能说明图像压缩得好,不代表语义信息丰富。我们在选择视觉Token的时候,会额外加入语义对齐损失,让视觉Token和对应的文本描述拉近。这个细节决定了后续模型对目标的分类能力。
部署时图像的输入分辨率需要兼顾效率和精度。分辨率太低,目标识别和空间位置估算都会变差;分辨率太高,视觉Token数量暴增,自回归推理延迟变大。我们在实机上使用的视觉Token数量大约在196到576之间,既保留了足够的空间信息,又不会让序列过长。
2.2 动作离散化:如何把连续控制信号变成Token
动作离散化是这套方案里决定成败的细节。我们最初犯过一个错误,直接对关节角度做等距分桶,每维映射到256个bin。结果模型训练很快,但实际执行时末端误差大得离谱。原因是7自由度关节角度的微小偏差在正运动学转换后会被放大,尤其靠近基座的关节角度误差对末端位置影响很严重。
更合理的做法是结合动作表征空间。一种常见方案是用一个小的动作编码器对一整段动作序列做压缩,学出一个动作码本,每个码字对应一段短时运动模式,模型预测的是运动模式索引而不是单帧关节角。另一种更轻量的方案是预测相对动作增量,同时把关节空间和笛卡尔空间分开离散化:关节角用粗粒度,笛卡尔位姿增量用更细粒度。
动作频率也要仔细选择。10Hz的控制频率下,模型要预测未来大概两秒的动作序列,就需要生成约20个动作Token。如果每个动作Token包含7个关节和1个夹爪,每个维度单独离散,序列会非常长。所以更倾向于每条动作帧用一个复合Token表示,先把多维动作编码成一个低维索引,再让模型预测索引。这样既控制了序列长度,又保留了动作维度的协同性。
2.3 统一序列的拼接与注意力掩码
统一序列是“一个模型处理所有模态”的关键。我们在实践中采用这样的拼接顺序:
图像Token放最前面,表示当前观测;随后是系统指令Token,告诉模型“你是一个机器人控制模型”;然后是用户任务指令Token;接着是推理Token,用自然语言写出执行计划和判断;最后是动作Token序列。这种排列让模型在生成动作时,可以同时看到视觉上下文、任务指令和自己刚刚生成过的推理内容,信息逐步累积。
需要特别注意的是序列中的Mask机制。我们并不需要对所有Token计算损失。图像Token只是输入条件,不需要被预测;系统指令和用户指令已经是给定的Prompt,也不需要反向传播重建。真正需要监督的只有两类Token:一是推理过程中的关键步骤Token,二是具体的动作Token。所以在计算交叉熵损失时,要把非预测位置的标签设为忽略值,只对目标位置的Token计算梯度。
注意力掩码上采用标准的因果掩码即可。当前Token位置只能看到前面的Token,看不到后面内容,这保证了推理时的行为一致性。如果是离线数据,可以通过输入右侧Padding来对齐Batch,推理时则先填完整段前缀,再自回归生成动作。
# 伪代码:统一序列拼接 seq = [] seq += visual_encoder.get_visual_tokens(image) # 视觉 Token seq += tokenizer.encode("[SYS] 你是一个机器人控制模型。") seq += tokenizer.encode("把红色杯子放到托盘上。") seq += tokenizer.encode("第一步,定位红色杯子;第二步,夹取;第三步,移动到托盘上方。") seq += action_encoder.encode(action_sequence) # 动作 Token labels = [-100] * len(prefix) labels += [token_id for token_id in action_token_ids]2.4 损失函数与训练目标设置
统一模型的目标可以看成是最大似然估计:给定前缀序列,最大化目标Token的条件概率。因为Token是离散的,我们用标准的交叉熵损失。不过因为只有部分位置需要计算损失,所以要在数据构造阶段就把标签序列设置好。
如果在推理前显式加入推理Token,建议对推理Token也计算损失,并且适当调大它们在总损失里的权重。否则模型会把推理当成装饰,生成一堆文字但不参与实际动作决策。我们曾经试过完全忽略推理Token损失,结果模型确实会生成类似“先抓把手”的文字,但机械臂动作却完全不管文字内容,推理和动作之间出现了明显的语义断裂。
训练中还需要处理类别不平衡问题。动作Token分布天然不平衡,比如“保持不动”这类零动作状态占比很高,如果不做处理,模型会倾向于输出默认动作。可以通过上采样接触任务数据、调整动作Token类别权重、或者在损失中加入Focal Loss机制来缓解。实践下来,最简单的有效做法是限制“零动作”样本在Batch中的比例,而不是只靠损失加权。
3. 实操过程与核心环节实现
3.1 数据准备:多源数据清洗与采样
统一模型最需要的是成对的观测、指令、动作序列。数据来源大致有三类:真实机械臂遥操作数据、仿真环境自动生成数据、以及互联网图像文本对。真实遥操作轨迹最可靠,但成本高;仿真数据可以大规模生成,但存在域差;互联网数据能提供丰富的语义理解能力,但缺少动作监督。
我们在实际项目中按比例混合数据:大约20%的真实机器人轨迹,50%的仿真操作数据,30%的互联网图像文本对。混合前必须做两件事:统一时间戳和统一坐标系。图像和动作数据如果没有严格同步,模型会在时间上产生幻觉,看到这一帧图像,却输出了上一帧的动作。为此我们在采集端加了硬件同步,并写了一个脚本对所有样本重新插值到固定频率。
采样时,每条样本的长度也要控制。上下文窗口有限,图像Token占掉了一部分,留给动作Token的空间不多。我们的策略是把一段长轨迹以固定窗口切成长度为20到30个动作帧的样本。如果任务特别长,就把前序动作作为历史信息输入进来,而不是每次都从第一帧开始。这样既保证了上下文完整,又不会把序列撑爆。
3.2 预训练阶段:先从看听说到动手
预训练阶段我们不接触动作数据,主要做的是视觉语言对齐。训练数据基本都是图像文本对:图像字幕、视觉问答、目标描述、空间关系推理。目标是让模型能够准确地把图像内容转成语言表达,同时把语义理解和空间推理能力沉淀到参数里。
这个阶段用的是标准的自回归语言建模目标,但输入里会插入视觉Token。为了让视觉Token与文本Token的位置编码兼容,我们专门设计了相对位置关系:视觉Token本身是一组二维Patch,在展开成序列后,位置编码要能保留空间邻居关系。简单做法是给视觉Token单独加一组可学习的绝对位置编码,但在性能上不如二维相对位置编码。我们选用了后者,明显提升了空间关系任务的表现。
预训练常用学习率一般在1e-4到3e-4之间,Batch Size需要根据显存调整。上下文长度建议做到1024或2048。我们发现模型在预训练阶段如果只看短序列,微调时很难适应长序列的机器人数据,所以预训练数据里要混入一定比例的长文本样本,把序列长度稳拉上去。
预训练到底需要多少步没有统一标准,但有一个可测量的指标:视觉问答准确率和目标空间位置预测误差。当这两项趋于稳定后,再去接机器人数据微调,能明显感觉到动作Token的学习起点更高。从零直接训练机器人数据则容易陷入局部最优,动作出来的成功率会低不少。
3.3 机器人数据微调:行为克隆与安全约束
微调阶段要把机器人操作信号接入预训练模型。具体做法是固定视觉encoder和大部分主干层的参数,只训练动作相关模块与顶层,前期我们还会用较低的学习率逐步激活所有层。直接用全量微调很容易遗忘预训练学到的视觉语言能力,后面再想改就很麻烦。
行为克隆的损失计算已经讲过,就是对每个动作Token位置计算分类交叉熵。数据质量对微调结果的影响比想象中大。遥操作数据里如果有一段不合格的轨迹,模型不仅能学坏,还会把错误模式泛化到其他类似场景。我们在微调前对每条轨迹做了速度平滑度检查、碰撞检测和任务完成度评分,不合格的直接丢弃。
安全约束方面,模型预测的动作Token不一定都安全。我们在动作Token解码后增加了一个安全滤波器:判断目标位置是否超出工作空间、关节角度是否超限、夹爪是否碰到环境。滤波器拦截异常动作后,会退回到上一个安全动作或者进入保位模式。这个机制必须放在推理服务层,不能只靠训练数据隐含的安全倾向。
微调阶段的另一个技巧是保持混合预训练数据。我们发现如果样本里只放机器人数据,模型对指令的响应会越来越机械化,稍微换一种措辞就不执行了。所以每个Batch中会混入大约30%的图像文本任务数据,让模型维持语言和视觉的泛化能力。这个比例可以根据实际表现调整,但不要低于20%。
3.4 推理部署:从Token生成到真实动作
部署时我们不是直接把整个动作轨迹一次性生成,而是采用滚动预测方式。先采集当前图像,拼接固定指令前缀,让模型生成一小段推理Token和动作Token,然后解码出第一个动作帧并发送给底层控制器。机械臂执行该动作的同时,下一帧图像到达,再进行下一轮预测。
这样做的好处是能实时响应环境变化。如果目标是移动的,模型每一轮都能根据最新的图像调整动作。但代价是推理延迟必须足够低,整个流水线得在第二个动作帧准备好之前完成。通常我们给视觉编码、模型推理、动作解码和安全检查总计留出100毫秒左右,才能和底层控制器配合好。
推理时的Token解码策略也会直接影响实际动作质量。贪心解码最稳定,适合精确控制;采样解码会增加随机性,但有时能探索出更多可行路径。我们最终采用的是低温度采样,温度设在0.1到0.3之间,既保住了稳定性,又保留了少量多样性。动作Token解码后还要做个低通滤波,把相邻两步之间的突变平滑掉,避免机械臂出现抖动。
推理服务端到端的延迟优化也是一块硬骨头。视觉Token推理可以提前做KV Cache缓存,指令前缀Token也可以提前预处理,只有动作Token生成和下一帧图像Token更新需要逐帧计算。我们在实际系统中用了提前批推理和异步IO,将整体单次推理延迟压到了70毫秒左右。
3.5 训练与部署的关键参数
| 阶段 | 学习率 | 批大小 | 上下文长度 | 损失权重 | 主要数据 |
|---|---|---|---|---|---|
| 视觉语言预训练 | 2e-4 | 256 | 2048 | 文本Token: 1.0 | 图像文本对 |
| 机器人数据微调 | 1e-5 | 64 | 1024 | 推理Token: 0.6, 动作Token: 1.0 | 机器人轨迹混合数据 |
| 策略微调 | 5e-6 | 32 | 512 | 动作Token: 1.2 | 目标任务数据 |
参数选择没有绝对标准。上面这组参数是我们在某个机械臂平台上反复调出来的,如果你的模型规模和数据量不同,学习率可以按比例调整。总的原则是:预训练阶段用大学习率快速收敛,微调阶段用小学习率保护已有能力,策略微调阶段再进一步缩小学习率提升稳定性。
4. 常见问题与排查技巧实录
4.1 动作离散化粒度不合适导致精度不足
这是最常遇到的问题。动作Token的离散bin数设少了,模型学得快但精度不够;bin数设多了,分类难度上升,模型容易在相邻bin之间反复横跳。我们第一次用每维256个bin,末端执行时误差超过两厘米,抓取小物体直接失败。
后来改成每维1024个bin,精度明显提升,但训练难度也上去了。折中方案是先用256个bin做预训练,再用1024个bin在目标数据集上做短期微调,相当于用低分辨率学宏观行为,再用高分辨率精调局部细节。另外可以尝试只对末端位姿使用高分辨率离散化,对关节角使用低分辨率,重点关注最终执行效果。
4.2 模型输出动作抖动与乱跳
动作Token序列在时间上如果频繁在相邻bin之间切换,解码出来的连续动作就会抖动。即使低通滤波能压制一部分,机器人任务里高频抖动依然会导致夹爪定位不稳。
检查思路是先区分是数据问题还是模型问题。我们把训练数据里的动作序列做了差分统计,发现不少遥操作数据本身就存在高频抖动,这会在训练时被模型原样学走。所以先对训练数据做平滑处理,再检查模型输出。推理侧还可以把温度调低,减少随机采样导致的bin跳变。
4.3 微调后视觉语言能力明显退化
全量微调会让模型过度拟合机器人任务,忘记预训练时学会的通用视觉语言能力。典型表现是换个场景、换种指令措辞后,模型动作成功率和语义理解明显变差。
建议采用LoRA或Adapter方式微调,只更新一部分低秩参数。我们在真实项目里用LoRA微调后,机器人动作成功率提升了约12%,同时视觉问答能力几乎没有退化。还有一个很有效的技巧是分层解冻:第一步只训练动作Token相关模块,第二步再解冻部分Transformer层,最后再全量训练一小段时间。每一步都要评估视觉语言基准指标,发现掉点就退回上一步。
4.4 长序列训练时的内存与速度问题
统一序列不长,但一张图加一句话加几十个动作Token,总量也能轻松突破一千多Token。如果还用标准多头自注意力,显存消耗非常快,训练速度更是惨不忍睹。
我们在训练和推理两端都做了优化。训练端开启梯度检查点、混合精度和Flash Attention,把每样本显存占用降了差不多一半。推理端利用KV Cache缓存之前计算的Key和Value,避免每次生成动作Token都重新计算前面的注意力。另外把图像Token数量动态裁剪,在不影响任务成功率的前提下,该省的空间都要省。
4.5 失败案例速查表
| 现象 | 可能原因 | 快速处理 |
|---|---|---|
| 动作精度差 | 离散化bin数太少 | 增大bin数,或细化末端位姿 |
| 输出抖动 | 数据本身有高频噪声 | 训练数据先平滑,推理降低温度 |
| 不执行指令 | 微调数据里指令风格太单一 | 混合多样指令,减少过拟合 |
| 视觉语义退化 | 全量微调覆盖预训练知识 | 使用LoRA或冻结底层参数 |
| 训练显存爆掉 | 序列过长且未开优化 | 开启梯度检查点和Flash Attention |
| 动作总是同一个默认值 | 零动作样本比例过高 | 上采样接触类任务,限制零动作比例 |
| 推理延迟过高 | 动作Token生成太慢 | 缓存前缀KV,滚动推理,缩短视觉Token |
最后再分享一个小技巧。如果你刚开始做这个方向,不要急着上复杂数据集和全尺寸模型。先用一个自由度较少的机械臂,比如四轴的模拟环境,把“图像Token、指令Token、动作Token统一自回归预测”这条链路跑通。等你在小规模任务上调通了稳定输出,再往更复杂、更多自由度的真机场景扩展。这条技术路线的瓶颈往往不再模型容量,而在数据质量、离散化设计和工程链条的稳定程度。把基础问题先解决掉,后面的扩张会顺利很多。