Smart Object Primitive TODO
本文档单独定义 object_sockets -> keyframe_targets -> Kimodo constraints 这条规范和第一版 procedural 生成方式。目标是把 MotionBricks 的 Smart Object 思路迁移到当前 SAGE + Kimodo + Isaac/ProtoMotions pipeline,但第一版不训练模型、不生成 full-body pose,只生成 sparse targets 和 constraints。
1. 核心概念
1.1 object_sockets
object_sockets 是物体侧的 affordance anchors,绑定在 object local frame 上。
它回答:
它应该跟着物体移动和旋转:
示例:
{
"object_id": "pouf:e55447",
"object_sockets": {
"seat_center": {"xyz": [0.0, 0.0, 0.42], "frame": "object"},
"seat_front_axis": {"xy": [0.0, -1.0], "frame": "world_or_object"},
"seat_right_axis": {"xy": [1.0, 0.0], "frame": "world_or_object"},
"seat_surface": {"height": 0.42, "frame": "world"},
"front_floor_region": {"center_xy": [0.0, -0.55], "radius": 0.30, "frame": "object"}
}
}
第一版来源:
scene_context.json:
explicit_interaction_targets_by_mode["sit"]
seat_front_direction_xy
seat_front_standing_point_hint_xy
object bbox / dimensions / rotation
task_validation.json:
standing_xy
target_xyz
path_points
1.2 keyframe_targets
keyframe_targets 是 humanoid 侧的 sparse targets,由 object_sockets 通过 procedural binding rules 生成。
它回答:
在这个 interaction 中,humanoid root/pelvis/feet/hands 应该去哪里?
heading 应该朝哪里?
哪些目标导出成 root / end-effector / fullbody constraint?
它不是 full-body pose,不包含完整 SMPL joint angles。
示例:
{
"primitive_id": "sit:pouf:e55447:slot0",
"keyframe_targets": {
"pre_sit_root": {
"xyz": [2.8, 4.6, 0.95],
"heading_xy": [0.0, -1.0],
"kimodo_type": "root2d"
},
"final_sit_pose": {
"root_xyz": [2.8, 4.2, 0.50],
"heading_xy": [0.0, -1.0],
"kimodo_type": "end-effector",
"joint_targets": [
{"joint_name": "Hips", "xyz": [2.8, 4.2, 0.48]},
{"joint_name": "LeftFoot", "xyz": [2.67, 4.75, 0.0]},
{"joint_name": "RightFoot", "xyz": [2.93, 4.75, 0.0]}
]
}
}
}
注意:kimodo_type 在这里表示最终应导出的 Kimodo native constraint type,但这段 JSON 仍是我们的中间层目标描述,不是 Kimodo 原生 constraints.json。Kimodo 当前不接受 per-target hardness = soft/hard。
1.3 Kimodo constraints
Kimodo constraints 是 motion/stage 侧的实际输入。它把 keyframe_targets 绑定到具体 frame。
它回答:
推荐第一版尽量只用 Kimodo native constraint type:
root2d:
root path / start-end root xy / heading
end-effector:
final sit Hips / LeftFoot / RightFoot / LeftHand / RightHand targets
通过 canonical/retrieved pose frame + FK/IK 转成 Kimodo 原生 JSON
fullbody:
少量关键姿态的强约束;只在确实需要时使用,避免过约束
Kimodo 原生支持的 constraint type 是 root2d、fullbody、left-foot、right-foot、left-hand、right-hand、end-effector。当前 repo 里的 root3d 是 wrapper-side monkey patch,不是 Kimodo native type。长期设计应该把 root3d 降级为 legacy/quick baseline,而不是作为主接口。
当前 plan_to_constraints.py 还没有把 procedural keypoints 导出成 native end-effector / left-foot / right-foot constraints,但这是需要实现的核心能力,不是只能做 validation。
当前 Kimodo constraint schema 不支持 hardness 这种 per-target 软硬约束字段。当前可用的控制方式是:
1. 选择是否导出某个 target 到 Kimodo。
2. 选择导出成哪种 native constraint type:root2d / fullbody / left-foot / right-foot / left-hand / right-hand / end-effector。
3. 选择约束在哪些 frame 生效。
4. 调整 generation 时的 cfg constraint weight,例如 meta/cfg 里的 constraint_weight。
因此本文档里的 kimodo_type 只表示我们最终要走哪种 Kimodo native constraint:
root2d:
导出到 Kimodo root2d。
left-foot / right-foot:
通过 Kimodo 原生 left-foot / right-foot constraint 导出。
left-hand / right-hand:
通过 Kimodo 原生 left-hand / right-hand constraint 导出。
end-effector:
通过 Kimodo 原生 end-effector constraint 导出,joint_names 可以包含 Hips/LeftFoot/RightFoot/LeftHand/RightHand。
fullbody:
通过 Kimodo 原生 fullbody constraint 导出;只用于少数 high-confidence keyframe。
关键实现点:Kimodo native end-effector / left-foot / right-foot JSON 不是直接传一个 xyz 点,而是通过 local_joints_rot、root_positions、smooth_root_2d 还原出一帧 skeleton,再从这帧 skeleton 里取对应 joint position。Kimodo 的 UI 可以让用户点选/移动 keypoint,是因为它背后仍然有一帧 motion pose 作为载体。我们的 exporter 要做同样的事情:
keyframe_targets xyz
-> 选择 canonical/retrieved pose frame
-> root/heading align 到 object socket
-> optional lightweight IK 让 FK 后的 feet/hands 更接近 target xyz
-> 得到 local_joints_rot + root_positions + smooth_root_2d
-> 导出 Kimodo native end-effector / left-foot / right-foot / left-hand / right-hand constraint
所以这里不是“Kimodo 不支持这些点”,而是“Kimodo 支持这些 constraint set,但我们要实现 procedural keypoint target 到 Kimodo constraint JSON 的适配层”。
root3d wrapper 的作用只是直接写 smooth_root_2d + root_y_pos + global_root_heading,不锁 full-body joint。这个接口直观,但不是 native。native-only 路线下,final sit 的 3D root/pelvis 目标应优先表达为:
root2d:
final root xy + heading
end-effector:
joint_names = ["Hips", "LeftFoot", "RightFoot"]
由 aligned seated pose frame 导出 local_joints_rot / root_positions / smooth_root_2d
风险:Kimodo native end-effector 会同时约束 selected joints 的 position/rotation、root_y_pos、global heading 和 smooth_root_2d;它不是纯 position-only constraint。因此需要和旧 root3d wrapper 做 A/B test,确认是否会因为过约束导致 sit motion 质量下降。
2. 数据流
完整流程:
SAGE scene object
-> object_sockets
-> procedural binding rules
-> keyframe_targets
-> Kimodo constraints.json
-> Kimodo multi-stage generation
-> kinematic validation
-> ProtoMotions tracker / physics validation
关键区分:
object_sockets:
object-side anchors
long-lived
object-local
keyframe_targets:
humanoid-side sparse targets
interaction-specific
usually world-frame after binding
Kimodo constraints:
frame-specific model inputs
generated from keyframe_targets + stage timeline
3. SitSmartObject procedural 生成
3.1 输入字段
从 scene_context.json 读取:
seat_center_world = explicit_interaction_targets_by_mode["sit"]
seat_front_world_xy = seat_front_direction_xy
seat_stand_hint_xy = seat_front_standing_point_hint_xy
object bbox / dimensions / rotation
从 task_validation.json 读取:
3.2 建立 seat frame
定义:
其中:
需要在坐标系测试中确认 seat_front_direction_xy 的正方向。如果发现 heading 翻转,就统一在 coordinates.py 中修正,而不是在每个 primitive 中临时修。
3.3 生成 object_sockets
seat_center:
position = C
orientation = [r, f, u]
seat_surface:
height = seat_h
center = C
front_floor_region:
center_xy = C.xy - f * 0.50
radius = 0.25 ~ 0.35
approach_region:
center_xy = seat_stand_hint_xy or C.xy - f * 0.55
radius = 0.30 ~ 0.50
对 sofa/bench,先沿 r 方向采多个 slot:
usable_width = object_width - 2 * armrest_margin
slot_width = 0.55 ~ 0.65
num_slots = max(1, floor(usable_width / slot_width))
slot_center_i = C + r * offset_i
每个 slot_center_i 都生成一套 seat_center/front_floor_region/approach_region。
3.4 生成 keyframe_targets
默认参数:
root_stand_height = 0.90 ~ 1.00
root_sit_offset = 0.05 ~ 0.12
pelvis_offset = 0.03 ~ 0.08
pre_sit_distance = 0.40 ~ 0.60
foot_forward_offset = 0.45 ~ 0.60
stance_width = 0.22 ~ 0.32
公式:
pre_sit_root.xy =
seat_stand_hint_xy
or C.xy - f * pre_sit_distance
pre_sit_root.z =
floor_z + root_stand_height
final_sit_root.xy =
C.xy
final_sit_root.z =
seat_h + root_sit_offset
pelvis_target.xyz =
[C.x, C.y, seat_h + pelvis_offset]
left_foot_target.xy =
C.xy - f * foot_forward_offset - r * stance_width / 2
right_foot_target.xy =
C.xy - f * foot_forward_offset + r * stance_width / 2
left_foot_target.z =
floor_z
right_foot_target.z =
floor_z
heading_xy =
f
3.5 生成 Kimodo stage constraints
对 multi-stage Kimodo:
stage 0:
prompt = "A person walks toward the chair/sofa/pouf."
constraint = root2d path from task_validation.path_points
stage 1:
prompt = "A person sits down on the chair/sofa/pouf."
constraint:
final fullbody 或 end-effector keyframe = aligned seated pose frame
global_root_heading = heading_xy, 由 keyframe pose 自身或 sparse root2d 提供
推荐第一版 native-only 直接导出,不把 root3d 作为 sit 主接口:
fullbody:
type = "fullbody"
frame_indices = [sit_end_frame]
local_joints_rot = aligned seated pose local rotations
root_positions = aligned seated pose root position
smooth_root_2d = final_sit_root.xy after SAGE->Kimodo axis mapping
end-effector:
type = "end-effector"
frame_indices = [sit_end_frame]
joint_names = ["Hips", "LeftFoot", "RightFoot"]
local_joints_rot = aligned seated pose local rotations
root_positions = aligned seated pose root position
smooth_root_2d = final_sit_root.xy after SAGE->Kimodo axis mapping
root2d 在 sit 阶段只用于进入交互前的 locomotion/align。不要在 lowering/sit transition 中继续塞 dense root2d path;否则它可能和 final seated keyframe 互相打架。推荐:
walk/align stage:
dense 或 sparse root2d 到 pre_sit_root
sit stage:
不使用 dense root2d
final frame 使用 native fullbody 或 end-effector keyframe
暂时不直接喂给 Kimodo,但需要保存:
用途:
root3d 只保留为 legacy ablation / fallback,不作为目标实现:
root3d wrapper:
frame_indices = [sit_end_frame]
smooth_root_2d = final_sit_root.xy
root_y_pos = final_sit_root.z after SAGE->Kimodo axis mapping
global_root_heading = heading_xy
3.6 基于本周 sit demo 的最小改动测试
目的不是先重写 SAGE converter,而是用本周已经能跑的 Kimodo sit demo 做 isolated test:
固定不变:
prompt
frame timeline
root2d path/heading
Kimodo model/config/seed
output/evaluation pipeline
只替换:
final seated fullbody keyframe 的生成方式
实验组:
A. original demo
使用本周 demo 里手调/保存的 final fullbody keyframe。
B. generated keyframe, legacy context
其它 constraints 完全保留,包括 demo 里已有的 root3d height guides。
只替换 final fullbody keyframe。
用来隔离判断 keyframe_factory 生成的 pose 是否合理。
C. generated keyframe, native-only sit
删除 sit transition 里的 root3d height guides。
保留 locomotion/align 的 root2d。
final frame 只用 generated fullbody 或 end-effector keyframe。
用来回答 sit 是否可以不依赖 root3d。
判断标准:
motion naturalness:
walk -> align -> sit transition 没有明显瞬移、抖动、脚滑、突然蹲地再升起。
keyframe control:
final Hips/root 落在 seat support region。
final root heading 与 seat_front_direction 一致。
LeftFoot/RightFoot 在 floor/free-space region。
collision:
final keyframe pose 不能明显穿入 seat/back/armrest。
feet 不能穿地。
lower legs 不能和 chair/sofa front 发生大穿透。
如果 B 失败,问题优先归因于 keyframe_factory 的 pose alignment / collision validation。
如果 B 成功但 C 失败,说明当前 Kimodo 仍需要中间 height/pose guidance;下一步不是继续用 root3d,而是加一个 native mid-sit keyframe 或 end-effector(Hips) 中间帧。
4. 其他 primitive 的规则草案
4.1 PickupSmartObject
object_sockets:
keyframe_targets:
MVP 规则:
small object:
one-hand wrist target at object top/front
box/cushion/pillow:
two-hand wrist targets on left/right bbox sides
floor object:
lower torso / bend style prompt
tabletop object:
reach style prompt
4.2 PlaceSmartObject
object_sockets:
keyframe_targets:
MVP 规则:
release_object_pose.xyz =
support_surface_center + [0, 0, object_half_height + clearance]
wrist_release_target =
release_object_pose.xyz + hand_offset
4.3 CarrySmartObject
object_sockets:
keyframe_targets:
MVP 规则:
carry object kinematically attached to wrist/torso proxy
validate hand-object distance and object-scene collision
5. TODO
5.1 Schema
- 新增
utils/hsi_primitive_factory/schemas.py。 - 定义
ObjectSocket。 - 定义
KeyframeTarget。 - 定义
SmartObjectBinding。 - 定义
PrimitiveConstraintBundle。 - 明确 frame 字段:
sage_world、kimodo_world、object_local。
5.2 Sit MVP
- 从
scene_context.json提取 sit object。 - 读取
explicit_interaction_targets_by_mode["sit"]。 - 读取
seat_front_direction_xy。 - 读取
seat_front_standing_point_hint_xy。 - 生成
object_sockets。 - 生成
keyframe_targets。 - 导出
primitive_constraints.json。 - 将
pre_sit_root/final_sit_root导出为 Kimodoroot2dstart/end。 - 将
heading_xy导出为global_root_heading。 - 选一个 canonical/retrieved seated pose frame。
- 将 seated pose frame 做 root/heading align 到
final_sit_root。 - 基于 aligned pose 导出 Kimodo native
fullbodyconstraint,复现本周 sit demo 的 final keyframe 接口。 - 基于同一个 aligned pose 导出 Kimodo native
end-effectorconstraint,joint_names = ["Hips", "LeftFoot", "RightFoot"],作为 ablation。 - 如 foot target 误差过大,再加 lightweight IK 或 CEM search 微调 seated pose。
- 做本周 demo isolated test:只替换 final seated keyframe,其余 prompt/timeline/root2d/seed 不变。
- 做 native-only sit test:删除
root3dheight guides,只保留 finalfullbody或end-effectorkeyframe。 - 如果 native-only 失败,改用 native mid-sit
fullbody/end-effector(Hips)keyframe,而不是优先恢复root3d。
5.3 Validation
- 检查 foot targets 是否在 free floor。
- 检查 final_sit_root 是否靠近 seat target。
- 检查 heading 是否和 seat front 一致。
- 检查 pelvis target 是否在 seat support region。
- 检查 aligned seated keyframe pose 与 chair/sofa proxy 的碰撞。
- 检查 feet-floor penetration、thigh-seat penetration、shin-front collision、torso-back/armrest penetration。
- 对 collision 失败的 keyframe 尝试 root offset / yaw / foot placement 小范围 search,仍失败则 reject。
- 记录每个 rejected candidate 的 reason。
5.4 Debug visualization
- 在 SAGE topdown overlay 画 object_sockets。
- 在 SAGE topdown overlay 画 keyframe_targets。
- 在 Kimodo viewer overlay 画 native end-effector target markers。
- 在 ProtoMotions playback 中画 target object 和 humanoid target markers。
5.5 后续扩展
- 支持 sofa/bench multi-slot。
- 支持 pickup object grasp sockets。
- 支持 support surface place sockets。
- 支持 object trajectory for carry/place。
- 实现通用
keyframe_targets到 Kimodoleft-foot/right-foot/left-hand/right-hand/end-effectorconstraints 的 exporter。 - 为 end-effector exporter 实现 canonical pose construction、retrieval pose alignment 或 lightweight IK,把 sparse xyz targets 转成 Kimodo 需要的
local_joints_rot+root_positions+smooth_root_2d。 - 评估 pelvis/Hips target 是否通过 native
end-effector(["Hips"])、nativefullbody、还是 legacyroot3dfallback 处理。 - 如果 procedural success rate 不够,再加入 retrieval 或 CEM search。