From 68a5ca0a875fd1d374dd678a395ff7b885a49eb0 Mon Sep 17 00:00:00 2001 From: zijunchiang Date: Tue, 15 Sep 2026 21:34:50 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=85=B6=E4=BD=99=E6=98=93=E6=B7=B7?= =?UTF-8?q?=E6=B7=86=E5=AD=97=E7=AC=A6=EF=BC=88x/>=3D/<=3D/~/OK=20?= =?UTF-8?q?=E5=8F=96=E4=BB=A3=20=C3=97=20=E2=88=92=20=E2=89=A5=20=E2=89=A4?= =?UTF-8?q?=20=E2=89=88=20=E2=9C=93=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DSH.md | 8 ++++---- README.md | 14 +++++++------- README.txt | 10 +++++----- contracts/data-models.md | 10 +++++----- contracts/model-formats.md | 4 ++-- contracts/skeleton-topology.md | 2 +- contracts/websocket-keypoints.md | 2 +- tasks/004-keypoint-deserialize.md | 4 ++-- tasks/006-skeleton-retarget.md | 6 +++--- tasks/007-scoring-dtw.md | 10 +++++----- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/DSH.md b/DSH.md index 2e9eab1..555227c 100644 --- a/DSH.md +++ b/DSH.md @@ -53,9 +53,9 @@ Web 服务端 ──GET /api/catalog/export──────┤ | 项 | 建议值 | |--|--| -| 单任务文件数 | ≤ 6 | -| 单任务代码行数 | ≤ 1500 | -| 单任务 Token | ≤ 12K | +| 单任务文件数 | <= 6 | +| 单任务代码行数 | <= 1500 | +| 单任务 Token | <= 12K | | 契约引用 | 只引用锚点,不加载全文 | ## 5. 目录结构 @@ -100,7 +100,7 @@ opendance-windows/ 1. **绝不让视频帧离开设备**:本端只接收 33 个关键点的坐标,不接收也不存储图像。 2. **契约先行**:改交互先改 `opendance-web` 的 `contracts/`,再回本仓库同步快照。 3. **不假设骨骼轴向约定**:重定向基于「静止朝向 -> 目标朝向」的最小旋转, - `pose = parent_global_basis⁻¹ × desired` 这一合成公式由真实 VRM 实测标定, + `pose = parent_global_basis⁻¹ x desired` 这一合成公式由真实 VRM 实测标定, 有回归测试守护,不得凭记忆改写。 4. **上传/下载的模型不入库**:真实 VRM 样例仅放在 `_scratch/`(已 gitignore), 版权原因不得提交。 diff --git a/README.md b/README.md index 10b522e..d3e7160 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,13 @@ OpenDance Studio **阶段二**:Windows 客户端(Godot 4)。 | 验收项 | 结果 | |--|--| -| 脚本编译 | ✓ 全部通过(无 SCRIPT ERROR) | -| 无头测试 | ✓ **580 项全部通过**(8 个测试套件) | -| 应用启动 | ✓ 180 帧无运行时错误 | -| Windows 导出 | ✓ 生成 `OpenDance.exe`(104 MB,PE 头校验通过)+ `OpenDance.pck` | -| 真实 VRM 验证 | ✓ 154 骨骼 VRoid 模型,humanoid 覆盖率 **100%** | -| 骨骼重定向 | ✓ 关键点方向与骨骼实际指向点积 **1.0000** | -| 跨实现交叉校验 | ✓ 关节角与 Web 服务端 TypeScript 实现**逐帧一致** | +| 脚本编译 | OK 全部通过(无 SCRIPT ERROR) | +| 无头测试 | OK **580 项全部通过**(8 个测试套件) | +| 应用启动 | OK 180 帧无运行时错误 | +| Windows 导出 | OK 生成 `OpenDance.exe`(104 MB,PE 头校验通过)+ `OpenDance.pck` | +| 真实 VRM 验证 | OK 154 骨骼 VRoid 模型,humanoid 覆盖率 **100%** | +| 骨骼重定向 | OK 关键点方向与骨骼实际指向点积 **1.0000** | +| 跨实现交叉校验 | OK 关节角与 Web 服务端 TypeScript 实现**逐帧一致** | ## 快速开始 diff --git a/README.txt b/README.txt index 6ad6f64..4b70de4 100644 --- a/README.txt +++ b/README.txt @@ -173,7 +173,7 @@ OpenDance.exe 的用户看第一节即可。 listen_host WebSocket 监听地址(默认 0.0.0.0) listen_port WebSocket 监听端口(默认 8765) ws_subprotocol 子协议(默认 opendance.pose.v1) - dtw_band DTW 带宽,单位帧(默认 15 ≈ 0.5 秒抖动容忍) + dtw_band DTW 带宽,单位帧(默认 15 ~ 0.5 秒抖动容忍) score_tolerance 评分容差:平均每步代价达到该值即 0 分(默认 0.25) mirror_preview 镜像预览 @@ -193,12 +193,12 @@ OpenDance.exe 的用户看第一节即可。 六、评分说明 -- -- 每帧特征 = 33 点 × (x,y,z) + 9 个关节角余弦 = 108 维 +- 每帧特征 = 33 点 x (x,y,z) + 9 个关节角余弦 = 108 维 (与 Web 服务端预处理产物、Android 端推流结构完全一致) - 置信度低于 0.5 的关键点不参与代价计算,避免遮挡导致误判 -- 采用带约束 DTW(|i−j| ≤ band)对齐,能吸收一定的节奏快慢差异 -- 得分 = clamp(1 − 平均代价 / 容差, 0, 1) × 100,并做指数平滑 -- 等级:S ≥ 95,A ≥ 85,B ≥ 70,C ≥ 55,D 其余 +- 采用带约束 DTW(|i-j| <= band)对齐,能吸收一定的节奏快慢差异 +- 得分 = clamp(1 - 平均代价 / 容差, 0, 1) x 100,并做指数平滑 +- 等级:S >= 95,A >= 85,B >= 70,C >= 55,D 其余 -- 七、从源码构建 diff --git a/contracts/data-models.md b/contracts/data-models.md index 978f5f3..f377aea 100644 --- a/contracts/data-models.md +++ b/contracts/data-models.md @@ -54,9 +54,9 @@ rejected 已驳回 | 字段 | 类型 | 必填 | 说明 | |--|--|--|--| | `id` | number | 读 | | -| `title` | string | ✅ | 曲名,≤200 | -| `artist` | string | ✅ | 演唱者,≤200 | -| `album` | string | | 专辑,≤200 | +| `title` | string | ✅ | 曲名,<=200 | +| `artist` | string | ✅ | 演唱者,<=200 | +| `album` | string | | 专辑,<=200 | | `language` | string | | `zh` / `en` / `ja` / `ko` / `instrumental` | | `durationMs` | number | | 时长毫秒 | | `bpm` | number | | 节拍 | @@ -74,7 +74,7 @@ rejected 已驳回 |--|--|--|--| | `id` | number | 读 | | | `songId` | number\|null | | 关联曲目 | -| `name` | string | ✅ | 动作名称,≤200 | +| `name` | string | ✅ | 动作名称,<=200 | | `format` | enum | ✅ | `bvh` / `vmd` / `fbx` / `gltf` / `glb` / `keypoints-json` | | `fileUrl` | string | 读 | 原始动作文件 | | `fileSize` | number | 读 | 字节 | @@ -95,7 +95,7 @@ rejected 已驳回 | 字段 | 类型 | 必填 | 说明 | |--|--|--|--| | `id` | number | 读 | | -| `name` | string | ✅ | 模型名称,≤200 | +| `name` | string | ✅ | 模型名称,<=200 | | `format` | enum | ✅ | `glb` / `vrm`(主力)/ `gltf` / `fbx`(历史数据)。上传的 PMX/FBX 会被服务端转换成 glb 或 vrm 再入库,见 model-formats.md 1.1 | | `vrmVersion` | string\|null | 读 | `0.x` / `1.0`,非 VRM 为 null | | `isPrimary` | boolean | | 全局默认模型;**同一时刻全库至多一个 `true`** | diff --git a/contracts/model-formats.md b/contracts/model-formats.md index c8db38d..4155755 100644 --- a/contracts/model-formats.md +++ b/contracts/model-formats.md @@ -126,7 +126,7 @@ VRM 版本识别:读取 GLB 的 JSON chunk,检查 `extensions.VRM`(0.x) VMD 是二进制格式:文件头 `Vocaloid Motion Data 0002`(30 字节)+ 模型名(20 字节, Shift-JIS)+ 骨骼关键帧数组(每条 **111 字节**:15 字节骨骼名、uint32 帧号、 -float32×3 位移、float32×4 旋转四元数、64 字节贝塞尔插值参数)+ morph / 相机 / +float32x3 位移、float32x4 旋转四元数、64 字节贝塞尔插值参数)+ morph / 相机 / 灯光 / 自阴影 / IK / 可见性段落。**VMD 不含骨骼长度**,只有旋转(少数骨骼带位移), 因此服务端必须自带一套标准 MMD 骨架才能做 FK。 @@ -161,7 +161,7 @@ FK 旋转)。两份示例文件分别命中两种路径。 **标准骨架数值来源**:解析参考模型的 PMX 骨骼段实测得到(而非凭经验估计), 再去掉模型自身原点偏移并抹平左右不对称。关键比例:足首 1.43 / ひざ 6.57 / 髋 11.50 / センター 10.88 / 首 16.21 / 頭 17.16(总高约 19.4,髋高比约 59%); -大腿 4.94 + 小腿 5.15 ≈ 腿长 10.07。 +大腿 4.94 + 小腿 5.15 ~ 腿长 10.07。 ## 3. 上传限制 diff --git a/contracts/skeleton-topology.md b/contracts/skeleton-topology.md index d8c9db0..2fd6624 100644 --- a/contracts/skeleton-topology.md +++ b/contracts/skeleton-topology.md @@ -121,5 +121,5 @@ 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, y, z), 9 个关节角余弦] = 108 维`。 +**DTW 序列结构(冻结)**:每帧 = `[33 x (x, y, z), 9 个关节角余弦] = 108 维`。 低置信度点(`visibility < 0.5`)在插值阶段由相邻帧线性补全,不参与代价计算。 diff --git a/contracts/websocket-keypoints.md b/contracts/websocket-keypoints.md index fd40f48..ce1ec53 100644 --- a/contracts/websocket-keypoints.md +++ b/contracts/websocket-keypoints.md @@ -27,7 +27,7 @@ ws://:8765/pose bit2 = 上半身模式(仅 0..22 有效) bit3-7 保留,必须为 0 4 4 frame_id u32 LE 单调递增,从 0 开始 -8 231 keypoints 33 × 7B 见下 +8 231 keypoints 33 x 7B 见下 -- 合计 239 ``` diff --git a/tasks/004-keypoint-deserialize.md b/tasks/004-keypoint-deserialize.md index e28742e..5b6592c 100644 --- a/tasks/004-keypoint-deserialize.md +++ b/tasks/004-keypoint-deserialize.md @@ -24,7 +24,7 @@ src/net/keypoint_frame.gd 2 1 version u8 = 1 3 1 flags u8 bit0 镜像 bit1 低置信度 bit2 上半身 4 4 frame_id u32 LE -8 231 keypoints 33 × 7B:s16 x, s16 y, s16 z, u8 conf +8 231 keypoints 33 x 7B:s16 x, s16 y, s16 z, u8 conf 合计 239 ``` @@ -35,7 +35,7 @@ src/net/keypoint_frame.gd ## 关键实现 - 归一化系数统一 32767,与 opendance-web 的 TypeScript 参考实现逐位对齐 -- 编解码往返误差 < 1e-4(定点精度 1/32767 ≈ 3.05e-5) +- 编解码往返误差 < 1e-4(定点精度 1/32767 ~ 3.05e-5) - `unmirrored_points()` 按 flags.bit0 翻转 x - 低置信度阈值 0.5:`is_reliable()` 与 `compute_low_confidence()` - 极值(±1.0)不溢出 int16 diff --git a/tasks/006-skeleton-retarget.md b/tasks/006-skeleton-retarget.md index 6ac52d4..e85b202 100644 --- a/tasks/006-skeleton-retarget.md +++ b/tasks/006-skeleton-retarget.md @@ -21,8 +21,8 @@ src/anim/skeleton_retarget.gd 2. 取骨骼静止朝向 `rest_dir`(由该骨与首个子骨的位置差得出,**不假设模型骨骼轴向**) 3. 目标朝向由两个关键点在世界空间的方向得出 4. `delta` = rest_dir -> target_dir 的最小旋转四元数(保留扭转/roll) -5. `desired = Basis(delta) × rest_global_basis` -6. `pose_local = parent_global_basis⁻¹ × desired` +5. `desired = Basis(delta) x rest_global_basis` +6. `pose_local = parent_global_basis⁻¹ x desired` **第 6 步的合成公式是实测标定的**:Godot 文档未写明 rest/pose 的合成顺序, 在真实 VRoid 模型上暴力枚举 5 种候选公式,只有该式误差为 1.9e-8(其余 ~0.14)。 @@ -31,7 +31,7 @@ src/anim/skeleton_retarget.gd ## 坐标转换 契约 4 的 MediaPipe 归一化坐标 -> Godot 世界方向: -`Vector3(x × aspect, −y, −z × aspect)`(x/z 以图像宽为基准故乘宽高比)。 +`Vector3(x x aspect, -y, -z x aspect)`(x/z 以图像宽为基准故乘宽高比)。 ## 验收 diff --git a/tasks/007-scoring-dtw.md b/tasks/007-scoring-dtw.md index 0def0e6..c112f06 100644 --- a/tasks/007-scoring-dtw.md +++ b/tasks/007-scoring-dtw.md @@ -20,20 +20,20 @@ src/score/scoring.gd ## 特征(契约 5 冻结) -每帧 = `[33 × (x,y,z), 9 个关节角余弦]` = **108 维**。 +每帧 = `[33 x (x,y,z), 9 个关节角余弦]` = **108 维**。 低置信度点(conf < 0.5)对应维度**权重置 0**,不参与代价计算。 ## DTW -- Sakoe-Chiba 带状约束 `|i − j| ≤ band`(默认 15 帧 ≈ 0.5 秒) +- Sakoe-Chiba 带状约束 `|i - j| <= band`(默认 15 帧 ~ 0.5 秒) - `DtwMatcher.compute_full()` 离线段;`StreamingDtw` 逐帧流式推进, - 每帧只计算带内一行,总体复杂度 O(n·m·band) 但摊到每帧 O(m·band) + 每帧只计算带内一行,总体复杂度 O(n m band) 但摊到每帧 O(m band) - 归一化:代价 / max(i,j)+1,得到「平均每步代价」 ## 评分规则(`scoring.gd`) -`score = clamp(1 − cost/tolerance, 0, 1) × 100`,EMA 平滑抑制抖动, -等级 S(≥95) / A(≥85) / B(≥70) / C(≥55) / D,连击阈值 70。 +`score = clamp(1 - cost/tolerance, 0, 1) x 100`,EMA 平滑抑制抖动, +等级 S(>=95) / A(>=85) / B(>=70) / C(>=55) / D,连击阈值 70。 ## 验收