Files
opendance-android/contracts/skeleton-topology.md
T

8.3 KiB

contracts/skeleton-topology.md - 骨骼拓扑契约 v0.2.0

唯一拓扑:MediaPipe Pose 33 点。禁止引入 SMPL 及其衍生拓扑(许可风险)。

1. 33 点定义与索引

idx name 中文 说明
0 nose 鼻
1 left_eye_inner 左眼内
2 left_eye 左眼
3 left_eye_outer 左眼外
4 right_eye_inner 右眼内
5 right_eye 右眼
6 right_eye_outer 右眼外
7 left_ear 左耳
8 right_ear 右耳
9 mouth_left 左嘴角
10 mouth_right 右嘴角
11 left_shoulder 左肩
12 right_shoulder 右肩
13 left_elbow 左肘
14 right_elbow 右肘
15 left_wrist 左腕
16 right_wrist 右腕
17 left_pinky 左小指
18 right_pinky 右小指
19 left_index 左食指
20 right_index 右食指
21 left_thumb 左拇指
22 right_thumb 右拇指
23 left_hip 左髋
24 right_hip 右髋
25 left_knee 左膝
26 right_knee 右膝
27 left_ankle 左踝
28 right_ankle 右踝
29 left_heel 左足跟
30 right_heel 右足跟
31 left_foot_index 左足尖
32 right_foot_index 右足尖

2. 虚拟关节(由 33 点派生,索引 33/34,仅用于层级与角度特征)

idx name 计算
33 neck midpoint(left_shoulder(11), right_shoulder(12))
34 pelvis midpoint(left_hip(23), right_hip(24))

3. 父节点表(parent[i], -1 表示世界根)

对称树,便于骨骼重定向与关节角计算:

34 pelvis             -> -1 (world root)
33 neck               -> 34
23 left_hip           -> 34
24 right_hip          -> 34
11 left_shoulder      -> 33
12 right_shoulder     -> 33
0  nose               -> 33
1  left_eye_inner     -> 0
2  left_eye           -> 0
3  left_eye_outer     -> 0
4  right_eye_inner    -> 0
5  right_eye          -> 0
6  right_eye_outer    -> 0
7  left_ear           -> 3
8  right_ear          -> 6
9  mouth_left         -> 0
10 mouth_right        -> 0
25 left_knee          -> 23
26 right_knee         -> 24
27 left_ankle         -> 25
28 right_ankle        -> 26
29 left_heel          -> 27
30 right_heel         -> 28
31 left_foot_index    -> 29
32 right_foot_index   -> 30
13 left_elbow         -> 11
14 right_elbow        -> 12
15 left_wrist         -> 13
16 right_wrist         -> 14
17 left_pinky         -> 15
18 right_pinky         -> 16
19 left_index         -> 15
20 right_index        -> 16
21 left_thumb         -> 15
22 right_thumb        -> 16

4. 坐标系

  • 归一化图像坐标: x, y ∈ [0, 1],原点左上角,y 向下;x 相对图像宽,y 相对图像高。
  • 深度: z 与 x 同尺度,原点位于髋中心,负值朝向相机。
  • 镜像: Android 前置摄像头默认输出镜像画面;是否镜像由帧头 flags.bit0 声明, 接收端据此决定是否翻转 x(x' = 1 - x)。
  • 置信度: visibility ∈ [0, 1],落盘/传输为 uint8(round(v * 255))。 归一化比例统一使用 32767(见 websocket-keypoints.md)。

5. 派生特征(DTW 输入,Windows 端实现,服务端预处理产物同构)

服务端 T-WEB-006 预处理产出的关键点序列与 Android 端输出的帧结构完全一致, 因此 Windows 端 DTW 引擎只有一条输入通路。

关节角特征(弧度,余弦值便于插值):

shoulder_L = angle(left_elbow,  left_shoulder,  left_hip)
shoulder_R = angle(right_elbow, right_shoulder, right_hip)
elbow_L    = angle(left_shoulder,  left_elbow,  left_wrist)
elbow_R    = angle(right_shoulder, right_elbow, right_wrist)
hip_L      = angle(left_shoulder,  left_hip,    left_knee)
hip_R      = angle(right_shoulder, right_hip,   right_knee)
knee_L     = angle(left_hip,   left_knee,  left_ankle)
knee_R     = angle(right_hip,  right_knee, right_ankle)
neck       = angle(nose, neck, pelvis)
spine      = angle(neck, pelvis, midpoint(left_ankle, right_ankle))

angle(a, b, c) = 向量 ba 与 bc 的夹角余弦,即 dot(normalize(a-b), normalize(c-b)),落在 [-1, 1]。

DTW 序列结构(冻结):每帧 = [33 x (x, y, z), 9 个关节角余弦] = 108 维。 低置信度点(visibility < 0.5)在插值阶段由相邻帧线性补全,不参与代价计算。


6. 内部动作标准与模型骨骼重绑定(v0.2.0)

目的:动作数据必须能匹配系统内的任意模型,因此把"关键点标准 + 骨架标准 + humanoid 标准" 冻结成本节,模型上传时按此重绑定骨骼,再包装为 VRM。

6.1 三层标准

层 标准 权威实现
关键点 MediaPipe Pose 33 点(顺序见 §1),动作一律先转成关键点序列(NDJSON)再入库 preprocess/(BVH / VMD / FBX -> 33 点)
动作骨架 MMD 标准骨架(静止比例实测自参考 PMX; 足IK 带位移时走两骨骼腿部 IK,否则 FK) preprocess/mmd-skeleton.ts
模型骨架 22 根 humanoid 规范骨(顺序即层级,父在前),其中 16 根为必需(§6.2) models/model-inspector.ts 的 HUMANOID_CANDIDATES

评分只依赖 33 点关键点序列,与模型骨架解耦;因此"动作能匹配任何模型"的充要条件是 模型可驱动(16 根必需骨齐全,drivable=true)。

6.2 22 根 humanoid 规范骨与 16 根必需骨

hips -> spine -> chest -> upperChest -> neck -> head
                  ├── leftShoulder  -> leftUpperArm  -> leftLowerArm  -> leftHand
                  └── rightShoulder -> rightUpperArm -> rightLowerArm -> rightHand
hips -> leftUpperLeg  -> leftLowerLeg  -> leftFoot  -> leftToes
hips -> rightUpperLeg -> rightLowerLeg -> rightFoot -> rightToes

必需 16 根(缺一即 drivable=false):hips、spine、neck、head、 leftUpperArm、leftLowerArm、leftHand、rightUpperArm、rightLowerArm、rightHand、 leftUpperLeg、leftLowerLeg、leftFoot、rightUpperLeg、rightLowerLeg、rightFoot。

6.3 重绑定规则(模型上传时执行,服务端与三端一致)

  1. 归一化:小写 → 只保留 a-z0-9 + 中文/日文(CJK、平假名、片假名、长音符、々); 全角 ASCII(A-Z a-z 0-9)折成半角(MMD 常写「足IK」「つま先IK」)。 v0.2.0 修正:此前只保留 a-z0-9, MMD 骨名(上半身/左ひじ/首)被归一化成空串, humanoid 覆盖率恒为 0/22。
  2. 剥前缀:只剥 J_Bip / mixamorig / Bip01; 不剥 J_Aim_ / J_Roll_ / J_Sec_ (VRoid 的扭转辅助骨,剥了会与真正的骨骼撞名)。
  3. 变体: left/right 与 l/r 互转(leftupperarm <-> lupperarm)。
  4. 精确匹配:归一化+变体后的名字必须与候选表完全相等(前缀不匹配), 因此 左足IK(-> 左足ik)不会抢占 左足(leftUpperLeg)。
  5. 候选表覆盖四类命名(顺序即优先级;服务端与客户端逐字一致,由测试断言): VRoid(J_Bip_C_Hips 等)、Mixamo(mixamorig:LeftArm)、 MMD 标准骨架(下半身/上半身/上半身2/首/頭/左肩/左腕/左ひじ/左手首/左足/左ひざ/左足首/左つま先)、 常见中文名(臀部/头/脖子/左大臂/左小臂/左大腿/左脚 等)。
  6. 结果写入 VRM:命中结果写成 VRMC_vrm.humanoid.humanBones 的节点索引(VRM 规范 不要求节点名固定,因此以索引为准,不改动几何与贴图);未命中的骨骼保留原名作为附加骨。
  7. 结论必须上报: models.stats.humanoidMapped / humanoidTotal / missingRequiredBones / drivable 四项写库,审核界面展示;usable 与是否进入客户端目录见 model-formats.md 1.1。

6.4 实测(v0.2.0 重绑定后)

样本 重绑定前 重绑定后 结果
【奥黛塔】_by_原神.zip(PMX,日文骨名) 0/22,不可驱动 21/22, drivable=true 交付 VRM
模型_初音未来Hatsune Miku_by_Boku_Wa_Luka.zip(PMX) 0/22,不可驱动 19/22, drivable=true 交付 VRM

未命中的 1-3 根均为非必需骨(如 upperChest / leftToes),不影响驱动与评分。