Instructions to use Bigenlight/diffusion_banana_in_pot_joint with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LeRobot
How to use Bigenlight/diffusion_banana_in_pot_joint with LeRobot:
- Notebooks
- Google Colab
- Kaggle
Upload folder using huggingface_hub
Browse files- README.md +409 -0
- assets/diffusion_joint_overfit_diag.png +0 -0
- config.json +96 -0
- model.safetensors +3 -0
- policy_postprocessor.json +32 -0
- policy_postprocessor_step_0_unnormalizer_processor.safetensors +3 -0
- policy_preprocessor.json +64 -0
- policy_preprocessor_step_3_normalizer_processor.safetensors +3 -0
- train_config.json +193 -0
README.md
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
library_name: lerobot
|
| 4 |
+
tags:
|
| 5 |
+
- robotics
|
| 6 |
+
- lerobot
|
| 7 |
+
- diffusion-policy
|
| 8 |
+
- imitation-learning
|
| 9 |
+
- ur7e
|
| 10 |
+
- manipulation
|
| 11 |
+
pipeline_tag: robotics
|
| 12 |
+
datasets:
|
| 13 |
+
- Bigenlight/banana_in_pot_lerobot_v3
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# Diffusion Policy — Put the right banana in the pot (UR7e, JOINT action space)
|
| 17 |
+
|
| 18 |
+
A **Diffusion Policy** (visuomotor DDPM, 1D-conv UNet denoiser) trained by imitation
|
| 19 |
+
learning to perform the manipulation task *"put the right banana in the pot"* on a
|
| 20 |
+
**Universal Robots UR7e** arm with two RGB cameras. Actions are **7-D absolute joint
|
| 21 |
+
targets** (6 UR joints in radians + gripper).
|
| 22 |
+
|
| 23 |
+
- **Policy:** LeRobot `DiffusionPolicy` — per-camera **ResNet18** visual encoder
|
| 24 |
+
(ImageNet-pretrained) + **SpatialSoftmax** keypoints, conditioning a **1D convolutional
|
| 25 |
+
UNet** denoiser. Receding-horizon action generation: `horizon = 64`, `n_obs_steps = 2`,
|
| 26 |
+
`n_action_steps = 32`.
|
| 27 |
+
- **Noise model:** **DDPM**, `num_train_timesteps = 100`, `beta_schedule =
|
| 28 |
+
squaredcos_cap_v2`, `prediction_type = epsilon` (ε-prediction), `clip_sample = true`.
|
| 29 |
+
- **Trained on:** [`Bigenlight/banana_in_pot_lerobot_v3`](https://huggingface.co/datasets/Bigenlight/banana_in_pot_lerobot_v3)
|
| 30 |
+
— 51 teleoperated episodes / 21,524 frames, UR7e follower + GELLO leader, 2 RGB cameras.
|
| 31 |
+
- **This checkpoint:** step **80,000** (best by open-loop held-out MAE; see
|
| 32 |
+
[Results](#results--the-headline-finding)).
|
| 33 |
+
- **Framework:** [LeRobot](https://github.com/huggingface/lerobot) v0.6.1.
|
| 34 |
+
|
| 35 |
+
> **Headline finding (read this first):** on a held-out split the diffusion **denoising
|
| 36 |
+
> `eval_loss` ROSE ~5× (0.029 → 0.149)** over training, which naively screams "severe
|
| 37 |
+
> overfitting". But the deployment-relevant **open-loop rollout MAE kept IMPROVING** to
|
| 38 |
+
> step 80k (0.119 → 0.085 rad). **For a Diffusion Policy the held-out denoising loss is a
|
| 39 |
+
> misleading overfit/early-stop signal — select checkpoints by open-loop MAE, not by
|
| 40 |
+
> `eval_loss`.** (Contrast the ACT sibling, where the two signals agreed.)
|
| 41 |
+
|
| 42 |
+
---
|
| 43 |
+
|
| 44 |
+
## Task & data
|
| 45 |
+
|
| 46 |
+
**"put the right banana in the pot."** The tabletop holds several distractor objects —
|
| 47 |
+
**two bananas, an apple, carrots/peppers, and a slice of watermelon** — plus a **silver
|
| 48 |
+
pot**. The operator must grasp the **RIGHT banana** (the target) and place it inside the
|
| 49 |
+
pot. Success = the right banana ends up inside the pot. Every demonstration is a success.
|
| 50 |
+
|
| 51 |
+
- **Dataset:** [`Bigenlight/banana_in_pot_lerobot_v3`](https://huggingface.co/datasets/Bigenlight/banana_in_pot_lerobot_v3)
|
| 52 |
+
(LeRobot v3.0 format).
|
| 53 |
+
- **Scale:** **51 episodes / 21,524 frames / 30 fps / ~12 min.**
|
| 54 |
+
- **Action / state space:** 7-D absolute joint (6 UR joints in radians + gripper), i.e.
|
| 55 |
+
`[cmd1..cmd6, grip_cmd]`. The gripper channel is effectively binary (open/close).
|
| 56 |
+
- **Cameras:** two RGB viewpoints (Intel RealSense D435 + D435if), captured at 1280×720
|
| 57 |
+
(720p) @ 30 fps, **RGB only** (no depth / IR). `cam1 ↔ cam2` order is fixed and must be
|
| 58 |
+
preserved at deploy time.
|
| 59 |
+
|
| 60 |
+
### Train / held-out split
|
| 61 |
+
|
| 62 |
+
Training used `--dataset.eval_split=0.117`, which holds out the **LAST
|
| 63 |
+
`ceil(51 × 0.117) = 6` episodes (indices 45–50)** as a true validation split and trains on
|
| 64 |
+
the other **45** episodes (0–44). The held-out episodes 45–50 are used both for the
|
| 65 |
+
in-training denoising `eval_loss` probe and for all offline open-loop evaluation below.
|
| 66 |
+
|
| 67 |
+
---
|
| 68 |
+
|
| 69 |
+
## Model architecture
|
| 70 |
+
|
| 71 |
+
LeRobot `DiffusionPolicy`. All values below are quoted directly from the checkpoint's
|
| 72 |
+
`config.json` / `train_config.json`.
|
| 73 |
+
|
| 74 |
+
**Observation encoder (vision):**
|
| 75 |
+
|
| 76 |
+
| Item | Value |
|
| 77 |
+
|---|---|
|
| 78 |
+
| Vision backbone | `resnet18` |
|
| 79 |
+
| Pretrained weights | `ResNet18_Weights.IMAGENET1K_V1` (ImageNet) |
|
| 80 |
+
| Per-camera encoder | `use_separate_rgb_encoder_per_camera = true` (separate ResNet18 per view) |
|
| 81 |
+
| Pooling | **SpatialSoftmax**, `spatial_softmax_num_keypoints = 32` |
|
| 82 |
+
| Group norm in encoder | `use_group_norm = false` (keeps BatchNorm from the pretrained backbone) |
|
| 83 |
+
| Cameras | 2 × RGB (`observation.images.cam1`, `observation.images.cam2`) |
|
| 84 |
+
| Network input resolution | **360 × 640** (`resize_shape = [360, 640]`; see [why](#why-the-two-non-default-flags-are-required)) |
|
| 85 |
+
| Crop | **OFF** — `crop_shape = null`, `crop_ratio = 1.0` (`crop_is_random` is moot with no crop) |
|
| 86 |
+
| State input | `observation.state`, shape `(7,)` |
|
| 87 |
+
|
| 88 |
+
> Note: `config.json` records the raw dataset image feature shape as `[3, 720, 1280]`, but
|
| 89 |
+
> the on-the-fly `Resize` to `[360, 640]` (`resize_shape`) means the network actually sees
|
| 90 |
+
> **360 × 640** frames. See the training section for why this must match at inference.
|
| 91 |
+
|
| 92 |
+
**Denoiser (conditional 1D-conv UNet):**
|
| 93 |
+
|
| 94 |
+
| Item | Value |
|
| 95 |
+
|---|---|
|
| 96 |
+
| Denoiser | 1D convolutional UNet (Diffusion Policy / Janner-style) |
|
| 97 |
+
| `down_dims` | `[512, 1024, 2048]` |
|
| 98 |
+
| `kernel_size` | `5` |
|
| 99 |
+
| `n_groups` (GroupNorm) | `8` |
|
| 100 |
+
| `diffusion_step_embed_dim` | `128` |
|
| 101 |
+
| FiLM conditioning | `use_film_scale_modulation = true` |
|
| 102 |
+
| `horizon` | `64` (prediction horizon, in frames) |
|
| 103 |
+
| `n_obs_steps` | `2` (observation context length) |
|
| 104 |
+
| `n_action_steps` | `32` (actions executed before replanning) |
|
| 105 |
+
|
| 106 |
+
**Diffusion process (noise scheduler):**
|
| 107 |
+
|
| 108 |
+
| Item | Value |
|
| 109 |
+
|---|---|
|
| 110 |
+
| `noise_scheduler_type` | `DDPM` |
|
| 111 |
+
| `num_train_timesteps` | `100` |
|
| 112 |
+
| `beta_schedule` | `squaredcos_cap_v2` (cosine) |
|
| 113 |
+
| `beta_start` / `beta_end` | `0.0001` / `0.02` |
|
| 114 |
+
| `prediction_type` | `epsilon` (predict noise) |
|
| 115 |
+
| `clip_sample` | `true`, `clip_sample_range = 1.0` |
|
| 116 |
+
| `num_inference_steps` | `null` in config → defaults to the full DDPM schedule at inference unless overridden (evals here used **DDIM, 10 steps** for speed — see [Usage](#usage--inference)) |
|
| 117 |
+
|
| 118 |
+
**Normalization (`normalization_mapping`):**
|
| 119 |
+
|
| 120 |
+
| Feature group | Mode |
|
| 121 |
+
|---|---|
|
| 122 |
+
| `VISUAL` (images) | `MEAN_STD` (ImageNet stats, `use_imagenet_stats = true`) |
|
| 123 |
+
| `STATE` (observation.state) | `MIN_MAX` |
|
| 124 |
+
| `ACTION` (action) | `MIN_MAX` |
|
| 125 |
+
|
| 126 |
+
Normalizer statistics are baked into the pre/post-processor pipelines saved alongside the
|
| 127 |
+
checkpoint (`policy_preprocessor.json` / `policy_postprocessor.json`), not into
|
| 128 |
+
`forward()`.
|
| 129 |
+
|
| 130 |
+
**I/O summary:**
|
| 131 |
+
|
| 132 |
+
| I/O | Spec |
|
| 133 |
+
|---|---|
|
| 134 |
+
| `observation.state` | `(7,)` — UR joints `q1..q6` (radians) + gripper position |
|
| 135 |
+
| `observation.images.cam1` / `cam2` | RGB, network input **360 × 640** |
|
| 136 |
+
| `action` | `(7,)` — `[cmd1..cmd6, grip_cmd]`, **absolute** joint targets (radians) + ~binary gripper |
|
| 137 |
+
|
| 138 |
+
---
|
| 139 |
+
|
| 140 |
+
## Training setup
|
| 141 |
+
|
| 142 |
+
Trained with `lerobot-train` (LeRobot 0.6.1). Exact invocation:
|
| 143 |
+
`train_diffusion_joint_valdiag.sh`. Values below are from that script and the saved
|
| 144 |
+
`train_config.json`.
|
| 145 |
+
|
| 146 |
+
| Item | Value |
|
| 147 |
+
|---|---|
|
| 148 |
+
| Policy | `diffusion` (`--policy.type=diffusion`) |
|
| 149 |
+
| Dataset | `banana_in_pot_lerobot_v3`, `--dataset.eval_split=0.117` (holds out eps 45–50) |
|
| 150 |
+
| Batch size | **8** |
|
| 151 |
+
| Steps | script requested 100,000; this release / analysis is the run **to 80,000** (`steps: 80000` in `train_config.json`); checkpoints saved every 10,000 |
|
| 152 |
+
| Optimizer | **Adam**, `lr = 1e-4`, `betas = [0.95, 0.999]`, `eps = 1e-8`, `weight_decay = 1e-6` |
|
| 153 |
+
| Grad clip | `grad_clip_norm = 10.0` |
|
| 154 |
+
| LR scheduler | **cosine** (`scheduler.type = diffuser`, `name = cosine`), `num_warmup_steps = 500` |
|
| 155 |
+
| Seed | **1000** |
|
| 156 |
+
| Precision | **fp32** (`use_amp = false`) |
|
| 157 |
+
| EMA | **none** (no EMA weights in this config) |
|
| 158 |
+
| Image transform | on-the-fly `Resize` to `[360, 640]`, `max_num_transforms = 1`, deterministic |
|
| 159 |
+
| `drop_n_last_frames` | **31** (non-default; see below) |
|
| 160 |
+
| `resize_shape` | `[360, 640]` (non-default; see below) |
|
| 161 |
+
| Workers | `num_workers = 4`, `prefetch_factor = 4`, `persistent_workers = true` |
|
| 162 |
+
| Eval probe | held-out denoising `eval_loss` every 2,000 steps; train loss logged every 200 |
|
| 163 |
+
| GPU | single **RTX 3060 12 GB**, ~**9.7 GB** used, ~**2.2 step/s** |
|
| 164 |
+
| W&B | disabled |
|
| 165 |
+
|
| 166 |
+
### Why the two non-default flags are required
|
| 167 |
+
|
| 168 |
+
Both `resize_shape=[360,640]` and `drop_n_last_frames=31` are **not** the LeRobot defaults;
|
| 169 |
+
they are mandatory for this dataset/config and encode real operational knowledge:
|
| 170 |
+
|
| 171 |
+
1. **`resize_shape=[360,640]` — SpatialSoftmax is shape-rigid.** The Diffusion Policy RGB
|
| 172 |
+
encoder ends in a `SpatialSoftmax` layer whose keypoint geometry is fixed to the spatial
|
| 173 |
+
dimensions of the feature map at build time. The network must therefore be *constructed*
|
| 174 |
+
for the exact input resolution it will ever see. Setting `resize_shape=[360,640]` builds
|
| 175 |
+
the encoder for 360×640 and — combined with crop being **off** (`crop_shape=null`) —
|
| 176 |
+
guarantees the training image path, the offline-eval image path, and any deploy image
|
| 177 |
+
path all feed the encoder identically. A mismatched resolution (or leaving crop on)
|
| 178 |
+
changes the SpatialSoftmax grid and breaks the model. The same 360×640 `Resize` is
|
| 179 |
+
reproduced in `eval_offline.py` (`build_image_transforms`).
|
| 180 |
+
|
| 181 |
+
2. **`drop_n_last_frames=31` — horizon / n_action off-by-one at episode ends.** The
|
| 182 |
+
trajectory sampler must not draw a window that runs past the end of an episode. With
|
| 183 |
+
`horizon=64`, `n_obs_steps=2`, and `n_action_steps=32`, the last valid start frame in an
|
| 184 |
+
episode has to leave room for the horizon, so the correct number of trailing frames to
|
| 185 |
+
drop is `horizon - n_action_steps - (n_obs_steps - 1) = 64 - 32 - 1 = 31`. Using the
|
| 186 |
+
default (7, tuned for the reference `horizon=16` config) would let the sampler pull
|
| 187 |
+
frames off the end of an episode and corrupt the action targets. **If you change
|
| 188 |
+
`horizon`/`n_obs_steps`/`n_action_steps`, recompute `drop_n_last_frames`.**
|
| 189 |
+
|
| 190 |
+
---
|
| 191 |
+
|
| 192 |
+
## Results & the headline finding
|
| 193 |
+
|
| 194 |
+

|
| 195 |
+
|
| 196 |
+
Offline **open-loop** evaluation on the held-out episodes **45–50** with `eval_offline.py`
|
| 197 |
+
(each logged observation is fed to `select_action`; the predicted action is compared to the
|
| 198 |
+
dataset ground truth). Sampling used **DDIM with 10 inference steps**
|
| 199 |
+
(`--scheduler DDIM --num-inference-steps 10`) for ~10× faster rollouts; DDIM is a valid
|
| 200 |
+
sampler for a DDPM-trained ε model (same beta schedule). `poseMAE` is the mean absolute
|
| 201 |
+
error over the 6 joint dims (radians); `gripAcc` is the binary gripper-open/close accuracy
|
| 202 |
+
(threshold 0.5); `overall L1` averages all 7 dims.
|
| 203 |
+
|
| 204 |
+
| checkpoint | poseMAE (rad) | gripAcc | overall L1 |
|
| 205 |
+
|---|---|---|---|
|
| 206 |
+
| 10k | 0.1193 | 0.729 | 0.1454 |
|
| 207 |
+
| 20k | 0.1037 | 0.888 | 0.1078 |
|
| 208 |
+
| 30k | 0.0921 | 0.919 | 0.0928 |
|
| 209 |
+
| 40k | 0.0907 | 0.949 | 0.0862 |
|
| 210 |
+
| 50k | 0.0865 | 0.942 | 0.0832 |
|
| 211 |
+
| 60k | 0.0849 | 0.944 | 0.0812 |
|
| 212 |
+
| 70k | 0.0855 | 0.951 | 0.0809 |
|
| 213 |
+
| **80k** ⭐ | **0.0845** | **0.953** | **0.0796** |
|
| 214 |
+
|
| 215 |
+
**Best checkpoint = 80k** (tied-best poseMAE, best gripAcc). Open-loop poseMAE improves
|
| 216 |
+
monotonically then **plateaus at ~0.085 rad from 60k onward** (60k/70k/80k =
|
| 217 |
+
0.0849/0.0855/0.0845, within eval noise); gripper accuracy climbs all the way to **0.953 @
|
| 218 |
+
80k**. There is **no open-loop overfitting through 80k**.
|
| 219 |
+
|
| 220 |
+
### The misleading `eval_loss` (the lesson)
|
| 221 |
+
|
| 222 |
+
During training the held-out **denoising `eval_loss`** (LeRobot's in-training validation
|
| 223 |
+
probe, computed under `policy.eval()` on eps 45–50) did the opposite of the rollout metric:
|
| 224 |
+
|
| 225 |
+
| step | train loss | held-out eval_loss |
|
| 226 |
+
|---|---|---|
|
| 227 |
+
| 2k | 0.0310 | 0.0361 |
|
| 228 |
+
| 4k | 0.0240 | 0.0289 (min region) |
|
| 229 |
+
| 6k | 0.0210 | 0.0303 |
|
| 230 |
+
| 20k | 0.0120 | 0.0399 |
|
| 231 |
+
| 40k | 0.0080 | 0.0660 |
|
| 232 |
+
| 60k | 0.0050 | 0.1272 |
|
| 233 |
+
| 80k | 0.0050 | ~0.1487 |
|
| 234 |
+
|
| 235 |
+
Read naively, the held-out `eval_loss` bottoms near step 4k–6k and then rises ~5×, so an
|
| 236 |
+
early-stop rule would pick **~step 6k** and declare "severe overfit". **That
|
| 237 |
+
recommendation is wrong for deployment:** the same held-out episodes, evaluated by
|
| 238 |
+
open-loop rollout, get *monotonically better* out to 80k.
|
| 239 |
+
|
| 240 |
+
**Why:** the denoising loss is a per-sample ε-regression on a *randomly re-sampled noise
|
| 241 |
+
vector and diffusion timestep at every forward pass* — it is (a) high-variance/stochastic
|
| 242 |
+
by construction and (b) only loosely coupled to closed-loop action quality. As the model
|
| 243 |
+
sharpens its learned action distribution, the average ε-MSE on unseen frames can rise even
|
| 244 |
+
while the *sampled* action trajectories become more accurate. **Takeaway: for a Diffusion
|
| 245 |
+
Policy, select checkpoints and early-stop by open-loop rollout MAE, not by held-out
|
| 246 |
+
denoising `eval_loss`.** (The ACT sibling did not show this divergence — there the two
|
| 247 |
+
signals agreed — so this is a diffusion-specific pitfall.)
|
| 248 |
+
|
| 249 |
+
---
|
| 250 |
+
|
| 251 |
+
## Usage / inference
|
| 252 |
+
|
| 253 |
+
### Load the policy (LeRobot 0.6.1)
|
| 254 |
+
|
| 255 |
+
Normalization is **not** baked into `forward()` in LeRobot 0.6.1 — it lives in the
|
| 256 |
+
pre/post-processor pipelines saved with the checkpoint. `select_action` returns a
|
| 257 |
+
**normalized** action; the post-processor converts it back to radians.
|
| 258 |
+
|
| 259 |
+
```python
|
| 260 |
+
import torch
|
| 261 |
+
from lerobot.configs import PreTrainedConfig
|
| 262 |
+
from lerobot.policies.factory import get_policy_class, make_pre_post_processors
|
| 263 |
+
|
| 264 |
+
CKPT = "Bigenlight/diffusion_banana_in_pot_joint"
|
| 265 |
+
device = "cuda"
|
| 266 |
+
|
| 267 |
+
# (optional) speed up sampling: DDIM with 10 steps instead of the full DDPM schedule.
|
| 268 |
+
cfg = PreTrainedConfig.from_pretrained(CKPT)
|
| 269 |
+
cfg.pretrained_path = CKPT
|
| 270 |
+
cfg.device = device
|
| 271 |
+
cfg.noise_scheduler_type = "DDIM" # valid sampler for a DDPM-trained epsilon model
|
| 272 |
+
cfg.num_inference_steps = 10 # ~10x faster rollouts
|
| 273 |
+
|
| 274 |
+
policy = get_policy_class(cfg.type).from_pretrained(CKPT, config=cfg) # -> DiffusionPolicy
|
| 275 |
+
policy.to(device)
|
| 276 |
+
policy.eval()
|
| 277 |
+
|
| 278 |
+
preprocessor, postprocessor = make_pre_post_processors(
|
| 279 |
+
policy_cfg=cfg,
|
| 280 |
+
pretrained_path=CKPT,
|
| 281 |
+
preprocessor_overrides={"device_processor": {"device": device}},
|
| 282 |
+
)
|
| 283 |
+
```
|
| 284 |
+
|
| 285 |
+
### Run the control loop
|
| 286 |
+
|
| 287 |
+
Build the observation dict exactly as training did: joint state `(7,)` plus **both**
|
| 288 |
+
cameras as RGB CHW tensors in `[0, 1]`, **resized to 360×640** (aspect-preserving
|
| 289 |
+
half-resolution). `cam1`/`cam2` must map to the same physical viewpoints as at collection.
|
| 290 |
+
|
| 291 |
+
```python
|
| 292 |
+
policy.reset() # once at the start of each episode/rollout
|
| 293 |
+
preprocessor.reset()
|
| 294 |
+
postprocessor.reset()
|
| 295 |
+
|
| 296 |
+
# obs = {
|
| 297 |
+
# "observation.state": state_7, # (7,) float32, radians + gripper
|
| 298 |
+
# "observation.images.cam1": img1_chw, # (3, 360, 640) float32 in [0,1]
|
| 299 |
+
# "observation.images.cam2": img2_chw, # (3, 360, 640) float32 in [0,1]
|
| 300 |
+
# "task": "put the right banana in the pot",
|
| 301 |
+
# }
|
| 302 |
+
|
| 303 |
+
with torch.inference_mode():
|
| 304 |
+
proc = preprocessor(obs) # rename -> add batch dim -> device -> normalize
|
| 305 |
+
action = policy.select_action(proc) # (1, 7) NORMALIZED
|
| 306 |
+
action = postprocessor(action) # (1, 7) radians, on cpu
|
| 307 |
+
q_target = action.squeeze(0).numpy() # (7,) -> [cmd1..cmd6, grip_cmd]
|
| 308 |
+
```
|
| 309 |
+
|
| 310 |
+
`select_action` returns **one** action per call from an internal queue. Because
|
| 311 |
+
`n_action_steps = 32`, the policy denoises a fresh action sequence, executes 32 actions
|
| 312 |
+
from it, then replans (with `n_obs_steps = 2` frames of observation context). Call
|
| 313 |
+
`policy.reset()` at the start of every episode to clear that queue. The gripper channel
|
| 314 |
+
`grip_cmd` is ~binary — threshold at `> 0.5 → close` and map to your gripper driver.
|
| 315 |
+
|
| 316 |
+
### Reproduce the offline evaluation
|
| 317 |
+
|
| 318 |
+
The repo's `eval_offline.py` runs the exact open-loop protocol used for the results table
|
| 319 |
+
(same 360×640 `Resize`, same normalization via the saved processors):
|
| 320 |
+
|
| 321 |
+
```bash
|
| 322 |
+
python eval_offline.py \
|
| 323 |
+
--checkpoint outputs/train/diffusion_joint_val_diag/checkpoints/080000/pretrained_model \
|
| 324 |
+
--episodes 45,46,47,48,49,50 \
|
| 325 |
+
--device cuda \
|
| 326 |
+
--scheduler DDIM --num-inference-steps 10 \
|
| 327 |
+
--out eval_out_diffusion_80k
|
| 328 |
+
```
|
| 329 |
+
|
| 330 |
+
`--scheduler DDIM --num-inference-steps 10` gives the ~10× rollout speedup; omit them to
|
| 331 |
+
sample with the full trained DDPM schedule (`num_train_timesteps = 100`).
|
| 332 |
+
|
| 333 |
+
---
|
| 334 |
+
|
| 335 |
+
## Deployment on a real UR7e
|
| 336 |
+
|
| 337 |
+
Closed-loop deployment targets a real **UR7e** through the ROS 2 Humble stack in
|
| 338 |
+
[**Bigenlight/gello_software**](https://github.com/Bigenlight/gello_software) — the same
|
| 339 |
+
stack used to collect this dataset (UR7e follower + GELLO leader, dual RealSense cameras).
|
| 340 |
+
LeRobot ships **no UR robot class**, so deployment requires a small **policy deploy node**
|
| 341 |
+
that, each control tick (target **30 Hz**):
|
| 342 |
+
|
| 343 |
+
1. reads the UR7e measured joints + gripper → `observation.state` `(7,)`;
|
| 344 |
+
2. grabs both camera frames, BGR→RGB, **resizes to 360×640**, CHW `[0,1]` →
|
| 345 |
+
`observation.images.cam1` / `cam2`;
|
| 346 |
+
3. `preprocessor → policy.select_action → postprocessor` → `q_target` (7,);
|
| 347 |
+
4. streams `q_target[:6]` to the arm (e.g. `servoJ` via `ur_rtde` / the ROS 2 driver) and
|
| 348 |
+
drives the gripper from `grip_cmd`.
|
| 349 |
+
|
| 350 |
+
This diffusion policy would need a **deploy node analogous to the ACT one**
|
| 351 |
+
([`Bigenlight/act_banana_in_pot`](https://huggingface.co/Bigenlight/act_banana_in_pot)),
|
| 352 |
+
with two differences: (a) the action queue length is `n_action_steps = 32` (not ACT's 100),
|
| 353 |
+
so it replans ~every 32 ticks; and (b) inference runs a diffusion sampler — use **DDIM /
|
| 354 |
+
10 steps** to keep per-replan latency low enough for 30 Hz.
|
| 355 |
+
|
| 356 |
+
**Safety — actions are ABSOLUTE joint positions:**
|
| 357 |
+
|
| 358 |
+
1. **Start near the dataset initial pose** before enabling the policy, or the first absolute
|
| 359 |
+
command is a large jump.
|
| 360 |
+
2. **First-command jump guard:** if `max(|q_target − getActualQ()|)` exceeds a small
|
| 361 |
+
threshold (~0.15 rad), **abort**.
|
| 362 |
+
3. **Clamp per-tick joint change** and clamp to UR software joint limits; run at reduced
|
| 363 |
+
speed for first trials with a hand on the **E-stop**.
|
| 364 |
+
4. **`cam1`/`cam2` mapping is fixed** — swap the two views and the policy fails silently.
|
| 365 |
+
Verify wiring every session.
|
| 366 |
+
|
| 367 |
+
---
|
| 368 |
+
|
| 369 |
+
## Limitations & intended use
|
| 370 |
+
|
| 371 |
+
- **Small, single-task lab dataset:** 51 demonstrations, one scene layout, one operator.
|
| 372 |
+
Expect limited generalization to novel object arrangements, lighting, or camera placement.
|
| 373 |
+
- **Success-only demonstrations:** no failure/recovery data; not suited as-is for methods
|
| 374 |
+
that need negative examples.
|
| 375 |
+
- **Offline metrics only:** the best checkpoint (80k) reaches **held-out poseMAE ≈ 0.085
|
| 376 |
+
rad** and gripper accuracy ≈ 0.953 in open-loop rollout. These are *not* closed-loop task
|
| 377 |
+
success rates — real closed-loop success on hardware has not been measured here and must
|
| 378 |
+
be validated on the arm.
|
| 379 |
+
- **Absolute-joint action space** demands the safety guards above; the policy was only ever
|
| 380 |
+
conditioned on states near the data-collection start pose.
|
| 381 |
+
- **Not for production.** Intended for research in imitation learning / diffusion policies
|
| 382 |
+
for robot manipulation. Workspace-, robot-, and camera-specific.
|
| 383 |
+
- The ResNet18 encoders are **ImageNet-pretrained** (not robotics-pretrained); the UNet
|
| 384 |
+
denoiser is trained from scratch on this task.
|
| 385 |
+
|
| 386 |
+
---
|
| 387 |
+
|
| 388 |
+
## Links
|
| 389 |
+
|
| 390 |
+
- **Dataset:** [`Bigenlight/banana_in_pot_lerobot_v3`](https://huggingface.co/datasets/Bigenlight/banana_in_pot_lerobot_v3)
|
| 391 |
+
- **Experiments repo:** [github.com/Bigenlight/banana-in-pot-experiments](https://github.com/Bigenlight/banana-in-pot-experiments)
|
| 392 |
+
- **ACT sibling model:** [`Bigenlight/act_banana_in_pot`](https://huggingface.co/Bigenlight/act_banana_in_pot)
|
| 393 |
+
- **Deployment stack (ROS 2 Humble):** [github.com/Bigenlight/gello_software](https://github.com/Bigenlight/gello_software)
|
| 394 |
+
- **Framework:** [LeRobot](https://github.com/huggingface/lerobot) v0.6.1
|
| 395 |
+
|
| 396 |
+
## Citation
|
| 397 |
+
|
| 398 |
+
```bibtex
|
| 399 |
+
@misc{theo2026bananainpotdiffusion,
|
| 400 |
+
title = {Diffusion Policy for "put the right banana in the pot"
|
| 401 |
+
(UR7e, joint action space)},
|
| 402 |
+
author = {Theo and {Bigenlight}},
|
| 403 |
+
year = {2026},
|
| 404 |
+
howpublished = {\url{https://huggingface.co/Bigenlight/diffusion_banana_in_pot_joint}},
|
| 405 |
+
note = {LeRobot 0.6.1 DiffusionPolicy, trained on banana_in_pot_lerobot_v3}
|
| 406 |
+
}
|
| 407 |
+
```
|
| 408 |
+
|
| 409 |
+
License: **Apache-2.0**.
|
assets/diffusion_joint_overfit_diag.png
ADDED
|
config.json
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"type": "diffusion",
|
| 3 |
+
"n_obs_steps": 2,
|
| 4 |
+
"input_features": {
|
| 5 |
+
"observation.state": {
|
| 6 |
+
"type": "STATE",
|
| 7 |
+
"shape": [
|
| 8 |
+
7
|
| 9 |
+
]
|
| 10 |
+
},
|
| 11 |
+
"observation.images.cam1": {
|
| 12 |
+
"type": "VISUAL",
|
| 13 |
+
"shape": [
|
| 14 |
+
3,
|
| 15 |
+
720,
|
| 16 |
+
1280
|
| 17 |
+
]
|
| 18 |
+
},
|
| 19 |
+
"observation.images.cam2": {
|
| 20 |
+
"type": "VISUAL",
|
| 21 |
+
"shape": [
|
| 22 |
+
3,
|
| 23 |
+
720,
|
| 24 |
+
1280
|
| 25 |
+
]
|
| 26 |
+
}
|
| 27 |
+
},
|
| 28 |
+
"output_features": {
|
| 29 |
+
"action": {
|
| 30 |
+
"type": "ACTION",
|
| 31 |
+
"shape": [
|
| 32 |
+
7
|
| 33 |
+
]
|
| 34 |
+
}
|
| 35 |
+
},
|
| 36 |
+
"device": "cuda",
|
| 37 |
+
"use_amp": false,
|
| 38 |
+
"use_peft": false,
|
| 39 |
+
"push_to_hub": false,
|
| 40 |
+
"repo_id": null,
|
| 41 |
+
"private": null,
|
| 42 |
+
"tags": null,
|
| 43 |
+
"license": null,
|
| 44 |
+
"pretrained_path": "outputs/train/diffusion_joint_val_diag/checkpoints/last/pretrained_model",
|
| 45 |
+
"pretrained_revision": null,
|
| 46 |
+
"horizon": 64,
|
| 47 |
+
"n_action_steps": 32,
|
| 48 |
+
"normalization_mapping": {
|
| 49 |
+
"VISUAL": "MEAN_STD",
|
| 50 |
+
"STATE": "MIN_MAX",
|
| 51 |
+
"ACTION": "MIN_MAX"
|
| 52 |
+
},
|
| 53 |
+
"drop_n_last_frames": 31,
|
| 54 |
+
"vision_backbone": "resnet18",
|
| 55 |
+
"resize_shape": [
|
| 56 |
+
360,
|
| 57 |
+
640
|
| 58 |
+
],
|
| 59 |
+
"crop_ratio": 1.0,
|
| 60 |
+
"crop_shape": null,
|
| 61 |
+
"crop_is_random": true,
|
| 62 |
+
"pretrained_backbone_weights": "ResNet18_Weights.IMAGENET1K_V1",
|
| 63 |
+
"use_group_norm": false,
|
| 64 |
+
"spatial_softmax_num_keypoints": 32,
|
| 65 |
+
"use_separate_rgb_encoder_per_camera": true,
|
| 66 |
+
"down_dims": [
|
| 67 |
+
512,
|
| 68 |
+
1024,
|
| 69 |
+
2048
|
| 70 |
+
],
|
| 71 |
+
"kernel_size": 5,
|
| 72 |
+
"n_groups": 8,
|
| 73 |
+
"diffusion_step_embed_dim": 128,
|
| 74 |
+
"use_film_scale_modulation": true,
|
| 75 |
+
"noise_scheduler_type": "DDPM",
|
| 76 |
+
"num_train_timesteps": 100,
|
| 77 |
+
"beta_schedule": "squaredcos_cap_v2",
|
| 78 |
+
"beta_start": 0.0001,
|
| 79 |
+
"beta_end": 0.02,
|
| 80 |
+
"prediction_type": "epsilon",
|
| 81 |
+
"clip_sample": true,
|
| 82 |
+
"clip_sample_range": 1.0,
|
| 83 |
+
"num_inference_steps": null,
|
| 84 |
+
"compile_model": false,
|
| 85 |
+
"compile_mode": "reduce-overhead",
|
| 86 |
+
"do_mask_loss_for_padding": false,
|
| 87 |
+
"optimizer_lr": 0.0001,
|
| 88 |
+
"optimizer_betas": [
|
| 89 |
+
0.95,
|
| 90 |
+
0.999
|
| 91 |
+
],
|
| 92 |
+
"optimizer_eps": 1e-08,
|
| 93 |
+
"optimizer_weight_decay": 1e-06,
|
| 94 |
+
"scheduler_name": "cosine",
|
| 95 |
+
"scheduler_warmup_steps": 500
|
| 96 |
+
}
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d4722b60caee5d76d004a37e16b2d7adecc1668f79703259ea7260fc9c723c57
|
| 3 |
+
size 1111651364
|
policy_postprocessor.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "policy_postprocessor",
|
| 3 |
+
"steps": [
|
| 4 |
+
{
|
| 5 |
+
"registry_name": "unnormalizer_processor",
|
| 6 |
+
"config": {
|
| 7 |
+
"eps": 1e-08,
|
| 8 |
+
"features": {
|
| 9 |
+
"action": {
|
| 10 |
+
"type": "ACTION",
|
| 11 |
+
"shape": [
|
| 12 |
+
7
|
| 13 |
+
]
|
| 14 |
+
}
|
| 15 |
+
},
|
| 16 |
+
"norm_map": {
|
| 17 |
+
"VISUAL": "MEAN_STD",
|
| 18 |
+
"STATE": "MIN_MAX",
|
| 19 |
+
"ACTION": "MIN_MAX"
|
| 20 |
+
}
|
| 21 |
+
},
|
| 22 |
+
"state_file": "policy_postprocessor_step_0_unnormalizer_processor.safetensors"
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"registry_name": "device_processor",
|
| 26 |
+
"config": {
|
| 27 |
+
"device": "cpu",
|
| 28 |
+
"float_dtype": null
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
]
|
| 32 |
+
}
|
policy_postprocessor_step_0_unnormalizer_processor.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:035168b1aa56baa0e57608cf3058bcbe872d35d7a945a29271a2482c31fd094f
|
| 3 |
+
size 7608
|
policy_preprocessor.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "policy_preprocessor",
|
| 3 |
+
"steps": [
|
| 4 |
+
{
|
| 5 |
+
"registry_name": "rename_observations_processor",
|
| 6 |
+
"config": {
|
| 7 |
+
"rename_map": {}
|
| 8 |
+
}
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"registry_name": "to_batch_processor",
|
| 12 |
+
"config": {}
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"registry_name": "device_processor",
|
| 16 |
+
"config": {
|
| 17 |
+
"device": "cuda",
|
| 18 |
+
"float_dtype": null
|
| 19 |
+
}
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"registry_name": "normalizer_processor",
|
| 23 |
+
"config": {
|
| 24 |
+
"eps": 1e-08,
|
| 25 |
+
"features": {
|
| 26 |
+
"observation.state": {
|
| 27 |
+
"type": "STATE",
|
| 28 |
+
"shape": [
|
| 29 |
+
7
|
| 30 |
+
]
|
| 31 |
+
},
|
| 32 |
+
"observation.images.cam1": {
|
| 33 |
+
"type": "VISUAL",
|
| 34 |
+
"shape": [
|
| 35 |
+
3,
|
| 36 |
+
720,
|
| 37 |
+
1280
|
| 38 |
+
]
|
| 39 |
+
},
|
| 40 |
+
"observation.images.cam2": {
|
| 41 |
+
"type": "VISUAL",
|
| 42 |
+
"shape": [
|
| 43 |
+
3,
|
| 44 |
+
720,
|
| 45 |
+
1280
|
| 46 |
+
]
|
| 47 |
+
},
|
| 48 |
+
"action": {
|
| 49 |
+
"type": "ACTION",
|
| 50 |
+
"shape": [
|
| 51 |
+
7
|
| 52 |
+
]
|
| 53 |
+
}
|
| 54 |
+
},
|
| 55 |
+
"norm_map": {
|
| 56 |
+
"VISUAL": "MEAN_STD",
|
| 57 |
+
"STATE": "MIN_MAX",
|
| 58 |
+
"ACTION": "MIN_MAX"
|
| 59 |
+
}
|
| 60 |
+
},
|
| 61 |
+
"state_file": "policy_preprocessor_step_3_normalizer_processor.safetensors"
|
| 62 |
+
}
|
| 63 |
+
]
|
| 64 |
+
}
|
policy_preprocessor_step_3_normalizer_processor.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1798a108428d8b529f04a915825c270ed877b2489c744abe55164ecf13f43604
|
| 3 |
+
size 7616
|
train_config.json
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"dataset": {
|
| 3 |
+
"repo_id": "theo/banana_in_pot",
|
| 4 |
+
"root": "./banana_in_pot_lerobot",
|
| 5 |
+
"episodes": null,
|
| 6 |
+
"image_transforms": {
|
| 7 |
+
"enable": true,
|
| 8 |
+
"max_num_transforms": 1,
|
| 9 |
+
"random_order": false,
|
| 10 |
+
"tfs": {
|
| 11 |
+
"resize": {
|
| 12 |
+
"weight": 1.0,
|
| 13 |
+
"type": "Resize",
|
| 14 |
+
"kwargs": {
|
| 15 |
+
"size": [
|
| 16 |
+
360,
|
| 17 |
+
640
|
| 18 |
+
]
|
| 19 |
+
}
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
},
|
| 23 |
+
"revision": null,
|
| 24 |
+
"use_imagenet_stats": true,
|
| 25 |
+
"video_backend": "torchcodec",
|
| 26 |
+
"return_uint8": false,
|
| 27 |
+
"depth_output_unit": "mm",
|
| 28 |
+
"streaming": false,
|
| 29 |
+
"eval_split": 0.117
|
| 30 |
+
},
|
| 31 |
+
"env": null,
|
| 32 |
+
"policy": {
|
| 33 |
+
"type": "diffusion",
|
| 34 |
+
"n_obs_steps": 2,
|
| 35 |
+
"input_features": {
|
| 36 |
+
"observation.state": {
|
| 37 |
+
"type": "STATE",
|
| 38 |
+
"shape": [
|
| 39 |
+
7
|
| 40 |
+
]
|
| 41 |
+
},
|
| 42 |
+
"observation.images.cam1": {
|
| 43 |
+
"type": "VISUAL",
|
| 44 |
+
"shape": [
|
| 45 |
+
3,
|
| 46 |
+
720,
|
| 47 |
+
1280
|
| 48 |
+
]
|
| 49 |
+
},
|
| 50 |
+
"observation.images.cam2": {
|
| 51 |
+
"type": "VISUAL",
|
| 52 |
+
"shape": [
|
| 53 |
+
3,
|
| 54 |
+
720,
|
| 55 |
+
1280
|
| 56 |
+
]
|
| 57 |
+
}
|
| 58 |
+
},
|
| 59 |
+
"output_features": {
|
| 60 |
+
"action": {
|
| 61 |
+
"type": "ACTION",
|
| 62 |
+
"shape": [
|
| 63 |
+
7
|
| 64 |
+
]
|
| 65 |
+
}
|
| 66 |
+
},
|
| 67 |
+
"device": "cuda",
|
| 68 |
+
"use_amp": false,
|
| 69 |
+
"use_peft": false,
|
| 70 |
+
"push_to_hub": false,
|
| 71 |
+
"repo_id": null,
|
| 72 |
+
"private": null,
|
| 73 |
+
"tags": null,
|
| 74 |
+
"license": null,
|
| 75 |
+
"pretrained_path": "outputs/train/diffusion_joint_val_diag/checkpoints/last/pretrained_model",
|
| 76 |
+
"pretrained_revision": null,
|
| 77 |
+
"horizon": 64,
|
| 78 |
+
"n_action_steps": 32,
|
| 79 |
+
"normalization_mapping": {
|
| 80 |
+
"VISUAL": "MEAN_STD",
|
| 81 |
+
"STATE": "MIN_MAX",
|
| 82 |
+
"ACTION": "MIN_MAX"
|
| 83 |
+
},
|
| 84 |
+
"drop_n_last_frames": 31,
|
| 85 |
+
"vision_backbone": "resnet18",
|
| 86 |
+
"resize_shape": [
|
| 87 |
+
360,
|
| 88 |
+
640
|
| 89 |
+
],
|
| 90 |
+
"crop_ratio": 1.0,
|
| 91 |
+
"crop_shape": null,
|
| 92 |
+
"crop_is_random": true,
|
| 93 |
+
"pretrained_backbone_weights": "ResNet18_Weights.IMAGENET1K_V1",
|
| 94 |
+
"use_group_norm": false,
|
| 95 |
+
"spatial_softmax_num_keypoints": 32,
|
| 96 |
+
"use_separate_rgb_encoder_per_camera": true,
|
| 97 |
+
"down_dims": [
|
| 98 |
+
512,
|
| 99 |
+
1024,
|
| 100 |
+
2048
|
| 101 |
+
],
|
| 102 |
+
"kernel_size": 5,
|
| 103 |
+
"n_groups": 8,
|
| 104 |
+
"diffusion_step_embed_dim": 128,
|
| 105 |
+
"use_film_scale_modulation": true,
|
| 106 |
+
"noise_scheduler_type": "DDPM",
|
| 107 |
+
"num_train_timesteps": 100,
|
| 108 |
+
"beta_schedule": "squaredcos_cap_v2",
|
| 109 |
+
"beta_start": 0.0001,
|
| 110 |
+
"beta_end": 0.02,
|
| 111 |
+
"prediction_type": "epsilon",
|
| 112 |
+
"clip_sample": true,
|
| 113 |
+
"clip_sample_range": 1.0,
|
| 114 |
+
"num_inference_steps": null,
|
| 115 |
+
"compile_model": false,
|
| 116 |
+
"compile_mode": "reduce-overhead",
|
| 117 |
+
"do_mask_loss_for_padding": false,
|
| 118 |
+
"optimizer_lr": 0.0001,
|
| 119 |
+
"optimizer_betas": [
|
| 120 |
+
0.95,
|
| 121 |
+
0.999
|
| 122 |
+
],
|
| 123 |
+
"optimizer_eps": 1e-08,
|
| 124 |
+
"optimizer_weight_decay": 1e-06,
|
| 125 |
+
"scheduler_name": "cosine",
|
| 126 |
+
"scheduler_warmup_steps": 500
|
| 127 |
+
},
|
| 128 |
+
"reward_model": null,
|
| 129 |
+
"output_dir": "outputs/train/diffusion_joint_val_diag",
|
| 130 |
+
"job_name": "diffusion_joint_val_diag",
|
| 131 |
+
"resume": true,
|
| 132 |
+
"seed": 1000,
|
| 133 |
+
"cudnn_deterministic": false,
|
| 134 |
+
"num_workers": 4,
|
| 135 |
+
"batch_size": 8,
|
| 136 |
+
"prefetch_factor": 4,
|
| 137 |
+
"persistent_workers": true,
|
| 138 |
+
"steps": 80000,
|
| 139 |
+
"env_eval_freq": 20000,
|
| 140 |
+
"log_freq": 200,
|
| 141 |
+
"eval_steps": 2000,
|
| 142 |
+
"max_eval_samples": 0,
|
| 143 |
+
"tolerance_s": 0.0001,
|
| 144 |
+
"save_checkpoint": true,
|
| 145 |
+
"save_freq": 10000,
|
| 146 |
+
"use_policy_training_preset": true,
|
| 147 |
+
"optimizer": {
|
| 148 |
+
"type": "adam",
|
| 149 |
+
"lr": 0.0001,
|
| 150 |
+
"weight_decay": 1e-06,
|
| 151 |
+
"grad_clip_norm": 10.0,
|
| 152 |
+
"betas": [
|
| 153 |
+
0.95,
|
| 154 |
+
0.999
|
| 155 |
+
],
|
| 156 |
+
"eps": 1e-08
|
| 157 |
+
},
|
| 158 |
+
"scheduler": {
|
| 159 |
+
"type": "diffuser",
|
| 160 |
+
"num_warmup_steps": 500,
|
| 161 |
+
"name": "cosine"
|
| 162 |
+
},
|
| 163 |
+
"eval": {
|
| 164 |
+
"n_episodes": 50,
|
| 165 |
+
"batch_size": 14,
|
| 166 |
+
"use_async_envs": true,
|
| 167 |
+
"recording": false,
|
| 168 |
+
"recording_repo_id": null,
|
| 169 |
+
"recording_private": false
|
| 170 |
+
},
|
| 171 |
+
"wandb": {
|
| 172 |
+
"enable": false,
|
| 173 |
+
"disable_artifact": false,
|
| 174 |
+
"project": "lerobot",
|
| 175 |
+
"entity": null,
|
| 176 |
+
"notes": null,
|
| 177 |
+
"run_id": null,
|
| 178 |
+
"mode": null,
|
| 179 |
+
"add_tags": true
|
| 180 |
+
},
|
| 181 |
+
"peft": null,
|
| 182 |
+
"job": {
|
| 183 |
+
"target": null,
|
| 184 |
+
"image": "huggingface/lerobot-gpu:latest",
|
| 185 |
+
"timeout": "2d",
|
| 186 |
+
"detach": false,
|
| 187 |
+
"tags": []
|
| 188 |
+
},
|
| 189 |
+
"save_checkpoint_to_hub": false,
|
| 190 |
+
"sample_weighting": null,
|
| 191 |
+
"rename_map": {},
|
| 192 |
+
"checkpoint_path": "outputs/train/diffusion_joint_val_diag/checkpoints/last"
|
| 193 |
+
}
|