ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

谷歌机器人攻克「最后几厘米」:VLA技术解析与Python实验

谷歌机器人攻克「最后几厘米」:VLA技术解析与Python实验 最近谷歌机器人的一系列动作又把「具身智能」推到了聚光灯下。这次官方放出的重点不只是“机器人能走、能看”而是瞄准了一个让机器人行业头疼很久的问题——「最后几厘米」。说白了机器人做粗活已经不难了规划一条路径把机械臂挪到目标附近行业里成熟方案很多。真正难的是最后那几厘米的精细操作插拔接口、穿针引线、叠衣服、倒水、拧瓶盖。差一厘米任务就失败。这篇文章就围绕这个热门话题展开聊聊谷歌机器人这次“大活儿”背后的 VLA视觉-语言-动作技术路线分析「最后几厘米」难在哪、谷歌怎么解并提供一个可以本地运行的 Python 小实验帮你理解动作分箱与连续回归的精度差异。文章末尾还会顺带介绍开源 VLA 模型的推理思路。不管你是做机器人控制、强化学习还是单纯关注多模态大模型落地这篇都能给你一个相对完整的视角。1. 背景谷歌机器人这次“整了什么大活儿”1.1 谷歌机器人的最新动作根据公开信息谷歌 DeepMind 在 2025 年初发布了 Gemini Robotics 系列模型把 Gemini 多模态大模型的能力延伸到机器人控制上。这条路线被官方称为 VLA也就是 Vision-Language-Action Models视觉 - 语言 - 动作模型。另外还有一个专门做具身推理的 Gemini Robotics-ER 模型它不直接输出电机指令而是输出“动作规划”和“环境理解”比如识别物体位置、判断抓取姿态、规划路径等。两者搭配起来机器人既能“看懂”任务描述又能把语义转化为可执行的动作。这套组合拳在很多机器人社区里被概括为“大活儿”因为它把机器人从“预设脚本控制”往“自然语言指挥”方向推了一大步。1.2 什么是「最后几厘米」难题机器人领域有个经典说法远程操作或者路径规划可以解决百分之九十的行程但最后几厘米才是决定成败的关键。举个直观的例子任务让机械臂把一个 USB 接口插进电脑。前 80% 的路程机械臂只需要移动到接口上方大概几厘米的位置这个精度误差几毫米都没问题。最后 5 厘米接口要对准、角度要对、力度要合适偏差超过 1 毫米就可能插不进去甚至损坏设备。这种最后阶段的精准操作被行业里称为「最后一厘米」或「最后几厘米」问题。它涉及视觉反馈、触觉感知、力控、动态规划等多个环节传统基于运动学和预设轨迹的方法很难覆盖所有情况。1.3 为什么 VLA 是解决这个问题的关键方向传统机器人控制往往需要工程师为每个动作编写规则或标定轨迹换一个物体、换一个环境规则可能就要重写。VLA 的核心思想是直接用海量图文数据和机器人操作数据训练一个模型让模型学习“看到什么、理解什么指令、该输出什么动作”。这样一来模型的泛化能力来自大语言模型和多模态模型的知识而不是人工硬编码的规则。谷歌在 RT-2 论文中已经证明经过 VLA 训练的机器人能够从未见过的物体、从未见过的指令上迁移出合理的操作策略。这种“语义理解 动作输出”的组合恰好就是解决「最后几厘米」精细操作的潜在方案之一。2. 环境准备与版本说明理解 VLA 原理之前我们先准备一个可以动手的环境。下面给出两种实践路径路径 A本地 Python 小实验用合成数据模拟“精细动作决策”不需要 GPU几分钟能跑完。路径 B使用开源 VLA 模型 OpenVLA 做推理需要 GPU适合想亲手看模型输出动作的朋友。2.1 路径 A 环境配置推荐环境依赖版本建议说明Python3.9主流版本即可PyTorch2.0用于构建和训练小型模型NumPy1.24数据生成与处理scikit-learn1.3用于评估指标可选安装命令pip install torch numpy scikit-learn2.2 路径 B 环境配置可选OpenVLA 是一个开源 VLA 模型基于 7B 参数规模的视觉-语言模型训练而成。它的推理环境要求比较高pip install transformers torch bitsandbytes另外 OpenVLA 官方仓库还依赖flash-attn、tensorflow等组件建议直接参考官方 README 安装不同版本的 API 可能有差异。注意OpenVLA 模型体积较大7B 参数单卡 24GB 显存运行 fp16 都比较紧张建议使用 40GB 以上显存或者搭配量化方案。2.3 项目结构规划为了让实验清晰建议创建下面的目录结构last-centimeter-vla/ ├── data_gen.py # 生成合成精细操作数据集 ├── models.py # 定义连续回归模型和分箱分类模型 ├── train.py # 训练入口 └── evaluate.py # 对比两种模型的精度接下来我们先用一个小实验把 VLA 中最核心的“动作表征”问题讲清楚。3. VLA 核心原理拆解VLA 的完整链路非常复杂但核心可以拆成三件事输入表征、模型结构、动作输出。3.1 从 RT-1 到 RT-2机器人 Transformer 的进化谷歌 DeepMind 的 VLA 路线可以追溯到 RT-1Robotics Transformer 1。RT-1 收集了 13 万条机器人演示数据用 Transformer 模型学习“图像 指令 → 动作”。它的动作用 7 维向量表示6 维末端执行器位姿位置和旋转加 1 维夹爪开合。RT-2 则把思路再往前推了一步把海量网络图文数据也拿进来训练。模型不再只依赖机器人轨迹数据而是先学习大量“视觉-语言”知识再迁移到机器人动作上。实验中RT-2 对未见过的物体、未出现过的指令表现出了很强的泛化能力。3.2 动作 token 化连续动作如何进入语言模型VLA 一个关键设计是“动作分箱”。机器人的动作通常是一个连续向量例如[dx, dy, dz, droll, dpitch, dyaw, gripper]要让语言模型处理连续数值有几种选择方式一直接对连续值做回归输出浮点数方式二把每个动作维度离散化为固定数量的区间然后当作 token 让模型做分类方式三用专门的 action head 输出连续值对应不同维度的动作。RT-2 实际采用的是“分箱 分类”的策略。每个动作维度被切分成多个区间模型输出的不是浮点数而是区间索引。这样做的好处是能直接复用语言模型已有的分类能力训练也更稳定但代价是分箱粒度直接决定了动作精度。设想一下如果末端执行器位置范围是 0~10 厘米分箱数量是 256那么每个箱子代表约 0.4 毫米的精度。对于插拔接口这类任务0.4 毫米的量化误差可能正好撞上“最后几厘米”难题的关键阈值。3.3 协同微调让模型同时学会“理解”和“动作”VLA 训练时通常采用“协同微调”策略也就是把多个任务目标一起训练图像描述任务 视觉问答任务 机器人动作预测任务好处很明显语言任务提供语义知识动作任务提供控制能力。两者共享同一个视觉编码器和语言模型可以让动作预测更好地利用语义特征。以 RT-2 的公开论文为例模型训练中使用了几十万条机器人轨迹数据同时混合了大量图文语料。这种做法后来也被很多开源 VLA 模型继承包括 OpenVLA。3.4 Gemini Robotics 与 ER 模型亮点Gemini Robotics 是这套思路的最新体现。它基于 Gemini 2.0 的多模态能力能够直接输出机器人动作也可以输出中间表示如 3D 场景理解、抓取点预测。Gemini Robotics-ER 则负责“推理”比如规划抓取顺序、预测物体移动后的状态。结合「最后几厘米」这个话题Gemini Robotics 的亮点是它能在复杂场景下利用语言模型的知识推断物体的操作方式而不是只依赖预设轨迹。例如面对一个没见过的抽屉把手模型可以根据“把手”的语义信息推测抓取位置。3.5 为什么「最后几厘米」一直是行业难题下面从控制理论的角度拆解一下难点说明非线性接触动力学最后阶段往往存在接触摩擦力、形变很难精确建模视觉遮挡末端执行器靠近目标时目标容易被机械臂自身遮挡传感器噪声相机误差、编码器误差在毫米级别被放大动态不确定性物体被移动后状态变化模型需要实时调整安全风险力度过大会损坏工件或伤到人传统方法对每个难点都需要专门设计模块而 VLA 试图通过大量数据学习一个端到端的映射把“看 想 动”合并成一步。4. 实战本地模拟“最后几厘米”动作精度实验4.1 问题定义与数据生成为了理解动作分箱的作用我们构造一个简化的“精细操作”任务机械臂末端当前位置为(px, py)目标位置为(tx, ty)模型需要输出末端位移(dx, dy)让末端接近目标。任务的难点设置为目标点非常密集距离越近需要输出的动作越精细。我们用 NumPy 生成 10000 个样本每个样本包含当前位置、目标位置和目标位移# 文件路径last-centimeter-vla/data_gen.py import numpy as np def generate_dataset(num_samples10000, seed42): rng np.random.default_rng(seed) data [] labels [] for _ in range(num_samples): # 当前位置在 [0, 1] 区域内随机采样 px, py rng.uniform(0, 1, size2) # 目标位置在当前位置附近模拟“最后几厘米”场景 # 距离越小代表任务越精细 distance rng.uniform(0.01, 0.3) angle rng.uniform(0, 2 * np.pi) tx px distance * np.cos(angle) ty py distance * np.sin(angle) # 限制边界 tx np.clip(tx, 0, 1) ty np.clip(ty, 0, 1) dx tx - px dy ty - py data.append([px, py, tx, ty]) labels.append([dx, dy]) return np.array(data, dtypenp.float32), np.array(labels, dtypenp.float32) if __name__ __main__: X, y generate_dataset() np.savez(synthetic_data.npz, XX, yy) print(f数据集大小: {X.shape[0]}) print(f动作位移范围: dx [{y[:, 0].min():.4f}, {y[:, 0].max():.4f}], dy [{y[:, 1].min():.4f}, {y[:, 1].max():.4f}])运行python data_gen.py预期输出类似数据集大小: 10000 动作位移范围: dx [-0.3000, 0.3000], dy [-0.3000, 0.3000]4.2 连续回归模型我们先用一个两层 MLP 做连续动作回归直接输出(dx, dy)# 文件路径last-centimeter-vla/models.py import torch import torch.nn as nn class ContinuousActionModel(nn.Module): 连续回归模型输入当前状态与目标位置直接输出位移。 def __init__(self, input_dim4, hidden_dim64): super().__init__() self.net nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, 2) # dx, dy ) def forward(self, x): return self.net(x)训练连续回归模型# 文件路径last-centimeter-vla/train_continuous.py import torch import torch.nn as nn import numpy as np from torch.utils.data import DataLoader, TensorDataset from models import ContinuousActionModel # 数据加载 data np.load(synthetic_data.npz) X torch.tensor(data[X], dtypetorch.float32) y torch.tensor(data[y], dtypetorch.float32) dataset TensorDataset(X, y) loader DataLoader(dataset, batch_size256, shuffleTrue) # 模型与优化器 model ContinuousActionModel() optimizer torch.optim.Adam(model.parameters(), lr1e-3) loss_fn nn.MSELoss() # 训练 for epoch in range(50): total_loss 0.0 for xb, yb in loader: pred model(xb) loss loss_fn(pred, yb) optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() * xb.size(0) if (epoch 1) % 10 0: print(fEpoch {epoch 1}, Loss: {total_loss / len(X):.6f}) torch.save(model.state_dict(), continuous_model.pth)4.3 动作分箱分类模型再来看分箱分类模型。我们把dx和dy各自离散化成 N 个 bin模型输出的是 N×N 类每一类对应一个位移区间组合# 文件路径last-centimeter-vla/models.py追加 class DiscreteActionModel(nn.Module): 动作分箱分类模型把连续动作映射到离散区间索引。 def __init__(self, input_dim4, hidden_dim64, num_bins20): super().__init__() self.num_bins num_bins self.net nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, num_bins * num_bins) # 分类 logits ) def forward(self, x): logits self.net(x) return logits.view(-1, self.num_bins, self.num_bins)数据标签需要从连续值转换为分箱索引# 文件路径last-centimeter-vla/train_discrete.py import torch import torch.nn as nn import numpy as np from torch.utils.data import DataLoader, TensorDataset from models import DiscreteActionModel data np.load(synthetic_data.npz) X torch.tensor(data[X], dtypetorch.float32) y torch.tensor(data[y], dtypetorch.float32) # 分箱参数 NUM_BINS 20 BOUND 0.3 # dx, dy 理论范围 [-0.3, 0.3] def to_bin_index(value): 把连续位移映射到 [0, NUM_BINS-1] 的索引 normalized (value BOUND) / (2 * BOUND) normalized torch.clamp(normalized, 0.0, 1.0) return (normalized * (NUM_BINS - 1)).long() # 标签转分箱 dx_bin to_bin_index(y[:, 0]) dy_bin to_bin_index(y[:, 1]) labels dx_bin * NUM_BINS dy_bin dataset TensorDataset(X, labels) loader DataLoader(dataset, batch_size256, shuffleTrue) # 模型与优化器 model DiscreteActionModel(num_binsNUM_BINS) optimizer torch.optim.Adam(model.parameters(), lr1e-3) loss_fn nn.CrossEntropyLoss() # 训练 for epoch in range(50): total_loss 0.0 correct 0 total 0 for xb, yb in loader: logits model(xb) loss loss_fn(logits.view(-1, NUM_BINS * NUM_BINS), yb) optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() * xb.size(0) pred_idx logits.argmax(dim-1).view(-1) correct (pred_idx yb).sum().item() total yb.size(0) if (epoch 1) % 10 0: acc correct / total print(fEpoch {epoch 1}, Loss: {total_loss / len(X):.6f}, Acc: {acc:.4f}) torch.save(model.state_dict(), discrete_model.pth)4.4 对比训练与结果评估训练完成后我们写一个评估脚本比较两种模型在“最后几厘米”场景中的平均位置误差# 文件路径last-centimeter-vla/evaluate.py import torch import numpy as np from models import ContinuousActionModel, DiscreteActionModel data np.load(synthetic_data.npz) X torch.tensor(data[X], dtypetorch.float32) y torch.tensor(data[y], dtypetorch.float32) NUM_BINS 20 BOUND 0.3 # 加载模型 cont_model ContinuousActionModel() cont_model.load_state_dict(torch.load(continuous_model.pth)) cont_model.eval() disc_model DiscreteActionModel(num_binsNUM_BINS) disc_model.load_state_dict(torch.load(discrete_model.pth)) disc_model.eval() # 预测 with torch.no_grad(): cont_pred cont_model(X) disc_logits disc_model(X) disc_idx disc_logits.argmax(dim-1).view(-1) # 分箱索引转回连续值 def bin_index_to_value(idx): bin_size 2 * BOUND / (NUM_BINS - 1) value -BOUND idx * bin_size return value dx_bin disc_idx // NUM_BINS dy_bin disc_idx % NUM_BINS dx_pred bin_index_to_value(dx_bin.float()) dy_pred bin_index_to_value(dy_bin.float()) # 计算误差 cont_error torch.norm(cont_pred - y, dim1) disc_error torch.norm(torch.stack([dx_pred, dy_pred], dim1) - y, dim1) # 重点看“最后几厘米”目标距离小于 0.05 的样本 target_distance torch.norm(y, dim1) fine_mask target_distance 0.05 print( 整体误差平均距离误差) print(f连续回归模型: {cont_error.mean().item():.5f}) print(f分箱分类模型: {disc_error.mean().item():.5f}) print() print( 「最后几厘米」样本位移 0.05) print(f连续回归模型: {cont_error[fine_mask].mean().item():.5f}) print(f分箱分类模型: {disc_error[fine_mask].mean().item():.5f})4.5 结果说明与启发运行后你会看到类似结果 整体误差平均距离误差 连续回归模型: 0.00521 分箱分类模型: 0.01453 「最后几厘米」样本位移 0.05 连续回归模型: 0.00436 分箱分类模型: 0.01267分箱模型在细粒度区域明显吃亏。原因在于当位移范围被固定为[-0.3, 0.3]时20 个 bin 的粒度约为 0.03而精细操作本来就需要 0.01 以下的精度。这个实验虽然简单却揭示了 VLA 落地时一个非常重要的问题分箱数量越多动作精度越高但分类任务越难为了兼顾精度和训练难度很多 VLA 模型会采用“多分辨率分箱”或“分维分箱”策略这也是为什么部分最新工作会加入“连续动作头”用于修正离散化带来的精度损失。5. 进阶使用开源 VLA 模型跑一次推理如果你有 GPU可以再往前一步用开源 VLA 模型 OpenVLA 真实跑一次“图 文 → 动作”的推理。5.1 OpenVLA 是什么OpenVLA 是一个开源 VLA 模型训练数据来自 Open X-Embodiment 数据集其中包含大量真实机器人操作轨迹。它的模型结构延续了“视觉编码器 大语言模型 动作头”的路线用 LoRA 等技术降低了微调门槛。5.2 运行环境说明OpenVLA 官方推荐使用 Python 3.10、CUDA 11.8 环境。启动推理前建议先确认几个依赖pip install torch2.1.2 transformers4.36.2 bitsandbytes0.42.0具体依赖版本请以官方仓库为准这里只是示例。5.3 推理代码示例下面的代码对应 OpenVLA 官方仓库中常见的使用方式核心逻辑是输入一张图片和一个自然语言指令模型输出 7 维动作 token再解码成具体动作# 文件路径openvla_inference.py # 这段代码依赖 OpenVLA 官方仓库建议先根据 README 安装依赖 from transformers import AutoModelForVision2Seq, AutoProcessor import torch from PIL import Image # 1. 加载模型和处理器 model_id openvla/openvla-7b processor AutoProcessor.from_pretrained(model_id) model AutoModelForVision2Seq.from_pretrained( model_id, torch_dtypetorch.bfloat16, device_mapauto, ) # 2. 输入数据 image Image.open(test_image.png) instruction pick up the red cup and place it on the saucer # 3. 构造输入格式以官方函数为准这里是简化示意 prompt f Task: {instruction} inputs processor( image, textprompt, return_tensorspt, ).to(cuda) # 4. 模型推理 with torch.inference_mode(): output_ids model.generate( **inputs, max_new_tokens32, do_sampleFalse, ) # 5. 打印原始动作 token action_tokens output_ids[0, inputs[input_ids].shape[1]:] print(动作 token:, action_tokens)实际操作时OpenVLA 的图片缩放、提示词格式、动作 token 解码函数都在官方仓库中建议直接克隆仓库后按 README 运行example_inference.py。不要直接把上面的简化代码当作生产级脚本。5.4 参数与注意事项max_new_tokensVLA 模型通常一次输出 7 个动作 token再加上特殊 token一般 32 足够。do_sampleFalse机器人动作需要确定性输出推理阶段不建议采样。显存OpenVLA 7B 在 bf16 下需要约 16GB 显存加上中间变量建议 24GB 起步。6. 常见问题与排查思路在实际复现或部署过程中比较容易遇到下面几类问题问题现象常见原因解决思路本地实验分箱模型精度远低于回归模型分箱粒度不够增加NUM_BINS或采用多分辨率分箱训练 loss 不下降学习率设置不当尝试更小的学习率例如1e-4分箱分类准确率很高但动作误差大标签不均衡检查动作分布必要时使用加权损失函数OpenVLA 加载时 OOM模型超过显存容量使用量化加载或减少图像输入尺寸OpenVLA 输出动作异常图像输入比例不对按官方要求 resize 到 224×224 或 336×336真实机器人执行时抖动动作频率低导致控制延迟在真机部署时加滤波或提高推理频率精细任务插不进去模型没有足够细粒度的动作表征增加分箱粒度或在最后阶段切换精细化策略排查时建议按“数据 → 模型 → 部署”顺序排查先确认输入数据是否正确比如归一化范围、标签索引再确认模型输出是否有明显规律例如画出预测值与真实值散点图最后确认部署端控制频率和坐标变换是否正确。7. 最佳实践与工程建议7.1 数据层面动作分布要覆盖“难例”很多人做机器人数据采集时只关注任务完成的成功率忽略了失败案例和边界样本。实际上「最后几厘米」考验的是小位移、高精度的动作这些样本往往只占数据集的很小比例。建议在采集中专门增加这类难例样本或者在离线数据增强阶段对精细动作做上采样。7.2 动作表征不要盲目追求分箱数量分箱数量直接决定模型输出类别的维度。假设 7 维动作每维分成 256 个 bin那动作空间就是 256 的 7 次方模型需要训练的分类类别极其庞大。工程上更常见的做法是对位置和姿态分开分箱位置用更高分辨率的 bin姿态用较低分辨率让模型同时输出“离散 token”和“连续残差”。这样既能利用分类训练稳定性的优势又能保留连续动作的精度。7.3 仿真与真机安全边界在真实机器人上做 VLA 实验时以下几点必须提前确认真机实验前先验证紧急停止按钮和急停逻辑在仿真环境中先跑通完整链路再迁移到真机所有动作预测必须通过安全过滤器例如限幅、限速、碰撞检测涉及人机协作时明确授权和操作边界不要在没有保护措施的环境下测试。VLA 模型具有很强的泛化能力但也意味着它可能输出意想不到的动作。生产环境落地时建议在模型输出与电机控制之间保留一个安全校验层这个层负责拒绝高风险动作。7.4 工程落地建议构建任务评估集时单独统计“最后几厘米”样本的成功率不要把整体指标掩盖了局部问题VLA 模型上线前做多场景迁移测试避免过拟合到单一环境机器人的执行频率和模型推理频率如果不同步需要设计动作插值策略。8. 总结与后续学习方向谷歌机器人在 VLA 路线上的连续押注说明“让机器人学会精细操作”已经不只是控制算法的课题而是一个“数据 模型 机器人硬件”的系统工程。这篇文章帮你梳理了几件事「最后几厘米」为什么是行业难题VLA 模型如何把视觉、语言、动作统一到一个模型里动作分箱是 VLA 的关键设计但也可能是精度瓶颈通过本地实验对比了连续回归和分箱分类的精度差异了解了 OpenVLA 这类开源模型的基本推理流程。如果接下来想继续深入建议按下面顺序动手实践先把本地实验跑通把NUM_BINS改成 50、100观察误差变化进一步尝试“先分类、再回归残差”的混合动作头有条件的话用开源机械臂或仿真环境跑一次 OpenVLA 的端到端推理关注谷歌 Gemini Robotics 后续公开的技术细节和数据集。如果你还在入门阶段建议从仿真环境开始比如 MuJoCo、Isaac Lab 或者是带物理引擎的机器人模拟器先积累操作数据再训练 VLA 模型。机器人精细操作这块内容更新很快但底层逻辑——数据质量、动作表征、安全边界——是不变的理解这些核心点之后再追新模型效率会高很多。希望这篇能帮你在「最后几厘米」的问题上少走弯路也欢迎收藏备用后面自己动手实验时可以直接参考。
返回列表