pi05-base-p300x2
pi-0.5 (lerobot/pi05_base) on two Tenstorrent Blackhole p300 boards via tt-nn, in two serve profiles: single-robot (53 ms per action chunk) and multi-robot (two chip pairs, 47 action chunks per second for four robots). These figures were measured on 2026-09-18 with the published image, which still contains the unfixed code (see 2026-09-29 code fix).
Runs on p300x2 — see the serve profiles below.
Packaged and published with tt-model-manager 0.1.0 (manifest schema 5.1).
2026-09-29 code fix
The image was not rebuilt.
code/now holds the fixed source, butimage/still holds the 2026-09-18 build, andtt-model serveruns that build. Serving the image as published still has both bugs below until the image is rebuilt on p300 hardware (see Provenance).
The port had two bugs compared with openpi. Its torch reference had the same two bugs, so the PCC tests could not detect them:
- No padding mask. The server right-pads the prompt to 224 tokens. The VLM prefix and the action expert both attended the pad tokens. The fused expert-attention kernel also applied its mask to the last key tile only.
- Action-token RoPE. The 50 action tokens were rotated at positions
[0, 50). openpi usesn_valid_prefix + [0, 50).
What changed in code/models/experimental/pi0_5/:
- Torch reference (
reference/torch_pi0_model.py): it now applies openpi's prefix key mask and positions, and the expert's mask and positions. - Fused graph (from changh95/tt-pi-0.5@e89eff8):
- Each request's key masks and action-token RoPE rows are persistent trace inputs (
common/fused_host.pyattention_inputs,tt/ttnn_pi0_model.py). They are rewritten before a replay whenever the prompt length changes. - The fused expert-attention kernel now masks every key tile, and each request reads its own mask and RoPE rows (
tt/kernels/fused_attn/,tt/ttnn_fused_attn.py). One batch can therefore mix prompt lengths.
- Each request's key masks and action-token RoPE rows are persistent trace inputs (
- Multi-chip (new for this repo):
- The attention inputs are replicated on every chip, like the other inputs. Tensor parallelism splits only the VLM query heads; the single K/V head and the whole action expert are replicated.
- The server passes each request's prompt mask through the batchers of both profiles (
server/app.py). PI05_LAYOUT=pipeline(tt/ttnn_disagg.py) gives each stage its own attention inputs: the VLM mask on the prefix chips, the expert mask and RoPE rows on the expert chips.
- Unchanged: the unfused path (
TT_FUSED=0) still has both bugs. It runs on a single chip only, and no serve profile uses it.
Validation of the fixed code/:
| path | run? | result |
|---|---|---|
single chip: Blackhole p150a, tt-metal 668c2907 (not the image's pin 975015c2) |
run | LIBERO shape vs the openpi GPU golden (lerobot/pi05_libero, 8 observations, 32-token prompt, H 10): PCC over 7 action dims mean 0.99984, min 0.99971. Served shape with padded prompts (pi05_base, 40 / 97 / 12 / 150 / 201 / 224 real tokens of 224) vs the fixed torch reference: PCC 0.998 / 0.984 / 0.938 / 0.995 / 0.980 / 0.988. Treating the pads as real tokens changes the output (max abs diff 1.66). Other ids in the pad slots give a bit-identical output. Ten trace replays are bit-identical. 84.3 ms per call on the p150a. Test: tests/pcc/test_pcc_pi05_fused.py |
| host, no device | run | 25 tests pass (tests/test_fused_host.py). They include fp64 emulations against openpi's attention of (a) the tensor-parallel VLM prefill at TP 1, 2 and 4 with replicated masks and (b) the fused kernel's per-request operand addressing at batch 2. Positive controls (no mask, a batch-sharded mask, request 0's operands) all fail as they should. The torch reference matches the openpi golden (tests/pcc/test_reference_vs_openpi.py: PCC mean 0.999995). |
| multi-chip graph on a mock cluster (no card; compile and host-side checks only, no numerics) | run | Eager sample_actions_fused completed on a mock 1x2 p300 mesh (TP 2, one multi-robot pair) and on a mock 4-chip Blackhole mesh as 1x4 TP 4, dp 2 x 1x2, and pipeline 2 + 2. That mesh uses a 4x p150 descriptor because no 2x p300 descriptor exists. The runs covered mixed prompt lengths at batch 2, and the attention inputs were rewritten exactly when the prompt length changed. The mock cannot capture traces; the unfixed code fails there the same way. |
single-robot, multi-robot, PI05_LAYOUT=pipeline on p300x2 hardware |
not run | No p300 hardware was available. tests/pcc/test_pcc_pi05_mesh.py (now with padded prompts and a mixed-length batch of 2; run it with PI05_MESH=1x4 and PI05_MESH=1x2) is the check to run before rebuilding the image. |
| the published image | not rebuilt | It still contains the unfixed code. |
Quickstart
tt-model pull changh95/pi05-base-p300x2 --with-weights
tt-model serve changh95/pi05-base-p300x2
pull --with-weights fetches the image and the pinned lerobot/pi05_base weights. serve listens on port 20000 and is ready when it logs Application startup complete (a few minutes on first start).
Run with tt-cli
tt serve changh95/pi05-base-p300x2 # single-robot (default)
tt serve changh95/pi05-base-p300x2 --profile multi-robot # several robots share the box
printf '{"images":["%s","%s"],"prompt":"pick up the cube","state":[0.1,-0.2,0.3,0,0,0,0.5,-0.5]}' \
"$(base64 -w0 media/sample_base.png)" "$(base64 -w0 media/sample_wrist.png)" > req.json
curl -s localhost:20000/predict -H 'Content-Type: application/json' -d @req.json
tt model stop changh95/pi05-base-p300x2
API
POST /predictimages: 1-2 base64 PNG/JPEG, base camera then wrist camera.prompt(task text) ortokens(≤224 PaliGemma ids).- optional
state(≤32 floats in [-1, 1]) andseed.
- Response:
actions(50 × 32, normalised),batched_as,timing_ms. GET /health,GET /info.
single-robot profile (default)
All four chips work on every request. The SigLIP + Gemma-2B prefix is tensor-parallel over the 4 chips; the Gemma-300M action expert is replicated. One Metal trace, batch 1. Concurrent requests run one after another.
Every number in this section was measured on 2026-09-18 with the published image, i.e. the unfixed code. The PCCs compare it against the torch reference, which had the same two bugs. The fixed code has not been measured on p300 hardware.
Latency vs batch (in process, traced; 2 × 224² images, 224 tokens, 10 denoising steps):
| batch | prefix (4 chips) | action expert, 10 steps (4 chips) | total | per request |
|---|---|---|---|---|
| 1 | 18.4 ms | 33.7 ms | 50.5 ms | 50.5 ms |
| 2 | 26.7 ms | 39.8 ms | 64.9 ms | 32.4 ms |
| 4 | 50.9 ms | 65.6 ms | 117.3 ms | 29.3 ms |
The profile serves batch 1. Rows 2 and 4 are the same layout with PI05_BATCH_SIZES=1,2,4.
Accuracy and speed:
| metric | value |
|---|---|
| PCC vs the fp32 torch reference, 2 observations | 0.9986 / 0.9988 |
| PCC, 16 random observations | min 0.93, mean 0.98, median 0.99 |
| HTTP, 1 client | 53 ms per chunk |
| HTTP, 2 / 4 / 8 clients | 19.5 req/s at 102 / 204 / 408 ms per chunk |
| same code on one p300 chip | 84 ms per chunk |
| determinism | repeats bit-exact |
multi-robot profile
The four chips form two independent pairs. Each pair runs the whole model: the prefix tensor-parallel over its 2 chips, the action expert replicated on both. A request goes to the pair with fewer requests in flight; requests arriving within 4 ms at a busy pair share a batch of 2. Sized for up to 4 robots.
Every number in this section was measured on 2026-09-18 with the published image, i.e. the unfixed code, against the torch reference with the same bugs. The fixed code has not been measured on p300 hardware.
Latency vs batch, one pair (in process, traced):
| requests on the pair | prefix (2 chips) | action expert, 10 steps (2 chips) | total |
|---|---|---|---|
| 1 | 24.6 ms | 33.7 ms | 57.2 ms |
| 2 | 38.0 ms | 42.5 ms | 78.1 ms |
Robots vs latency and throughput (HTTP, closed loop, median per request):
| robots | per pair | latency | throughput |
|---|---|---|---|
| 1 | 1 | 63 ms | 15.8 req/s |
| 2 | 1 + 1 | 64 ms | 31.0 req/s |
| 3 | 2 + 1 | 81 ms | 37.5 req/s |
| 4 | 2 + 2 | 84 ms | 47.3 req/s |
| 8 | queueing | 158 ms | 49.2 req/s |
Accuracy and speed:
| metric | value |
|---|---|
| PCC vs the fp32 torch reference, 2 observations | 0.9955 / 0.9879 (2-chip prefix partials) |
| two pairs concurrently, 1 / 2 requests each (in process) | 57.9 / 82.0 ms wall |
| determinism | repeats bit-exact; both pairs give identical outputs |
Beyond four robots, PI05_DP_GROUP=1 runs four independent single chips instead of two pairs. A chip alone takes 84 ms per chunk, but the expert is no longer duplicated, so the ceiling is higher:
| robots | two pairs (this profile) | four single chips |
|---|---|---|
| 4 | 85 ms, 47.1 req/s | 94 ms, 42.3 req/s |
| 6 | 119 ms, 45.4 req/s | 95 ms, 47.4 req/s |
| 8 | 160 ms, 48.5 req/s | 146 ms, 51.3 req/s |
| 16 | 320 ms, 48.8 req/s | 273 ms, 56.9 req/s |
Response
An example response from the 2026-09-18 image:
{"actions": [[-0.0203, -0.0197, -0.0349, ...], ...], "action_horizon": 50, "action_dim": 32, "normalized": true,
"denoising_steps": 10, "num_tokens": 142, "batched_as": 1, "timing_ms": {"preprocess": 1.1, "inference": 50.5, "total": 51.6}}
actions are in lerobot's normalised QUANTILES space, zero-padded to 32 dims. Denormalise with (a+1)*(q99-q01)/2+q01 from your dataset and slice to your action dim.
Caveats
- Fixed inputs: 1-2 images at 224 × 224, ≤224 prompt tokens, ≤32 state floats, 10 steps.
- Needs all four chips of a 2× p300 box. One profile at a time.
multi-robotalone costs 63 ms per chunk (2-chip prefix; 2026-09-18 image); beyond 4 robots requests queue. - The tokenizer
google/paligemma-3b-pt-224is gated: accept the Gemma terms andhf auth login, or sendtokens. - Base checkpoint, normalised actions. No closed-loop benchmark was run.
Licensing
- Weights: lerobot/pi05_base under the Gemma Terms of Use, not redistributed here.
- Port and serving code (
code/): Apache-2.0 headers, same Gemma terms.
Links
lerobot/pi05_base · arXiv:2504.16054 · Physical-Intelligence/openpi · single chip: changh95/pi05-base-p150 · design notes: code/models/experimental/pi0_5/README.md
Serve profiles
Pick one with --profile: single-robot (default) or multi-robot. Both run on p300x2; see the table above.
Provenance
The image/ OCI build and tt_kernel_manifest.json are unchanged from 2026-09-18. They contain the unfixed model code, code digest c9c924f989682aea. code/ in this repo is now the fixed source from 2026-09-29, so it is no longer byte-identical to the model code inside the image. Serving the image as published still has both attention bugs until the image is rebuilt on p300 hardware.
| component | built from |
|---|---|
| tt-metal (image) | 975015c2f03bb818eaee2422c3845fba381eaf8c |
| model code inside the image | digest c9c924f989682aea (sha256, first 16 hex digits; code_sha256 in tt_kernel_manifest.json), unfixed |
code/ in this repo |
digest 028d1e507f85e296 (sha256, first 16 hex digits, computed the same way tt-model computes code_sha256), fixed on 2026-09-29 |
| image built | 2026-09-18T21:21:27+00:00 by tt-model 0.1.0 |
| code fix validated on | a single Blackhole p150a with tt-metal 668c290757550588d0ce46b180c344a462a2aaf5; not yet compiled against 975015c2 or run on p300 |
Model tree for changh95/pi05-base-p300x2
Base model
lerobot/pi05_base