Keyframe Binding 使用说明
这份文档说明当前 repo 里的 MotionBricks-style keyframe binding 是怎么把 canonical sit keyframe 合成到具体 SAGE object 上的,以及如何运行和检查输出。
当前实现文件:
当前 demo 输出目录:
1. 目标
我们不让 Kimodo 直接“理解”某个具体 pouf/chair/sofa 的 geometry。当前做法是:
canonical seated keyframe
+ concrete SAGE object mesh
+ scene_context binding hints
-> bound fullbody keyframe constraint
也就是仿照 MotionBricks 的 Smart Object 思路:
然后把合成后的 fullbody keyframe 输出成 Kimodo native constraint 可用的结构。
2. 输入数据
脚本默认使用:
plan:
utils/sage_task_generator/real_vlm_runs/20260423_drop_pen_keep_sit
target object:
pouf:e55447
canonical keyframe library:
data/hsi_primitives/keyframe_library
taxonomy:
data/hsi_primitives/taxonomy/sage_sit_object_types.json
scene_context.json 里需要有:
entities[*].explicit_interaction_targets_by_mode["sit"]
entities[*].seat_front_direction_xy
entities[*].seat_front_standing_point_hint_xy
entities[*].position_xyz
entities[*].dimensions_xyz
layout_json 里需要有对应 object 的:
脚本会用 source_id 读取真实 SAGE object mesh:
缺少这些字段会直接报错;当前实现不做 bbox fallback。
3. Binding 逻辑
3.1 Object-side socket
脚本先读取目标 object 的真实 mesh,并找最高处的 top surface patch:
然后用 top patch 的 xy median 作为 support surface center。
对于 pouf/ottoman/stool,当前会把 socket 沿 seat_front_direction_xy 往前移动:
这是为了避免低矮圆柱/软凳场景里,小腿插入 object volume。
当前 demo 的 object socket 大约是:
也就是说 contact point 不在 top center,而是在 top support surface 的前半部。
3.2 Body-side socket
对 canonical seated pose,脚本调用 Kimodo 的官方 SOMA skin:
得到完整 SOMA mesh 后,从 lower pelvis / upper thigh 区域提取 body-side contact patch:
然后取该 patch 的低处 vertices,得到 body_contact_socket。
3.3 Rigid binding
绑定时只做刚体变换:
yaw:
canonical heading -> seat_front_direction
translation:
body_contact_socket -> object seat_contact_socket
然后保持 fullbody local pose 不变,输出新的:
4. Scoring / filtering
当前 score 不是只看 Hips/root,而是以 full SOMA mesh 为主。
主要 metrics:
contact_patch_support_ratio
contact_patch_z_gap_m
contact_patch_center_xy_error_m
full_body_penetration_vertex_count
hips_seat_offset_m
foot_max_abs_floor_error_m
Hard filters:
contact patch vertices 太少 -> invalid
contact z gap > 3.5cm -> invalid
contact patch support ratio < 0.55 -> invalid
full body penetration vertices > 50 -> invalid
hips height 不合理 -> invalid
feet-floor error > 12cm -> invalid
注意:full_body_penetration_vertex_count 是对完整 SOMA mesh 检查是否进入 object volume proxy,而不是只检查 Hips 或 top support 区域。
5. 运行命令
使用当前默认 demo:
也可以显式指定:
/home/user/anaconda3/envs/isaac-sim/bin/python tools/kimodo/object_keyframe_binding.py \
--plan-dir utils/sage_task_generator/real_vlm_runs/20260423_drop_pen_keep_sit \
--target-label pouf:e55447 \
--output-dir data/hsi_primitives/object_bindings/demo_pouf_e55447
6. 输出文件
输出目录:
关键文件:
binding_manifest.json
candidates.json
top_candidate_fullbody_constraint.json
bound_pose_gallery.html
bound_poses/*.npz
mesh_data/object.json
mesh_data/human_*.json
top_candidate_fullbody_constraint.json 是后续喂给 Kimodo 的 fullbody constraint:
{
"type": "fullbody",
"frame_indices": [0],
"local_joints_rot": [...],
"root_positions": [...],
"smooth_root_2d": [...]
}
bound_poses/*.npz 保存 bound 后的 fullbody pose 和 contact patch:
local_rot_mats
global_rot_mats
posed_joints
posed_joints_sage
root_position
smooth_root_2d
global_root_heading
body_contact_socket_sage
body_contact_patch_sage
body_contact_patch_vertex_indices
seat_contact_socket_sage
7. 可视化
当前 gallery 用完整 mesh:
如果 server 已经启动,可以打开:
建议加 query string 强制刷新:
如果本地没有 server,可以在输出目录上启动:
python -m http.server 8898 --bind 0.0.0.0 --directory data/hsi_primitives/object_bindings/demo_pouf_e55447
8. 当前 sit-on-pouf demo 结果
最近一次生成:
top candidate 的关键指标:
contact_patch_support_ratio: 0.675
full_body_penetration_vertex_count: 37
hips_center_distance_m: 0.124
root_y_sage: 5.326
这版是当前人工检查后较合理的 compromise:
9. 当前限制
当前实现仍然是 rigid binding,不做 leg IK / pose repair。
如果一个 canonical seated pose 的小腿本身会插入低矮 object,仅靠平移 socket 会遇到 tradeoff:
下一步更稳的方案是: