视觉-语言-动作模型(VLA)入门
理解VLA模型的核心思想,学习如何使用大语言模型驱动机器人执行自然语言指令。
VLA 大语言模型 机器人 自然语言控制 Embodied AI
视觉-语言-动作模型(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分解子任务)。