跳转至

HSI Generation Pipeline 交接说明

日期:2026-07-10

这份文档给下一位继续调 utils/hsi_generation 的同学使用。当前核心目标是:保持 pipeline general + simple,同时继续提升 motion quality。

当前接口约定

HSI generation 的公开入口是 task-first:

PYTHONPATH=/mnt/data/PhysHSI python tools/run_hsi_generation.py \
  --task-plan /abs/path/task_plan.json \
  --layout-json /abs/path/layout.json \
  --out-dir /abs/path/output_dir

不要把旧 config 接口带回来。特别注意:

  • 不要使用 v13_alignmentbaseline-case-dirconfig_from_baseline_case,也不要把旧 pipeline_manifest.json 当输入状态。
  • pipeline_manifest.json 只能是 provenance 输出,不能作为后续阶段的运行状态。
  • 不要新增 utils/scriptsutils/*/scriptsutils/*/cli.py
  • 人工运行入口放在 tools/
  • 必需 task 字段必须显式存在。缺字段应该立刻报错,不要在 HSI generation 里猜。
  • 最终 motion.npz 的 dynamic object 只保存 object id、pose 轨迹和 token 字段;不要保存 mesh vertices/faces/source mesh。渲染和 metrics 必须用 layout_json + dynamic_object_id 严格加载真实 asset,缺 mesh/texture 直接报错。

当前 Pipeline 结构

建议按这个顺序读代码:

task_plan + layout_json
-> planning.py      task steps -> ordered intents
-> anchors.py       intents -> semantic anchors
-> constraint.py    anchors -> executable operation events
-> kimodo.py        task scaffold + Kimodo full-body scaffold generation
-> object_motion.py object interaction 的物体运动 refine
-> hands.py         hand pose bake / local interaction refine
-> evaluation.py    metrics、render、validation
-> files.py         case files、canonical outputs、provenance

最核心的通用连续性规则是:

action_i output state -> action_{i+1} input state

只继承 action 真正改变并且仍然有效的状态。例如:

  • sit 结束时是 seated posture;get_up 必须从这个 seated state 开始。
  • pick 把 object 绑定到 hand;carry 必须从这个 bound object 开始。
  • place 把 object 释放到 support。
  • reachpushpullkick 应该把 root/heading continuity 传给下一个 action。

当前 Action 覆盖

当前已经跑过的 action family:

  • locomotion/posture scaffold:reachturnsitget_uplie_down
  • grasp interaction:pickcarryplace
  • direct-contact object interaction:pushpullkick

最近实现状态:

  • push/pull/kick 已经从 planning.require_implemented_plan 的 gate 里放开。
  • push/pull/kick 通过 interaction.object_motion.contact_events 走 direct contact object motion。
  • pick/carry/place 通过 interaction.object_motion.bound_intervals 表达 object bound interval。
  • 混合 object interaction 现在通过 planning.COMPOSITE_OBJECT_MOTION 支持:一次 run 里可以同时包含 grasp-bound object 和 direct-contact object。
  • composite object motion 是一次 in-memory merge 后写最终 motion_augmented.npz,不是先写中间 .npz 再读回来串状态。

已知限制:

  • 当前 pick/carry/place 的最终 place/release binding 仍然基本只支持单手。旧 mixed case 一开始试过 active_hands=["left", "right"],会报 only left/right single-hand tasks are supported。现在可跑的 mixed sample 用的是 left

重要 Task Contract

原则:task generator 负责几何决策,HSI generation 只消费明确字段。

对于 seat-like action,task JSON 必须显式提供:

{
  "seat_front_direction_xy": [0.0, -1.0],
  "seat_front_standing_point_xy": [3.1, 3.0],
  "interaction_point_xyz": [3.05, 3.65, 0.85]
}

不要让 HSI generation 从 scene context 猜 seat 朝向。旧 task 缺这些字段时,应该重新生成 task,或者在调用 HSI 前做确定性 task contract 补全。

对于 pick/carry/place,task JSON 必须显式提供 hand policy:

{
  "active_hands": ["left"],
  "primary_hand": "left"
}

不要在 HSI generation 里推断 active hand。

对于 kick,task JSON 必须显式提供冲击脚:

{
  "active_foot": "left"
}

kick 的 phase template 负责提供 impact/recovery full-body keyframe;impact root 由候选脚相对 root 的局部偏移和 task interaction_point_xyz 共同反解。物体在 impact 前保持静止,impact 后按 task 的 object target 做独立 impulse motion,不再绑定左右脚中点,也不再使用固定 contact fraction。

当前可跑样例

运行前默认先执行:source /home/user/anaconda3/etc/profile.d/conda.sh && conda activate isaac-sim

1. Sit/Get-Up Sanity

动作序列:reach -> sit -> get_up 生成命令:

PYTHONPATH=/mnt/data/PhysHSI python tools/run_hsi_generation.py --task-plan /mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_existing_sit_sanity/task_inputs/layout_2a8fbb56_sit_getup_enriched.json --layout-json /mnt/data/PhysHSI/data/selected_SAGE10k/scene_bank/2a8fbb56/layout_2a8fbb56.json --out-dir /mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_existing_sit_sanity/layout_2a8fbb56_sit_getup_enriched_full_rerun
视频路径:/mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_existing_sit_sanity/layout_2a8fbb56_sit_getup_enriched_full_rerun/kinematic_video.mp4 Validation:已有输出可用,用户肉眼反馈效果很好;自动 case_quality_pass=false 主要是 temporal QA 仍需人工确认。

2. Non-Grasp Composition

动作序列:reach -> push -> reach -> kick -> reach -> sit -> get_up 生成命令:

PYTHONPATH=/mnt/data/PhysHSI python tools/run_hsi_generation.py --task-plan /mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_general_action_sanity/task_inputs/layout_616f1245_task_plan_contract.json --layout-json /mnt/data/PhysHSI/data/selected_SAGE10k/scene_bank/616f1245/layout_616f1245.json --out-dir /mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_general_action_sanity/layout_616f1245_non_grasp_full_rerun
视频路径:/mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_general_action_sanity/layout_616f1245_non_grasp_full_rerun/kinematic_video.mp4 Validation:无 errors;case_quality_pass=false 只是因为 temporal render QA 是 pending_manual_review

3. Repeated Pull

动作序列:reach -> pull -> reach -> pull -> reach -> pull 生成命令:

PYTHONPATH=/mnt/data/PhysHSI python tools/run_hsi_generation.py --task-plan /mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_general_action_sanity/task_inputs/layout_4105c76c_task_plan_contract.json --layout-json /mnt/data/PhysHSI/data/selected_SAGE10k/scene_bank/4105c76c/layout_4105c76c.json --out-dir /mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_general_action_sanity/layout_4105c76c_repeated_pull_full_rerun
视频路径:/mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_general_action_sanity/layout_4105c76c_repeated_pull_full_rerun/kinematic_video.mp4 Validation:无 errors;case_quality_pass=false 只是因为 temporal render QA 是 pending_manual_review

4. Mixed Grasp + Direct Contact + Posture

动作序列:reach -> pick -> carry -> place -> reach -> pull -> reach -> sit 生成命令:

PYTHONPATH=/mnt/data/PhysHSI python tools/run_hsi_generation.py --task-plan /mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_general_action_sanity/task_inputs/layout_7ff33042_task_plan_contract.json --layout-json /mnt/data/PhysHSI/data/selected_SAGE10k/scene_bank/7ff33042/layout_7ff33042.json --out-dir /mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_general_action_sanity/layout_7ff33042_mixed_full_rerun
视频路径:/mnt/data/PhysHSI/output/auto_experiments/20260709_hsi_generation_general_action_sanity/layout_7ff33042_mixed_full_rerun/kinematic_video.mp4 Validation:无 errors;case_quality_pass=false 只是因为 temporal render QA 是 pending_manual_review。当前 task 用 active_hands=["left"],双手 release binding 还未完成。

5. Left Pick-Carry-Place

动作序列:reach -> pick -> carry -> place 生成命令:

PYTHONPATH=/mnt/data/PhysHSI python tools/run_hsi_generation.py --task-plan /mnt/data/PhysHSI/output/auto_experiments/20260705_hsi_generation_task_inputs/task_plan_left.json --layout-json /mnt/data/PhysHSI/data/selected_SAGE10k/e2e_test_scenes/2cdc81db/layout_2cdc81db.json --out-dir /mnt/data/PhysHSI/output/auto_experiments/20260710_hsi_generation_handoff_left_rerun
视频路径:重新运行后为 /mnt/data/PhysHSI/output/auto_experiments/20260710_hsi_generation_handoff_left_rerun/kinematic_video.mp4 Validation:当前交接轮未重新生成;用于 left-hand pick-carry-place 回归,需跑完后和 v13 reference 肉眼对齐。

6. Right Pick-Carry-Place

动作序列:reach -> pick -> carry -> place 生成命令:

PYTHONPATH=/mnt/data/PhysHSI python tools/run_hsi_generation.py --task-plan /mnt/data/PhysHSI/output/auto_experiments/20260705_hsi_generation_task_inputs/task_plan_right.json --layout-json /mnt/data/PhysHSI/data/selected_SAGE10k/e2e_test_scenes/2cdc81db/layout_2cdc81db.json --out-dir /mnt/data/PhysHSI/output/auto_experiments/20260710_hsi_generation_handoff_right_rerun
视频路径:重新运行后为 /mnt/data/PhysHSI/output/auto_experiments/20260710_hsi_generation_handoff_right_rerun/kinematic_video.mp4 Validation:当前交接轮未重新生成;用于 right-hand pick-carry-place 回归,需跑完后和 v13 reference 肉眼对齐。

测试命令

如果改了 planning、constraint、object motion,先跑这个快速聚焦测试:

PYTHONPATH=/mnt/data/PhysHSI pytest -q \
  tests/test_hsi_generation_pipeline.py \
  -k 'object_motion or contact_object or movable or runtime_allows_movable or binding'

最近一次结果:

22 passed, 81 deselected

完整 HSI/Kimodo 聚焦回归:

source /home/user/anaconda3/etc/profile.d/conda.sh
conda activate isaac-sim
PYTHONPATH=/mnt/data/PhysHSI pytest -q \
  tests/test_hsi_generation_pipeline.py \
  tests/test_hsi_generation_binding.py \
  tests/test_expected_contact_labels.py \
  tests/test_measured_contact_labels.py \
  tests/test_kimodo_contact_region_labels.py \
  tests/test_physhsi_metrics.py \
  tests/test_kimodo_plan_to_constraints.py \
  tests/test_object_keyframe_binding.py \
  tests/test_lie_keyframe_binding.py \
  tests/test_kimodo_render_contact_overlay.py

最近一次结果(2026-07-12,包含 kick impulse 和 high-seat posture 回归):

212 passed in 6.13s

肉眼检查重点

看每个 kinematic_video.mp4 时重点检查:

  • action boundary 处 root path 是否连续。
  • heading 是否突然跳变。
  • push/pull/kick 是否真的像对应动作,而不是普通 walk 或奇怪摆动。
  • direct-contact object motion 在 contact window 里是否跟 hand/foot 对齐。
  • sit/get_up 是否朝向 support,是否走进椅子/沙发/凳子内部。
  • pick/carry/place 的 grasp、carry、release、最终放置是否连贯。
  • camera 是否掩盖或夸大 root jitter。不要用最终全局 smoothing 去掩盖真实 motion bug。

已知后续工作

优先级较高的下一步:

  1. 补完整 two-hand release binding,让 pick/carry/place 真正支持双手最终放置。
  2. 提升 push/pull/kick 的动作质量。接口已经接上,但 motion semantics 可能还不够好。
  3. 给 composite object motion 增加更明确的 metadata / multi-object rendering 测试。
  4. 用当前代码重新跑 left/right pick-carry-place,并和早期 v13 reference 行为对齐。
  5. 继续精简 utils/hsi_generation,但只在真正减少复杂度时改,不要新增 wrapper 层。
  6. 保持 task generator 负责显式几何 contract,例如 seat direction、standing point、hand policy。

2026-07-12 补充:

  • kick 已使用显式 active_foot、phase impact event、task-target impulse object motion 和对应 event metrics。
  • sit pose 不再只按 object taxonomy 选取;现有 SMPL-X 候选会按连续 seat height 选最近几何姿态。get-up 末帧增加 locomotion-ready full-body constraint,避免高凳场景中 pelvis 穿过座面后仍未站稳。
  • 当前 sit candidate library 仍只有 individual/broad 两个几何高度原型。高度选择逻辑是通用的,但候选覆盖仍有限;新增姿态应扩充 library/metadata,不要增加 object-type 特判。

代码风格提醒

保持代码朴素直接:

  • 优先用普通函数和显式 dict 字段。
  • 优先 in-memory handoff,少写中间文件。
  • runner/stage 只做编排,不写 geometry/math。
  • 不要把 manifest 当状态总线。
  • 必需 task semantics 不要 fallback 猜。
  • 不要只为 pick-carry-place 写死逻辑。它应该只是 general action-template pipeline 的一个 regression case。