Training Pi0.5 on an SO-100

Pi0.5 is the most precise policy this platform trains and the slowest to run on hardware. This guide covers the whole loop on an SO-100: what your dataset needs, what the defaults mean, what the GPU hours cost, and how to tell a latency problem from a data problem once the arm is moving.

25 min · intermediate · Last updated 2026-08-09

Trainer key
pi0
GPU tier
A100 80 GB or H100 80 GB
Inference
about 485 ms per action step
Batch size
1, gradient accumulation 16
Learning rate
5e-5
Steps
30,000
Dataset format
LeRobot v2.1
Typical run
3 to 6 hours, about 4 to 12 USD
Servos
Feetech STS3215 bus servos
Servo voltage
7.4 V
Short answer

Upload a LeRobot v2.1 dataset of at least 50 SO-100 episodes, open Training, pick Pi0.5, and start the run with the defaults: batch size 1, learning rate 5e-5, 30000 steps, gradient accumulation 16 on an A100 80 GB. The run takes 3 to 6 hours and costs about 4 to 12 USD, after which the policy runs on the arm at roughly 485 ms per action step.

What you need before you start

  • A calibrated SO-100 with all six STS3215 servos responding, as covered in /learn/so-100-getting-started
  • The desktop client installed and paired, so there is no Python environment to build
  • At least 50 kept episodes of one task, recorded as a LeRobot v2.1 dataset and uploaded
  • Two cameras that have not moved since the first episode was recorded
  • A billing method on the account, because the run rents an 80 GB card by the hour
  • A task that rewards precision rather than speed, which is the whole reason to pick this model
The SO-100 runs on 7.4 V

Feetech STS3215 servos are 7.4 V units. A 12 V supply, which is correct for other arms and for the LeKiwi base, destroys them in seconds and the damage is not recoverable. Read the label on the supply, not the label on the box it came in.

Why Pi0.5 earns its 485 ms on this arm

Pi0.5 is a flow-matching vision-language-action model on a PaliGemma backbone, around three billion parameters. Flow matching means the action head learns a continuous path from noise to an action chunk instead of choosing among discretized bins. The practical consequence on an SO-100 is that the commanded trajectory is smooth by construction, and small corrections stay small instead of snapping to the nearest quantized value.

That matters most when the gripper is already touching something. Seating a lid, sliding a peg into a hole, stacking a second block on a first, closing a latch: these are tasks where the correct next action differs from the current one by a fraction of a degree, and where a policy that jumps produces a nudge that ruins the alignment. The SO-100 has no force sensing and a noticeable amount of backlash, so it cannot recover from a nudge the way a stiffer arm can. Continuity in the output is the cheapest substitute for compliance in the hardware.

It does not pay everywhere. At about 485 ms per action step the arm issues just over two commands per second. On a task that is mostly free-space transit, that time is spent doing nothing useful and the model is beaten on both cost and appearance by something smaller. Pick Pi0.5 when the hard part of your task happens in contact.

Pi0.5, pi0 and pi05: three names, one model

The naming trips people up on the first API call. The model id the training backend takes is the string pi0. The policy type written into the trained checkpoint is pi05. The dropdown in the UI says Pi0.5, and the vendor writes it with a Greek letter. Search traffic arrives here as "pi zero", "pi 0.5" and "π0", all of which mean the same model. If you pass pi05 as the model id it will not resolve, so use pi0 in anything programmatic and expect pi05 back in the artifact metadata.

json
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "start_training",
    "arguments": {
      "dataset_id": "ds_7c41a9",
      "policy_name": "so100-lid-seat-pi05-seed-42",
      "model_id": "pi0",
      "max_steps": 30000,
      "confirm_spend": true
    }
  }
}
Starting the same run over MCP at https://www.ay-robots.com/api/mcp, protocol version 2025-06-18, with the API key as a Bearer token. The model id is pi0; the checkpoint you get back reports policy type pi05.

What the dataset has to look like

The trainer reads LeRobot v2.1, which is what the client writes by default. Fifty episodes is the point where this model starts producing something you can evaluate seriously; below that you are measuring noise. Beyond the usual rules for clean demonstrations, two things matter specifically for a flow-matching policy.

  1. Demonstration smoothness transfers directly. The model reproduces the shape of the trajectories you recorded, so a demonstration with a hesitation in it teaches a hesitation.
  2. One task string, phrased identically across every episode. The language side of the backbone attends to it, and two phrasings of the same task split your data in half.
  3. Vary object placement, not your approach strategy. Two incompatible strategies average into a path that touches neither.
  4. Keep the grasp moment visible. If the wrist camera loses sight of the contact, the part of the task Pi0.5 is good at is the part it cannot see.
Teleoperate at a speed the policy can reproduce

A demonstration recorded as fast as your hand can move it is a target the policy will chase at just over two action steps per second and miss. Deliberate, even motion during recording is not only better data, it is closer to what the deployed policy is physically able to output.

Start the run

  1. 1
    Open Training and choose the dataset

    The dataset picker lists what has finished processing. Open the preview once and confirm the episode count and both camera streams before you spend anything: a dataset that uploaded with one camera missing trains perfectly well and gives you a worse policy for the same money.

  2. 2
    Select Pi0.5 in the model list

    The card shows the A100 80 GB requirement and the inference figure. Selecting it swaps the parameter block to the pi0 defaults and reveals the two extra fields this trainer family exposes, seed and log frequency.

  3. 3
    Review the parameters

    Batch size 1, learning rate 5e-5, 30000 steps, gradient accumulation 16. Set a seed now rather than later, and give the policy a name that contains the seed and the dataset version so the run is identifiable in three weeks.

  4. 4
    Start it and let the pool find a card

    GPUs are rented on a spot market and selected by VRAM rather than by model name, so the job may sit queued for a while before an 80 GB card is available. Queued time is not billed.

  5. 5
    Watch the run page, not the loss alone

    The run page plots loss, learning rate and gradient norm alongside GPU utilization, VRAM, temperature and power. Utilization tells you the job is really training; a card sitting near idle with the loss frozen means the data loader is the bottleneck, not the model.

  6. 6
    Take two or three checkpoints from the second half

    The final checkpoint is the newest, not automatically the best. Keep a couple from the back half of the schedule and let the arm decide between them, because nothing on this page can.

The defaults, and when to change them

ParameterDefault for Pi0.5Change this when
Batch size1Effectively never. The pi0 path is configured around accumulation, and raising this changes the effective batch you would be comparing your earlier runs against.
Learning rate5e-5The loss spikes inside the first thousand steps and never recovers, which means lower. Raising it on a 50-episode SO-100 set is rarely the fix people hope for.
Max steps30000You have well over a hundred episodes and the curve is still falling steeply at the end, so extend. Under fifty episodes, a shorter schedule saves GPU hours you were going to waste.
Gradient accumulation16Rarely. Lowering it makes each update noisier without making the run finish sooner, since the same number of samples still passes through the model.
Effective batch16 (1 x 16)This is the number to hold constant when you compare two runs. If you change batch size or accumulation, note the product or your comparison means nothing.
SeedYou set it, lerobot family onlyAlways. It costs nothing and it is the difference between "the second run was better" and "the second run was different".
Log frequencyYou set it, lerobot family onlyLower it on a short diagnostic run so the chart has points early. Raise it on a full 30000-step run so the curve stays readable.
GPU tierA100 80 GBNot yours to choose. The pool requests an 80 GB card for this model and takes whichever qualifying card the spot market offers.

Cost and wall clock

An A100-tier run on this platform takes 3 to 6 hours at 1.20 to 2.00 USD per hour, so about 4 to 12 USD in total. Pi0.5 sits at the upper end of that band, and the reason is worth understanding, because it is not the reason most people assume.

Batch size 1 on an 80 GB card leaves memory to spare. The constraint is the clock: 30000 steps with 16 accumulation steps behind each of them is a lot of forward and backward passes, and they happen one small sample at a time. Nothing you can set in the form makes that materially faster, which is why the practical lever on cost is the step count and the honest question of whether this task needs this model at all.

Prove the dataset on a cheap card first

A SmolVLA run on a 24 GB card takes 2 to 5 hours at 0.30 to 0.60 USD per hour, about 1 to 3 USD. If that run produces nothing on the arm, the fault is almost certainly in the demonstrations, and paying A100 prices to rediscover that is an expensive way to be told.

Reproducing a run exactly

Seed and log frequency are exposed for the lerobot family, which is ACT, SmolVLA and pi0. GR00T does not have this: its fine-tune entry point has no seed field at all, so two GR00T runs on the same data are simply two different runs. With Pi0.5 you fix the seed and the run is deterministic, which turns hyperparameter changes into something you can actually attribute.

Use it properly. Hold the seed constant when you change one parameter, and vary only the seed when you want to know how much of the difference between two policies was luck. Put the number in the policy name; the run page keeps the configuration, but a name you can read in a list is faster than opening six runs to find out which was which.

Deploy the checkpoint to the arm

Before you load anything, confirm the arm is in the state the dataset assumes. Cameras in their recorded positions, same lighting, same scene reset, all six servos answering on the bus.

bash
ay-robots devices

# SERIAL PORTS
#   /dev/cu.usbmodem58FA0821911   SO-100  (6 servos responding)
# CAMERAS
#   0  Scene cam    1280x720
#   1  Wrist cam    1280x720

# Only if a joint reads visibly off from where it physically sits
ay-robots calibrate

Then select the checkpoint in the client and run it against the same task string you recorded with. Expect the arm to look thoughtful. At 485 ms per action step you can watch it move, settle, and move again, and on a careful insertion that reads as competence. On a fast pick-and-place the same cadence reads as hesitation: reach, pause, reach, pause. That pattern is the inference interval, not a broken policy, and no amount of retraining removes it.

First run, hand on the power

A policy that has learned a contact task will press into things by design, and a policy that has learned it badly will press into the table. Clear anything you care about out of reach, stay next to the switch, and do not leave the arm running while you fetch coffee.

Evaluate it properly

Twenty attempts with the object in different starting positions, written down as you go. Then repeat the identical protocol for the second checkpoint. A four-attempt impression of a policy that takes half a second per action step is not an evaluation, it is a mood.

Separate the two failure families before you conclude anything. If the arm reaches the right place and fumbles the contact, that is a data problem and more targeted episodes will fix it. If the arm arrives too late because the object is no longer where it was half a second earlier, that is a latency problem, and the answer is a different model or a slower task, not more demonstrations.

Troubleshooting

SymptomLikely causeWhat to do
Loss still falling steeply at step 30000Your dataset is larger or more varied than the default schedule assumesRun again with more steps and the same seed, then compare both policies on the arm under one protocol rather than judging the curves.
Loss barely moves after the first few hundred stepsThe data did not load the way you think, or the learning rate is wrong for itCheck the dataset preview for action dimensions and camera keys before touching any hyperparameter. Schema surprises look exactly like slow convergence.
Out of memory on a smaller cardThe job is not on the 80 GB tierPi0.5 needs an A100 80 GB. Batch size is already 1, so there is nothing left to trim; on 24 GB hardware use SmolVLA instead of fighting this one.
Jittery motion between action stepsShaky demonstrations, or a servo problem the dataset recorded faithfullyFlow matching smooths within a predicted chunk, not across bad training data. Re-record the shaky episodes, and check whether one joint jitters on its own.
Gripper closes late and pushes the object awayThe grasp moment is under-represented, and at this cadence one step late is half a second lateRecord extra episodes that dwell on approach and close, and demonstrate the close slightly earlier than feels natural during teleoperation.
Job sits queued for a long timeNo 80 GB card available on the spot market right nowNothing to fix locally. The pool selects by VRAM, not by GPU name, so it will start as soon as any qualifying card frees up.

Frequently asked questions

Is Pi0.5 the same thing as π0?

It is the Physical Intelligence model people write as π0.5 and type as "pi zero" or "pi 0.5". On this platform the selectable model is called Pi0.5, the trainer key is pi0, and the resulting checkpoint carries policy type pi05. Three spellings, one model.

Can I train it on my own RTX 4090?

No. Pi0.5 is requested on the A100 80 GB tier and there is no configuration that fits it into 24 GB, since batch size is already at 1. SmolVLA and ACT are the models that run on consumer hardware.

How many episodes before Pi0.5 is worth it over ACT?

Fifty is the floor where the model starts producing something evaluable, the same floor as ACT. The difference is not really about episode count: ACT is faster to train, faster at inference and cheaper, while Pi0.5 buys smoother behavior in contact. Run both on the same data and compare success rates on the arm.

Why does my arm pause between movements?

Because each action step takes about 485 ms. Just over two commands per second is what this model produces, so visible settling between motions is expected behavior rather than a fault. It looks purposeful on careful tasks and awkward on quick ones.

Will the same seed give me the same policy?

With the same dataset, the same parameters and the same seed, yes, the run is deterministic. Change a single episode in the dataset and the comparison is void, so version your datasets as carefully as you version your seeds.

Does it need LeRobot v2.1 or will v3.0 work?

Record v2.1. It is the format the client writes by default and the format the trainers here expect, and it keeps the same dataset usable if you later want to fine-tune GR00T, which rejects v3.0 outright.