rollout: config-driven viz, wristframe revert, HPT annotation support#482
rollout: config-driven viz, wristframe revert, HPT annotation support#482rl2aloha wants to merge 19 commits into
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
9772397 to
2f50589
Compare
The trained pi prompt format (`Task: ...; Action: ...`) is governed by
`data.{proprio,embodiment_label,control_mode,state_num_bins}` in the
hydra config. The `.ckpt` only stores the `model:` subtree (see
`trainHydra._build_model_config_tree`), so read the flags from the
run-output snapshot at `<run_dir>/.hydra/config.yaml` instead.
- rollout.py: `build_tokenized_collate` now wired from the run snapshot
rather than hardcoded args. `data["embodiment"]` is set to the
integer id; previously it was the string `"eva_bimanual"`, which
`_embodiment_name`'s `int(eid)` cast would have crashed on.
- hydra_utils.py: new `find_run_snapshot_path` / `load_run_snapshot`
helpers for reading hydra run-output `config.yaml` without
re-resolving runtime interpolations like
`${data.dataset.data_schematic}`.
Debug prints kept (TODO-marked) to ease verification on the rollout
host.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Local-only branch that should NOT be merged. Carries the patches the rollout host needs to bring up the policy: - pyproject.toml: relax `requires-python` to `>=3.10` (ARX `arx5_interface` ships as a cpython-310 .so, so the rollout host can't be 3.11) and drop `zarr==3.1.5` from the runtime deps (training-only). - pull_models.sh: REMOTE_PATH bumped to the EgoVerse4 logs dir. - rollout-requirements.txt: trimmed sibling of requirements.txt with zarr removed and 3.10-friendly pins. - uv.lock: 3.10-resolved lockfile (used to seed openpi's pip install via `uv export -c`). - ROLLOUT_INSTALL.md: end-to-end setup guide for the rollout host (Python 3.10, openpi fork, ARX bindings, HF auth, checkpoint conversion). External submodule pointer for `external/openpi` is intentionally left uncommitted here; commit inside the submodule first if you want to freeze the openpi rollout-fork rev. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "Branches" section claimed external/openpi is on a fork branch with the rollout patches "already committed" — it isn't. The submodule is detached at upstream 981483d with the patches as uncommitted working-tree edits, plus an untracked scripts/patch_transformers.py. The fork has a pi-rollout-changes branch with overlapping patches, but it diverges from this host's working tree (adds chex, loosens numpy / opencv-python, patches pi0_pytorch.py; missing .python-version, packages/openpi-client/pyproject.toml, uv.lock, patch_transformers.py). Calling out the divergence so future-us doesn't merge them blindly. Replaces the prior "checked out on a rollout branch" wording with an explicit "openpi patches to apply on top of 981483d" checklist — file-by-file, what to change and why — so the setup is reproducible even without a clean fork branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-task / per-object prompt files used by `rollout.py --annotation-path` (or the `a <path>` intervention command). Each file holds a single natural-language instruction tokenized into the `Task: ...` block of the pi prompt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PI's process_batch_for_training now owns prompt sampling, embodiment/ control-mode/State splicing, and tokenization (see PI._build_prompts / _tokenize_prompts). The collate's only job is to stack tensors and preserve list-valued keys. - Use annotation_collate instead of build_tokenized_collate. - Replace _build_collate_from_checkpoint_cfg with _apply_annotation_to_algo: override annotation_key="annotations", sampling_mode="first", default_prompt=<rollout annotation> on the loaded PI algo so rollout-time prompts deterministically flow through. - Drop _debug_print_proprio_norm and the post-collate sampled_prompt debug print -- both inspected fields the collate no longer produces. - Stop loading the hydra run snapshot in _load_policy; the only consumer was the removed collate builder. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The algo-side prompt refactor (9af36e0) moved State-block discretization from build_tokenized_collate into PI._discretize_state_for_sample. The old path normalized proprio via data_schematic.normalize_data before clipping to [-1,1] and binning; the new path skips that step. Raw xyz in meters saturates the [-1,1] clip, producing meaningless bin indices the model never saw during training (causing jerky rollouts). Match MultiDataset.__getitem__'s behavior at the rollout boundary: normalize the post-transform single sample via the model's norm_stats (itself a MultiDataset) before collating and handing to process_batch_for_training. norm_stats are the ones baked into the checkpoint, so train/inference State bins now agree. Also adds a one-shot debug print of the assembled prompt on the first inference step, so the State block can be eyeballed at runtime. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Read transform_list mode from .hydra/config.yaml instead of hardcoding cartesian_wristframe_ypr. The rollout now matches whatever frame the model was trained on (e.g. mode: cartesian for QWEN-HPT, mode: cartesian_wristframe_ypr for pi05_eva_aria_obj_gen_lang_wristframe). Add config-driven prediction visualization. _build_viz_func_from_config reads evaluator.viz_func from .hydra/config.yaml and uses hydra.utils.instantiate to bind image_key / action_key / mode. A runtime 'v' toggle in the intervention menu enables/disables saving viz_<step>.png to debug/ on every inference. The live camera image is resized to 640x480 inside _save_viz so the projection matches ARIA_INTRINSICS (the Aria camera publishes at 960x720 per configs.yaml, but the intrinsics in INTRINSICS['base'] are calibrated for 640x480). cv2.imwrite gets BGR conversion to avoid the inverted-color save bug. Add wrist-frame revert. _build_revert_transform_from_config reads evaluator.transform_lists.<embodiment> from config and instantiates the revert transform that converts wrist-frame predictions back to cam frame using the current obs ee_pose as the reference. rollout_step applies this to preds and the GT batch BEFORE both the viz call and the cam_frame_to_base_frame post-processing. Wrist-frame models now execute correctly on the robot AND project to the right pixels. Cam-frame models (revert is None) skip the revert as a no-op, so QWEN-HPT and old PI cartesian checkpoints continue to work unchanged. Add HPT (QWEN) annotation support. _apply_annotation_to_algo now sets annotation_sampling_mode='first' in addition to PI's sampling_mode='first', so --annotation-path works for both algos without subclass-specific code paths. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add 'm' intervention command (independent of 'v') that saves the prediction viz at the model's input resolution — 224x224 with padding mirroring resize_with_pad_torch — written to debug/viz_model_<step>.png. Intrinsics are scaled-and-padded to match (base intrinsics calibrated for 640x480 are first scaled to the live camera's resolution, then by the resize ratio, with padding added to cx/cy) so projections still land on the correct pixels. Pred is hidden (pred_alpha=0.0) so the saved image shows what the model actually sees, with only the green GT cluster overlaid. Fix the 'annotations' KeyError that crashed both viz modes when no --annotation-path was supplied. The viz_func partial bakes in annotation_key from the config, so viz_gt_preds does an unconditional batch[annotation_key] lookup. _build_viz_func_from_config now captures that key, and both _save_viz / _save_viz_model_res inject a default (self.annotation or empty string) into the batch before calling viz_func. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cherry-picked file-scoped from commit 2c53ef8 ("6D pi data transform"): - egomimic/utils/pose_utils.py: adds _ypr_to_rot6d / _rot6d_to_ypr helpers - egomimic/rldb/zarr/action_chunk_transforms.py: adds CartesianYPRToRot6D and CartesianRot6DToYPR transform classes (xyz+ypr <-> xyz+rot6d per arm, 14<->20 or 12<->18 dims; numpy/tensor passthrough) - egomimic/rldb/embodiment/eva.py: adds 'cartesian_6d' and 'cartesian_wristframe_6d' modes to Eva.get_transform_list, plus the two cam-frame revert builders the evaluator config references (_build_eva_cartesian_revert_6d_transform_list, _build_eva_cartesian_revert_6d_wristframe_transform_list). Unblocks rollout of pi05_eva_aria_wristframe_6d checkpoints, which the rollout branch was crashing on with 'NoneType is not iterable' (unknown mode falling through Eva.get_transform_list) and 'Error locating target ... _build_eva_cartesian_revert_6d_wristframe_transform_list' (missing revert builder). Skipped the training-side changes in 2c53ef8 (pi.py, hydra configs, action_utils, human.py, test files) — not needed for rollout and would conflict with rollout-branch edits to pi.py. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…on format Followup to 491496f (which only brought the data-side eva.py / transforms / pose_utils). The 6D checkpoint outputs 20-dim per-arm actions (xyz + rot6d + gripper) but RobotBimanualCartesianEuler.to32 still expected 14-dim, causing: ValueError: RobotBimanual: expected 14-dim, got 20 File-checkout from 2c53ef8 brings in: - The full raw-rotation refactor that landed on aidan/pi-6d before 2c53ef8 (1ddf896, 53e69ea, 59729c4) — PI05_CARTESIAN_ACTION_ENCODING_{RAW_ROT_6D, LEGACY, NORM_ROT_6D} constants, to32_raw_rotation/from32_raw_rotation, and raw/normalized rotation pipeline in PI.forward_eval / PI._unnormalize_action. - The new to32_norm_6d / from32_norm_6d converter methods on RobotBimanualCartesianEuler and HumanBimanualCartesianEuler. - The NORM_ROT_6D dispatch branches in PI.forward_eval (extract 6D, then unnormalize) and PI's action-encoding setup. Safe checkout: HEAD's pi.py and action_utils.py were byte-identical to the merge-base with 2c53ef8, so file-checkout doesn't overwrite any rollout-branch edits to these files. Purely EgoVerse-side; no openpi changes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Got rolled back by an earlier gt sync. The 20GB pi05 checkpoint OOM-killed the rollout between the patch-save and the main ModelWrapper.load_from_checkpoint: both held the full checkpoint simultaneously (~40GB peak on a host with less). Two mitigations: - If <ckpt>.patched already exists on disk, return its path immediately and skip both torch.load and torch.save. The first run still pays the double-load cost; every subsequent run is single-load. - del + gc.collect() between _patch_checkpoint_paths and the main load so the patching checkpoint is freed before the load load runs even on first-launch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Subtask-prediction PI models train with annotation_key='annotations_high'
(the user-supplied high-level goal) and predict the low-level subtask
under 'annotations'. The old rollout code hardcoded:
- _apply_annotation_to_algo: model.annotation_key = 'annotations'
- process_obs_for_transform_list: data['annotations'] = [text]
which would override the trained key and clobber the slot reserved for
the model's own predictions.
Now:
- _apply_annotation_to_algo no longer overrides annotation_key — the
config-loaded value (e.g. 'annotations_high') is preserved. Other
deterministic-inference knobs (sampling_mode / annotation_sampling_mode /
default_prompt) still get set.
- process_obs_for_transform_list reads model.annotation_key dynamically
and injects the user prompt under that key. Falls back to 'annotations'
for legacy models that don't have annotation_key.
Unblocks rollout of sort_pp_subtask_wf6d checkpoints.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both were dead-code — the solver was instantiated in main() and never used, and _get_model_xml_path only existed to feed it. The same auto- merge that left a stale CameraTransforms(...) call also kept these. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PI hierarchical models (subtask_prediction=true) decode a subtask from the high-level prompt and return it in forward_eval under <embodiment>_subtask_pred. The decoded text is otherwise discarded by the rollout — surface it as [rollout][subtask] step=30 predicted='Pick the screwdriver with the right hand.' so the user can verify the hierarchical planning at runtime. Falls back silently for models without subtask prediction (key not in fwd_out). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The model already re-decodes the subtask every query_frequency steps via forward_eval -> sample_actions; there's no caching, so each call runs the autoregressive subtask decode fresh. But there's no way to trigger a replan between query boundaries (e.g. right after loading a new annotation via 'a <path>'). Add 'p' to the intervention menu. Sets policy._force_replan = True; the next rollout_step checks the flag alongside the i % query_frequency gate, runs forward_eval, then clears the flag. Works the same for plain PI / HPT (just resamples actions) and PI hierarchical (also redecodes the subtask, surfaces it via [rollout][subtask]). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The intrinsic-zarr collapse renumbered EVA from 6/7/8 to 4/5/6 (after adding HUMAN_RIGHT_ARM/LEFT_ARM/BIMANUAL as 1/2/3). rollout.py's EMBODIMENT_MAP still hardcoded the old 8/7/6, so: data['embodiment'] = self.embodiment_id # = 8 ended up in the viz batch, then viz_gt_preds did: embodiment_id = batch['embodiment'][0].item() # 8 get_embodiment(8).lower() # None.lower() => AttributeError (Action prediction kept working — the model receives the batch keyed by the *name* 'eva_bimanual' and resolves the id internally via get_embodiment_id, which correctly returned 6.) Source the IDs directly from EMBODIMENT.<member>.value so the map can never drift from the enum again. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2f50589 to
0c73a6c
Compare
The intrinsic-zarr base used it; the replayed 'algo-side prompt assembly' commit removed all usages (annotation_collate now), and the conflict resolution kept the base's import line. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TYmG3nhxt7LYiaaSJPsKEV
0c73a6c to
794d6d3
Compare
Claude Code ReviewReview of PR #482SummaryThis PR bundles three logically distinct changes: (1) config-driven rollout viz + wristframe revert (per the PR title), (2) HPT annotation support, and (3) a large hierarchical subtask-prediction feature (PI0Subtask, new configs, embodiment keymap changes, 6D rotation transforms, proprio normalization in collate). The scope mismatch between title/description and diff is a serious concern. Key concerns1. Scope creep — PR description does not match the diffThe description covers only the rollout changes, but the diff adds:
This should be split into at least 3 PRs. The rollout fix is small and safe; the subtask work is a large research feature that needs its own review. Landing them together makes bisecting regressions painful. 2. Breaking API change without callers audited
3.
|

Read transform_list mode from .hydra/config.yaml instead of hardcoding
cartesian_wristframe_ypr. The rollout now matches whatever frame the
model was trained on (e.g. mode: cartesian for QWEN-HPT,
mode: cartesian_wristframe_ypr for pi05_eva_aria_obj_gen_lang_wristframe).
Add config-driven prediction visualization. build_viz_func_from_config
reads evaluator.viz_func from .hydra/config.yaml and uses hydra.utils.instantiate
to bind image_key / action_key / mode. A runtime 'v' toggle in the
intervention menu enables/disables saving viz.png to debug/
on every inference. The live camera image is resized to 640x480
inside _save_viz so the projection matches ARIA_INTRINSICS (the
Aria camera publishes at 960x720 per configs.yaml, but the intrinsics
in INTRINSICS['base'] are calibrated for 640x480). cv2.imwrite gets
BGR conversion to avoid the inverted-color save bug.
Add wrist-frame revert. _build_revert_transform_from_config reads
evaluator.transform_lists. from config and instantiates
the revert transform that converts wrist-frame predictions back to
cam frame using the current obs ee_pose as the reference. rollout_step
applies this to preds and the GT batch BEFORE both the viz call and
the cam_frame_to_base_frame post-processing. Wrist-frame models now
execute correctly on the robot AND project to the right pixels.
Cam-frame models (revert is None) skip the revert as a no-op, so
QWEN-HPT and old PI cartesian checkpoints continue to work unchanged.
Add HPT (QWEN) annotation support. _apply_annotation_to_algo now
sets annotation_sampling_mode='first' in addition to PI's
sampling_mode='first', so --annotation-path works for both algos
without subclass-specific code paths.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com