File size: 7,996 Bytes
5d08972
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# Turning VACE output into a trainable dataset β€” everything needed

After `run_aug32.sh` finishes you have per-episode videos. Training needs a LeRobot v2.1
"gr00t_views" dataset. This is the full inventory for doing that conversion on a machine
that has nothing but this repo.

Written 2026-09-19. The MimicGen side of the same job is documented in
`baseline/mimicgen/TRAINING_DATASET_PREP.md`; **the two are not the same procedure** β€” see
"Why VACE is the easy case" below.

---

## Why VACE is the easy case

VACE repaints pixels; it never changes the trajectory. So the augmented episode's
state/action rows are **the source episode's own rows, unchanged**. The builder copies that
parquet, rewrites indices and task ids, and links the new videos in. No simulator, no
re-render, no MuJoCo.

MimicGen is the opposite: every episode is a new rollout, so its parquet has to be rebuilt
from the generation HDF5 by replaying it in the simulator (~2 h for 256 episodes).

| | VACE | MimicGen |
|---|---|---|
| builder | `build_vace_objwise_gr00t_dataset.py` | `render_to_gr00t.py` |
| parquet | copied from the source episode | rebuilt from HDF5 |
| video | symlinked from the VACE output | re-rendered in MuJoCo |
| needs robosuite/robocasa/mujoco | **no** | yes |
| runtime for 256 eps | minutes | ~2 h |

---

## What to bring

### 1. The builder and its checker

```
train_robocasa/scripts/dataset_build/build_vace_objwise_gr00t_dataset.py
train_robocasa/scripts/dataset_build/repair_mimicgen_task_ids.py      # also needed here
train_robocasa/scripts/dataset_build/verify_geom_objwise_gr00t_dataset.py
train_robocasa/scripts/dataset_build/merge_gr00t_view_datasets.py     # only if merging with the 502 base
train_robocasa/scripts/dataset_build/verify_merged_gr00t_dataset.py
```

Only `numpy` and `pandas` beyond the standard library, plus `ffmpeg` on `PATH` for the
optional three-view preview (`--skip-three-view` avoids even that).

### 2. Inputs

| `--flag` | what | where it comes from |
|---|---|---|
| `--vace-root` | the VACE output tree, one directory per assignment holding `work_<view>/<inference_dir>/out_video.mp4` | `run_aug32.sh`'s work dir |
| `--manifest` | `assignments.jsonl` (repeatable) | `aug32/build_aug32_assignments.py` |
| `--source-root` | the original RoboCasa dataset β€” **the parquet is taken from here** | `datasets_pack/source_episodes.tar` |
| `--canonical-meta-source` | a dataset whose `info.json` / `modality.json` / `view_manifest.json` contract is copied | **`dataset_build/canonical_meta/`**, bundled here |
| `--output` | where the dataset is written | your choice |

The defaults all point at the original machine, so **pass all five explicitly**.

### The 502-episode gr00t_views set is NOT needed

Neither for training (we do not merge) nor for conversion:

- its `meta/` was the only thing `--canonical-meta-source` reads, and those six files are
  bundled at `dataset_build/canonical_meta/` (96 KB);
- the state/action rows come from `--source-root`, i.e. `pickplace_target_human`, whose
  parquet already carries **state 16-d and action 12-d** β€” the same schema as gr00t_views.

`--canonical-meta-source` expects a *dataset directory* and reads `<dir>/meta/`, so
`dataset_build/canonical_dataset/` is set up for exactly that β€” its `meta` is a symlink to
`canonical_meta`. Pass `--canonical-meta-source dataset_build/canonical_dataset`.

### 3. Manifest fields the builder reads

`case_dir`, `episode_index`, `provenance`, and the whole `source` block (episode, task,
`lang`, `source_mesh`, `source_category`, per-camera `videos`). `build_aug32_assignments.py`
writes all of them.

`provenance` decides what gets in:

```python
row.setdefault("provenance", "existing")
if row["provenance"] == "random_fill" and not include_random_fill:
    continue          # dropped
```

Our builder marks same-category assignments `existing` and the random remainder
`random_fill`. With 32 per target that is **59 same-category and 197 random**, so the
default keeps only 59. **Pass `--include-random-fill` to get all 256.**

---

## Run it

```bash
python build_vace_objwise_gr00t_dataset.py \
    --vace-root        /path/to/vace_aug32_work \
    --manifest         /path/to/vace_aug32_work/assignments.jsonl \
    --source-root      $PWD/data/pickplace_target_human \
    --canonical-meta-source $PWD/dataset_build/canonical_dataset \
    --output           /path/to/gr00t_views/vace_per_target_32_256eps \
    --include-random-fill \
    --skip-three-view

python repair_mimicgen_task_ids.py --dataset /path/to/gr00t_views/vace_per_target_32_256eps
python verify_geom_objwise_gr00t_dataset.py --dataset /path/to/gr00t_views/vace_per_target_32_256eps
```

`--copy-videos` materialises the videos instead of symlinking β€” needed if the dataset will
be moved away from the VACE output tree.

### `repair_mimicgen_task_ids.py` is not optional

Its docstring: the writer "stores all parquet task columns as 0 while episodes.jsonl stores
the intended language per episode", and training reads the task id from the parquet β€” so
without this pass **every augmented episode trains as task 0** and the language conditioning
silently collapses. It also renames the modality keys to the canonical
`left_view` / `right_view` / `wrist_view` that `single_panda_gripper` expects.

---

## What the result must look like

```
<output>/
  meta/{info,modality,episodes,tasks,stats}.json      all five, asserted by the loader
  data/chunk-000/episode_NNNNNN.parquet
  videos/chunk-000/observation.images.robot0_agentview_left/episode_NNNNNN.mp4
                  .../robot0_agentview_right/...   .../robot0_eye_in_hand/...
  extras/episode_NNNNNN/...                         eval only, training never reads it
```

parquet columns (11): `observation.state` (list, **16**), `action` (list, **12**),
`next.reward`, `next.done`, `timestamp`, `frame_index`, `episode_index`, `index`,
`task_index`, `annotation.human.task_description` (**int index into tasks.jsonl**, not the
string), `annotation.human.task_name`.

`modality.json` slicing:

```
state   base_position 0:3 | base_rotation 3:7 (quat) | EE_pos_rel 7:10
        | EE_rot_rel 10:14 (quat) | gripper_qpos 14:16
action  base_motion 0:4 | control_mode 4:5 | EE_pos 5:8 | EE_rot 8:11 | gripper_close 11:12
video   left_view / right_view / wrist_view -> observation.images.robot0_agentview_{left,right}
                                               and observation.images.robot0_eye_in_hand
```

---

## Then training

`train_robocasa/` is a separate self-contained bundle (49 MB + the `mygr00t` conda env and a
7.1 GB base checkpoint, both referenced by absolute path). Point it at the dataset by
absolute path rather than by name:

```bash
cd train_robocasa
DATASET_PATH=/path/to/gr00t_views/vace_per_target_32_256eps \
GPUS=0,1,2,3 MAX_STEPS=30000 SAVE_STEPS=5000 PER_GPU_BATCH=16 \
bash scripts/train_groot15_single_dataset.sh
```

`DATASETNAME` (the alternative) resolves against
`/lp-dev/jonghoon/robocasa_full/gr00t_views/`, which will not exist elsewhere.

Check first with `bash scripts/smoke_test.sh <gpu>` β€” imports, dataset load, and a real
20-step fine-tune.

---

## Checklist for the other machine

- [ ] `run_aug32.sh` finished; every assignment has three `out_video.mp4`
- [ ] `assignments.jsonl` carries `provenance` (re-run `build_aug32_assignments.py` if it predates 2026-09-19)
- [ ] `source_episodes.tar` unpacked β€” **the parquet (`data/`) is read from it**; the archive
      was repacked 2026-09-19 to include it, an earlier copy has only `videos/` and `extras/`
- [ ] `dataset_build/canonical_dataset/` present (its `meta` symlink must resolve; use
      `cp -rL` when moving)
- [ ] `build_vace_objwise_gr00t_dataset.py` + `repair_mimicgen_task_ids.py` + a verifier copied over
- [ ] python with `numpy`, `pandas`; `ffmpeg` unless `--skip-three-view`
- [ ] decided: `--include-random-fill` (256) or not (59)
- [ ] decided: merge with the 502 base or train on the augmented set alone