Instructions to use Dimios45/pickblock_bspline_act with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LeRobot
How to use Dimios45/pickblock_bspline_act with LeRobot:
- Notebooks
- Google Colab
- Kaggle
| license: apache-2.0 | |
| library_name: lerobot | |
| pipeline_tag: robotics | |
| tags: | |
| - robotics | |
| - lerobot | |
| - imitation-learning | |
| - act | |
| - b-spline | |
| - so101 | |
| - bspline_act | |
| datasets: | |
| - atharva-pantheon/pick_block_bowl | |
| model_name: pickblock_bspline_act | |
| # pickblock_bspline_act | |
| An [ACT](https://huggingface.co/papers/2304.13705) policy that predicts **B-spline | |
| trajectory segments** instead of discrete action chunks, trained on SO-101 black-cube | |
| picking. This is the "Reg.+BSP" variant from | |
| [B-spline Policy: Accelerating Manipulation Policies via B-spline Action Representations](https://arxiv.org/abs/2607.09648). | |
| The policy outputs a curve, so executing `a(n·t)` replays the same trajectory geometry | |
| n times faster without retraining. Speed-up is an inference flag, verified exact to | |
| 0.000e+00 deviation at 2× and 4×. | |
| > [!WARNING] | |
| > **This model is trained on 13 episodes and does not generalize well.** Held-out error | |
| > is 22.8° against 6.2° on training data — a 3.7× gap that never closed across 40,000 | |
| > steps. It has never been run on hardware. Treat it as a pipeline validation artifact, | |
| > not a working policy. See [Evaluation](#evaluation) and [Limitations](#limitations). | |
| ## Download | |
| ```bash | |
| hf download Dimios45/pickblock_bspline_act --local-dir ./pickblock_bspline_act | |
| ``` | |
| The repository root holds the recommended checkpoint, so `--policy.path` also accepts | |
| the repo id directly. Individual checkpoints live under `checkpoints/<step>/`. | |
| ## Run it | |
| ```bash | |
| lerobot-rollout \ | |
| --strategy.type=base \ | |
| --policy.path=./pickblock_bspline_act/checkpoints/010000/pretrained_model \ | |
| --policy.speed_up=1.0 \ | |
| --device=cuda \ | |
| --robot.type=so101_follower \ | |
| --robot.port=/dev/ttyACM0 --robot.id=FOLLOWER \ | |
| --robot.cameras='{front: {type: opencv, index_or_path: 0, width: 640, height: 360, fps: 30}, ee: {type: opencv, index_or_path: 4, width: 640, height: 360, fps: 30}}' \ | |
| --task="Grab the black cube" \ | |
| --fps=30 --duration=60 | |
| ``` | |
| `--policy.path` takes either a Hub repo id (`namespace/name`) or a **local directory | |
| containing `config.json`** — which is the `pretrained_model/` subdirectory, not the | |
| step directory above it. There is no `--policy.subfolder` flag. | |
| ### Two things differ from the cup-stacking model — check both | |
| **Resolution is 640×360, not 640×480.** The source demonstrations were recorded at | |
| 1920×1080; they were downscaled to 640×360 for training, preserving the 16:9 aspect | |
| ratio. Your cameras must be configured to match. | |
| **The cameras are `front` and `ee`**, not `front` and `gripper`: | |
| | Key | Shape | | |
| |---|---| | |
| | `observation.images.front` | `(3, 360, 640)` | | |
| | `observation.images.ee` | `(3, 360, 640)` | | |
| | `observation.state` | `(6,)` | | |
| Joint order is `shoulder_pan, shoulder_lift, elbow_flex, wrist_flex, wrist_roll, gripper`, | |
| in degrees. Swapping the two cameras produces confident but wrong behaviour rather than | |
| an error. | |
| To try the speed-up, raise `--policy.speed_up` to 2.0 or 4.0. The paper reaches 4× on | |
| cube picking specifically, so this task should tolerate it better than precise-placement | |
| tasks — but validate at 1× first, and note this policy has not been shown to complete | |
| the task at any speed. | |
| ### Requirements | |
| This policy type is not in upstream LeRobot. You need a checkout containing the | |
| `bspline_act` policy, installed with the scipy extra: | |
| ```bash | |
| uv pip install -e ".[feetech,bspline]" | |
| ``` | |
| `scipy>=1.15` is required for `interpolate.generate_knots`. | |
| Unnormalization statistics are stored as buffers inside `model.safetensors`, so each | |
| checkpoint is self-contained — you do not need the training dataset on the robot machine. | |
| ## Checkpoints | |
| | Step | Epoch | Train error | **Held-out error** | Eval loss | | | |
| |---|---|---|---|---|---| | |
| | **10000** | 58 | 6.19° | **22.81°** | **0.3903** | **recommended — best** | | |
| | 20000 | 116 | 4.67° | 24.15° | 0.3986 | | | |
| | 30000 | 174 | 3.90° | 24.77° | 0.3996 | | | |
| | 40000 | 234 | 3.44° | 26.22° | 0.4030 | final, worst held-out | | |
| Held-out error gets **monotonically worse** after step 10,000 while training error keeps | |
| falling. Use 10000. Later checkpoints are included only for reproducibility — they are | |
| strictly worse policies. | |
| Error is mean absolute joint error in degrees, worst joint, over each predicted | |
| segment's horizon (~0.76 s), measured by decoding the spline and comparing against | |
| ground-truth actions. | |
| ## Training | |
| | | | | |
| |---|---| | |
| | Steps | 40,000 | | |
| | Batch size | 32 | | |
| | Epochs | 234 | | |
| | Dataset | [`atharva-pantheon/pick_block_bowl`](https://huggingface.co/datasets/atharva-pantheon/pick_block_bowl) → downscaled → idle-trimmed → B-spline converted | | |
| | Episodes | **13 train / 3 held out** | | |
| | Frames | 6,422 (from 10,342; 37.9% boundary idle removed) | | |
| | Optimizer | AdamW, lr 2e-5 (backbone 1e-5), wd 1e-3 | | |
| | Precision | bf16 autocast + `channels_last` | | |
| | Augmentation | ColorJitter (brightness/contrast/saturation/hue/sharpness), up to 3 per frame | | |
| | Hardware | 1× RTX 4090, ~2 h 45 min total | | |
| | Seed | 1000 | | |
| Augmentation and 10× weight decay were added specifically to combat overfitting. They | |
| slowed the fit to training data and **moved held-out loss by 0.0003**. An unaugmented | |
| control run is available on request; the two are indistinguishable on unseen data. | |
| ### Preprocessing | |
| Source video is 1920×1080 AV1, which is unusable for ACT directly — a ResNet18 | |
| stride-32 feature map is 34×60, putting over 4,000 tokens into the transformer encoder | |
| across two cameras. Transcoded to 640×360 H.264, which also sped up random-access | |
| decode from 40 to 183 frames/s per worker. | |
| Leading and trailing motionless frames were then removed: 46 s of lead-in and 27 s of | |
| trailing idle. This dataset is unusually idle-heavy — 37.9% of frames — including | |
| 20 s and 32 s dead stretches at the ends of two episodes where recording continued | |
| after the task finished. | |
| Motion is measured as peak-to-peak joint range over a ±0.5 s window rather than | |
| frame-to-frame delta, because isolated jitter samples inside a dead stretch defeat a | |
| delta test and silently leave the idle in. | |
| ## Architecture | |
| Standard ACT with one change: the decoder emits a B-spline **parameter matrix** rather | |
| than an action chunk. | |
| ``` | |
| (n_knots, 1 + action_dim) = (16, 7) = 112 values per prediction | |
| column 0 knot vector, in source-frame units, 0 = "now" | |
| columns 1: control points, one per joint | |
| ``` | |
| | | | | |
| |---|---| | |
| | Params | 51.6 M | | |
| | Vision backbone | ResNet18 (ImageNet init, fine-tuned) | | |
| | dim_model / chunk_size | 512 / 16 | | |
| | VAE | enabled, kl_weight 10.0 | | |
| | B-spline degree | 3 (cubic, C² continuous) | | |
| | bspline_chunk_size | 10 | | |
| | Fitting tolerance ε | 0.2 (degrees) | | |
| | Max knot span | 6 frames | | |
| The paper uses a DINOv2 encoder for its regression variant; this port uses LeRobot's | |
| ResNet18, which is what LeRobot's ACT supports. The paper reports no backbone ablation, | |
| and holds the encoder fixed between its baseline and its B-spline variant, so none of | |
| its claims depend on that choice. | |
| Segments span 0.50–1.10 s (0.76 s mean). The network runs only when a segment is | |
| exhausted, which decouples policy rate from control rate. | |
| ### On `max_knot_span` | |
| This dataset is why the flag exists. Adaptive knot fitting covers a motionless stretch | |
| with one enormous knot span, and uncapped fitting here produced segments with a **p95 | |
| of 18.4 seconds** — the policy would commit to eighteen seconds of open-loop motion | |
| without looking at a camera. Capping span width at 6 frames bounds that at | |
| `chunk_size × 6` = 2 s, costing 0.04× compression. | |
| After correct idle-trimming the uncapped p95 drops to 1.17 s on its own, so the dead | |
| recording time was the real cause. The cap remains as a cheap safety rail. | |
| ## Evaluation | |
| **Offline only. No hardware evaluation has been performed.** | |
| | | Train (13 eps) | **Held-out (3 eps)** | | |
| |---|---|---| | |
| | Best checkpoint (10k) | 6.19° | **22.81°** | | |
| | Final (40k) | 3.44° | 26.22° | | |
| Reference points: | |
| - **Inter-demonstration spread: 54.83°** — how far apart two human demonstrations of | |
| this task are, phase-aligned. At 22.8° the policy is well inside that, so it is | |
| extracting real per-episode information from the cameras rather than replaying an | |
| average trajectory. This is the one genuinely positive result. | |
| - **Hold-still baseline: 16.23°.** The policy scores worse than this. Over a 0.76 s | |
| horizon on a slow task, "don't move" is a decent short-horizon *predictor* while | |
| being a useless *policy*, so this is not the damning comparison it looks like — but | |
| combined with the widening train/eval gap it is not encouraging either. | |
| Held-out loss plateaued at ~0.39 by step 2,000 and never improved across the remaining | |
| 38,000 steps. | |
| ### Why the held-out number is a pessimistic bound | |
| The split holds out the last 3 of 16 episodes. Inspecting the raw recording: | |
| - 8 of 15 episode boundaries are contiguous to under 1° (several to exactly 0.00°), so | |
| episodes were largely cut from a continuous teleoperation stream. Adjacent episodes | |
| are correlated, meaning an interleaved split would leak. | |
| - There is a 97.67° discontinuity between episodes 9 and 10, and episodes 10, 13, 14 | |
| and 15 all end with the gripper open (8–16°) while the other twelve end closed (~2°). | |
| - **All three held-out episodes sit after that change point**, and two of them *start* | |
| with the gripper open — a state the model never sees at t=0 in training. | |
| So the tail split is biased pessimistic and an interleaved split would be biased | |
| optimistic. With 16 semi-continuous episodes there is no clean holdout. 22.8° is a | |
| lower bound on quality, not a verdict. | |
| ## Limitations | |
| - **13 training episodes is too few for this task.** This is the dominant limitation and | |
| no hyperparameter fixed it. 40–50 demonstrations with varied cube placement and an | |
| **independent reset between takes** would address both the training and the | |
| measurement problem. | |
| - **No hardware evaluation.** Task success rate is unknown, and given the above it | |
| should not be assumed to be non-zero. | |
| - **Held-out split is confounded** — see above. | |
| - **Resolution and camera names differ** from the cup-stacking model. Check both. | |
| - **Camera assignment is silent when wrong.** | |
| ## Citation | |
| ```bibtex | |
| @article{han2026b, | |
| title={B-spline Policy: Accelerating Manipulation Policies via B-spline Action Representations}, | |
| author={Han, Xiaoshen and Xiong, Haoyu and Chen, Haonan and Liu, Chaoqi and | |
| Torralba, Antonio and Zhu, Yuke and Du, Yilun}, | |
| journal={arXiv preprint arXiv:2607.09648}, | |
| year={2026} | |
| } | |
| ``` | |