YAM Pick-Duster β€” B-spline Diffusion Policy (end-effector)

A B-spline Policy UNet diffusion policy trained on Dimios45/yam-pick-duster-ee for a single-arm I2RT YAM: pick up the duster.

This model emits absolute Cartesian poses and requires inverse kinematics on the robot. The joint-space counterpart β€” same 50 takes, no IK needed β€” is Dimios45/yam-pick-duster-bspline-joint. The pair exists to compare action spaces on identical demonstrations.

Action and observation space

raw actions (7,) = [pos(3), rotvec(3), gripper(1)]
   -> dataset expands to (10,) = [pos(3), rot6d(6), gripper(1)]
gripper: 0 = open, 1 = closed
key shape notes
wrist_image (3, 84, 84) RGB, RAW 640Γ—480 resized β€” not cropped
top_image (3, 84, 84) RGB, overhead
arm_pos (3,) TCP position, metres
arm_quat (4,) xyzw, restricted to the w >= 0 hemisphere
gripper_pos (1,) 0 = open, 1 = closed

rotation_rep: rotation_6d with abs_action: True, so _convert_actions expands the 7-D raw action to 10-D. infer_action_meta resolves this to single_yam_rot6d (verified on the published checkpoint).

Network output is (16, 11): column 0 is the knot vector in units of 10 Hz frames, columns 1–10 are control points.

Files

file size use
deploy_ema.ckpt 426 MB Inference. EMA weights only.
epoch0600_full.ckpt 1.5 GB model + ema_model + optimizer, for resuming/fine-tuning.

Training

data 50 episodes, 11,215 frames @ 10 Hz
hardware 1Γ— RTX 4090, ~1.6 h, 9.2 s/epoch, 175 batches/epoch
epochs / batch 601 / 64
optimizer AdamW, lr 1e-4, cosine, 500 warmup steps, EMA
scheduler DDIM, 100 train timesteps, 16 inference steps, epsilon prediction
B-spline degree 3, chunk_size 10, max_error 0.002, absolute knots

Loss: 0.829 (ep0) β†’ 0.014 (100) β†’ 0.009 (200) β†’ 0.006 (300) β†’ 0.005 (400) β†’ 0.003 (500–600), final in-epoch 0.00153.

These numbers are not comparable to the joint-space model's: different action space, units, and normalizer. Only the shape of each curve is meaningful on its own.

Rollout

cd ~/bspline-policy
export PYTHONPATH=$PWD/bspline_policy:$PWD/diffusion_policy:$PWD/real_env/yam_teleop

hf download Dimios45/yam-pick-duster-bspline-ee deploy_ema.ckpt --local-dir ./ckpt

# terminal 1 β€” arm server
sudo ip link set can_follower_r up type can bitrate 1000000
python real_env/yam_teleop/yam_server.py --channel can_follower_r

# terminal 2 β€” rollout
python real_env/yam_teleop/rollout_local_policy.py \
  --env yam --policy bspline \
  --ckpt-path ./ckpt/deploy_ema.ckpt \
  --diffusion-policy-dir $PWD/diffusion_policy \
  --control-freq 100 \
  --data-freq 10 \
  --origin-time-scale 10 \
  --predict-before-end 0.3 \
  --speed-up-times 1.0 \
  --save --output-dir data/rollouts_ee

Actions are decoded to arm_pos + arm_quat (from rot6d) + gripper and resolved by the pyroki velocity-IK step in yam_server at 100 Hz. This is the upstream single_yam_rot6d path, so no joint-space patches are needed for the action decoding itself β€” but the top_image camera still has to be wired into real_env.get_obs, since upstream RealEnv only populates wrist_image and a missing RGB key is silently replaced with a black frame rather than raising.

Flags that are not optional

flag value why
--origin-time-scale 10 Knots are in data-frame units; must equal the training rate. This dataset is 10 Hz β€” the joint-space counterpart is 25 Hz, so do not copy this flag between the two models. Using 25 here runs the arm at 2.5Γ— speed.
--data-freq 10 Must match the above.
--control-freq 100 Matches YAM_CONTROL_HZ.
--speed-up-times start at 1.0 Velocity scales linearly, acceleration quadratically.
--predict-before-end 0.3 (CPU), 0.06 (GPU) Must exceed inference latency or the arm stalls between chunks.

A 16-knot chunk spans ~1.1 s at 10 Hz, so this model replans ~2.5Γ— less often than the joint-space one.

Known kinematic caveat

The dataset card states these poses were computed from MuJoCo and disagree with bspline-policy's pyroki/URDF by up to ~9 mm. At deploy the IK will chase a target frame offset from the one the policy was trained against, so expect a systematic positional bias. The TCP frame is the flange origin with a fixed 90Β° z-rotation, ~13.5 cm from the actual grasp point. Resolve the frame mismatch before trusting fine manipulation, or use the joint-space model, which sidesteps kinematics entirely.

Do not mix this data with yam_server-recorded episodes without resolving that discrepancy.

Cameras and gripper

Trained uncropped (RAW 640Γ—480 β†’ 84Γ—84); reproduce that exactly at deployment. Gripper is 0 = open, 1 = closed β€” verify on hardware first (see the joint-space card for the one-line check).

Reproducing

cd real_env/yam_teleop && python convert_to_robomimic_hdf5.py \
  --input-dir ~/data/yam-pick-duster-ee \
  --output-path ../../data/yam_ee.hdf5

cd bspline_policy && python train.py \
  --config-name=yam_pick_ee_bspline \
  hydra.run.dir=../outputs/yam_pick_ee \
  training.resume=false logging.mode=offline \
  checkpoint.topk.k=601 dataloader.persistent_workers=True

Give this task config a cache_suffix distinct from the joint-space run's, or it will silently load the other run's zarr cache.

Citation

@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}
}
Downloads last month

-

Downloads are not tracked for this model. How to track
Video Preview
loading

Dataset used to train Dimios45/yam-pick-duster-bspline-ee

Paper for Dimios45/yam-pick-duster-bspline-ee