# π₀.₅ (Pi05) Policy

π₀.₅ is a **Vision-Language-Action model with open-world generalization**, from Physical Intelligence. The LeRobot implementation is adapted from their open source [OpenPI](https://github.com/Physical-Intelligence/openpi) repository.

## Model Overview

π₀.₅ represents a significant evolution from π₀, developed by [Physical Intelligence](https://www.physicalintelligence.company/blog/pi05) to address a big challenge in robotics: **open-world generalization**. While robots can perform impressive tasks in controlled environments, π₀.₅ is designed to generalize to entirely new environments and situations that were never seen during training.

### The Generalization Challenge

As Physical Intelligence explains, the fundamental challenge isn't performing tasks of agility or dexterity, but generalization, the ability to correctly perform tasks in new settings with new objects. Consider a robot cleaning different homes: each home has different objects in different places. Generalization must occur at multiple levels:

- **Physical Level**: Understanding how to pick up a spoon (by the handle) or plate (by the edge), even with unseen objects in cluttered environments
- **Semantic Level**: Understanding task semantics, where to put clothes and shoes (laundry hamper, not on the bed), and what tools are appropriate for cleaning spills
- **Environmental Level**: Adapting to "messy" real-world environments like homes, grocery stores, offices, and hospitals

### Co-Training on Heterogeneous Data

The breakthrough innovation in π₀.₅ is **co-training on heterogeneous data sources**. The model learns from:

1. **Multimodal Web Data**: Image captioning, visual question answering, object detection
2. **Verbal Instructions**: Humans coaching robots through complex tasks step-by-step
3. **Subtask Commands**: High-level semantic behavior labels (e.g., "pick up the pillow" for an unmade bed)
4. **Cross-Embodiment Robot Data**: Data from various robot platforms with different capabilities
5. **Multi-Environment Data**: Static robots deployed across many different homes
6. **Mobile Manipulation Data**: ~400 hours of mobile robot demonstrations

This diverse training mixture creates a "curriculum" that enables generalization across physical, visual, and semantic levels simultaneously.

## Installation Requirements

1. Install LeRobot by following our [Installation Guide](./installation).
2. Install Pi0.5 dependencies by running:

   ```bash
   pip install -e ".[pi]"
   ```

   If you installed LeRobot from PyPI:

   ```bash
   pip install 'lerobot[pi]'
   ```

## Usage

To use π₀.₅ in your LeRobot configuration, specify the policy type as:

```python
policy.type=pi05
```

## Training

### Quickstart on LIBERO

Finetune the LIBERO base model on [lerobot/libero](https://huggingface.co/datasets/lerobot/libero), a ~1.9 GB video-encoded copy of the demonstrations behind the [results below](#libero-benchmark-results).

It carries the keys π₀.₅ reads, which are also the ones the LIBERO environment produces at evaluation time:

| Feature                     | Shape in the dataset | How π₀.₅ consumes it                                    |
| --------------------------- | -------------------- | ------------------------------------------------------- |
| `observation.images.image`  | 256×256×3, agentview | resized to 224×224                                      |
| `observation.images.image2` | 256×256×3, wrist     | resized to 224×224                                      |
| `observation.state`         | 8                    | discretized into 256 bins and written into the prompt   |
| `action`                    | 7                    | padded to 32 internally; the loss uses the first 7 dims |

**No `--rename_map` is needed here** — the keys already match; see [Rename Map and Empty Cameras](./rename_map) if yours differ.

  π₀.₅ uses the gated
  [google/paligemma-3b-pt-224](https://huggingface.co/google/paligemma-3b-pt-224)
  tokenizer — accept its license on the Hub and log in with `hf auth login`
  before training.

Sized for a single 80 GB GPU:

```bash
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
```

**Mean/std normalization, not π₀.₅'s [quantile default](#quantile-statistics)** — matching [pi05_libero_finetuned_v044](https://huggingface.co/lerobot/pi05_libero_finetuned_v044), the checkpoint the results below were measured on.

**`--policy.n_action_steps=10` and `--policy.empty_cameras=1` are explicit** because `--policy.pretrained_path` loads weights only — `lerobot/pi05_libero_base` stores both, and they would otherwise fall back to `50` and `0` (see [Loading a checkpoint](#loading-a-checkpoint)).

Then evaluate a checkpoint with `lerobot-eval` and compare against the reference success rates — see [LIBERO](./libero).

### Quantile statistics

π₀.₅ normalizes `STATE` and `ACTION` with quantiles, so your dataset's `meta/stats.json` needs `q01` and `q99`. Older datasets carry only `min`/`max`/`mean`/`std` and fail on the first batch:

```
ValueError: QUANTILES normalization mode requires q01 and q99 stats
```

Recompute them:

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

**The result lands in `$HF_LEROBOT_HOME/your_dataset`**, not the cache `--dataset.repo_id` reads — so train with `--dataset.root=$HF_LEROBOT_HOME/your_dataset`, or add `--push_to_hub true` above.

Or keep the dataset as-is and pass `--policy.normalization_mapping='{"ACTION": "MEAN_STD", "STATE": "MEAN_STD", "VISUAL": "IDENTITY"}'`.

Recording, resuming, and merging aggregate quantiles from per-episode summaries, so `meta/stats.json` ends up holding a conservative envelope (`min` for `q <= 50`, `max` for `q > 50`) rather than whole-dataset quantiles. To estimate the latter, scan every episode with a running histogram:

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

`--skip-images` keeps the existing image statistics and avoids video decoding when only `STATE`/`ACTION` need recomputing, and `--root` reads a local dataset instead of the Hub. These values are histogram estimates, subject to discretization and rebinning error, so they can differ from the conservative ones — which changes π₀.₅'s normalized targets and therefore its loss scale. Statistics already saved inside an existing checkpoint are not affected.

### Training Command Example

The same finetune with the VLM frozen: less memory, at some cost in success rate. Swap `--dataset.repo_id` for your own dataset.

```bash
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
```

### Key Training Parameters

- **`--policy.compile_model=true`**: Enables model compilation for faster training
- **`--policy.gradient_checkpointing=true`**: Reduces memory usage significantly during training
- **`--policy.dtype=bfloat16`**: Use mixed precision training for efficiency
- **`--batch_size=64`**: Batch size for training, adapt this based on your GPU memory
- **`--policy.pretrained_path=lerobot/pi05_base`**: The base π₀.₅ model you want to finetune, options are:
  - [lerobot/pi05_base](https://huggingface.co/lerobot/pi05_base)
  - [lerobot/pi05_libero_base](https://huggingface.co/lerobot/pi05_libero_base) (specifically trained on the Libero dataset)

### Optional training-time RTC

Pi05 can optionally learn action-prefix conditioning for efficient
[Real-Time Chunking](./rtc), following
[Training-Time Action Conditioning for Efficient Real-Time Chunking](https://arxiv.org/abs/2512.05964).
Set `policy.rtc_training_max_delay` to the largest expected inference delay in
controller steps:

```bash
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
```

The default is `0`, which disables training-time RTC and leaves the standard
Pi05 objective unchanged. The configured delay must be smaller than
`chunk_size`. During training, Pi05 samples a clean prefix length independently
for each example and computes flow loss only on the remaining postfix.

Use the resulting checkpoint with trained RTC explicitly:

```bash
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
```

Set `inference.rtc.execution_horizon` between the checkpoint's
`rtc_training_max_delay` and `chunk_size - rtc_training_max_delay`, and
`inference.queue_threshold` to at least `rtc_training_max_delay`. A one-off
latency spike past the trained range discards that chunk and retries; rollout
stops with an explicit error only if the delay stays out of range. The default
`--inference.rtc.mode=guided` remains available for ordinary checkpoints.

### Loading a checkpoint

The two forms are not interchangeable:

|                                        | `--policy.path`                                | `--policy.pretrained_path`                |
| -------------------------------------- | ---------------------------------------------- | ----------------------------------------- |
| Loads                                  | weights **and** the checkpoint's `config.json` | weights only                              |
| Feature names                          | from the checkpoint                            | from your dataset, after `--rename_map`   |
| Stored settings, e.g. `n_action_steps` | inherited                                      | reset to the defaults                     |
| `--policy.type`                        | must be omitted                                | required                                  |
| `--rename_map`                         | needed when your camera keys differ            | supported for canonicalizing dataset keys |

Use `--rename_map` with either loading form when raw dataset feature names differ from the policy
feature names. For example, the RoboMME fine-tuning command maps its raw `image`, `wrist_image`,
`state`, and `actions` columns while initializing weights with `--policy.pretrained_path`.

### Training Parameters Explained

| Parameter               | Default | Description                                 |
| ----------------------- | ------- | ------------------------------------------- |
| `freeze_vision_encoder` | `false` | Do not freeze the vision encoder            |
| `train_expert_only`     | `false` | Do not freeze the VLM, train all parameters |

**💡 Tip**: Setting `train_expert_only=true` freezes the VLM and trains only the action expert and projections, allowing finetuning with reduced memory usage.

## Short-Horizon Observation Memory (MEM)

Pi05 can optionally use short-horizon visual and proprioceptive context based on
[MEM](https://arxiv.org/abs/2603.03596). Both paths are disabled by default, so
existing checkpoints and training commands retain the single-frame Pi05 behavior.

This implements MEM's short-horizon memory only — the video encoder of section
III-C and the proprioceptive projection of section III-D. MEM's long-horizon
_language_ memory (section III-B), in which a high-level policy predicts the next
subtask and a compressed natural-language summary of what has happened so far, is
not implemented. In the paper's ablations, video memory alone recovers only part
of full MEM's task progress on long-horizon tasks.

Introducing memory when finetuning from a checkpoint that was pretrained without
it also corresponds to the paper's weaker `MEM-Posttrain-Only` ablation. MEM's
headline results come from pretraining the video encoder on a diverse mixture of
robot and non-robot video, which no public Pi05 checkpoint currently provides.

Enable it while finetuning with:

```bash
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` includes the current observation. `memory_stride` is measured in
**dataset frames**, not seconds, so scale it with your dataset's `fps`: MEM
pretrains on six observations one second apart, which is `memory_stride=30` for a
30 fps dataset (the default) but `memory_stride=10` for a 10 fps dataset such as
`lerobot/robomme`.

Every fourth SigLIP layer replaces its attention with MEM's composed space-time
attention: causal temporal attention between matching patch tokens, composed with
the standard spatial attention and reusing the pretrained projections, so no
learnable parameters are added to the vision tower. Past-frame tokens are dropped
once the last such layer has run, keeping the downstream language/action prefix
length unchanged.

Unavailable history at the beginning of an episode is padded and masked. At
inference, Pi05 maintains the same strided image history internally and clears
it whenever the policy is reset.

Historical proprioception is an independent option:

```bash
--policy.use_proprioceptive_memory=true
```

When enabled, one projected continuous state token is added for each retained
frame, and the discretized state is removed from the text prompt so the state is
represented exactly once (MEM section III-D). This changes the prompt format, so
enable it from the start of training rather than partway through. Visual and
proprioceptive memory use the same `memory_frames` and `memory_stride` settings,
but either path can be enabled independently. Train or finetune with the selected
memory options; enabling them only at inference does not give a single-frame
checkpoint learned memory behavior.

Under PEFT, `proprio_history_proj` is trained and saved in full via
`modules_to_save`, since it has no pretrained weights to adapt.

At inference, MEM keeps one batched history queue and assumes every batch row
shares episode boundaries. `lerobot-eval` satisfies this by calling
`policy.reset()` before each batched rollout and before resetting the vector
environment. Independently autoresetting rows in an asynchronous vector batch
is not supported; start a new rollout so the complete policy and action queues
are reset together.

## Relative Actions

By default, π₀.₅ predicts absolute actions. You can enable **relative actions** so the model predicts offsets relative to the current robot state. This can improve training stability for certain setups.

To use relative actions, first recompute your dataset stats in relative space via the CLI:

```bash
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
```

Or equivalently in Python:

```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()
```

The `chunk_size` should match your policy's `chunk_size` (default 50 for π₀.₅). `relative_exclude_joints` lists joint names that should remain in absolute space (e.g. gripper commands). Use `--push_to_hub true` to upload the updated stats to the Hub.

Then train with relative actions enabled:

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

## Performance Results

### Libero Benchmark Results

π₀.₅ has demonstrated strong performance on the Libero benchmark suite. To compare and test its LeRobot implementation, we finetuned the libero base model for an additional 6k steps on the Libero dataset and compared the results to the OpenPI reference results.

| Benchmark          | LeRobot Implementation | OpenPI Reference |
| ------------------ | ---------------------- | ---------------- |
| **Libero Spatial** | 97.0%                  | 98.8%            |
| **Libero Object**  | 99.0%                  | 98.2%            |
| **Libero Goal**    | 98.0%                  | 98.0%            |
| **Libero 10**      | 96.0%                  | 92.4%            |
| **Average**        | 97.5%                  | 96.85%           |

These results demonstrate π₀.₅'s strong generalization capabilities across diverse robotic manipulation tasks. To reproduce these results, you can follow the instructions in the [Libero](https://huggingface.co/docs/lerobot/libero) section.

## License

This model follows the **Apache 2.0 License**, consistent with the original [OpenPI repository](https://github.com/Physical-Intelligence/openpi).

