视觉-语言-动作模型(VLA)入门

理解VLA模型的核心思想,学习如何使用大语言模型驱动机器人执行自然语言指令。

VLA 大语言模型 机器人 自然语言控制 Embodied AI
视觉-语言-动作模型(VLA)入门

视觉-语言-动作模型(VLA)入门

VLA(Vision-Language-Action)模型是Embodied AI的最新前沿。它将视觉感知、语言理解和动作生成统一在一个模型中,让机器人能够理解自然语言指令并执行相应操作。

什么是VLA模型?

VLA模型的核心思想:

自然语言指令 → 视觉观察 → 动作序列
"把红色方块放到蓝色盒子里" → 相机图像 → 机械臂运动

代表性模型:

  • RT-2(Google):基于PaLM-E,562亿参数
  • Octo(UC Berkeley):开源,基于Transformer
  • OpenVLA:开源复现,支持多任务

VLA模型架构

┌─────────────────────────────────────────┐
│              VLA Model                   │
├─────────────────────────────────────────┤
│  输入层:                                │
│  - 语言指令(Token序列)                 │
│  - 视觉观察(图像Token)                 │
│  - 本体感觉(关节角度)                  │
├─────────────────────────────────────────┤
│  骨干网络:Transformer / LLM            │
├─────────────────────────────────────────┤
│  输出层:                                │
│  - 动作序列(关节角度 / 末端位姿)       │
└─────────────────────────────────────────┘

使用OpenVLA

import torch
from transformers import AutoModelForVision2Seq, AutoProcessor
from PIL import Image

class OpenVLAAgent:
    def __init__(self, model_name="openvla/openvla-7b"):
        self.device = "cuda" if torch.cuda.is_available() else "cpu"

        # 加载模型
        self.model = AutoModelForVision2Seq.from_pretrained(
            model_name,
            torch_dtype=torch.float16,
            trust_remote_code=True
        ).to(self.device)

        self.processor = AutoProcessor.from_pretrained(
            model_name,
            trust_remote_code=True
        )

    def get_action(self, instruction, image, state=None):
        """根据指令和图像生成动作"""
        # 准备输入
        inputs = self.processor(
            text=instruction,
            images=image,
            return_tensors="pt"
        ).to(self.device)

        # 如果有本体感觉状态
        if state is not None:
            inputs["proprio"] = torch.tensor(state).to(self.device)

        # 生成动作
        with torch.inference_mode():
            outputs = self.model.generate(
                **inputs,
                max_new_tokens=128,
                do_sample=False
            )

        # 解码动作
        action = self.processor.decode_action(outputs[0])
        return action

# 使用示例
agent = OpenVLAAgent()

# 获取相机图像
image = Image.open("current_view.png")

# 发送指令
instruction = "pick up the red cube"
state = robot.get_joint_positions()

action = agent.get_action(instruction, image, state)
print(f"预测动作: {action}")

# 执行动作
robot.execute_action(action)

构建VLA数据集

class VLADataset:
    def __init__(self, data_dir):
        self.data_dir = data_dir
        self.episodes = self._load_episodes()

    def _load_episodes(self):
        """加载演示数据"""
        episodes = []
        for episode_dir in Path(self.data_dir).iterdir():
            if not episode_dir.is_dir():
                continue

            episode = {
                "instruction": self._load_instruction(episode_dir),
                "frames": self._load_frames(episode_dir),
                "actions": self._load_actions(episode_dir)
            }
            episodes.append(episode)
        return episodes

    def _load_instruction(self, episode_dir):
        """加载语言指令"""
        with open(episode_dir / "instruction.txt") as f:
            return f.read().strip()

    def _load_frames(self, episode_dir):
        """加载图像帧"""
        frames = []
        for img_path in sorted((episode_dir / "images").glob("*.png")):
            frames.append(Image.open(img_path))
        return frames

    def _load_actions(self, episode_dir):
        """加载动作序列"""
        actions = np.load(episode_dir / "actions.npy")
        return actions

    def __len__(self):
        return len(self.episodes)

    def __getitem__(self, idx):
        episode = self.episodes[idx]
        # 随机采样一个时间步
        t = np.random.randint(len(episode["frames"]))
        return {
            "instruction": episode["instruction"],
            "image": episode["frames"][t],
            "action": episode["actions"][t]
        }

微调VLA模型

from transformers import TrainingArguments
from trl import SFTTrainer

def finetune_vla(base_model, dataset, output_dir):
    """微调VLA模型"""
    model = AutoModelForVision2Seq.from_pretrained(
        base_model,
        torch_dtype=torch.float16,
        trust_remote_code=True
    )

    training_args = TrainingArguments(
        output_dir=output_dir,
        num_train_epochs=10,
        per_device_train_batch_size=4,
        learning_rate=1e-5,
        warmup_steps=100,
        logging_steps=10,
        save_strategy="epoch"
    )

    trainer = SFTTrainer(
        model=model,
        args=training_args,
        train_dataset=dataset,
        max_seq_length=512
    )

    trainer.train()

    # 保存模型
    trainer.save_model(f"{output_dir}/final")

# 使用
dataset = VLADataset("data/demonstrations")
finetune_vla("openvla/openvla-7b", dataset, "checkpoints/my_vla")

部署到真实机器人

class DeployedVLA:
    def __init__(self, model_path):
        self.agent = OpenVLAAgent(model_path)
        self.robot = RobotInterface()

    def execute_instruction(self, instruction):
        """执行自然语言指令"""
        max_steps = 50

        for step in range(max_steps):
            # 获取当前观察
            image = self.robot.get_camera_image()
            state = self.robot.get_joint_positions()

            # 生成动作
            action = self.agent.get_action(instruction, image, state)

            # 检查是否完成
            if self._is_task_complete(action):
                print("任务完成")
                return True

            # 执行动作
            self.robot.execute_action(action)

        print("达到最大步数")
        return False

    def _is_task_complete(self, action):
        """检查任务是否完成"""
        # 动作幅度很小,说明已经到位
        return np.linalg.norm(action) < 0.01

# 使用
vla = DeployedVLA("checkpoints/my_vla")
vla.execute_instruction("pick up the red cube and place it on the table")

FAQ

VLA模型需要多少数据才能训练?

开源模型(如OpenVLA)已经预训练,微调只需要几十到几百个演示。

VLA模型的推理速度如何?

7B模型在RTX 4090上约100ms/次。可以使用量化加速。

VLA能处理复杂任务吗?

当前VLA擅长简单操作任务。复杂任务需要结合规划器(如LLM分解子任务)。