lsnu commited on
Commit
2fd54bc
·
verified ·
1 Parent(s): 9ccea90

Add TWIN preprocessing and norm-stats helper scripts

Browse files
Files changed (1) hide show
  1. openpi/scripts/run_preprocess_twin.sh +519 -0
openpi/scripts/run_preprocess_twin.sh ADDED
@@ -0,0 +1,519 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ export PATH="$HOME/.local/bin:$PATH"
6
+
7
+ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
8
+ OPENPI_ROOT="${OPENPI_ROOT:-$(cd -- "$SCRIPT_DIR/.." && pwd)}"
9
+
10
+ log() {
11
+ printf '%s %s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" "$*"
12
+ }
13
+
14
+ on_exit() {
15
+ local rc=$?
16
+ if [[ $rc -eq 0 ]]; then
17
+ log "Run exited successfully"
18
+ else
19
+ log "Run exited with status $rc"
20
+ fi
21
+ }
22
+
23
+ trap on_exit EXIT
24
+
25
+ require_env() {
26
+ local name="$1"
27
+ if [[ -z "${!name:-}" ]]; then
28
+ echo "Required environment variable is not set: $name" >&2
29
+ exit 1
30
+ fi
31
+ }
32
+
33
+ project_python_path() {
34
+ echo "$OPENPI_ROOT/.venv/bin/python"
35
+ }
36
+
37
+ hf_cli() {
38
+ local venv_python
39
+ venv_python="$(project_python_path)"
40
+ if [[ -x "$venv_python" ]]; then
41
+ "$venv_python" -m huggingface_hub.commands.huggingface_cli "$@"
42
+ return
43
+ fi
44
+ if command -v huggingface-cli >/dev/null 2>&1; then
45
+ huggingface-cli "$@"
46
+ return
47
+ fi
48
+ if command -v hf >/dev/null 2>&1; then
49
+ hf "$@"
50
+ return
51
+ fi
52
+ echo "No Hugging Face CLI found in PATH or project venv." >&2
53
+ exit 1
54
+ }
55
+
56
+ project_python() {
57
+ local venv_python
58
+ venv_python="$(project_python_path)"
59
+ if [[ -x "$venv_python" ]]; then
60
+ "$venv_python" "$@"
61
+ return
62
+ fi
63
+ python3 "$@"
64
+ }
65
+
66
+ ensure_hf_auth() {
67
+ if hf_cli whoami >/dev/null 2>&1; then
68
+ return 0
69
+ fi
70
+ if [[ -n "${HF_TOKEN:-}" ]]; then
71
+ hf_cli login --token "$HF_TOKEN" >/dev/null
72
+ hf_cli whoami >/dev/null
73
+ return 0
74
+ fi
75
+ echo "Hugging Face auth is not available. Set HF_TOKEN or login with 'huggingface-cli login' first." >&2
76
+ exit 1
77
+ }
78
+
79
+ export HF_HUB_ENABLE_HF_TRANSFER=0
80
+
81
+ export ROOT="${ROOT:-$HOME/pi05prep-work}"
82
+ export INBOX="${INBOX:-$ROOT/inbox}"
83
+ export TMPDIR="${TMPDIR:-$ROOT/tmp}"
84
+ export LOGDIR="${LOGDIR:-$ROOT/logs}"
85
+ export MODEL_REPO="${MODEL_REPO:-lsnu/pi05tests-openpi-multiarm}"
86
+ export HF_HOME="${HF_HOME:-$ROOT/hf-home}"
87
+ export HF_HUB_CACHE="${HF_HUB_CACHE:-$HF_HOME/hub}"
88
+ export HF_DATASETS_CACHE="${HF_DATASETS_CACHE:-$HF_HOME/datasets}"
89
+ export HF_LEROBOT_HOME="${HF_LEROBOT_HOME:-$HF_HOME/lerobot}"
90
+ export STATS_BATCH_SIZE="${STATS_BATCH_SIZE:-64}"
91
+ export STATS_NUM_WORKERS="${STATS_NUM_WORKERS:-0}"
92
+
93
+ # LeRobot rejects the deprecated env var. Clear it even if inherited.
94
+ unset LEROBOT_HOME || true
95
+
96
+ export DP_TRAIN="${DP_TRAIN:-lsnu/twin_dual_push_256_train}"
97
+ export DP_VAL="${DP_VAL:-lsnu/twin_dual_push_256_val}"
98
+ export DP_TEST="${DP_TEST:-lsnu/twin_dual_push_256_test}"
99
+
100
+ export HO_TRAIN="${HO_TRAIN:-lsnu/twin_handover_256_train}"
101
+ export HO_VAL="${HO_VAL:-lsnu/twin_handover_256_val}"
102
+ export HO_TEST="${HO_TEST:-lsnu/twin_handover_256_test}"
103
+
104
+ export SR_TRAIN="${SR_TRAIN:-lsnu/twin_straighten_rope_256_train}"
105
+ export SR_VAL="${SR_VAL:-lsnu/twin_straighten_rope_256_val}"
106
+ export SR_TEST="${SR_TEST:-lsnu/twin_straighten_rope_256_test}"
107
+
108
+ mkdir -p "$INBOX" "$TMPDIR" "$LOGDIR" "$HF_HOME" "$HF_HUB_CACHE" "$HF_DATASETS_CACHE" "$HF_LEROBOT_HOME"
109
+
110
+ prepare_openpi_env() {
111
+ require_env HF_TOKEN
112
+ cd "$OPENPI_ROOT"
113
+
114
+ if rg -q 'jax\[cuda12\]==0\.5\.3' pyproject.toml; then
115
+ echo "pyproject.toml still points at jax[cuda12]; patch it before running." >&2
116
+ exit 1
117
+ fi
118
+
119
+ log "Authenticating Hugging Face CLI"
120
+ hf_cli login --token "$HF_TOKEN"
121
+ hf_cli whoami
122
+
123
+ log "Syncing uv environment"
124
+ GIT_LFS_SKIP_SMUDGE=1 uv sync --python 3.11
125
+ GIT_LFS_SKIP_SMUDGE=1 uv pip install -e .
126
+
127
+ log "LeRobot cache root: $HF_LEROBOT_HOME"
128
+ }
129
+
130
+ download_and_verify() {
131
+ local remote_path="$1"
132
+ project_python - "$remote_path" "$INBOX" "$MODEL_REPO" <<'PY'
133
+ import hashlib
134
+ import json
135
+ import os
136
+ import sys
137
+ from pathlib import Path
138
+
139
+ from huggingface_hub import HfApi, hf_hub_download
140
+
141
+ remote_path, inbox, repo_id = sys.argv[1:4]
142
+ api = HfApi()
143
+
144
+ parent = str(Path(remote_path).parent)
145
+ info = None
146
+ for item in api.list_repo_tree(repo_id, repo_type="model", path_in_repo=parent, recursive=False, expand=True):
147
+ if getattr(item, "path", None) == remote_path:
148
+ info = item
149
+ break
150
+ if info is None:
151
+ raise SystemExit(f"Remote file not found: {remote_path}")
152
+ if info.lfs is None:
153
+ raise SystemExit(f"Expected an LFS file for {remote_path}")
154
+
155
+ local_path = hf_hub_download(
156
+ repo_id=repo_id,
157
+ repo_type="model",
158
+ filename=remote_path,
159
+ local_dir=inbox,
160
+ )
161
+
162
+ size = os.path.getsize(local_path)
163
+ if size != info.size:
164
+ raise SystemExit(f"Size mismatch for {remote_path}: local={size} remote={info.size}")
165
+
166
+ digest = hashlib.sha256()
167
+ with open(local_path, "rb") as f:
168
+ for chunk in iter(lambda: f.read(16 * 1024 * 1024), b""):
169
+ digest.update(chunk)
170
+ sha256 = digest.hexdigest()
171
+ if sha256 != info.lfs.sha256:
172
+ raise SystemExit(
173
+ f"SHA256 mismatch for {remote_path}: local={sha256} remote={info.lfs.sha256}"
174
+ )
175
+
176
+ print(local_path)
177
+ print(
178
+ json.dumps(
179
+ {
180
+ "remote_path": remote_path,
181
+ "local_path": local_path,
182
+ "size": size,
183
+ "sha256": sha256,
184
+ "commit": info.last_commit.oid if info.last_commit else None,
185
+ }
186
+ )
187
+ )
188
+ PY
189
+ }
190
+
191
+ probe_squashfs() {
192
+ local local_file="$1"
193
+ local log_file="$2"
194
+ log "Inspecting squashfs archive: $local_file"
195
+ unsquashfs -s "$local_file" | tee "$log_file"
196
+ }
197
+
198
+ convert_local() {
199
+ local local_file="$1"
200
+ local repo_id="$2"
201
+ local log_file="$3"
202
+ log "Converting $local_file -> $repo_id"
203
+ (
204
+ cd "$OPENPI_ROOT"
205
+ .venv/bin/python scripts/convert_twin_squashfs_to_lerobot.py \
206
+ --squashfs-path "$local_file" \
207
+ --repo-id "$repo_id" \
208
+ --verbose
209
+ ) >"$log_file" 2>&1
210
+ log "Conversion log written to: $log_file"
211
+ }
212
+
213
+ verify_local_dataset() {
214
+ local repo_id="$1"
215
+ local log_file="$2"
216
+ log "Verifying local LeRobot dataset: $repo_id"
217
+ (
218
+ cd "$OPENPI_ROOT"
219
+ .venv/bin/python - "$repo_id" <<'PY'
220
+ import json
221
+ import sys
222
+ from pathlib import Path
223
+
224
+ import pyarrow.parquet as pq
225
+ from lerobot.common.constants import HF_LEROBOT_HOME
226
+
227
+ repo_id = sys.argv[1]
228
+ root = Path(HF_LEROBOT_HOME) / repo_id
229
+ if not root.exists():
230
+ raise SystemExit(f"Local dataset directory not found: {root}")
231
+
232
+ info_path = root / "meta" / "info.json"
233
+ episodes_path = root / "meta" / "episodes.jsonl"
234
+ episodes_stats_path = root / "meta" / "episodes_stats.jsonl"
235
+ if not info_path.exists():
236
+ raise SystemExit(f"Missing info.json: {info_path}")
237
+ if not episodes_path.exists():
238
+ raise SystemExit(f"Missing episodes.jsonl: {episodes_path}")
239
+ if not episodes_stats_path.exists():
240
+ raise SystemExit(f"Missing episodes_stats.jsonl: {episodes_stats_path}")
241
+
242
+ parquet_files = sorted(root.rglob("*.parquet"))
243
+ if not parquet_files:
244
+ raise SystemExit(f"No parquet files found under {root}")
245
+
246
+ table = pq.read_table(parquet_files[0])
247
+ columns = set(table.column_names)
248
+ required = {"front_image", "wrist_left_image", "wrist_right_image", "state", "action", "task_index"}
249
+ missing = sorted(required - columns)
250
+ if missing:
251
+ raise SystemExit(f"Missing required parquet columns in {parquet_files[0]}: {missing}")
252
+
253
+ state_type = str(table.schema.field("state").type)
254
+ action_type = str(table.schema.field("action").type)
255
+ if "16" not in state_type:
256
+ raise SystemExit(f"Unexpected state schema: {state_type}")
257
+ if "16" not in action_type:
258
+ raise SystemExit(f"Unexpected action schema: {action_type}")
259
+
260
+ info = json.loads(info_path.read_text())
261
+ features = info.get("features", {})
262
+ if features.get("state", {}).get("shape") != [16]:
263
+ raise SystemExit(f"Unexpected state feature shape in {info_path}: {features.get('state')}")
264
+ if features.get("action", {}).get("shape") != [16]:
265
+ raise SystemExit(f"Unexpected action feature shape in {info_path}: {features.get('action')}")
266
+
267
+ episode_lines = episodes_path.read_text().splitlines()
268
+ episodes_stats_lines = episodes_stats_path.read_text().splitlines()
269
+ if not episode_lines:
270
+ raise SystemExit(f"No episodes found in {episodes_path}")
271
+ if not episodes_stats_lines:
272
+ raise SystemExit(f"No episode stats found in {episodes_stats_path}")
273
+
274
+ episodes = info.get("total_episodes")
275
+ frames = info.get("total_frames")
276
+ if episodes is not None and episodes <= 0:
277
+ raise SystemExit(f"Invalid episode count in {info_path}: {episodes}")
278
+ if frames is not None and frames <= 0:
279
+ raise SystemExit(f"Invalid frame count in {info_path}: {frames}")
280
+
281
+ print(
282
+ json.dumps(
283
+ {
284
+ "repo_id": repo_id,
285
+ "root": str(root),
286
+ "parquet_files": len(parquet_files),
287
+ "first_parquet": str(parquet_files[0]),
288
+ "columns": sorted(columns),
289
+ "total_episodes": episodes,
290
+ "total_frames": frames,
291
+ "episode_lines": len(episode_lines),
292
+ "episodes_stats_lines": len(episodes_stats_lines),
293
+ }
294
+ )
295
+ )
296
+ PY
297
+ ) >"$log_file" 2>&1
298
+ cat "$log_file"
299
+ }
300
+
301
+ upload_dataset() {
302
+ local repo_id="$1"
303
+ log "Uploading dataset repo: $repo_id"
304
+ hf_cli upload-large-folder "$repo_id" "$HF_LEROBOT_HOME/$repo_id" --repo-type dataset --num-workers 16
305
+ }
306
+
307
+ verify_remote_dataset() {
308
+ local repo_id="$1"
309
+ project_python - "$repo_id" <<'PY'
310
+ import json
311
+ import sys
312
+
313
+ from huggingface_hub import HfApi
314
+
315
+ repo_id = sys.argv[1]
316
+ api = HfApi()
317
+ paths = [item.path for item in api.list_repo_tree(repo_id, repo_type="dataset", recursive=True, expand=True)]
318
+
319
+ required = {"meta/info.json", "meta/episodes.jsonl", "meta/episodes_stats.jsonl"}
320
+ missing = sorted(required - set(paths))
321
+ if missing:
322
+ raise SystemExit(f"Remote dataset is missing required files: {missing}")
323
+
324
+ parquet_files = [p for p in paths if p.endswith(".parquet")]
325
+ video_files = [p for p in paths if p.endswith(".mp4")]
326
+ if not parquet_files:
327
+ raise SystemExit(f"Remote dataset {repo_id} has no parquet files")
328
+
329
+ print(
330
+ json.dumps(
331
+ {
332
+ "repo_id": repo_id,
333
+ "files": len(paths),
334
+ "parquet_files": len(parquet_files),
335
+ "video_files": len(video_files),
336
+ }
337
+ )
338
+ )
339
+ PY
340
+ }
341
+
342
+ stats_one() {
343
+ local config_name="$1"
344
+ local repo_id="$2"
345
+ log "Computing norm stats: $config_name / $repo_id"
346
+ (
347
+ cd "$OPENPI_ROOT"
348
+ PYTHONUNBUFFERED=1 .venv/bin/python -u scripts/compute_norm_stats_repo.py \
349
+ --config-name "$config_name" \
350
+ --repo-id "$repo_id" \
351
+ --batch-size "$STATS_BATCH_SIZE" \
352
+ --num-workers "$STATS_NUM_WORKERS" \
353
+ --assets-base-dir ./assets
354
+ ) >"$LOGDIR/${config_name//\//_}__${repo_id//\//_}.stats.log" 2>&1
355
+ tail -n 40 "$LOGDIR/${config_name//\//_}__${repo_id//\//_}.stats.log" || true
356
+ }
357
+
358
+ upload_stats() {
359
+ local config_name="$1"
360
+ local repo_id="$2"
361
+ local src_dir="$OPENPI_ROOT/assets/$config_name/$repo_id"
362
+ local dst_dir="openpi/assets/$config_name/$repo_id"
363
+ log "Uploading norm stats: $MODEL_REPO::$dst_dir"
364
+ hf_cli upload "$MODEL_REPO" "$src_dir" "$dst_dir"
365
+ }
366
+
367
+ verify_remote_stats() {
368
+ local config_name="$1"
369
+ local repo_id="$2"
370
+ project_python - "$MODEL_REPO" "$config_name" "$repo_id" <<'PY'
371
+ import sys
372
+
373
+ from huggingface_hub import HfApi
374
+
375
+ model_repo, config_name, repo_id = sys.argv[1:4]
376
+ target = f"openpi/assets/{config_name}/{repo_id}/norm_stats.json"
377
+ api = HfApi()
378
+ paths = [item.path for item in api.list_repo_tree(model_repo, repo_type="model", recursive=True, expand=True)]
379
+ if target not in paths:
380
+ raise SystemExit(f"Missing remote norm stats file: {target}")
381
+ print(target)
382
+ PY
383
+ }
384
+
385
+ cleanup_local() {
386
+ local local_file="$1"
387
+ local repo_id="$2"
388
+ log "Cleaning local artifacts for $repo_id"
389
+ rm -f "$local_file"
390
+ rm -rf "$HF_LEROBOT_HOME/$repo_id"
391
+ }
392
+
393
+ process_train_split() {
394
+ local remote_path="$1"
395
+ local repo_id="$2"
396
+ local prefix="$3"
397
+
398
+ if verify_remote_dataset "$repo_id" >"$LOGDIR/${prefix}_verify_remote.json" 2>/dev/null; then
399
+ log "Remote dataset already verified; skipping conversion/upload for $repo_id"
400
+ else
401
+ log "Train split download+verify: $remote_path -> $repo_id"
402
+ mapfile -t dl_out < <(download_and_verify "$remote_path")
403
+ local local_file="${dl_out[0]}"
404
+ local meta_json="${dl_out[1]}"
405
+ log "Train split download verified: $local_file"
406
+ echo "$meta_json" | tee "$LOGDIR/${prefix}_download.json"
407
+ probe_squashfs "$local_file" "$LOGDIR/${prefix}_unsquashfs.txt"
408
+ convert_local "$local_file" "$repo_id" "$LOGDIR/${prefix}_convert.log"
409
+ verify_local_dataset "$repo_id" "$LOGDIR/${prefix}_verify_local.json"
410
+ upload_dataset "$repo_id"
411
+ verify_remote_dataset "$repo_id" | tee "$LOGDIR/${prefix}_verify_remote.json"
412
+ fi
413
+
414
+ if verify_remote_stats "pi05_twin_bimanual_finetune" "$repo_id" >"$LOGDIR/${prefix}_baseline_stats_remote.txt" 2>/dev/null; then
415
+ log "Baseline norm stats already verified remotely; skipping compute/upload for $repo_id"
416
+ else
417
+ stats_one "pi05_twin_bimanual_finetune" "$repo_id"
418
+ upload_stats "pi05_twin_bimanual_finetune" "$repo_id"
419
+ verify_remote_stats "pi05_twin_bimanual_finetune" "$repo_id" | tee "$LOGDIR/${prefix}_baseline_stats_remote.txt"
420
+ fi
421
+
422
+ if verify_remote_stats "pi05_twin_bimanual_parallel_finetune" "$repo_id" >"$LOGDIR/${prefix}_parallel_stats_remote.txt" 2>/dev/null; then
423
+ log "Parallel norm stats already verified remotely; skipping compute/upload for $repo_id"
424
+ else
425
+ stats_one "pi05_twin_bimanual_parallel_finetune" "$repo_id"
426
+ upload_stats "pi05_twin_bimanual_parallel_finetune" "$repo_id"
427
+ verify_remote_stats "pi05_twin_bimanual_parallel_finetune" "$repo_id" | tee "$LOGDIR/${prefix}_parallel_stats_remote.txt"
428
+ fi
429
+
430
+ if [[ -n "${local_file:-}" ]]; then
431
+ cleanup_local "$local_file" "$repo_id"
432
+ fi
433
+ }
434
+
435
+ process_eval_split() {
436
+ local remote_path="$1"
437
+ local repo_id="$2"
438
+ local prefix="$3"
439
+
440
+ if verify_remote_dataset "$repo_id" >"$LOGDIR/${prefix}_verify_remote.json" 2>/dev/null; then
441
+ log "Remote eval dataset already verified; skipping conversion/upload for $repo_id"
442
+ else
443
+ log "Eval split download+verify: $remote_path -> $repo_id"
444
+ mapfile -t dl_out < <(download_and_verify "$remote_path")
445
+ local local_file="${dl_out[0]}"
446
+ local meta_json="${dl_out[1]}"
447
+ log "Eval split download verified: $local_file"
448
+ echo "$meta_json" | tee "$LOGDIR/${prefix}_download.json"
449
+ probe_squashfs "$local_file" "$LOGDIR/${prefix}_unsquashfs.txt"
450
+ convert_local "$local_file" "$repo_id" "$LOGDIR/${prefix}_convert.log"
451
+ verify_local_dataset "$repo_id" "$LOGDIR/${prefix}_verify_local.json"
452
+ upload_dataset "$repo_id"
453
+ verify_remote_dataset "$repo_id" | tee "$LOGDIR/${prefix}_verify_remote.json"
454
+ cleanup_local "$local_file" "$repo_id"
455
+ fi
456
+ }
457
+
458
+ process_task() {
459
+ local task_name="$1"
460
+ local train_remote="$2"
461
+ local train_repo="$3"
462
+ local val_remote="$4"
463
+ local val_repo="$5"
464
+ local test_remote="$6"
465
+ local test_repo="$7"
466
+
467
+ log "=== Task: $task_name / train split ==="
468
+ process_train_split "$train_remote" "$train_repo" "${task_name}_train"
469
+
470
+ log "=== Task: $task_name / val+test splits ==="
471
+ process_eval_split "$val_remote" "$val_repo" "${task_name}_val" &
472
+ local val_pid=$!
473
+ process_eval_split "$test_remote" "$test_repo" "${task_name}_test" &
474
+ local test_pid=$!
475
+ wait "$val_pid"
476
+ wait "$test_pid"
477
+ }
478
+
479
+ main() {
480
+ if [[ "${SKIP_PREPARE:-0}" == "1" ]]; then
481
+ ensure_hf_auth
482
+ log "Skipping environment bootstrap; using HF_LEROBOT_HOME=$HF_LEROBOT_HOME"
483
+ else
484
+ prepare_openpi_env
485
+ fi
486
+
487
+ process_task \
488
+ "dual_push_buttons" \
489
+ "datasets/benchmarks/peract2_twin/bimanual/image_size_256_dual_push_buttons/bimanual_dual_push_buttons.train.squashfs" \
490
+ "$DP_TRAIN" \
491
+ "datasets/benchmarks/peract2_twin/bimanual/image_size_256_dual_push_buttons/bimanual_dual_push_buttons.val.squashfs" \
492
+ "$DP_VAL" \
493
+ "datasets/benchmarks/peract2_twin/bimanual/image_size_256_dual_push_buttons/bimanual_dual_push_buttons.test.squashfs" \
494
+ "$DP_TEST"
495
+
496
+ process_task \
497
+ "handover_item" \
498
+ "datasets/benchmarks/peract2_twin/bimanual/image_size_256/bimanual_handover_item/bimanual_handover_item.train.squashfs" \
499
+ "$HO_TRAIN" \
500
+ "datasets/benchmarks/peract2_twin/bimanual/image_size_256/bimanual_handover_item/bimanual_handover_item.val.squashfs" \
501
+ "$HO_VAL" \
502
+ "datasets/benchmarks/peract2_twin/bimanual/image_size_256/bimanual_handover_item/bimanual_handover_item.test.squashfs" \
503
+ "$HO_TEST"
504
+
505
+ process_task \
506
+ "straighten_rope" \
507
+ "datasets/benchmarks/peract2_twin/bimanual/image_size_256/bimanual_straighten_rope/bimanual_straighten_rope.train.squashfs" \
508
+ "$SR_TRAIN" \
509
+ "datasets/benchmarks/peract2_twin/bimanual/image_size_256/bimanual_straighten_rope/bimanual_straighten_rope.val.squashfs" \
510
+ "$SR_VAL" \
511
+ "datasets/benchmarks/peract2_twin/bimanual/image_size_256/bimanual_straighten_rope/bimanual_straighten_rope.test.squashfs" \
512
+ "$SR_TEST"
513
+
514
+ log "All tasks completed"
515
+ }
516
+
517
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
518
+ main "$@"
519
+ fi