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_alignment、baseline-case-dir、config_from_baseline_case,也不要把旧pipeline_manifest.json当输入状态。 pipeline_manifest.json只能是 provenance 输出,不能作为后续阶段的运行状态。- 不要新增
utils/scripts、utils/*/scripts或utils/*/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 真正改变并且仍然有效的状态。例如:
sit结束时是 seated posture;get_up必须从这个 seated state 开始。pick把 object 绑定到 hand;carry必须从这个 bound object 开始。place把 object 释放到 support。reach、push、pull、kick应该把 root/heading continuity 传给下一个 action。
当前 Action 覆盖
当前已经跑过的 action family:
- locomotion/posture scaffold:
reach、turn、sit、get_up、lie_down - grasp interaction:
pick、carry、place - direct-contact object interaction:
push、pull、kick
最近实现状态:
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:
不要在 HSI generation 里推断 active hand。
对于 kick,task JSON 必须显式提供冲击脚:
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'
最近一次结果:
完整 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 回归):
肉眼检查重点
看每个 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。
已知后续工作
优先级较高的下一步:
- 补完整 two-hand release binding,让
pick/carry/place真正支持双手最终放置。 - 提升
push/pull/kick的动作质量。接口已经接上,但 motion semantics 可能还不够好。 - 给 composite object motion 增加更明确的 metadata / multi-object rendering 测试。
- 用当前代码重新跑 left/right pick-carry-place,并和早期 v13 reference 行为对齐。
- 继续精简
utils/hsi_generation,但只在真正减少复杂度时改,不要新增 wrapper 层。 - 保持 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。