The AY-Robots training matrix with five policies as rows and four robot arms as columns, each cell a link to a specific fine-tuning guide
Pi0.5Flow MatchingVLA TrainingLeRobotFine-Tuning

How to Train Pi0.5 on Your Own Robot Data

AY-Robots ResearchAugust 23, 202616 min read

Fine-tune Pi0.5, the flow-matching VLA from Physical Intelligence, on your own robot data. Real commands for openpi and lerobot pi05, dataset format traps, VRAM and latency limits.

Pi0.5 is the model you reach for when a policy has to work in a room it has never seen. It is also the most awkward of the five trainable policies here to fine-tune by hand: the upstream repository is JAX first, the LeRobot port moved deployment out of lerobot-record in 0.6.0 and made the base install too small to train with, and a full fine-tune does not fit on a 4090. Below: what flow matching costs at inference, the two command routes, and the 485 ms number that decides whether the result is usable.

What you need to know

  • A flow-matching VLA: a 3B PaliGemma VLM plus a 300M action expert decoding continuous action chunks. Two routes to fine-tune it, openpi and LeRobot pi05, 0.65 points apart on the LIBERO average.
  • Full fine-tuning needs more than 70 GB of VRAM. openpi lists LoRA at 22.5 GB, on its JAX path only.
  • The dataset must be LeRobotDataset v3.0 with q01 and q99 quantiles in meta/stats.json, or batch one throws.
  • Check your lerobot version first: 0.6.0 made the base package lightweight and moved deployment out of lerobot-record.
  • Here it runs at 485 ms per action step, wants 50 episodes, and costs about 4 to 12 USD per run.

What Pi0.5 actually is

Physical Intelligence published pi0 in October 2024: a flow matching vision-language-action model on a pretrained VLM: PaliGemma at 3 billion parameters plus a 300M action expert initialised from scratch, 3.3 billion in total. The paper reports pre-training on over 10,000 hours of robot data across 7 robot configurations and 68 tasks. More in the pi0 article.

Pi0.5 (arXiv 2504.16054, 22 April 2025) keeps that skeleton and changes the training diet: web data, object detection, verbal instructions from humans coaching the robot, subtask labels, cross-embodiment data from robots in many homes. The result is a robot cleaning kitchens in houses that were not in the training set. The openpi README adds a detail the title does not: the released pi0.5 was trained with knowledge insulation (arXiv 2505.23705).

Propertypi0pi0.5
VLM backbone variantgemma_2b (PaliGemma)gemma_2b (PaliGemma)
Action expert variantgemma_300mgemma_300m
action_dim3232
action_horizon default5050
max_token_len48200
discrete_state_inputfalsetrue, state discretized into bins in the prompt
Base checkpointgs://openpi-assets/checkpoints/pi0_basegs://openpi-assets/checkpoints/pi05_base
What is public is not the whole paper

The openpi README is explicit: the repository supports only the flow matching head, for pi0.5 training and inference alike. The paper describes two stages and the code carries only the second: pre-training represents every action as discrete tokens through the FAST tokenizer, and at deployment the model infers a high-level subtask before each action chunk. Neither of those is in the repository. The lerobot/pi05_base card gives the same list and adds RL, although the pi0.5 paper describes no reinforcement learning stage at all. The LeRobot port inherits that scope, and so does every hosted trainer on it, including the one here. Licensing is split too: the pi05 doc page says Apache 2.0, the lerobot/pi05_base card is tagged license: gemma.

What flow matching changes about training and inference

A policy you can train on an SO-100 either regresses actions directly (ACT) or tokenizes them and decodes autoregressively (pi0-FAST). Flow matching does neither: it learns a vector field that transports noise to a clean action chunk, then integrates it. The pi0 paper uses 10 integration steps at step size 0.1; LeRobot's pi05 config carries the same num_inference_steps: int = 10.

  • Training: the loss regresses a noised action chunk. No tokenizer to tune, no discretisation of your joint angles.
  • Inference: one chunk costs ten forward passes through the action expert. That is structural, not a tuning problem.
  • Output: continuous actions of dimension 32, padded internally, loss taken on the dimensions your robot has. A 6-DoF arm plus gripper uses 7.
python
# openpi/src/openpi/models/pi0_config.py - the defaults every pi05 config inherits
@dataclasses.dataclass(frozen=True)
class Pi0Config(_model.BaseModelConfig):
    dtype: str = "bfloat16"
    paligemma_variant: _gemma.Variant = "gemma_2b"
    action_expert_variant: _gemma.Variant = "gemma_300m"

    action_dim: int = 32
    action_horizon: int = 50
    max_token_len: int = None      # 200 if pi05 else 48

    pi05: bool = False             # flips discrete_state_input to True
Read from src/openpi/models/pi0_config.py on the openpi main branch, 23 August 2026.

The PaliGemma backbone, and the gate in front of it

PaliGemma (arXiv 2407.07726) is a SigLIP-So400m vision encoder on a Gemma-2B language model, about 3B parameters. Pi0.5 inherits its tokenizer, and that tokenizer sits in a gated repository. This is the most common way a first run dies, before the first training step.

Accept the gated license before you rent a GPU

The LeRobot pi05 docs state it plainly: pi0.5 uses the gated google/paligemma-3b-pt-224 tokenizer, so accept its license on the Hub and run hf auth login first. Access is granted manually, not instantly. Skip it, start a paid cloud run, and you pay for a pod that cannot download a tokenizer.

Before you start: dataset format, episodes, hardware

Pi0.5 in LeRobot reads a LeRobot dataset in v3.0 layout, which landed with lerobot 0.4.0 in October 2025: many episodes per Parquet and MP4 file instead of one file per episode, with boundaries in meta/episodes/ rather than filenames. Record with the desktop client or follow record your first dataset and you have it.

ModeGPU memory requiredExample GPU
Inferencemore than 8 GBRTX 4090
Fine-tuning, LoRAmore than 22.5 GBRTX 4090
Fine-tuning, fullmore than 70 GBA100 80 GB or H100
The version trap that costs a day

Pi0.5 and SmolVLA want LeRobot v3.0. GR00T N1.7 and GR00T N1.5 want v2.0 or v2.1 and crash on a v3.0 dataset. One recording session cannot feed both without a conversion, and the error does not say "wrong dataset version". See dataset rejected: v3 required.

bash
# v2.1 -> v3.0, run against a dataset already on the Hub
python -m lerobot.scripts.convert_dataset_v21_to_v30 --repo-id=<HF_USER/DATASET_ID>

# aggregates episode-0000.parquet, episode-0001.parquet, ... -> file-0000.parquet
# aggregates episode-0000.mp4, episode-0001.mp4, ...         -> file-0000.mp4
# rewrites meta/episodes/* with per-episode lengths, tasks and offsets
From the LeRobotDataset v3.0 documentation.

The platform wants at least 50 episodes for Pi0.5, the same floor as GR00T and ACT. Pre-training does the heavy lifting, so 50 clean episodes beat 200 sloppy ones. New to this? Start with the data collection guide.

The AY-Robots policies page comparing the five trainable policies by parameters, GPU tier, latency and minimum episodes
Pi0.5 sits in the A100 and H100 tier at 485 ms per action step, with a 50 episode floor.

Route A: fine-tune with the openpi repository

The reference implementation: JAX first, tested on Ubuntu 22.04 only, driven by config objects rather than flags. A PyTorch path arrived in September 2025, validated on LIBERO. Three steps: convert your data, define a config, train and serve.

  1. 1
    Clone and install

    openpi uses uv. GIT_LFS_SKIP_SMUDGE is what lets LeRobot come in as a dependency without LFS blobs.

    bash
    git clone --recurse-submodules git@github.com:Physical-Intelligence/openpi.git
    cd openpi
    
    GIT_LFS_SKIP_SMUDGE=1 uv sync
    GIT_LFS_SKIP_SMUDGE=1 uv pip install -e .
  2. 2
    Convert your data to a LeRobot dataset

    Upstream ships converters for LIBERO and DROID and expects you to adapt one. The work is the key mapping.

    bash
    uv run examples/libero/convert_libero_data_to_lerobot.py --data_dir /path/to/your/data
  3. 3
    Add a training config

    Configs are TrainConfig entries in src/openpi/training/config.py. This one adapts pi05_droid_finetune, with the weight loader pointed at pi05_base.

    python
    TrainConfig(
        name="pi05_so100",
        model=pi0_config.Pi0Config(
            pi05=True,
            action_dim=32,        # pi05 is trained with 32-dim actions
            action_horizon=16,
        ),
        # Copy LeRobotLiberoDataConfig in the same file and rewrite the key
        # mapping for your camera names, then reference your copy here.
        data=LeRobotLiberoDataConfig(
            repo_id="your_hf_username/my_so100_dataset",
            base_config=DataConfig(prompt_from_task=True),
        ),
        weight_loader=weight_loaders.CheckpointWeightLoader(
            "gs://openpi-assets/checkpoints/pi05_base/params"
        ),
        batch_size=32,
        num_train_steps=20_000,
    )
  4. 4
    Compute norm stats

    Runs against the config name, not the dataset. Skipping it is the classic first failure here.

    bash
    uv run scripts/compute_norm_stats.py --config-name pi05_so100
  5. 5
    Train

    The variable lets JAX use 90 percent of GPU memory instead of the default 75. On an 80 GB card that decides whether the run survives.

    bash
    XLA_PYTHON_CLIENT_MEM_FRACTION=0.9 uv run scripts/train.py pi05_so100 \
        --exp-name=my_experiment \
        --overwrite
  6. 6
    Serve it

    The server listens on port 8000 and answers observation queries; your robot runtime is the client.

    bash
    uv run scripts/serve_policy.py policy:checkpoint \
        --policy.config=pi05_so100 \
        --policy.dir=checkpoints/pi05_so100/my_experiment/20000
The PyTorch path is not the JAX path

openpi's PyTorch implementation does not support pi0-FAST, mixed precision, FSDP, LoRA or EMA weights, so the LoRA that reaches 22.5 GB is JAX only, through the gemma_2b_lora and gemma_300m_lora variants. Worse: the setup copies patched files over your installed transformers 4.53.2, and the README warns that with uv's default hardlink mode this permanently modifies transformers in the uv cache and can leak into other projects. Undo it with uv cache clean transformers.

Route B: fine-tune with lerobot pi05

The LeRobot port wraps the same weights in the CLI you already use for ACT and SmolVLA. Everything below was checked against lerobot 0.6.1, the release since 3 August 2026, and the pi05 docs on 23 August 2026. Versions matter here: v3.0 datasets arrived with 0.4.0 in October 2025, the 0.5.0 blog of 9 March 2026 presents pi0-FAST and Real-Time Chunking, and 0.6.0 on 6 July 2026 made the base package lightweight and moved deployment to lerobot-rollout.

One thing did not move: lerobot-train and lerobot-record were already entry points in 0.3.2, August 2025. A tutorial that still calls python -m lerobot.scripts.train predates a year of changes and is worth distrusting on the rest too.

  1. 1
    Install with the right extras

    Since 0.6.0 the base install carries core ML dependencies only, and the pi extra adds no dataset or training code, so a fine-tune needs the training extra too. Older one-liners fail here at import time.

    bash
    # policy dependencies plus the training stack
    pip install 'lerobot[pi,training]'
    
    # from a source checkout
    pip install -e ".[pi,training]"
    
    # driving an SO-100 afterwards needs the robot extras too
    pip install 'lerobot[core_scripts,feetech]'
  2. 2
    Authenticate

    Accept the paligemma-3b-pt-224 license in a browser, then log in on the machine that trains.

    bash
    hf auth login
  3. 3
    Add quantile statistics

    Pi0.5 normalises STATE and ACTION with quantiles, so meta/stats.json needs q01 and q99. Older datasets carry only min, max, mean, std.

    bash
    lerobot-edit-dataset \
        --repo_id your_dataset \
        --new_repo_id your_dataset \
        --operation.type recompute_stats \
        --operation.overwrite true
  4. 4
    Fine-tune from lerobot/pi05_base

    Set batch size to what your card survives; the docs use 64 on LIBERO at 80 GB. Pass bfloat16 explicitly, the config default is float32.

    bash
    lerobot-train \
        --dataset.repo_id=${HF_USER}/my_so100_dataset \
        --policy.type=pi05 \
        --policy.pretrained_path=lerobot/pi05_base \
        --policy.gradient_checkpointing=true \
        --policy.dtype=bfloat16 \
        --policy.device=cuda \
        --policy.push_to_hub=false \
        --output_dir=./outputs/pi05_so100 \
        --job_name=pi05_so100 \
        --batch_size=4 \
        --num_workers=8 \
        --steps=30000 \
        --save_freq=5000 \
        --seed=1000
  5. 5
    Run it on the arm

    In 0.6.x this is lerobot-rollout: lerobot-record refuses a policy and rejects eval_ dataset names. Base drives the arm, sentry records the rollout.

    bash
    lerobot-rollout \
        --strategy.type=base \
        --policy.path=${HF_USER}/my_policy \
        --robot.type=so100_follower \
        --robot.port=/dev/ttyACM1 \
        --robot.cameras="{ up: {type: opencv, index_or_path: /dev/video10, width: 640, height: 480, fps: 30}}" \
        --task="Put lego brick into the transparent box" \
        --duration=60
    
    # same run, recorded into an eval_ dataset
    lerobot-rollout \
        --strategy.type=sentry \
        --policy.path=${HF_USER}/my_policy \
        --robot.type=so100_follower \
        --robot.port=/dev/ttyACM1 \
        --dataset.repo_id=${HF_USER}/eval_so100 \
        --dataset.single_task="Put lego brick into the transparent box" \
        --duration=600
FlagWhat it doesNote
--policy.type=pi05selects Pi0.5required with pretrained_path, omit with --policy.path
--policy.pretrained_pathloads weights onlyfeature names from your dataset, stored settings reset
--policy.pathweights plus the checkpoint config.jsonstored settings such as n_action_steps are inherited
--policy.n_action_stepssteps consumed per chunkfalls back to 50, never above chunk_size (default 50)
--policy.train_expert_onlyfreezes the VLM, trains the expertdefault false, less memory, some cost in success
--policy.gradient_checkpointingrecompute instead of store activationsdefault false, the first lever when a run will not fit
--peft.method_type=LORALoRA adapters instead of full weightsneeds the peft extra, documented example is SmolVLA
--policy.rtc_training_max_delayaction-prefix conditioning for RTCmain branch only, not in 0.6.1, must stay below chunk_size
--rename_mapmaps dataset columns onto policy featuresneeded when your camera keys differ
The error most first runs hit

Without quantiles you get ValueError: QUANTILES normalization mode requires q01 and q99 stats on batch one. Recompute the stats, but the result lands in $HF_LEROBOT_HOME/your_dataset, not the cache --dataset.repo_id reads, so train with --dataset.root pointing there or push to the Hub. Or skip quantiles with --policy.normalization_mapping='{"ACTION": "MEAN_STD", "STATE": "MEAN_STD", "VISUAL": "IDENTITY"}', as the LIBERO reference command does.

Three sets of defaults, and which ones reach the trainer

openpi's TrainConfig is the reference, LeRobot's PI05Config is what lerobot-train uses when you say nothing, and the form here sends a third set. The surprise is the learning rate: both upstream defaults peak at 2.5e-5, while openpi's own pi05_libero config and this platform raise it to 5e-5.

Settingopenpi TrainConfiglerobot pi05 and lerobot-trainAY-Robots sends
Batch size3281
Peak learning rate2.5e-5, cosine decayoptimizer_lr 2.5e-55e-5
Training steps30,000100,00030,000
Checkpoint interval1,000 steps20,000 stepsnot in the form
Seed421,000in the form
Action chunkaction_horizon 50chunk_size 50, n_action_steps 50not in the form
Weight dtypebfloat16float32 until you pass --policy.dtypenot in the form
Gradient accumulationno such knob, fsdp_devices insteadabsent from every release so far16, and it is dropped

Is the port faithful? The LeRobot team fine-tuned the LIBERO base model for a further 6k steps and compared with openpi's reference numbers on four suites: 97.5 percent average against 96.85, ahead on Libero 10, behind on Spatial. The route is a tooling choice, not a quality one.

Fine-tuning Pi0.5 on your own data
Advantages
  • More than 10,000 hours of robot pre-training behind it, so 50 episodes of your task can be enough.
  • Continuous actions: no tokenizer to tune, no discretisation floor on your joint angles.
  • The LeRobot port scores 97.5 percent on the LIBERO average against openpi's 96.85, so the friendlier CLI costs nothing measurable.
  • Parameter-efficient paths on both sides: openpi's JAX LoRA variants, LeRobot's PEFT integration.
Trade-offs
  • Full fine-tuning needs more than 70 GB. The 22.5 GB LoRA figure is openpi's JAX number; none is published for pi05 under PEFT.
  • 485 ms per action step, slowest of the five here: twice SmolVLA, twenty-four times ACT.
  • LeRobot v3.0 is required, so a dataset recorded for a GR00T run needs converting, and vice versa.
  • New options land on main first: training-time RTC and MEM memory are not in 0.6.1, so newest docs can mean installing from git.

The 485 ms reality, and where it stops being usable

This decides your project, so it comes before the cost table. The inference latency per action step measured here for Pi0.5 is 485 ms. Against the other four:

PolicyInference per action stepParamsGPU tierMinimum episodes
ACT20 ms~80 MRTX 4090 or any 24 GB card50
GR00T N1.7152 ms~3 BA100 80 GB or H100 80 GB50
GR00T N1.5165 ms~3 BA100 80 GB or H100 80 GB50
SmolVLA245 ms~450 MRTX 4090 or any 24 GB card30
Pi0.5485 ms~3 BA100 80 GB or H100 80 GB50
The AY-Robots head to head page for GR00T N1.7 against Pi0.5, with parameters, GPU tier, latency and dataset format
Same GPU tier, same episode floor, different dataset version, three-fold latency gap.
Inference has to sit next to the servos

The control loop across these five models runs 20 to 485 ms per action step. Public-internet round trips on top of 485 ms turn a working policy into a hesitant one. Remote inference is viable for slow pick-and-place, not for fast reactive motion. We would rather say that than sell a demo that only works on a LAN. If your arm pauses between chunks, start at policy freezes mid-motion.

Upstream has an answer, and half of it is already in the release you can install. Real-Time Chunking generates the next chunk while the arm is still executing the current one, then guides the overlapping steps of the new chunk to stay close to the part already executed, so the arm does not stall or jerk at the seam. The inference-time form shipped in the 0.4.2 changelog for Pi0, Pi0.5 and SmolVLA and is a rollout flag, not a retrain:

bash
lerobot-rollout \
    --strategy.type=base \
    --policy.path=${HF_USER}/my_policy \
    --inference.type=rtc \
    --inference.rtc.execution_horizon=10 \
    --inference.rtc.max_guidance_weight=10.0 \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM1 \
    --robot.cameras="{ up: {type: opencv, index_or_path: /dev/video10, width: 640, height: 480, fps: 30}}" \
    --task="Put lego brick into the transparent box" \
    --duration=60
execution_horizon is how many steps of the previous chunk the new one has to agree with; the RTC docs give 8 to 12 as the usual range. Default inference backend is --inference.type=sync.

It does not make the model faster. It hides the gap: the 485 ms is still spent, but off the critical path, so the queue does not run dry between chunks. The training-time variant from arXiv 2512.05964 goes further: the model learns to condition on a clean action prefix, so at inference the overlap is hard-inpainted with per-action flow timesteps instead of guided, and the guidance backward pass disappears. During training it samples a prefix length per example and takes the flow loss on the remaining postfix. That flag lives on main only, not in 0.6.1, and the delay you train for has to stay below chunk_size.

Two ways to get a Pi0.5 checkpoint

You rent or own an 80 GB card, install one of the stacks above, convert your dataset and babysit the run. You keep every knob: openpi's JAX LoRA, LeRobot's PEFT adapters, relative actions, custom key mappings. You also keep every failure.

  • Hardware: A100 80 GB or H100, or a 24 GB card on openpi's JAX LoRA path.
  • Setup: an afternoon for the first run, mostly key mapping and the gated tokenizer.
  • Not on the hosted form: PEFT adapters, relative actions, training-time RTC, MEM memory.
bash
lerobot-train \
    --dataset.repo_id=${HF_USER}/my_so100_dataset \
    --policy.type=pi05 \
    --policy.pretrained_path=lerobot/pi05_base \
    --policy.rtc_training_max_delay=10 \
    --policy.gradient_checkpointing=true \
    --policy.dtype=bfloat16 \
    --batch_size=4 --steps=30000 --seed=1000
The command no hosted form runs, and pip cannot install: training-time RTC is a main branch flag.

When it does not work

SymptomMost likely cause
Run rejected before it startsDataset v2.1 but Pi0.5 wants v3.0, or reverse for GR00T
ImportError, datasets package missinglerobot 0.6.x without the training extra
ValueError about q01 and q99 on batch oneNo quantiles in meta/stats.json; recompute or use MEAN_STD
CUDA out of memory at step 0Full fine-tune below 70 GB; try checkpointing or train_expert_only
401 or gated repo errorPaliGemma tokenizer license not accepted
Loss drops, robot does nothingNormalisation mismatch, or the policy copies the state
Arm pauses between chunksPer-step latency plus round trip; the chunk queue runs dry
Works in one setup, fails in anotherToo few episodes, or all in one configuration

What one run costs

Pi0.5 sits in the expensive tier because it needs an 80 GB card, and spot prices move, so the figure is a range rather than a price. For many SO-100 tasks, train SmolVLA or ACT on the 24 GB tier first, confirm the task is learnable at all, then spend A100 hours on it. Train your first policy walks that cheaper loop, and pricing carries the current tiers.

TierPoliciesTypical runPrice per hourCost per run
A100 80 GB or H100Pi0.5, GR00T N1.7, GR00T N1.53 to 6 hours1.20 to 2.00 USDabout 4 to 12 USD
RTX 4090 or any 24 GB cardSmolVLA, ACT2 to 5 hours0.30 to 0.60 USDabout 1 to 3 USD
The AY-Robots cost table showing which GPU each policy needs, the typical run time and price, and how many episodes are needed before a policy is useful
The same two tiers on the product page: Pi0.5 rents the 80 GB card, SmolVLA and ACT fit on a 4090.

Before committing an evening: GR00T N1.7 against Pi0.5 and Pi0.5 against SmolVLA lay the same numbers out head to head. The Arena holds 85 VLA models with 332 benchmark results, each linked to its source, and background on the family is in our VLA overview.

Train Pi0.5 without building the stack

Pick the dataset and the hyperparameters in a form. The backend rents an 80 GB card on the spot market, runs the trainer and writes the checkpoints to object storage. Guides for SO-100, SO-101, Koch v1.1 and LeKiwi.

Open the training guides
Can I fine-tune Pi0.5 on an RTX 4090?

Not a full fine-tune: openpi lists more than 70 GB for that, so an A100 80 GB or an H100. Its 22.5 GB LoRA figure belongs to the JAX path; the PyTorch implementation has no LoRA. LeRobot's PEFT integration exists, but its documented example is SmolVLA and no VRAM figure is published for pi05.

How many episodes do I need?

Fifty, the same floor as GR00T and ACT; only SmolVLA goes lower, at 30. The base checkpoint carries more than 10,000 hours of pre-training, so the fine-tune adapts rather than learns from nothing: 50 clean, varied episodes beat two hundred from one configuration.

What is the difference between --policy.path and --policy.pretrained_path?

--policy.path loads weights and the checkpoint's config.json, so stored settings such as n_action_steps are inherited and --policy.type must be omitted. --policy.pretrained_path loads weights only: feature names come from your dataset, stored settings reset, --policy.type is required. That is why the LIBERO command passes n_action_steps=10 and empty_cameras=1 explicitly; otherwise they fall back to 50 and 0.

Does the open version give me the full Pi0.5 from the paper?

No. Both support the flow matching action head only. The discrete-token pre-training stage with the FAST action tokenizer and the high-level subtask prediction were not released, and the lerobot/pi05_base card adds RL to that list even though the pi0.5 paper describes no reinforcement learning stage. You train a low-level policy conditioned on a language string you supply, not a model that decomposes a long task itself.

Which versions is this guide written against?

openpi on main and lerobot 0.6.1, the release since 3 August 2026, both read on 23 August 2026. v3.0 datasets arrived with 0.4.0 in October 2025. 0.6.0 made the base package lightweight, so lerobot[pi] alone no longer installs a training stack, and moved deployment to lerobot-rollout. Training-time RTC is on main only; the hosted trainer here runs 0.5.1.

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started