一、track_pose、pose
1. 概述
track_pose用于定义一套完整的“跟练操”中的一个独立动作。系统通过解析该文件,结合 Mediapipe 等姿态估计算法输出的骨骼姿态点数据,实时判断用户动作是否达标。
一个配置文件代表**一套操**,由若干个 `<track_pose>` 块组成。系统将按顺序执行这些块,只有当前 `<track_pose>` 内的所有 `<pose>` 姿态判定**同时满足**后,才会进入下一个 `<track_pose>` 动作。
2. 标签结构定义
2.1 <track_pose>标签(动作单元)
- 含义:定义操中的一个独立动作。
- 结构:该标签内可包含一个或多个 `<pose>` 子标签。
- 执行逻辑:内部的所有 `<pose>` 判定条件构成 **“与(AND)”** 逻辑关系。即只有当所有内部 `<pose>` 的判断结果均为 `True` 时,该 `<track_pose>` 才算达标,系统才会切换至下一个动作。
2.2 <pose>标签(姿态判断单元)
- 含义:定义单个具体的姿态约束条件。
- 结构:通过若干个配置属性(Attribute)定义计算类型、目标数值范围及提示语。
- 执行逻辑:根据 `type` 和 `operand` 计算出一个最终数值,并将其与 `min` 和 `max` 组成的闭区间 `[min, max]` 进行比较。若落在区间内,则判定成立。
3. <pose> 标签字段详细说明
| 字段名 | 必需 | 数据类型 | 说明 |
| landmarks | 是 | 字符串 | 指定参与计算的 Mediapipe 骨骼姿态点索引。根据 `type` 不同,格式有所区别(详见下文)。 |
| type | 是 | 枚举 | 判断类型。可选值:`angle3p`, `angle2p`, `diff`。 |
| operand | 是 | 枚举 | 运算单元类型。可选值:`point`, `x`, `y`, `dist_x`, `dist_y`。 |
| name | 是 | 字符串 | 该姿态判定的描述名称,用于日志记录或开发调试。 |
| min | 条件 | 浮点数 | 判定达标的下限阈值(包含该值)。如果不出现,视为 `-∞`(或根据算法默认),但 `min` 和 `max` 至少出现一个。 |
| max | 条件 | 浮点数 | 判定达标的上限阈值(包含该值)。如果不出现,视为 `+∞`(或根据算法默认),但 `min` 和 `max` 至少出现一个。 |
| unsatisfied_msgstr | 是 | 字符串 | 当未设置 unsatisfied_rmax_msgstr 时:只要姿态不满足范围(无论偏大还是偏小),均播报此提示语。 |
| unsatisfied_rmax_msgstr | 否 | 字符串 | 表示计算值大于 'max' 上限时的语音提示。与 'unsatisfied_msgstr'配合使用,实现差异化引导。 |
| abs | 否 | 布尔值 | 仅对 type="diff" 且 operand="x"或 operand="y"有效。true表示对计算结果取绝对值后再与 [min, max]比较;false(默认)或不填,则使用原始差值比较。 |
4. 姿态点索引解析规则(包含伪姿态点)
4.1 真实姿态点索引(0 ~ 32)
Mediapipe 输出的 33 个骨骼姿态点索引范围固定为 0 到 32。脚本中直接使用这些数字即可(如 11 代表左肩,0 代表鼻子)。
4.2 伪姿态点索引(从 1000 起始)
为了解决画面绝对位置(如画面中心)的参考需求,系统专门预留了从 1000 开始的大数字作为伪姿态点索引。这些索引并非真实的骨骼,而是引擎内部硬编码的物理坐标。
当前系统预置的伪姿态点如下:
| 伪索引数字 | 语义名称 | 等效物理坐标(归一化 0~1) |
| 1000 | 画面正中心 (CENTER) | (0.5, 0.5) |
注:为了便于脚本编写者理解,允许在底层解析时用单词替换。但在动作配置文件中,必须使用数字 1000 进行配置,以保证结构体字段 int 类型的统一。
5. landmarks 字符串的解析法则
在解析 landmarks 字段时,系统将严格遵循以下优先级:
- 分隔符:使用 分号 (;) 将 landmarks 字符串切割为左侧和右侧两个运算单元。
- 左侧/右侧单元内部解析:
逗号 (,) 规则:如果某个运算单元内部出现逗号(如 "11,12"),则强制解析为“这两点求坐标平均值(中点)”。
单数字规则:如果只有一个数字(如 "0" 或 "16"),则直接提取该关键点的坐标。
伪姿态点规则:如果数字 >= 1000(如 1000),则系统不访问 Mediapipe 数据,而是直接返回预置的固定坐标(如 (0.5, 0.5))。 - 计算执行:根据 operand 类型,提取两个单元的 (x, y) 或单一维度进行计算。
6. 数据类型(`type`)与 `landmarks` 格式详解
根据 `type` 的不同,`landmarks` 的解析方式及 `operand` 的取值范围均有明确规定。
6.1 类型:angle3p(三点角度)
- 功能:计算三点构成的内角,取值范围:[0, 180]。
- 几何定义:假设三点为 A、B、C,以 B 为顶点,计算射线 BA 与 BC 之间的夹角。
- landmarks 格式:`"A点索引; B点索引; C点索引"`(注意:必须是分号分隔的3个数字)。
- operand 限制:必须为 `point`。系统将提取这3个点的 `(x,y)` 坐标进行计算。
- 说明**:A、C互换位置(如 `11;13;15` 与 `15;13;11`)不影响计算结果。
6.2 类型:`angle2p`(两点逆时针角)
- 功能:计算两点连线相对于水平正X轴的逆时针旋转角,取值范围:[0, 360)。
- 几何定义:以第一个点 A 为原点,画一条水平向右的射线(极轴),计算该射线绕 A 点逆时针旋转到射线 AB 所经过的角度。
- `landmarks` 格式:`"A点索引, B点索引"`(**注意:必须是逗号分隔的2个数字**)。
- `operand` 限制:必须为 `point`。系统将提取这2个点的 `(x,y)` 坐标进行计算。
- 注意事项:图像坐标系中 Y 轴向下,但在 `angle2p` 计算中,角度严格按照数学坐标系(Y轴向上)的逆时针计算。
6.3 类型:`diff`(差值计算)
- 功能:计算两个运算单元(由 `operand` 定义)之间的数值差。
- `landmarks` 格式:`"单元1; 单元2"`(注意:必须是分号分隔)。
- 返回值:默认返回 `单元1计算结果 - 单元2计算结果`。结果可为负数、零或正数。
- `operand` 限制:由 `operand` 具体值决定计算细节。
7. 运算单元(`operand`)类型详解
`operand` 指定了从 `landmarks` 中提取的数据形式,它决定了参与 `diff` 计算或 `angle` 计算的基础实体是什么。
| `operand` 值 | 适用 `type` | `landmarks` 格式说明 | 运算单元解析说明 |
| point | angle3p angle2p diff | 1. `angle3p`/`angle2p`:**多个点** 2. `diff`:**`"A点索引; B点索引"`** | 欧式距离:当用于 `diff` 时,表示计算两个姿态点 A 和 B 在图像上的直线距离。公式:`sqrt((x_A-x_B)² + (y_A-y_B)²)`。结果为非负数。 |
| x | diff | "A点索引; B点索引" | X轴坐标差:计算 `A点.x - B点.x`。 注意:若配置了 `abs="true"`,则结果为 `abs(A点.x - B点.x)`。 |
| y | diff | "A点索引; B点索引" | Y轴坐标差:计算 `A点.y - B点.y`。 注意:若配置了 `abs="true"`,则结果为 `abs(A点.y - B点.y)`。 |
| dist_x | diff | "A点索引; B点索引" | 水平距离:计算两点X轴坐标差的绝对值,即 `abs(A点.x - B点.x)`。结果为非负数。自带绝对值效果。 |
| dist_y | diff | "A点索引; B点索引" | 垂直距离:计算两点Y轴坐标差的绝对值,即 `abs(A点.y - B点.y)`。结果为非负数。自带绝对值效果。 |
特别说明:当 landmarks 中出现了伪姿态点索引(如 1000)时,系统在运行期取点时会自动将其替换为对应的固定坐标 (0.5, 0.5),上述所有 operand 的计算逻辑对该伪坐标完全适用。
8. 编写规则与补充示例解析
规则强调:
- `min` 与 `max`:若只定义 `min=145`,则条件隐式为 `[145, +∞)`;若只定义 `max=0.16`,则条件为 `(-∞, 0.16]`。为避免混淆,建议显式定义两个边界。
- 坐标归一化:Mediapipe 输出的坐标通常是归一化的相对坐标。因此 `diff` 的阈值通常是极小的浮点数(如 0.16、0.07),代表相对画面宽高比例的距离。
- 安全校验:引擎在执行时,若 `landmarks` 中的某个姿态点被 Mediapipe 判定为隐藏(`visibility` 或 `presence` 为 0)或不在画面内,当前 `pose` 判断应直接视为**失败**。
补充示例 1:上下限差异化提示
[pose] landmarks="12; 14" max=280 min=260 name="右上臂角度" operand="point" type="angle2p" unsatisfied_msgstr="手肘弯曲太小,身体向后移一些" <!-- 当计算值 < 260时触发 --> unsatisfied_rmax_msgstr="手肘弯曲太大,身体向前移一些" <!-- 当计算值 > 280时触发 --> [/pose]
解析:这是一个 `angle2p` 角度判断。当角度低于 `min=260` 时,提示用户“向后移”;当角度高于 `max=280` 时,提示用户“向前移”。这提供了极高的用户容错引导能力。
补充示例 2:绝对差值判定(`abs`的应用)
[pose] abs=true landmarks="7; 8" min=0.015 name="abs(左耳.y-右耳.y)" operand="y" type="diff" unsatisfied_msgstr="让两耳更加倾斜" [/pose]
解析:计算 `左耳.y - 右耳.y`。默认情况下结果可能为负(右耳较高)或正(左耳较高)。有了 `abs="true"`,系统会取绝对值。只要**头部左右倾斜的绝对值**小于 `0.015`(即头部基本保持垂直不歪斜),就算达标。如果用于检测“歪头”超标,则可将此处的逻辑反转使用。结合 `unsatisfied_msgstr` 在未达标时给与用户清晰指引。