π₀.₅ (Pi05) policy

π₀.₅ 是来自 Physical Intelligence 的具有开放世界泛化能力的视觉-语言-action(VLA)模型。LeRobot 的实现改编自其开源 OpenPI 仓库。

模型概述

π₀.₅ 代表了 π₀ 的一次重大演进,由 Physical Intelligence 开发,旨在解决机器人领域的一大挑战:开放世界泛化。虽然机器人在受控环境中可以完成令人印象深刻的任务,但 π₀.₅ 的设计目标是泛化到训练中从未见过的全新环境和情境。

泛化挑战

正如 Physical Intelligence 所解释的,根本挑战不在于完成需要敏捷或灵巧的任务,而在于泛化——即在新环境中用新物体正确执行任务的能力。设想一台正在给不同家庭打扫卫生的机器人:每个家庭的物体各不相同,摆放位置也各异。泛化必须在多个层面上实现:

  • 物理层面:理解如何拿起勺子(抓握柄)或盘子(抓边缘),即使是在杂乱的环境中面对从未见过的物体
  • 语义层面:理解任务语义,比如衣服和鞋子应该放在哪里(洗衣篮里,而不是床上),以及用哪些工具清理洒出的液体
  • 环境层面:适应家庭、杂货店、办公室和医院等“杂乱”的真实世界环境

异构数据协同训练

π₀.₅ 的突破性创新在于异构数据源协同训练。该模型从以下数据中学习:

  1. 多模态网络数据:图像描述、视觉问答、目标检测
  2. 口头指令:人类逐步指导机器人完成复杂任务
  3. 子任务命令:高层语义行为标签(例如,对未整理的床铺执行“拿起枕头”)
  4. 跨具身机器人数据:来自具有不同能力的多种机器人平台的数据
  5. 多环境数据:部署在许多不同家庭中的静态机器人
  6. 移动操作数据:约 400 小时的移动机器人 demonstration

这种多样化的训练数据组合构建了一个“课程”,使模型能够在物理、视觉和语义层面上同时实现泛化。

安装要求

  1. 按照我们的 安装指南 安装 LeRobot。

  2. 运行以下命令安装 Pi0.5 的依赖:

    pip install -e ".[pi]"

    如果你通过 PyPI 安装了 LeRobot:

    pip install 'lerobot[pi]'

使用

要在你的 LeRobot 配置中使用 π₀.₅,请将 policy 类型指定为:

policy.type=pi05

训练

LIBERO 快速开始

lerobot/libero 上 fine-tune LIBERO 基础模型——该 dataset 是下方结果对应的 demonstration 数据的约 1.9 GB 视频编码副本。

它携带了 π₀.₅ 需要读取的键,这些也是 LIBERO 环境在评估时需要生成的键:

特征dataset 中的形状π₀.₅ 的使用方式
observation.images.image256×256×3, agentview调整为 224×224
observation.images.image2256×256×3, wrist调整为 224×224
observation.state8离散化为 256 个区间并写入提示中
action7内部填充至 32;损失只使用前 7 个维度

这里不需要 --rename_map — 键已经匹配;如果你的键不同,请参阅重命名映射与空相机

π₀.₅ 使用受门控的 [google/paligemma-3b-pt-224](https://huggingface.co/google/paligemma-3b-pt-224) tokenizer — 请在 Hub 上接受其许可证,并在训练前使用 `hf auth login` 登录。

面向单个 80 GB GPU 设计:

lerobot-train \
    --dataset.repo_id=lerobot/libero \
    --policy.type=pi05 \
    --policy.pretrained_path=lerobot/pi05_libero_base \
    --policy.normalization_mapping='{"ACTION": "MEAN_STD", "STATE": "MEAN_STD", "VISUAL": "IDENTITY"}' \
    --policy.n_action_steps=10 \
    --policy.empty_cameras=1 \
    --policy.freeze_vision_encoder=false \
    --policy.train_expert_only=false \
    --policy.gradient_checkpointing=true \
    --policy.dtype=bfloat16 \
    --policy.device=cuda \
    --policy.push_to_hub=false \
    --output_dir=./outputs/pi05_libero \
    --job_name=pi05_libero \
    --batch_size=64 \
    --num_workers=8 \
    --steps=30000 \
    --save_freq=5000 \
    --seed=1000

均值/标准差归一化,而非 π₀.₅ 的分位数默认设置 — 与 pi05_libero_finetuned_v044 一致,下方结果正是基于该 checkpoint 测得的。

--policy.n_action_steps=10--policy.empty_cameras=1 是显式设置的,因为 --policy.pretrained_path 只加载权重 — lerobot/pi05_libero_base 同时存储两者,否则它们会回退到 500(参见加载 checkpoint)。

然后使用 lerobot-eval 评估一个 checkpoint,并与参考成功率进行比较 — 参见 LIBERO

分位数统计

π₀.₅ 使用分位数对 STATEACTION 进行归一化,因此你的 dataset 的 meta/stats.json 需要包含 q01q99。较旧的 dataset 只有 min/max/mean/std,会在第一个批次上失败:

ValueError: QUANTILES normalization mode requires q01 and q99 stats

重新计算它们:

lerobot-edit-dataset \
    --repo_id your_dataset \
    --new_repo_id your_dataset \
    --operation.type recompute_stats \
    --operation.overwrite true

结果会写入 $HF_LEROBOT_HOME/your_dataset,而不是 --dataset.repo_id 读取的缓存 — 因此请使用 --dataset.root=$HF_LEROBOT_HOME/your_dataset 训练,或在上面添加 --push_to_hub true

或者保持 dataset 不变,直接传入 --policy.normalization_mapping='{"ACTION": "MEAN_STD", "STATE": "MEAN_STD", "VISUAL": "IDENTITY"}'

录制、恢复和合并操作会聚合各 episode 摘要中的分位数,因此 meta/stats.json 最终保存的是保守的包络(q <= 50minq > 50max),而不是整个 dataset 的分位数。要估算后者,请使用滑动直方图扫描每一个 episode:

python src/lerobot/scripts/augment_dataset_quantile_stats.py \
    --repo-id=your_dataset \
    --overwrite \
    --skip-images

当只需要重新计算 STATE/ACTION 时,--skip-images 会保留现有的图像统计信息并避免下载或解码视频数据,--root 则读取本地 dataset 而不是 Hub。这些值是直方图估计值,存在离散化和重新分箱误差,因此可能与保守值不同——这会改变 π₀.₅ 的归一化目标,进而改变其损失规模。已经保存在现有 checkpoint 中的统计信息不受影响。

训练命令示例

同样的 fine-tune,但冻结 VLM:内存占用更低,但会牺牲一些成功率。将 --dataset.repo_id 换成你自己的 dataset。

lerobot-train \
    --dataset.repo_id=lerobot/libero \
    --policy.type=pi05 \
    --policy.pretrained_path=lerobot/pi05_libero_base \
    --policy.normalization_mapping='{"ACTION": "MEAN_STD", "STATE": "MEAN_STD", "VISUAL": "IDENTITY"}' \
    --policy.n_action_steps=10 \
    --policy.empty_cameras=1 \
    --policy.freeze_vision_encoder=true \
    --policy.train_expert_only=true \
    --policy.gradient_checkpointing=true \
    --policy.dtype=bfloat16 \
    --policy.device=cuda \
    --policy.push_to_hub=false \
    --output_dir=./outputs/pi05_libero_expert \
    --job_name=pi05_libero_expert \
    --batch_size=64 \
    --num_workers=8 \
    --steps=30000 \
    --save_freq=5000 \
    --seed=1000

关键训练参数

  • --policy.compile_model=true:启用模型编译以加快训练
  • --policy.gradient_checkpointing=true:在训练期间显著减少内存占用
  • --policy.dtype=bfloat16:使用混合精度训练以提高效率
  • --batch_size=64:训练的批次大小,请根据你的 GPU 内存进行调整
  • --policy.pretrained_path=lerobot/pi05_base:你想要 fine-tune 的基础 π₀.₅ 模型,可选值有:

可选的训练时 RTC

Pi05 可以选择性地学习 action 前缀条件化,以实现高效的 Real-Time Chunking,该方法遵循 Training-Time Action Conditioning for Efficient Real-Time Chunking 一文。 将 policy.rtc_training_max_delay 设置为预期的最大 inference latency(以 控制器步数计):

lerobot-train \
    --dataset.repo_id=${HF_USER}/my_dataset \
    --policy.type=pi05 \
    --policy.pretrained_path=lerobot/pi05_base \
    --policy.rtc_training_max_delay=10 \
    --policy.dtype=bfloat16 \
    --policy.device=cuda \
    --batch_size=8 \
    --steps=30000 \
    --output_dir=outputs/pi05_rtc \
    --job_name=pi05_rtc

默认值为 0,这会禁用训练时 RTC,并保持标准 Pi05 目标不变。配置的延迟必须小于 chunk_size。训练期间,Pi05 会为每个示例独立采样一个干净的前缀长度, 并只计算剩余后缀部分的流损失。

显式使用经过 RTC 训练的 checkpoint:

lerobot-rollout \
    --strategy.type=base \
    --policy.path=outputs/pi05_rtc/checkpoints/last/pretrained_model \
    --inference.type=rtc \
    --inference.rtc.mode=trained \
    --inference.rtc.execution_horizon=10 \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --task="pick up the cube" \
    --fps=50 \
    --device=cuda

inference.rtc.execution_horizon 设置在 checkpoint 的 rtc_training_max_delaychunk_size - rtc_training_max_delay 之间,并将 inference.queue_threshold 设置为至少 rtc_training_max_delay。一次性的 延迟尖峰超出训练范围时会丢弃该块并重试;rollout 只有在延迟持续超出范围时才会以显式错误停止。默认的 --inference.rtc.mode=guided 仍可用于普通 checkpoint。

加载 checkpoint

这两种形式不可互换:

--policy.path--policy.pretrained_path
加载内容权重以及checkpoint 的 config.json仅权重
特征名称来自 checkpoint来自你的 dataset,经 --rename_map 之后
存储的设置,例如 n_action_steps继承重置为默认值
--policy.type必须省略必需
--rename_map当你的相机键不同时需要支持规范化 dataset 键

当原始 dataset 的特征名称与 policy 特征名称不一致时,请结合 --rename_map 使用以上任意一种加载形式。例如,RoboMME fine-tune 命令在将原始 imagewrist_imagestateactions 列映射过来的同时,用 --policy.pretrained_path 初始化权重。

训练参数说明

参数默认值描述
freeze_vision_encoderfalse不冻结视觉编码器
train_expert_onlyfalse不冻结 VLM,训练所有参数

💡 提示:设置 train_expert_only=true 会冻结 VLM,只训练 action expert 和投影层,从而以更低的内存占用进行 fine-tune。

短视界 observation 记忆(MEM)

Pi05 可以选择性地使用基于 MEM 的短视界视觉和本体感觉上下文。两条路径默认都是关闭的,因此 现有 checkpoint 和训练命令会保留单帧 Pi05 行为。

这里只实现了 MEM 的短视界记忆——即第 III-C 部分的视频编码器和第 III-D 部分的本体感觉投影。MEM 的长视界 语言 记忆(第 III-B 部分)没有实现,该机制由高层 policy 预测下一步的 子任务,并生成到目前为止所发生事件的压缩自然语言摘要。在论文的消融实验中,单靠视频记忆只能恢复完整 MEM 在长视界任务上的一部分任务进度。

在从未预训练过记忆的 checkpoint 上进行 fine-tune 时引入记忆,也对应论文中较弱的 MEM-Posttrain-Only 消融。MEM 的核心成果来自在机器人和非机器人视频的多样混合数据上预训练视频编码器,而目前没有任何公开的 Pi05 checkpoint 提供这种数据。

fine-tune 时通过以下方式启用:

lerobot-train \
    --dataset.repo_id=your_dataset \
    --policy.type=pi05 \
    --policy.pretrained_path=lerobot/pi05_base \
    --policy.use_visual_memory=true \
    --policy.memory_frames=6 \
    --policy.memory_stride=30 \
    --policy.memory_temporal_attention_every=4 \
    ...

memory_frames 包含当前 observation。memory_stridedataset 帧为单位,而非秒,因此请根据 dataset 的 fps 进行缩放:MEM 在相隔一秒的六个 observation 上进行预训练,对于 30 fps dataset(默认)来说为 memory_stride=30, 但对于像 lerobot/robomme 这样的 10 fps dataset 则为 memory_stride=10

每第四个 SigLIP 层都会用 MEM 的组合时空注意力替换其注意力:匹配的补丁 token 之间的因果时间注意力,与标准空间注意力组合,并复用预训练的投影,因此视觉塔不会增加任何可学习参数。最后一个此类层运行完成后,过去帧的 token 会被丢弃,从而保持下游语言/action 前缀的长度不变。

episode 开头不可用的历史会被填充并掩码。在 inference 时,Pi05 在内部维护相同的步幅图像历史,并在 policy 每次被重置时将其清空。

历史本体感觉是一个独立选项:

--policy.use_proprioceptive_memory=true

启用后,每个保留的帧会添加一个投影的连续 state token,同时离散化的 state 会从文本提示中移除,使 state 恰好被表示一次(MEM 第 III-D 部分)。这会改变提示格式,因此 请从训练一开始就启用它,而不是中途启用。视觉和 本体感觉记忆使用相同的 memory_framesmemory_stride 设置, 但每条路径都可以独立启用。请使用所选记忆选项进行训练或 fine-tune;只在 inference 时启用它们,不会让单帧 checkpoint 获得学习到的记忆行为。

在 PEFT 下,proprio_history_proj 会通过 modules_to_save 进行完整训练并保存,因为它没有可适配的预训练权重。

inference 时,MEM 保持一个批处理历史队列,并假定批次中的每一行 共享 episode 边界。lerobot-eval 通过在每次批量 rollout 之前及重置向量环境之前调用 policy.reset() 来满足这一点。不支持在异步向量批次中独立自动重置行;请启动一次新的 rollout,让完整的 policy 和 action 队列 一起被重置。

relative action

默认情况下,π₀.₅ 预测 absolute action。你可以启用relative action,让模型预测相对于当前机器人 state 的偏移量。这在某些配置下可以提高训练稳定性。

要使用 relative action,请先通过 CLI 在相对空间中重新计算 dataset 统计信息:

lerobot-edit-dataset \
    --repo_id your_dataset \
    --operation.type recompute_stats \
    --operation.relative_action true \
    --operation.chunk_size 50 \
    --operation.relative_exclude_joints "['gripper']" \
    --push_to_hub true

或者在 Python 中实现等效操作:

from lerobot.datasets import LeRobotDataset, recompute_stats

dataset = LeRobotDataset("your_dataset")
recompute_stats(dataset, relative_action=True, chunk_size=50, relative_exclude_joints=["gripper"])
dataset.push_to_hub()

chunk_size 应与 policy 的 chunk_size 匹配(π₀.₅ 默认为 50)。relative_exclude_joints 列出应保持在绝对空间的关节名称(例如 gripper 命令)。使用 --push_to_hub true 将更新后的统计信息上传到 Hub。

然后启用 relative action 进行训练:

lerobot-train \
    --dataset.repo_id=your_dataset \
    --policy.type=pi05 \
    --policy.use_relative_actions=true \
    --policy.relative_exclude_joints='["gripper"]' \
    ...

性能结果

Libero benchmark 结果

π₀.₅ 在 Libero benchmark 套件上展现了强劲的性能。为了比较和测试其在 LeRobot 中的实现,我们在 Libero dataset 上额外 fine-tune libero 基础模型 6k 步,并将结果与 OpenPI 参考结果进行了比较。

benchmarkLeRobot 实现OpenPI 参考
Libero Spatial97.0%98.8%
Libero Object99.0%98.2%
Libero Goal98.0%98.0%
Libero 1096.0%92.4%
平均97.5%96.85%

这些结果证明了 π₀.₅ 在各种机器人操作任务中的强大泛化能力。要复现这些结果,你可以按照 Libero 一节中的说明操作。

许可证

该模型遵循 Apache 2.0 许可证,与原始 OpenPI 仓库 保持一致。

在 GitHub 上更新