状态:草稿(2026-10-13),部分反映实测。 姿态实验(2026-10-11–12)和教练画面的速度是实测值。RacketVision 仅清点了数据,未运行模型(未实测)。论文数值标记为「论文」。编写规则:以实测数据为准,未测出的标记为「未实测」。
目录
- 1. 为什么要观察人和球拍
- 2. RTMPose:将坐标转换为两个 1D 分类
- 3. MediaPipe Pose:划分寻找与追踪
- 4. RacketVision:将球拍表示为五个点
- 5. 我们的实际应用场景与结果
- 6. 总结:什么用在什么地方
- 参考
1. 为什么要观察人和球拍
正如第 5 篇中将详细看到的,当前引擎出错的最大原因是 事件提取。其中,将「台面上球击中球拍」误读为「弹跳」的失误最多。只看球的轨迹,两者在画面中都会画出「先下降后上升」的 V 字。虽然引擎通过「弹跳保持水平方向,击球则会翻转」的规则过滤掉了大部分,但并不能全部过滤掉。
人很容易区分这两者。如果球旁有球拍且手腕挥动了,那就是击球。因此,我们试图将姿态(人的关节坐标)和球拍位置作为辅助信号输入。其用途有三个。
| 信号 | 想使用的场景 |
|---|---|
| 手腕是否在球附近 | 区分击球与弹跳 |
| 选手的发球姿态、站立的一侧 | 谁在发球、是在多拍相持中还是在休息 |
| 拍面与球 | 当球被选手遮挡而看不见时确认击球 |
并且抛开分数不谈,TT Lab 的 姿态教练 画面使用的也是相同的模型。
2. RTMPose:将坐标转换为两个 1D 分类
自顶向下(top-down)
RTMPose(arXiv 2303.07399, MMPose, Apache-2.0)分为两个阶段。首先,人体检测器(RTMDet)寻找人体边界框,并裁剪该边界框以供关键点模型寻找 17 个关节。即使人在画面一侧拍得很小,通过裁剪放大边界框,关节也会变得清晰。它对练习视频中挥臂较快、人体较小的视频非常有效。
SimCC:用两个 1D 向量代替 2D 地图
第 2 篇中的找球绘制了 2D 热力图。如果为 17 个关节中的每一个绘制 2D 地图,计算量会很大,而且地图的分辨率直接决定了位置分辨率。若要做到精确,就必须放大地图,代价也随之昂贵。
SimCC(arXiv 2107.03332)改变了提问方式。它将「该关节的 x 坐标是第几格」、「y 坐标是第几格」作为 各自的分类问题来解决。每个关节输出两个长度分别为 W·k 和 H·k 的 1D 向量,并在每个向量中选择最高的格子。其中 k 是将一个像素分割成多少格(MMPose 的 simcc_split_ratio,默认 2.0),当 k = 2 时可获得半像素分辨率。这相当于无需增大 2D 地图即可获得比像素更精确的位置。这与第 2 篇中 TTNet 以 x·y 两个 1D 向量输出球的位置的想法如出一辙。
解码非常简单。
# open-mmlab/mmpose mmpose/codecs/utils/post_processing.py get_simcc_maximum() 에서(Apache-2.0)
x_locs = np.argmax(simcc_x, axis=1)
y_locs = np.argmax(simcc_y, axis=1)
locs = np.stack((x_locs, y_locs), axis=-1).astype(np.float32)
max_val_x = np.amax(simcc_x, axis=1)
max_val_y = np.amax(simcc_y, axis=1)
mask = max_val_x > max_val_y
max_val_x[mask] = max_val_y[mask] # 신뢰도 = 두 축 최댓값 중 작은 쪽
vals = max_val_x
locs[vals <= 0.] = -1
然后将 keypoints /= simcc_split_ratio 的格号还原为像素。值得注意的是,置信度取两个轴中 较小的一个。如果 x 确定但 y 模糊,那么该关节就是模糊的。
数值
RTMPose-m 拥有 1,359 万参数、1.93 GFLOPs、COCO AP 74.9。桌面 CPU(ONNX Runtime)延迟为 11.06ms,桌面级 GPU(TensorRT FP16)延迟为 2.29ms(官方 README,输入 256×192)。论文摘要中的 75.8 AP 是因为训练数据版本不同,引用时需要核对具体是哪个版本。
3. MediaPipe Pose:划分寻找与追踪
MediaPipe Pose Landmarker(BlazePose,arXiv 2006.10204,代码 Apache-2.0)是一个旨在手机浏览器上运行的模型。其构思如下。
- 寻找(detector):仅在最初一次或丢失人体时运行。将人脸检测器用作人体检测的替代方案,并同时预测骨盆中心以及包裹身体的圆的半径与倾斜度来确定人体位置。
- 追踪(tracker):每帧运行。它同时预测 33 个关节、「人体是否仍然存在」以及 下一帧将查看的区域。如果判定无人,则在下一帧重新运行寻找功能。
不在每帧运行繁重的寻找功能正是速度的秘诀。除了屏幕坐标之外,它还提供以 骨盆为原点的以米为单位的 3D 坐标(world landmarks)。
许可证注意:代码为 Apache-2.0,但模型文件附带了单独的模型卡。我们尚未确认该卡的使用条件(未确认)。
4. RacketVision:将球拍表示为五个点
RacketVision(arXiv 2511.17045, AAAI 2026, 代码与数据卡 MIT)是一个针对乒乓球、网球、羽毛球转播画面的数据集和基准模型,包含球的位置和 球拍边界框 + 五个点。这五个点是:上方(top)、下方(bottom)、手柄(handle)、左侧(left)、右侧(right)。上方、下方、左侧、右侧四个点决定了拍面的轮廓,手柄决定了方向。有了拍面可见的角度和方向,就可以询问「球是否在拍面附近」。
直接清点乒乓球部分的结果(2026-10-13)
我们以只读方式清点了已下载的乒乓球部分的注释文件(原始数据和脚本参见第 7 篇)。
| 项目 | 值 |
|---|---|
| 剪辑 / 比赛 | 780 / 50 |
| 帧 / 长度 | 170,027 / 3,878 秒。剪辑长度中位数 5.0 秒 |
| 分辨率 / fps | 全部 1920×1080。fps 混合了 |
| 25(209个剪辑)·60(201)·50(191)·30(112)·59.94(67) | 带有注释的帧 |
| 16,961(与球 CSV 中 19,495 行的「可见」数量相同)。帧间隔中位数 10 | 有球拍的注释帧 / 球拍数 |
| 6,648 / 8,060(单帧 0 个 12,847,1 个 5,409,2 个 1,084,3 个以上 155) | 单把球拍可见的点数 |
| 4点 6,187(77%),5点 1,400,3点 467 | 各点的可见比例 上方 99.6%,左侧 99.7%,右侧 99.2%,手柄 73.7%,下方 39.1% |
| 球拍边界框面积 | 中位数 1,876px²(约 43×43px),10% 分位数 883,90% 分位数 4,665 |
虽然被称为五个点,但实际上 大部分球拍只能看到四个点。这推测是因为从转播视角来看,球拍的下端被手遮挡或与拍面重合。论文也报告称,左、右侧点的准确率(PCK@0.2 约 65–80%)低于上、下等结构点(超过 92%)。在 1080p 下,球拍边界框单边仅为 43px 左右,因此在手机侧面视频中找齐五个点会更加困难(推测)。
论文的另一个结果对设计非常重要。在预测球的后续轨迹时,如果将球拍特征 直接拼接起来,效果反而不如只用球,必须通过交叉注意力机制(cross-attention)合并才有改善(乒乓球,过去 80 帧 → 未来 20 帧,ADE/FDE 113.9/184.3 → 101.3/161.3px,论文)。这得出了与下方我们实测相同的结论:单纯增加信号并不会自然带来变好。
5. 我们的实际应用场景与结果
得分引擎:擦除即亏损,标记即获利(2026-10-11–12)
人体检测器(RTMDet)的 ONNX 文件由于图中的 NMS 运算无法被 OpenCV DNN 读取,且实验镜像中也没有 ONNX Runtime。因此,我们没有使用人体边界框,而是将 以球为中心的 540×720 边界框输入到 RTMPose-m 中,只询问「手腕是否在球附近」。由于使用 CPU(OpenCV DNN)单框耗时 74ms(与其它任务共用 CPU 的中位数),因此仅对引擎生成的 2,499 个弹跳候选帧运行了该模型。
首先,我们在 5 场训练比赛中观察了手腕是否能区分球。1,982 个弹跳候选中,有 1,481 个正确弹跳、27 个将击球误读为弹跳的假阳性、474 个其他假阳性。「擦除手腕分数 0.5 以上、手腕与球距离 60px 以内」的操作清除了 10 个正确项、5 个击球假阳性和 24 个其他假阳性。 几乎无法区分正确项和假阳性。 这部分原因也是因为引擎已经过滤掉了绝大多数击球假阳性,只剩下 27 个。
以下是第 12 篇中 275 个多拍相持的实际结果。
| 规则 | 胜者正确 | 未经确认错误 | 需要确认 |
|---|---|---|---|
| 基准(无姿态) | 93.1% | 10 | 41.1% |
| 用姿态擦除弹跳,60px | 92.0% | 11 | 42.9% |
| 用姿态擦除弹跳,90px | 87.6% | 12 | 44.0% |
| 姿态仅用于「需要确认」,60 / 90 / 120px | 93.1% | 10 / 9 / 6 | 44.0 / 49.1 / 58.2% |
结论只有两行。用目前水平的姿态信号来 擦除弹跳是亏损的。如果在不改变判定的情况下,仅 标记让人工重新查看 ,就可以把 4 起未经确认的错误判定拉到人工面前。代价是需要确认的比例上升了 17%p。因此,引擎确立了「姿态不擦除弹跳(仅标记为需要确认)」的规则。正确接入人体检测器的版本为未实测。
姿态教练:浏览器端使用 MediaPipe,服务器端使用 RTMPose
| 分支 | 模型 | 原因 | 实测 |
|---|---|---|---|
| 实时骨架(浏览器内) | MediaPipe Pose Landmarker | 视频不流出浏览器。提供 33 个点和以米为单位的 3D 坐标。T3Set 数据集的姿态资料是相同的 MediaPipe 3D 坐标,可以用相同的标准进行比较 | 笔记本电脑无头 Chrome(GPU 代理)每帧 12–15ms。实际手机端未实测 |
| 精准分析(服务器端) | RTMDet-m + RTMPose-m (ONNX Runtime) | 对模糊和小人物表现强劲的 top-down。不需要编译 mmcv,而且 ONNX 不是 pickle,代码无法执行权重 | 服务器 GPU(共享时)寻人 50–98ms,关键点 6–10ms/帧。将寻人频率降至每 5 帧一次,30 秒视频片段 21–36 秒 |
6. 总结:什么用在什么地方
| 模型 | 原理一句话 | 当前应用场景 | 在评分引擎中 |
|---|---|---|---|
| RTMPose | 人体检测框 → 每个关节的 x·y 一维分类 (SimCC) | 教练精确分析、姿势实验 | 仅作为“需确认”信号 |
| MediaPipe Pose | 偶尔检测并持续追踪,33 点 + 3D | 实时骨骼教练 | 不使用 |
| RacketVision 球拍 5 点 | 球拍框 + 五个点,与球进行交叉注意力(cross-attention) | 仅接收数据并计算 | 未实测。击球时球被遮挡的确认候选 |
能够同时接收球、姿势和球拍并输出“谁在什么时候击球”的模型在乒乓球领域也开始出现(例如:利用球、球台和上半身关键点输出球拍接触时刻的 EventNet,arXiv 2610.08286,在转播数据 LATTE-MV 上 F1 分数达 91.16,误差 ±3 帧,论文)。在我们的侧面视频中重新测试此类模型是下一个实验候选(进行中)。
TT 点评。 看到手腕在球旁边,不能马上大喊“击球!”。这种情况下,请求“请帮我看一下”才是守护比分的途径。
参考
- RTMPose: https://arxiv.org/abs/2303.07399, https://github.com/open-mmlab/mmpose (Apache-2.0)
- SimCC: https://arxiv.org/abs/2107.03332
- BlazePose: https://arxiv.org/abs/2006.10204, MediaPipe Pose Landmarker https://developers.google.com/edge/mediapipe/solutions/vision/pose_landmarker
- RacketVision: https://arxiv.org/abs/2511.17045, https://github.com/OrcustD/RacketVision (MIT)
- EventNet: https://arxiv.org/abs/2610.08286
- 我们的测量:仓库
docs/tt/PERFORMANCE.md2.9.5,姿势教练设计文档第 9 节,docs/experiments/2026-10-13-tt-dataset-stats/