π₀.₅ — SO-ARM101 Right Arm Pick-and-Place (v1, action-expert-only fine-tune)
A π₀.₅ Vision-Language-Action policy fine-tuned to drive a single SO-ARM101 right
follower arm on a tabletop pick-and-place task. Action expert was fine-tuned
on top of
lerobot/pi05_base; the
PaliGemma vision-language backbone was frozen.
TL;DR: A first iteration on a small (50 episode) dataset that demonstrates
the full pick → grasp → transport → place behavior, with a notable emergent
recovery from an out-of-distribution box orientation. It is not yet robust
across the full workspace.
Demonstration videos
Both clips are real-time, recorded on a phone during live evaluation on the
SO-ARM101 right follower. Inference was served from a Modal A100 endpoint
with ~1.3 s round-trip per 50-action chunk.
Clean pick-and-place
A nominal trial inside the policy's "sweet spot": reach → grasp → lift →
transport → release into the cardboard box, no corrections needed.
Emergent recovery from vertical → horizontal box orientation
The most interesting trial we observed. The box was placed standing
vertically. The policy attempted to grasp it, instead knocked it over so
it lay horizontally on the table, then re-approached, grasped it in the
new orientation, and transported it to the cardboard box.
The vertical → knocked → horizontal transition inside a single trajectory
was not present in the training data — vertical and horizontal box poses
both appear, but as separate clean demos, not as a single in-episode
sequence. This is consistent with the action expert having internalized the
~15 % failure-and-correction subdistribution as a general "re-approach if
grasp fails" behavior rather than a per-instance template. Documented here
as a one-shot observation, not a guaranteed reproducible capability.
Task
Pick up a small rectangular box (a Feetech servo's black-and-white packaging,
~5 × 5 × 8 cm) from the table in front of the arm and place it into an open
cardboard box on the left.
The dataset and inference task string is intentionally generic:
"Pick up the cube and place it into the bowl." The physical scene at
demonstration and evaluation time used a Feetech box + cardboard box, but the
language prompt was held fixed across all 50 episodes so the prompt-conditioning
acts as a constant cue rather than per-instance language grounding.
Model details
| |
|---|
| Base model | lerobot/pi05_base (4B params, PaliGemma 3B VLM + Gemma 300M action expert) |
| Fine-tuning mode | train_expert_only=true (PaliGemma frozen, action expert trained) |
| Total parameters | ~4.14B (≈300M trainable in this run) |
| Training dtype | bfloat16 with gradient checkpointing |
| Action space | 6 joint positions in degrees: shoulder_pan, shoulder_lift, elbow_flex, wrist_flex, wrist_roll, gripper |
| Observation space | observation.state (6) + observation.images.wrist (224×224×3) + observation.images.top (224×224×3) + task language |
| Action chunk size | 50 (≈5 s of actions at 10 Hz) |
| Image resolution | 640×480 at recording, internally downsampled to 224×224 |
| Normalization | Per-feature quantile (QUANTILES) |
Training data
zkxuerb/so101_right_arm_pick_cube_v1 — 50 episodes, 17,314 frames, 15 fps,
recorded via leader-follower teleoperation with two cameras:
observation.images.wrist — wrist-mounted camera on the right gripper (close-up egocentric view of the object).
observation.images.top — overhead workspace camera.
Data-collection methodology
The 50 episodes were deliberately diversified to push the policy beyond a single canonical trajectory:
- Spatial variation — the box was placed at different positions within the
same reachable patch of the table across episodes (no fixed grid; varied by
hand). Position variation was the dominant axis.
- Orientation variation — the rectangular box was placed in both
vertical (long axis up) and horizontal (long axis flat) orientations.
- Asymmetric texture flip — the box has a black half and a white half;
roughly half the demos had the white half on top, the other half had black
on top, to discourage the model from anchoring on the texture pattern.
- Deliberate failure-and-correction episodes (~15%) — about 7 of the 50
episodes contain an intentional near-miss or mis-grasp followed by an
in-episode correction. This adds recovery behavior to the training
distribution rather than only showing clean successes. (Inspired by
the well-known finding that pure success-only imitation learning is brittle
under distributional shift.)
- Consistent home pose — every episode starts from approximately the
same arm configuration (
shoulder_pan ≈ -20°, shoulder_lift ≈ -105°,
elbow_flex ≈ 98°, wrist_flex ≈ 76°, wrist_roll ≈ 3°, gripper closed).
Training hyperparameters
1lerobot-train \
2 --policy.type=pi05 \
3 --policy.pretrained_path=lerobot/pi05_base \
4 --dataset.repo_id=zkxuerb/so101_right_arm_pick_cube_v1 \
5 --policy.compile_model=false \
6 --policy.gradient_checkpointing=true \
7 --policy.dtype=bfloat16 \
8 --policy.train_expert_only=true \
9 --batch_size=8 \
10 --steps=10000 \
11 --optimizer.lr=2.5e-5 \
12 --save_freq=1000 \
13 --log_freq=50 \
14 --num_workers=4 \
15 --policy.device=cuda
| Param | Value | Notes |
|---|
train_expert_only | true | Freezes the 3B PaliGemma backbone; trains only the 300M action expert. Required to fit AdamW optimizer state on a 40 GB A100. |
compile_model | false | torch.compile=true was incompatible with PEFT/expert-only training at this lerobot version. |
gradient_checkpointing | true | Mandatory at this scale on a single 40 GB GPU. |
dtype | bfloat16 | |
| Batch size | 8 (per GPU) | |
| Steps | 10 000 | Originally targeted 20 000; reduced after observing convergence trend on small dataset to avoid overfitting. |
| Optimizer | AdamW, β=(0.9, 0.95), wd=0.01, grad-clip=1.0 | Pi05 defaults. |
| LR schedule | Cosine decay, peak 2.5e-5, 1000-step warmup, decay to 2.5e-6 | Pi05 defaults (auto-scaled to 10k total steps). |
| Hardware | 1× NVIDIA A100 40 GB (Google Colab Pro+) | |
Evaluation observations
Evaluation was qualitative (live robot rollouts), not automated. Inference was
served from a
Modal A100 80 GB endpoint with ~390 ms
server-side latency per call and ~1.3 s round-trip including JPEG-encoded image
upload. Robot client used a per-step
max_relative_target=15° safety clamp,
with action-range clipping relaxed to ~10 % beyond the dataset's
action.min /
action.max (the model routinely overshoots the training
extremes by 1–3°; clipping to exact dataset bounds costs ~3–5 cm of reach
because two joints —
shoulder_lift and
elbow_flex — saturate together).
What it does well
- Complete pick-and-place trajectory. Reach → pre-grasp → close gripper →
lift → transport → release → return-to-home, all from a single policy call
chain. No scripted phases.
- Reasonable grasp accuracy within a "sweet spot" of the workspace —
typically a band slightly right of center where teleoperation demos were
most concentrated.
- One genuinely surprising emergent behavior (observed in a single run,
documented for transparency, not claimed as a reproducible capability):
the policy was presented with the box standing vertically, attempted
to grasp, knocked the box over so it landed horizontally on the table,
re-approached, grasped it in the new orientation, and successfully
transported it to the cardboard box. The vertical → knocked → horizontal
recovery was not present in the training data — vertical and horizontal
box poses both appear in demos but as separate episodes, not within a single
trajectory. This is consistent with the action expert having internalized
the ~15 % failure-and-correction subdistribution as a general "re-approach
if grasp fails" behavior rather than a per-instance template.
What it does poorly
- Lateral generalization is limited. Outside the "sweet spot," the
gripper tends to land 3-10 cm off the box in the lateral direction.
Empirically, the model only weakly conditions on the camera images for
lateral object localization and falls back toward the dataset's mean grasp
position. This is a well-known small-dataset failure mode for VLAs:
with 50 episodes, the model learned a robust temporal policy
(the motion shape of a pick-and-place) but did not get enough lateral
visual variation to robustly ground "where the object actually is"
from the camera feed.
- Calibration sensitivity. State observations enter the policy directly
(and dominate the visual signal at small dataset sizes). A
shoulder_pan
motor zeroing drift of ~2.5° produces a ~1.5 cm physical lateral shift
that compounds with the visual-grounding bias above. Operators should
verify the right follower's calibration matches the recording session
before evaluating.
- Gripper depth/closing precision. The model often stops the gripper
~3-5 cm short of full reach unless the action clamp is widened beyond
the strict dataset extremes. (Concretely: the model frequently predicts
elbow_flex ≈ 98-100° and shoulder_lift ≈ -113° while the dataset
caps at 96.13° and -111.69° respectively. Clipping to the exact dataset
range loses the last few cm of reach.)
Practical inference notes
- Use the trained task prompt verbatim:
"Pick up the cube and place it into the bowl."
Changing object names in the prompt does not improve behavior at this dataset size.
- Relax per-joint action clipping by ~10 % beyond the dataset's
action.min /
action.max to let the model's modest overshoot through. The per-step
max_relative_target clamp in LeRobot still prevents jerky motion.
num_inference_steps=10 (default for π₀.₅ flow-matching) is sufficient;
higher values do not noticeably improve grounding here.
- Start the arm at the recorded home pose every rollout; the model is more
sensitive to start-state drift than to camera framing drift.
Intended use
- Pedagogical / research reference for the LeRobot + π₀.₅ + SO-ARM101 fine-tuning
workflow on a single arm.
- Sanity-test starting point for further fine-tuning on a larger, more
laterally varied dataset (anticipated
v2).
Not intended for:
- Production / deployed manipulation.
- Tasks involving objects materially different in size, color, or shape from
the demonstrated Feetech-style box.
- Workspaces with significantly different lighting, table material, or camera
geometry than the recording session.
How to use
Run inference (LeRobot rollout)
1pip install "lerobot[pi]"
2
3lerobot-rollout \
4 --policy.path=zkxuerb/pi05_so101_pick_cube_v1_expert_only \
5 --policy.type=pi05 \
6 --robot.type=so101_follower \
7 --robot.port=/dev/tty.usbmodemXXXXXXXXXXXX \
8 --robot.id=so101_right_follower \
9 --robot.cameras='{ wrist: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 15, warmup_s: 10}, top: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 15, warmup_s: 10} }' \
10 --task="Pick up the cube and place it into the bowl." \
11 --inference.type=rtc
Run remotely (Modal cloud GPU)
A reference cloud-inference server (scripts/modal_inference_server.py) and
single-arm client (scripts/robot_client_pi05_single.py) are available in the
companion repository. Round-trip latency on a warm Modal A100 is ~1.3 s per
chunk (50 actions, i.e. ~5 s of motion at 10 Hz), so one chunk every couple
of seconds is enough to keep the arm moving.
Reproduction
To reproduce this fine-tune end-to-end:
- Record a 50-episode SO-ARM101 right-arm pick-and-place dataset matching the
methodology above (varied position, orientation, texture flip,
~15 % failure+correction), at 15 fps with
wrist and top camera keys.
- Push as
<your_user>/so101_right_arm_pick_cube_v1.
- Run the
lerobot-train command shown under
Training hyperparameters, substituting your
dataset and policy repo IDs.
- ~1.5-2.5 h wallclock on a single A100 40 GB.
Limitations and bias
- Single-arm, single-task, single-object. No claim of generality.
- Small dataset (50 episodes). The pi0.5 paper and LeRobot community
consensus is that pi0.5 typically needs 100-400 episodes for reliable
visual grounding on a new task. Behavior here is therefore at the
boundary of what a 50-episode fine-tune can produce.
- Geometric tie to one robot and camera setup. The policy implicitly
encodes the recording-time arm and camera positions. Significant changes
to camera mount geometry, arm base position, or motor calibration are
expected to degrade performance.
- Task prompt is not meaningfully grounded. All 50 demos used the same
language string, so the policy effectively does not condition on
language — it conditions on state + images and the prompt is constant.
Acknowledgements
- Built on top of Physical Intelligence's
open-sourced π₀.₅ via the LeRobot port.
- Trained and deployed using LeRobot
and Modal.
- SO-ARM101 hardware design from
The Robot Studio's SO-ARM project.
Citation
If you reference this model in academic or industrial work, please cite the
underlying π₀.₅ work and LeRobot:
1@misc{pi05_2025,
2 title = {{π_{0.5}}: a Vision-Language-Action Model with Open-World Generalization},
3 author = {Physical Intelligence},
4 year = {2025},
5 url = {https://www.physicalintelligence.company/blog/pi05}
6}
7
8@misc{lerobot,
9 title = {LeRobot: State-of-the-art Machine Learning for Real-World Robotics in Pytorch},
10 author = {{Hugging Face}},
11 year = {2024},
12 url = {https://github.com/huggingface/lerobot}
13}