{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "# 05 pi_0 权限 smoke 与训练门控\n\n        这一节不急着启动长训练，而是先检查 PaliGemma、Hugging Face gated 权限、权重下载、数据加载和 1-step smoke。只有 smoke 通过后，正式训练才有意义。\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "## 重要更正：旧 30-seed 面板不是位置泛化\n\n        `y_env2.py` 曾把任意环境 seed 都写成 `np.random.seed(seed=0)`。旧 `1010-1039` 面板只改变了 Pi0 策略采样，杯子和盘子位置始终固定。那些结果仍能分析 scaffold 的动作时序，却不能作为空间泛化证据。修复后，固定环境 raw/head 对照是 `0/12 -> 6/12`，真正随机环境 gate 只有 `1/4`；下一步必须重采多位置数据。\n"
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": "from pathlib import Path\nimport json\nimport os\nimport shutil\nimport subprocess\nimport sys\n\n\ndef find_topic_root():\n    cwd = Path.cwd().resolve()\n    for candidate in [cwd, *cwd.parents]:\n        if (candidate / \"assets\" / \"metrics_snapshot.json\").exists():\n            return candidate\n    raise RuntimeError(\"请从 AMD ROCm 专题目录或 notebooks 子目录启动 Jupyter。\")\n\n\nTOPIC_ROOT = find_topic_root()\nASSET_DIR = TOPIC_ROOT / \"assets\"\nNOTEBOOK_DIR = TOPIC_ROOT / \"notebooks\"\nPROJECT_ROOT = Path(os.environ.get(\"PROJECT_ROOT\", \"/path/to/every-embodied/mujoco_pnp\"))\nDATA_ROOT = Path(os.environ.get(\"DATA_ROOT\", \"/path/to/datasets/every_embodied\"))\nOUTPUT_ROOT = Path(os.environ.get(\"OUTPUT_ROOT\", TOPIC_ROOT / \"outputs\"))\nMODEL_ROOT = Path(os.environ.get(\"MODEL_ROOT\", PROJECT_ROOT / \"ckpt\"))\n\nprint(\"TOPIC_ROOT =\", TOPIC_ROOT)\nprint(\"PROJECT_ROOT =\", PROJECT_ROOT)\nprint(\"DATA_ROOT =\", DATA_ROOT)\nprint(\"OUTPUT_ROOT =\", OUTPUT_ROOT)\nprint(\"MODEL_ROOT =\", MODEL_ROOT)\n"
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": "try:\n    from IPython.display import Image, Markdown, display\nexcept Exception:\n    class Markdown(str):\n        pass\n\n    def display(obj):\n        print(obj)\n\n    def Image(filename=None, width=None):\n        return f\"[image] {filename}\"\n\n\ndef show_asset(filename, width=960):\n    path = ASSET_DIR / filename\n    if path.exists():\n        display(Image(filename=str(path), width=width))\n    else:\n        print(f\"缺少素材：{path}\")\n\n\ndef md_table(headers, rows):\n    lines = [\"| \" + \" | \".join(headers) + \" |\", \"| \" + \" | \".join([\"---\"] * len(headers)) + \" |\"]\n    for row in rows:\n        lines.append(\"| \" + \" | \".join(str(x) for x in row) + \" |\")\n    display(Markdown(\"\\n\".join(lines)))\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "## Checkpoint 1：权限检查命令模板\n"
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": "print(\"\"\"\ncd \"$PROJECT_ROOT\"\nHF_TOKEN_STDIN=1 REQUIRE_PROXY=1 ./install_hf_token_for_pi0.sh\n\"\"\")\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "这个命令形态强调两件事：token 不写进 Notebook，脚本先验证 `whoami`、PaliGemma 和 `lerobot/pi0`，全部通过后再保存。\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "## Checkpoint 2：1-step smoke 证明什么\n"
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": "rows = [\n    (\"gated model 权限\", \"能读取 PaliGemma / pi0 所需配置\"),\n    (\"数据加载\", \"能读取 LeRobot 数据集和 observation/action key\"),\n    (\"policy 构造\", \"pi0 policy 能初始化\"),\n    (\"一次训练步\", \"forward/backward/optimizer step 能跑通\"),\n    (\"checkpoint 写出\", \"输出目录出现 smoke checkpoint\"),\n]\nmd_table([\"检查项\", \"通过后说明什么\"], rows)\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "1-step smoke 不证明策略收敛，也不代表成功率。它只是正式训练前的门槛。\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "## Checkpoint 3：训练命令模板\n"
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": "print(\"\"\"\ncd \"$PROJECT_ROOT\"\nRUN_SMOKE=1 RUN_FULL_TRAIN=1 PI0_STEPS=20000 PI0_BATCH_SIZE=4 \\\\\n  ./run_pi0_train_eval_after_hf_ready.sh\n\"\"\")\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "正式训练建议放到终端、tmux 或后台脚本中执行。Notebook 负责记录参数、解释门控和读取训练后的 summary。\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "## Checkpoint 4：阶段性状态图\n"
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": "show_asset(\"model_status_summary.png\", width=900)\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "## Checkpoint 5：`tcp_to_plate` 后段 finisher 诊断\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "raw pi_0 目前还不能写成复刻成功。更有价值的一轮诊断是把后段 finisher 的 state 从 19 维扩到 22 维，额外加入 `tcp_to_plate = tcp_link_xyz - plate_xyz`。这一步让模型显式知道盘心相对 TCP 的方向，避免后段只靠图像和 phase 隐式猜目标。\n\n        这个实验仍然是诊断 scaffold：前缀由 oracle 提供，schedule 从 `move_preplace` 对齐，finisher 使用 22D state。后续 30-seed 排查发现，短 `move_preplace` schedule 会提前 lower / release；强制使用长 schedule 模板后，改好版 scaffold 能稳定通过 30 个策略采样 seed（环境实际固定为 seed 0）。这个结果证明瓶颈被定位和修复，但不代表 raw pi_0 已经端到端成功。\n\n        下一步开始拆 scaffold。第一个被替换的是手写 gripper 规则：用 phase-only logistic head 预测夹爪开闭，EEF/arm 仍由 22D finisher 输出。full-state gripper head 虽然训练集准确率 `100%`，但闭环上线失败；phase-only head 只看 `timestamp + phase_index_norm + phase_onehot11`，在完整策略采样 seeds（环境实际固定为 seed 0） `1010-1039` 上仍保持 strict `30/30`。这个对照说明，小数据短事件 head 的输入要克制，不要把闭环会漂移的机械臂状态全塞进去。\n\n        第二块脚手架先用固定阈值 adaptive gate 替代强制长 `move_preplace` 模板，完整策略采样 seeds（环境实际固定为 seed 0） `1010-1039` 仍是 strict `30/30`，但其中一部分依赖 `max_steps=180` 安全兜底。继续往前走，我们训练了 logistic transition head。第一版 full head 训练集准确率约 `99.27%`，但 seed `1010` 闭环早放失败；第二版只保留 `tcp_to_plate_xy + local_step_norm`，训练集准确率约 `95.01%`，却在完整策略采样 seeds（环境实际固定为 seed 0） `1010-1039` 上达到 strict `30/30`、legacy `30/30`。30 条里 `29/30` 是 transition head 主动触发，只有 seed `1021` 走到 max-step 安全兜底。这个对照说明，小数据 phase head 的输入也要克制，稳定几何线索比训练集高准确率更重要。\n\n        第三层继续拆前段 contact primitive。naive all-head 把所有 transition 都按 phase tail 学，训练集指标不差，但 smoke `1010/1011` 变成 `0/2`，因为 `descend_to_close` 会在 TCP 还高出抓取 floor 约 `0.08 m` 时提前触发。修复版把 `pregrasp_to_descend` 改成几何标签，特征加入 TCP 到 grasp / pregrasp / floor 的相对量，并在部署时加 `descend_floor_guard`。完整策略采样 seeds（环境实际固定为 seed 0） `1010-1039` 为 strict `30/30`、legacy `30/30`；核心切换 `pregrasp->descend`、`descend->close`、`close->lift` 都是 `30/30` 由 head 触发，floor guard 共阻挡 `342` 次高空 close。这仍是 contact scaffold，不是 raw pi_0 端到端成功。\n\n        第四层拆掉 `dataset_schedule` 尾段，改成 `dynamic_timed` finisher。第一次完整 30 seed 只有 strict `27/30`，失败 seeds `1021/1031/1036` 看起来像固定 dwell 不合适；复盘代码后发现根因是 `--tcpplate-prefix-target-state` 这个全局开关越界了：prefix 需要 `tcp_to_target`，但 dynamic finisher 也误用了它，而 finisher 训练时应该吃 `tcp_to_plate`。把 state builder 改成 stage-aware 后，prefix 阶段用 target-relative，finisher 阶段用 plate-relative；三个失败 seed 复测 `3/3`，旧版同一环境连续跑完整策略采样 seeds（环境实际固定为 seed 0） `1010-1039` 回到 strict `30/30`、legacy `30/30`。但继续 trace 又发现，连续评估时前一个 episode 的 MuJoCo qvel / ctrl / free-joint 动态残留可能污染下一个 seed，seed `1035` 曾出现采样范围外的初始位置。于是 evaluator 增加 `--fresh-env-per-episode` 和 `--hard-reset-sim-data`。最终用 `--hard-reset-sim-data` clean protocol 重跑完整策略采样 seeds（环境实际固定为 seed 0） `1010-1039`，仍是 strict `30/30`、legacy `30/30`，红杯 `18/18`、蓝杯 `12/12`；mean `xy=0.0219 m`，max `xy=0.0450 m`，最小 lift `0.1093 m`，最小 upright cos `0.9504`。之前 seed `1036` 的 `0.0993 m` 更像 reset 残留造成的评估伪影，不再作为边界样本处理。\n"
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": "rows = [\n    (\"raw full20 open-loop\", \"1/20 final，3/20 ever\", \"raw policy 仍未过关\"),\n    (\"raw closed-loop strict\", \"0/20\", \"最接近部署口径\"),\n    (\"template-tail full20\", \"4/20\", \"固定尾段只能救部分样本\"),\n    (\"旧 19D finisher handoff\", \"prefix 120/180/240/300 均为 0/2\", \"后段目标仍偏在杯子侧\"),\n    (\"22D tcp_to_plate finisher\", \"strict 5/10，legacy 6/10\", \"plate-relative state 是有效线索，但仍是 scaffold\"),\n    (\"22D + phase-scripted gripper\", \"strict 7/10，legacy 7/10\", \"夹爪时序能救一批失败，剩余主要是红杯落点\"),\n    (\"固定环境 30 个 policy seeds，schedule 0..9\", \"strict 21/30，legacy 23/30\", \"失败全部来自短 move_preplace 模板\"),\n    (\"固定环境 30 个 policy seeds，强制长 schedule\", \"strict 30/30，legacy 30/30\", \"仅证明固定场景 scaffold 稳定\"),\n    (\"长 schedule + phase-only learned gripper head\", \"strict 30/30，legacy 30/30\", \"第一块脚手架被可学习 head 替代\"),\n    (\"schedule 0..9 + adaptive move gate + learned gripper\", \"strict 30/30，legacy 30/30\", \"第二块脚手架工程版：不再强制长 episode0\"),\n    (\"schedule 0..9 + xy-step transition head + learned gripper\", \"strict 30/30，legacy 30/30\", \"第二块脚手架可学习版：29/30 主动触发\"),\n    (\"policy prefix + contact transition heads + floor guard\", \"strict 30/30，legacy 30/30\", \"第三层去脚手架：核心接触切换由 head 触发，但仍保留 scaffold\"),\n    (\"stage-aware dynamic_timed finisher + hard-reset clean protocol\", \"strict 30/30，legacy 30/30\", \"第四层去脚手架：prefix 用 target，finisher 用 plate；mean xy=0.0219 m，max xy=0.0450 m\"),\n]\nmd_table([\"口径\", \"结果\", \"怎么理解\"], rows)\n"
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": "print(\"\"\"\ncd \"$PROJECT_ROOT\"\n\n# 1. 把 19D phase-state 数据转换成 22D tcp_to_plate 数据。\npython code/pi0/build_lerobot_state_phase_tcpplate.py \\\\\n  --src-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_eef_abs_10eps_v1 \\\\\n  --src-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_eef_abs_10eps_v1 \\\\\n  --dst-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --dst-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --overwrite\n\n# 2. 评估 22D finisher。注意：这个命令需要已有 22D checkpoint。\nPYTHONPATH=\"$PWD:$TOPIC_ROOT/code/pi0:${PYTHONPATH:-}\" \\\\\npython code/pi0/evaluate_pi0_two_stage_tcpplate.py \\\\\n  --tcpplate-base-evaluator code/pi0/evaluate_pi0_two_stage_eef_abs.py \\\\\n  --tcpplate-schedule-start-phase move_preplace \\\\\n  --prefix-source oracle \\\\\n  --prefix-steps 180 \\\\\n  --finisher-phase-state dataset_schedule \\\\\n  --finisher-policy-path \"$MODEL_ROOT/pi0_tcpplate_finisher/checkpoints/000250/pretrained_model\" \\\\\n  --finisher-dataset-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --finisher-dataset-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --seeds 1000,1001,1002,1003,1004,1005,1006,1007,1008,1009 \\\\\n  --finisher-phase-schedule-episodes 0,1,2,3,4,5,6,7,8,9 \\\\\n  --output-jsonl \"$OUTPUT_ROOT/pi0_tcpplate_eval/results.jsonl\" \\\\\n  --summary-json \"$OUTPUT_ROOT/pi0_tcpplate_eval/summary.json\"\n\n# 3. 更大样本量复核时，可以强制使用长 move_preplace 模板。\nPYTHONPATH=\"$PWD:$TOPIC_ROOT/code/pi0:${PYTHONPATH:-}\" \\\\\npython code/pi0/evaluate_pi0_two_stage_tcpplate.py \\\\\n  --tcpplate-base-evaluator code/pi0/evaluate_pi0_two_stage_eef_abs.py \\\\\n  --tcpplate-schedule-start-phase move_preplace \\\\\n  --tcpplate-force-schedule-episode 0 \\\\\n  --prefix-source oracle \\\\\n  --prefix-steps 180 \\\\\n  --finisher-phase-state dataset_schedule \\\\\n  --finisher-scripted-gripper \\\\\n  --finisher-policy-path \"$MODEL_ROOT/pi0_tcpplate_finisher/checkpoints/000250/pretrained_model\" \\\\\n  --finisher-dataset-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --finisher-dataset-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --seeds 1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039 \\\\\n  --output-jsonl \"$OUTPUT_ROOT/pi0_tcpplate_eval_unseen30_long_schedule/results.jsonl\" \\\\\n  --summary-json \"$OUTPUT_ROOT/pi0_tcpplate_eval_unseen30_long_schedule/summary.json\"\n\n# 4. 训练 phase-only gripper head。这个 head 只替代 gripper 规则，不改 EEF/arm。\npython code/pi0/train_tcpplate_gripper_head.py \\\\\n  --dataset-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --dataset-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --feature-mode phase \\\\\n  --label-source action \\\\\n  --output \"$MODEL_ROOT/pi0_tcpplate_finisher/gripper_head_phase_logreg.npz\" \\\\\n  --summary-json \"$MODEL_ROOT/pi0_tcpplate_finisher/gripper_head_phase_logreg_summary.json\"\n\n# 5. 用 learned gripper head 复核 30 个策略采样 seed（环境实际固定为 seed 0）。\nPYTHONPATH=\"$PWD:$TOPIC_ROOT/code/pi0:${PYTHONPATH:-}\" \\\\\npython code/pi0/evaluate_pi0_two_stage_tcpplate.py \\\\\n  --tcpplate-base-evaluator code/pi0/evaluate_pi0_two_stage_eef_abs.py \\\\\n  --tcpplate-schedule-start-phase move_preplace \\\\\n  --tcpplate-force-schedule-episode 0 \\\\\n  --tcpplate-gripper-head-path \"$MODEL_ROOT/pi0_tcpplate_finisher/gripper_head_phase_logreg.npz\" \\\\\n  --prefix-source oracle \\\\\n  --prefix-steps 180 \\\\\n  --finisher-phase-state dataset_schedule \\\\\n  --finisher-scripted-gripper \\\\\n  --finisher-policy-path \"$MODEL_ROOT/pi0_tcpplate_finisher/checkpoints/000250/pretrained_model\" \\\\\n  --finisher-dataset-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --finisher-dataset-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --seeds 1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039 \\\\\n  --output-jsonl \"$OUTPUT_ROOT/pi0_tcpplate_eval_unseen30_gripper_head/results.jsonl\" \\\\\n  --summary-json \"$OUTPUT_ROOT/pi0_tcpplate_eval_unseen30_gripper_head/summary.json\"\n\n# 6. 去掉强制长 episode0，改用 adaptive move_preplace gate。\n# 这个版本使用 schedule 0..9 正常重复；如果 live TCP-to-plate 还太远，\n# 就 hold move_preplace，直到 xy 足够小或达到保守 max-step 兜底。\nPYTHONPATH=\"$PWD:$TOPIC_ROOT/code/pi0:${PYTHONPATH:-}\" \\\\\npython code/pi0/evaluate_pi0_two_stage_tcpplate.py \\\\\n  --tcpplate-base-evaluator code/pi0/evaluate_pi0_two_stage_eef_abs.py \\\\\n  --tcpplate-schedule-start-phase move_preplace \\\\\n  --tcpplate-adaptive-move-preplace \\\\\n  --tcpplate-adaptive-move-preplace-xy-threshold 0.05 \\\\\n  --tcpplate-adaptive-move-preplace-min-steps 20 \\\\\n  --tcpplate-adaptive-move-preplace-max-steps 180 \\\\\n  --tcpplate-gripper-head-path \"$MODEL_ROOT/pi0_tcpplate_finisher/gripper_head_phase_logreg.npz\" \\\\\n  --prefix-source oracle \\\\\n  --prefix-steps 180 \\\\\n  --finisher-phase-state dataset_schedule \\\\\n  --finisher-scripted-gripper \\\\\n  --finisher-policy-path \"$MODEL_ROOT/pi0_tcpplate_finisher/checkpoints/000250/pretrained_model\" \\\\\n  --finisher-dataset-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --finisher-dataset-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --seeds 1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039 \\\\\n  --finisher-phase-schedule-episodes 0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 \\\\\n  --output-jsonl \"$OUTPUT_ROOT/pi0_tcpplate_eval_unseen30_adaptive_gate/results.jsonl\" \\\\\n  --summary-json \"$OUTPUT_ROOT/pi0_tcpplate_eval_unseen30_adaptive_gate/summary.json\"\n\n# 7. 训练 xy-step transition head。full-state transition head 训练集更准，\n# 但会被高度/方向相关性误导；稳定版只用 tcp_to_plate_xy + local_step_norm。\npython code/pi0/train_tcpplate_transition_head.py \\\\\n  --dataset-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --dataset-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --feature-mode xy_step \\\\\n  --step-scale 180 \\\\\n  --output \"$MODEL_ROOT/pi0_tcpplate_finisher/transition_head_tcpplate_xy_step_logreg.npz\" \\\\\n  --summary-json \"$MODEL_ROOT/pi0_tcpplate_finisher/transition_head_tcpplate_xy_step_logreg_summary.json\"\n\n# 8. 用 learned transition head 替代固定阈值 gate，schedule 0..9 正常重复。\nPYTHONPATH=\"$PWD:$TOPIC_ROOT/code/pi0:${PYTHONPATH:-}\" \\\\\npython code/pi0/evaluate_pi0_two_stage_tcpplate.py \\\\\n  --tcpplate-base-evaluator code/pi0/evaluate_pi0_two_stage_eef_abs.py \\\\\n  --tcpplate-schedule-start-phase move_preplace \\\\\n  --tcpplate-transition-head-path \"$MODEL_ROOT/pi0_tcpplate_finisher/transition_head_tcpplate_xy_step_logreg.npz\" \\\\\n  --tcpplate-transition-head-threshold 0.5 \\\\\n  --tcpplate-adaptive-move-preplace-min-steps 20 \\\\\n  --tcpplate-adaptive-move-preplace-max-steps 180 \\\\\n  --tcpplate-gripper-head-path \"$MODEL_ROOT/pi0_tcpplate_finisher/gripper_head_phase_logreg.npz\" \\\\\n  --prefix-source oracle \\\\\n  --prefix-steps 180 \\\\\n  --finisher-phase-state dataset_schedule \\\\\n  --finisher-scripted-gripper \\\\\n  --finisher-policy-path \"$MODEL_ROOT/pi0_tcpplate_finisher/checkpoints/000250/pretrained_model\" \\\\\n  --finisher-dataset-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --finisher-dataset-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --seeds 1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039 \\\\\n  --finisher-phase-schedule-episodes 0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 \\\\\n  --output-jsonl \"$OUTPUT_ROOT/pi0_tcpplate_eval_unseen30_transition_head/results.jsonl\" \\\\\n  --summary-json \"$OUTPUT_ROOT/pi0_tcpplate_eval_unseen30_transition_head/summary.json\"\n\n# 9. 训练 target-relative contact transition heads。\n# naive all-head 会学到“阶段快结束了”，不等于“现在可以安全闭爪”；\n# 这里用 pregrasp geometry label + grasp/floor geometry features。\npython code/pi0/train_tcptarget_contact_transition_head.py \\\\\n  --dataset-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --dataset-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --task-set all \\\\\n  --early-label-mode pregrasp_geometry \\\\\n  --feature-mode grasp_geom_count_step \\\\\n  --step-scale 180 \\\\\n  --output-npz \"$MODEL_ROOT/pi0_tcpplate_finisher/contact_transition_head_pregraspgeom_graspgeom_step.npz\" \\\\\n  --summary-json \"$MODEL_ROOT/pi0_tcpplate_finisher/contact_transition_head_pregraspgeom_graspgeom_step_summary.json\"\n\n# 10. 用 contact transition heads 替代前段 pregrasp/descend/close 的部分手写切换。\n# descend_floor_guard 仍保留，防止 TCP 还明显高于抓取 floor 时提前闭爪。\nPYTHONPATH=\"$PWD:$TOPIC_ROOT/code/pi0:${PYTHONPATH:-}\" \\\\\npython code/pi0/evaluate_pi0_two_stage_tcpplate.py \\\\\n  --tcpplate-base-evaluator code/pi0/evaluate_pi0_two_stage_eef_abs.py \\\\\n  --tcpplate-schedule-start-phase move_preplace \\\\\n  --tcpplate-prefix-target-state \\\\\n  --tcpplate-contact-primitive guided_contact_lift \\\\\n  --tcpplate-contact-transition-head-path \"$MODEL_ROOT/pi0_tcpplate_finisher/contact_transition_head_pregraspgeom_graspgeom_step.npz\" \\\\\n  --tcpplate-contact-transition-head-threshold 0.5 \\\\\n  --tcpplate-contact-pregrasp-hold 999 \\\\\n  --tcpplate-contact-descend-hold 999 \\\\\n  --tcpplate-contact-transition-head-strict \\\\\n  --tcpplate-contact-descend-floor-guard \\\\\n  --tcpplate-gripper-head-path \"$MODEL_ROOT/pi0_tcpplate_finisher/gripper_head_phase_logreg.npz\" \\\\\n  --tcpplate-transition-head-path \"$MODEL_ROOT/pi0_tcpplate_finisher/transition_head_tcpplate_xy_step_logreg.npz\" \\\\\n  --tcpplate-adaptive-move-preplace-min-steps 20 \\\\\n  --tcpplate-adaptive-move-preplace-max-steps 180 \\\\\n  --prefix-source policy \\\\\n  --prefix-policy-path \"$MODEL_ROOT/pi0_prefix_tcptarget/checkpoints/001000/pretrained_model\" \\\\\n  --prefix-dataset-repo-id datawhale_eai_pnp_fullprefix_oracle_prefix_until_mpreplace_state_phase_tcptarget_eef_abs_10eps_v1 \\\\\n  --prefix-dataset-root ./demo_data_pi0_fullprefix_oracle_prefix_until_mpreplace_state_phase_tcptarget_eef_abs_10eps_v1 \\\\\n  --prefix-steps 180 \\\\\n  --finisher-phase-state dataset_schedule \\\\\n  --finisher-scripted-gripper \\\\\n  --finisher-policy-path \"$MODEL_ROOT/pi0_tcpplate_finisher/checkpoints/000250/pretrained_model\" \\\\\n  --finisher-dataset-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --finisher-dataset-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --seeds 1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039 \\\\\n  --finisher-phase-schedule-episodes 0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9 \\\\\n  --output-jsonl \"$OUTPUT_ROOT/pi0_contact_transition_head_unseen30/results.jsonl\" \\\\\n  --summary-json \"$OUTPUT_ROOT/pi0_contact_transition_head_unseen30/summary.json\"\n\n# 11. 拆掉 dataset_schedule 尾段，改用 stage-aware dynamic_timed finisher。\n# 关键点：--tcpplate-prefix-target-state 只让 prefix 使用 tcp_to_target；\n# stage-aware evaluator 会让 finisher 回到 tcp_to_plate。\nPYTHONPATH=\"$PWD:$TOPIC_ROOT/code/pi0:${PYTHONPATH:-}\" \\\\\npython code/pi0/evaluate_pi0_two_stage_tcpplate.py \\\\\n  --tcpplate-base-evaluator code/pi0/evaluate_pi0_two_stage_eef_abs.py \\\\\n  --tcpplate-prefix-target-state \\\\\n  --tcpplate-contact-primitive guided_contact_lift \\\\\n  --tcpplate-contact-transition-head-path \"$MODEL_ROOT/pi0_tcpplate_finisher/contact_transition_head_pregraspgeom_graspgeom_step.npz\" \\\\\n  --tcpplate-contact-transition-head-threshold 0.5 \\\\\n  --tcpplate-contact-pregrasp-hold 999 \\\\\n  --tcpplate-contact-descend-hold 999 \\\\\n  --tcpplate-contact-transition-head-strict \\\\\n  --tcpplate-contact-descend-floor-guard \\\\\n  --tcpplate-gripper-head-path \"$MODEL_ROOT/pi0_tcpplate_finisher/gripper_head_phase_logreg.npz\" \\\\\n  --prefix-source policy \\\\\n  --prefix-policy-path \"$MODEL_ROOT/pi0_prefix_tcptarget/checkpoints/001000/pretrained_model\" \\\\\n  --prefix-dataset-repo-id datawhale_eai_pnp_fullprefix_oracle_prefix_until_mpreplace_state_phase_tcptarget_eef_abs_10eps_v1 \\\\\n  --prefix-dataset-root ./demo_data_pi0_fullprefix_oracle_prefix_until_mpreplace_state_phase_tcptarget_eef_abs_10eps_v1 \\\\\n  --prefix-steps 180 \\\\\n  --finisher-phase-state dynamic_timed \\\\\n  --finisher-start-phase move_preplace \\\\\n  --timed-phase-dwell-spec move_preplace:260,lower_to_plate:40,retreat:40 \\\\\n  --hard-reset-sim-data \\\\\n  --finisher-scripted-gripper \\\\\n  --finisher-policy-path \"$MODEL_ROOT/pi0_tcpplate_finisher/checkpoints/000250/pretrained_model\" \\\\\n  --finisher-dataset-repo-id datawhale_eai_pnp_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --finisher-dataset-root ./demo_data_pi0_dagger_prefix120_step1500_state_phase_tcpplate_eef_abs_10eps_v1 \\\\\n  --seeds 1010,1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021,1022,1023,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039 \\\\\n  --output-jsonl \"$OUTPUT_ROOT/pi0_dynamic_timed_stageaware_unseen30/results.jsonl\" \\\\\n  --summary-json \"$OUTPUT_ROOT/pi0_dynamic_timed_stageaware_unseen30/summary.json\"\n\"\"\")\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "如果要跑夹爪脚本化对照，在评估命令里额外加入 `--finisher-scripted-gripper`。这一项只改 gripper，不改 EEF/arm，用来判断开闭爪时序是不是主要瓶颈。`--tcpplate-force-schedule-episode 0` 用于复核长 `move_preplace` 模板是否能消掉提前释放问题。`--tcpplate-gripper-head-path` 会在 `--finisher-scripted-gripper` 的钩子里加载 learned head，把手写 phase 规则替换成可训练的小模型。`--tcpplate-adaptive-move-preplace` 把强制长模板换成 live progress gate；`--tcpplate-transition-head-path` 则把固定阈值 gate 换成 logistic transition head；`--tcpplate-contact-transition-head-path` 进一步替代前段 contact primitive 的部分 phase 切换。`dynamic_timed` 版本要特别注意 stage-aware state：prefix 可以用 `tcp_to_target`，finisher 必须用 `tcp_to_plate`。批量评估建议加 `--hard-reset-sim-data`，先清掉跨 episode 的 MuJoCo 动力学残留；`--fresh-env-per-episode` 更干净但反复创建环境时可能遇到资产 provider 偶发报错。当前 stage-aware dynamic finisher 在 hard-reset clean protocol 的 30 个策略采样 seed（环境实际固定为 seed 0） 上是 strict `30/30`，但仍保留 target-relative contact scaffold、floor guard、learned gripper head 和后段 finisher。\n"
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": "如果旧 checkpoint 的 state normalizer 是 19 维，而新数据是 22 维，会出现 `mean/std` shape mismatch。诊断实验里可以复制 checkpoint 并删除 `normalize_inputs.buffer_observation_state.mean/std`，让 22D 数据集统计重新初始化；不要把这个临时处理误写成模型结构创新。\n"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
