摘要
VR 头显只能提供头部与双手三路 6DOF 信号,靠这点观测反推全身姿态是典型的欠约束问题;而头显的算力又极其有限,让"精度"和"实时"很难兼得。CVPR 2024 的 HMD-Poser 用一个统一框架同时解决两件事:输入侧允许 IMU 数量在 0/2/3 之间自由伸缩,靠零填充保持 135 维输入不变;网络侧把 Transformer 的注意力从"时间维"搬到"分量维",序列长度从 40 降到 8,再用 LSTM 隐状态承担时序记忆。此外它额外回归 SMPL 体型参数 β,把关节位置误差压下去。最终在 AMASS 上 MPJPE 做到 1.89 cm,RTX 3080 上 205.7 Hz,PICO 4 头显端 90.0 Hz。
论文 :HMD-Poser: On-Device Real-time Human Motion Tracking from Scalable Sparse Observations(CVPR 2024,pp. 874-884)
代码 :Pico-AI-Team/HMD-Poser(MIT License,默认分支
master)作者单位 :论文署名原文为
PICO, ByteDance,项目页写作ByteDance Pico Inc.
一、问题背景:两个方向各自的天花板
全身动作追踪(HMT, Human Motion Tracking)在 VR 里的现有做法基本分两条路线,各有各的死角。
第一条路:只用 HMD 的三路 6DOF。 头显 + 两个手柄能通过 SLAM 给出可靠的全局位置,这是它相对纯 IMU 方案最大的优势。但上半身信号推下半身是欠约束的------论文点得很直白:当上半身几乎静止而小腿在动时,这类方法直接失效。AvatarPoser、AGRoL、AvatarJLM 都属于这一类。
第二条路:六个 3DOF IMU。 头、双前臂、腰、双小腿各绑一个,TransPose、PIP 是代表。加了腿部 IMU 后下半身姿态确实准了,但 IMU 只有角速度和加速度,积分必然漂移,要它给出准确的关节全局位置在原理上就很吃力。
至于 SparsePoser 那种用 6DOF tracker 的方案,需要额外基站,成本和易用性都不占优。
HMD-Poser 的切入点是把两条路线的长处对接起来:全局位置锚点来自 HMD,下半身姿态先验来自小腿 IMU。剩下的问题就是------怎么让一套模型同时吃得下"戴 0 个 IMU"和"戴 3 个 IMU"两种输入。
二、核心方法
2.1 整体框架

图 1:HMD-Poser 的单帧推理数据流。重点看两处:左上角的输入维度核算(3×18 + 3×15 + 2×18 = 135),以及中间绿色 TSFL 框里的红字------Transformer 的序列长度 M 指的是"输入分量数 = 8",不是时间步数,这是它能跑到 200+ FPS 的根本原因。来源:本文重绘,论文原图未标注输入维度构成。
流水线四段:特征嵌入 → TSFL 时空特征学习 → 双回归头 → FK 前向运动学。下面逐段拆。
论文原始的 Overview 图如下,可以对照着看,它额外画出了 LSTM 的隐状态传递路径和 Feature Aggregation 模块:

图 2:论文原文 Figure 2。看图重点在右下角展开的 TSFL 内部结构------八路 LSTM 各自维护自己的隐状态 h t − 1 h^{t-1} ht−1,输出 h t h^t ht 后再一起送进 Transformer 做跨分量注意力。来源:HMD-Poser 论文 Fig. 2,仅供学习。
2.2 可伸缩输入:零填充是怎么"免费"实现场景兼容的
输入向量 x t x^t xt 由八个分量拼成:
x t = x h t , x l h t , x r h t , x p e l t , x l f t , x r f t , x l h / h t , x r h / h t ∈ R 1 × 135 x^{t}=\leftx\^{t}_{h},\\ x\^{t}_{lh},\\ x\^{t}_{rh},\\ x\^{t}_{pel},\\ x\^{t}_{lf},\\ x\^{t}_{rf},\\ x\^{t}_{lh/h},\\ x\^{t}_{rh/h}\\right\in\mathbb{R}^{1\times 135} xt=xht, xlht, xrht, xpelt, xlft, xrft, xlh/ht, xrh/ht∈R1×135
其中 x h t , x l h t , x r h t x^t_h, x^t_{lh}, x^t_{rh} xht,xlht,xrht 是头与左右手的 6DOF 表示,沿用 AvatarPoser 的做法拼接位置(3) + 线速度(3) + 旋转(6) + 角速度(6) = 18 维 ; x p e l t , x l f t , x r f t x^t_{pel}, x^t_{lf}, x^t_{rf} xpelt,xlft,xrft 是腰与左右小腿的 IMU,拼接旋转(6) + 角速度(6) + 加速度(3) = 15 维。旋转和角速度都用 6D 连续表示而非欧拉角或四元数,避免表示不连续带来的回归困难。
最后两项 x l h / h t , x r h / h t x^t_{lh/h}, x^t_{rh/h} xlh/ht,xrh/ht 是本文额外加的------手在头坐标系下的相对表示,各 18 维。它的作用后面 2.4 节讲。
关键的工程处理是:无论用户戴几个 IMU,输入维度恒为 135,缺失的分量整块填零,Transformer encoder 再用 mask 把这些位置屏蔽掉。这意味着三种佩戴场景共用同一份权重、同一个计算图,部署时不需要为每种配置各导一个模型。
#mermaid-svg-eyIEgZQvsfYTM4Uq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eyIEgZQvsfYTM4Uq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eyIEgZQvsfYTM4Uq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eyIEgZQvsfYTM4Uq .error-icon{fill:#552222;}#mermaid-svg-eyIEgZQvsfYTM4Uq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eyIEgZQvsfYTM4Uq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eyIEgZQvsfYTM4Uq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eyIEgZQvsfYTM4Uq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eyIEgZQvsfYTM4Uq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eyIEgZQvsfYTM4Uq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eyIEgZQvsfYTM4Uq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eyIEgZQvsfYTM4Uq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eyIEgZQvsfYTM4Uq .marker.cross{stroke:#333333;}#mermaid-svg-eyIEgZQvsfYTM4Uq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eyIEgZQvsfYTM4Uq p{margin:0;}#mermaid-svg-eyIEgZQvsfYTM4Uq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eyIEgZQvsfYTM4Uq .cluster-label text{fill:#333;}#mermaid-svg-eyIEgZQvsfYTM4Uq .cluster-label span{color:#333;}#mermaid-svg-eyIEgZQvsfYTM4Uq .cluster-label span p{background-color:transparent;}#mermaid-svg-eyIEgZQvsfYTM4Uq .label text,#mermaid-svg-eyIEgZQvsfYTM4Uq span{fill:#333;color:#333;}#mermaid-svg-eyIEgZQvsfYTM4Uq .node rect,#mermaid-svg-eyIEgZQvsfYTM4Uq .node circle,#mermaid-svg-eyIEgZQvsfYTM4Uq .node ellipse,#mermaid-svg-eyIEgZQvsfYTM4Uq .node polygon,#mermaid-svg-eyIEgZQvsfYTM4Uq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eyIEgZQvsfYTM4Uq .rough-node .label text,#mermaid-svg-eyIEgZQvsfYTM4Uq .node .label text,#mermaid-svg-eyIEgZQvsfYTM4Uq .image-shape .label,#mermaid-svg-eyIEgZQvsfYTM4Uq .icon-shape .label{text-anchor:middle;}#mermaid-svg-eyIEgZQvsfYTM4Uq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eyIEgZQvsfYTM4Uq .rough-node .label,#mermaid-svg-eyIEgZQvsfYTM4Uq .node .label,#mermaid-svg-eyIEgZQvsfYTM4Uq .image-shape .label,#mermaid-svg-eyIEgZQvsfYTM4Uq .icon-shape .label{text-align:center;}#mermaid-svg-eyIEgZQvsfYTM4Uq .node.clickable{cursor:pointer;}#mermaid-svg-eyIEgZQvsfYTM4Uq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eyIEgZQvsfYTM4Uq .arrowheadPath{fill:#333333;}#mermaid-svg-eyIEgZQvsfYTM4Uq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eyIEgZQvsfYTM4Uq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eyIEgZQvsfYTM4Uq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eyIEgZQvsfYTM4Uq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eyIEgZQvsfYTM4Uq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eyIEgZQvsfYTM4Uq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eyIEgZQvsfYTM4Uq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eyIEgZQvsfYTM4Uq .cluster text{fill:#333;}#mermaid-svg-eyIEgZQvsfYTM4Uq .cluster span{color:#333;}#mermaid-svg-eyIEgZQvsfYTM4Uq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eyIEgZQvsfYTM4Uq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eyIEgZQvsfYTM4Uq rect.text{fill:none;stroke-width:0;}#mermaid-svg-eyIEgZQvsfYTM4Uq .icon-shape,#mermaid-svg-eyIEgZQvsfYTM4Uq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eyIEgZQvsfYTM4Uq .icon-shape p,#mermaid-svg-eyIEgZQvsfYTM4Uq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eyIEgZQvsfYTM4Uq .icon-shape .label rect,#mermaid-svg-eyIEgZQvsfYTM4Uq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eyIEgZQvsfYTM4Uq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eyIEgZQvsfYTM4Uq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eyIEgZQvsfYTM4Uq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 0 个:仅头显+手柄
2 个:双小腿
3 个:+腰部
采集一帧原始传感器读数
用户实际戴了几个 IMU
x_pel / x_lf / x_rf
三块整体置 0
x_pel 置 0
x_lf / x_rf 填真实测量
三块全部填真实测量
拼成固定 135 维 x^t
Feature Embedding
8 路独立 → 各 256 维
TSFL × 2
Transformer 用 mask 屏蔽零填充分量
Pose Head → θ (132)
Shape Head → β (16)
FK: SMPL(θ, β) + HMD 头部全局位置
输出 22 关节全局旋转与位置
一个容易被忽略的细节:所有 IMU 的旋转和加速度在喂进模型前都要标定到统一的 body-centric 坐标系。论文在补充材料里给了标定动作序列------站直保持 5 秒以上、屈膝前倾保持 5 秒、依次抬左右腿,靠这三个动作自动解出每个 IMU 的转换矩阵。这一步在真机部署里是绕不开的,合成数据实验不会暴露它的误差。
2.3 TSFL:把 Transformer 的序列维度从"时间"换成"分量"
这是全文最值得细读的一段。
标准 Transformer block 对长度为 M M M 的序列,时间复杂度是
O ( M 2 d + M d 2 ) \mathcal{O}\left(M^{2}d + Md^{2}\right) O(M2d+Md2)
d d d 是隐藏维度。注意力部分对 M M M 是平方级 。之前的 Transformer 方案(AvatarPoser 用 M = 40 M=40 M=40,AvatarJLM 用 M = 45 M=45 M=45)把一整个时间窗口的帧当成序列送进去,且 Transformer 不保留隐状态,每来一帧都要把整个历史窗口重算一遍。AvatarJLM 在 RTX 3080 上只有 1.9 Hz,代价就出在这里。
HMD-Poser 的做法是把两件事拆开:
- 时序记忆交给 LSTM :每个输入分量配一路独立的单向单层 LSTM(hidden size 256,正交初始化),历史信息全部压进隐状态 h t − 1 h^{t-1} ht−1,复杂度只有 O ( d 2 ) \mathcal{O}(d^2) O(d2),相对 Transformer 可忽略;
- 空间相关交给 Transformer :只在当前帧内 跨 8 个分量做注意力,于是 M = 8 M = 8 M=8。
代入算一下(取 d = 256 d = 256 d=256):
M 40 2 d + M 40 d 2 M 8 2 d + M 8 d 2 = 40 2 × 256 + 40 × 256 2 8 2 × 256 + 8 × 256 2 = 3,031,040 540,672 ≈ 5.6 \frac{M_{40}^{2}d + M_{40}d^{2}}{M_{8}^{2}d + M_{8}d^{2}} = \frac{40^{2}\times256 + 40\times256^{2}}{8^{2}\times256 + 8\times256^{2}} = \frac{3{,}031{,}040}{540{,}672} \approx 5.6 M82d+M8d2M402d+M40d2=82×256+8×2562402×256+40×2562=540,6723,031,040≈5.6
正好对得上论文那句"单层 Transformer 提速 5 倍以上"。这个数字不是拍出来的,是 M M M 从 40 降到 8 的直接结果。
TSFL 由 N = 2 N = 2 N=2 个相同 block 堆叠,每个 block 内部是"八路 LSTM → 3 层 Transformer encoder(8 头,FFN hidden 256)"。 N N N 的消融见 Table D:
| N | MPJRE | MPJPE | MPJVE | Jitter |
|---|---|---|---|---|
| 1 | 2.40 | 3.31 | 22.85 | 11.77 |
| 2(最终配置) | 2.28 | 3.19 | 17.47 | 6.07 |
| 3 | 2.28 | 3.18 | 16.97 | 5.20 |
从 1 到 2 收益明显(Jitter 几乎减半),从 2 到 3 精度几乎不动、只有平滑度略有改善,但要多付一层的算力。在头显这种算力受限的场景下选 N = 2 N=2 N=2 是合理的。
2.4 Shape Head:为什么"用默认体型"是个真问题
之前的方法(AvatarPoser、AGRoL、AvatarJLM、HMD-NeMo)都只回归姿态参数 θ \theta θ,用 SMPL 的默认体型 去算关节位置。HMD-Poser 加了一个 Shape Head,2 层 MLP,输出 16 维的 β \beta β。
道理不复杂:骨长错了,FK 出来的关节位置就一定错,而且误差沿运动学链累积。但这个改动带来的提升幅度还是超出直觉------Table 4(protocol1,HMD 场景):
| 配置 | MPJRE | MPJPE | H-PE | Jitter |
|---|---|---|---|---|
| w/o ShapeHead | 2.32 | 5.08 | 4.25 | 6.11 |
| with ShapeHead | 2.28 | 3.19 | 1.65 | 6.07 |
MPJRE 几乎不变(2.32 → 2.28),MPJPE 却从 5.08 掉到 3.19,H-PE 从 4.25 掉到 1.65。 这组对比说明得很清楚:旋转估得准不代表位置估得准,位置误差的大头来自体型不匹配,而不是姿态回归本身。
那模型凭什么能估出体型?靠的就是 2.2 节提到的手相对头坐标系的表示 x l h / h t , x r h / h t x^t_{lh/h}, x^t_{rh/h} xlh/ht,xrh/ht。头到手这条运动学链的长度直接被这两个量编码了,模型可以从中反推骨长。Table 3 验证了这一点:
| 配置 | MPJRE | MPJPE | H-PE | Jitter |
|---|---|---|---|---|
| w/o { x l h / h t , x r h / h t } \{x^{t}{lh/h}, x^{t}{rh/h}\} {xlh/ht,xrh/ht} | 2.45 | 3.43 | 2.36 | 6.25 |
| with | 2.28 | 3.19 | 1.65 | 6.07 |
H-PE 从 2.36 降到 1.65,降幅最大的正是手部------和"编码头到手骨长"这个解释完全一致。
2.5 损失函数
总损失是五项加权和:
L = α o r i L o r i + α l r o t L l r o t + α g r o t L g r o t + α j o i n t L j o i n t + α s m o o t h L s m o o t h \mathcal{L}=\alpha_{ori}\mathcal{L}{ori}+\alpha{lrot}\mathcal{L}{lrot}+\alpha{grot}\mathcal{L}{grot}+\alpha{joint}\mathcal{L}{joint}+\alpha{smooth}\mathcal{L}_{smooth} L=αoriLori+αlrotLlrot+αgrotLgrot+αjointLjoint+αsmoothLsmooth
其中 L o r i \mathcal{L}{ori} Lori 是根节点朝向损失, L l r o t \mathcal{L}{lrot} Llrot 是局部姿态损失, L g r o t \mathcal{L}{grot} Lgrot 是全局姿态损失, L j o i n t \mathcal{L}{joint} Ljoint 是关节位置损失,前四项都用 L1 范数。权重依次取 1.0 / 5.0 / 1.0 / 1.0 / 0.5------局部姿态权重给到 5.0,是最重的一项。
平滑损失定义在加速度层面:
L s m o o t h = 1 ( T − 2 ) × ( 3 J ) ∑ t = 1 T − 1 ∑ i = 0 3 J ∣ a i t − a ^ i t ∣ 1 \mathcal{L}{smooth}=\frac{1}{(T-2)\times(3J)}\sum{t=1}^{T-1}\sum_{i=0}^{3J}\left|a^{t}{i}-\hat{a}^{t}{i}\right|_{1} Lsmooth=(T−2)×(3J)1t=1∑T−1i=0∑3J ait−a^it 1
a t a^t at 与 a ^ t \hat{a}^t a^t 分别是预测与真值加速度, T T T 是训练序列长度, J J J 是关节数。Table E 的留一消融显示:去掉 L s m o o t h \mathcal{L}_{smooth} Lsmooth 后 MPJPE 反而略好(3.16 vs 3.19),但 Jitter 从 6.07 涨到 6.71------这是一个明确的精度换平滑的取舍,在 VR 里抖动比零点几毫米的位置误差更影响体感,取舍方向没问题。
三、工程实现
3.1 超参一览
| 项 | 取值 |
|---|---|
| 关节数 J J J | 22(SMPL 前 22 个关节) |
| 输入维度 | 135(固定,缺失分量零填充) |
| Embedding 输出 | 每分量 256 维 |
| LSTM | 单向、单层、hidden 256、正交初始化,共 8 路 |
| Transformer encoder | 3 层,8 头,FFN hidden 256, M = 8 M=8 M=8 |
| TSFL block 数 N N N | 2 |
| Pose Head 输出 | 22 × 6 = 132 22\times6=132 22×6=132(6D 旋转表示) |
| Shape Head 输出 | 16 |
| 训练片段长度 | 40 帧 |
| 优化器 / batch | Adam / 256 |
| 学习率 | 1 × 10 − 3 1\times10^{-3} 1×10−3,第 300 epoch 后 ×0.1 |
| 总 epoch | 400 |
| 头显端运行频率 | 论文实测可达 90.0 Hz,为对齐训练设定固定到 60 Hz |
3.2 仓库结构与数据准备
bash
# 仓库目录
# dataset/ model/ options/ prepare_data/data_split
# pretrained_model/ runner/ utils/
# prepare_data.py train.py test.py
# 1. 预处理 AMASS(protocol1 的划分文件直接复制自 AvatarPoser)
python ./prepare_data.py \
--support_dir ./body_models/ \
--root_dir ./data/AMASS/ \
--save_dir [path_to_save]
# 2. 训练:先把 train_config.yaml 里的 dataset_path 指向 [path_to_save]
python train.py --config ./options/train_config.yaml
# 3. 评估
python test.py --config ./options/test_config.yaml
两个协议的子集划分(正文只说了"twelve subsets",具体名单在 prepare_data.py 里):
- Protocol 1 :
BioMotionLab_NTroje、CMU、MPI_HDM05三个子集,随机 90% 训练 / 10% 测试 - Protocol 2 训练(12 个) :
ACCAD、BioMotionLab_NTroje、BMLmovi、CMU、EKUT、Eyes_Japan_Dataset、KIT、MPI_HDM05、MPI_Limits、MPI_mosh、SFU、TotalCapture - Protocol 2 测试 :
HumanEva、Transitions_mocap
依赖要求为 Python ≥ 3.9、PyTorch ≥ 2.0.1、numpy ≥ 1.23.1,外加 human_body_prior;SMPL+H 体模型需自行从官网下载(AMASS 版本,带 DMPL blendshape)。
四、实验分析
4.1 指标定义(先把单位对齐)
论文沿用 AGRoL 的 9 个指标,分三类。这组单位很容易记串,先列清楚:
| 缩写 | 全称 | 单位 |
|---|---|---|
| MPJRE | Mean Per-Joint Rotation Error | degree(度) |
| MPJPE | Mean Per-Joint Position Error | cm |
| H-PE / U-PE / L-PE / R-PE | Hand / Upper / Lower / Root 位置误差 | cm |
| MPJVE | Mean Per-Joint Velocity Error | cm/s |
| Jitter | 抖动 | 10 2 m / s 3 10^{2}\,m/s^{3} 102m/s3 |
| FPS | Frames Per Second | Hz |
⚠️ 两个高频误读点:MPJVE 是速度误差、单位 cm/s,不是加速度 ;Jitter 单位是 10 2 m / s 3 10^2\,m/s^3 102m/s3(即 100 m/s³),不是 km/s³ 。另外论文里写的是 R-PE 而不是 Root PE。
4.2 AMASS 定量结果
Protocol 1(Table 1),全部为重训练结果:
| Method | MPJRE ↓ | MPJPE ↓ | MPJVE ↓ | Jitter ↓ | H-PE ↓ | U-PE ↓ | L-PE ↓ | R-PE ↓ |
|---|---|---|---|---|---|---|---|---|
| AvatarPoser | 2.94 | 5.84 | 26.60 | 13.97 | 4.58 | 3.24 | 9.59 | 5.05 |
| AGRoL | 2.70 | 5.73 | 19.08 | 7.65 | 4.29 | 3.16 | 9.44 | 5.15 |
| AvatarJLM | 2.81 | 5.03 | 20.91 | 6.94 | 2.01 | 3.00 | 7.96 | 4.58 |
| TransPose | 3.05 | 4.57 | 22.41 | 7.98 | 3.83 | 3.05 | 6.76 | 4.62 |
| PIP | 2.45 | 4.54 | 19.02 | 8.13 | 4.54 | 3.15 | 6.53 | 4.54 |
| HMD-Poser: HMD | 2.28 | 3.19 | 17.47 | 6.07 | 1.65 | 1.67 | 5.40 | 3.02 |
| HMD-Poser: HMD+2IMUs | 1.83 | 2.27 | 13.28 | 5.96 | 1.39 | 1.51 | 3.35 | 2.74 |
| HMD-Poser: HMD+3IMUs | 1.73 | 1.89 | 11.03 | 5.35 | 1.27 | 1.46 | 2.46 | 2.37 |
Protocol 2(Table 2):
| Method | MPJRE ↓ | MPJPE ↓ | MPJVE ↓ | Jitter ↓ | H-PE ↓ | U-PE ↓ | L-PE ↓ | R-PE ↓ |
|---|---|---|---|---|---|---|---|---|
| AvatarPoser | 4.68 | 6.62 | 33.16 | 10.79 | 3.93 | 2.97 | 11.89 | 5.30 |
| AGRoL | 4.38 | 6.74 | 24.14 | 6.33 | 3.53 | 3.02 | 12.11 | 5.86 |
| AvatarJLM | 4.45 | 5.96 | 27.50 | 6.91 | 2.30 | 2.97 | 10.28 | 5.22 |
| TransPose | 4.31 | 5.29 | 28.18 | 5.16 | 7.38 | 3.86 | 7.36 | 4.80 |
| PIP | 3.61 | 4.16 | 22.22 | 6.89 | 4.28 | 2.97 | 5.89 | 4.30 |
| HMD-Poser: HMD | 4.27 | 5.44 | 30.15 | 5.62 | 2.56 | 2.44 | 9.77 | 4.83 |
| HMD-Poser: HMD+2IMUs | 3.66 | 3.68 | 20.29 | 6.22 | 1.65 | 2.14 | 5.92 | 4.51 |
| HMD-Poser: HMD+3IMUs | 3.49 | 3.13 | 16.17 | 4.93 | 1.81 | 2.17 | 4.51 | 3.88 |
这里需要一句公道话:protocol2 上 HMD-Poser 并不是每一项都赢。 在 HMD 单场景下它的 MPJVE(30.15)明显差于 AGRoL(24.14)和 AvatarJLM(27.50),MPJRE 也只是持平。论文补充材料自己也承认在 protocol2 上"MPJRE 与 MPJVE 略高于 AGRoL"。跨数据集泛化(protocol2 用 HumanEva + Transitions 做完全 held-out 测试)确实比同分布划分更能暴露问题。
还有一处对照时必须注意:这些基线数字都是作者用 GT 体型参数重新训练出来的,不能直接和 AvatarPoser / AGRoL / AvatarJLM 原论文报告的数字对表。论文明确说"Instead of using the same default shape ... we utilize the ground-truth body shape parameters to calculate the joint positions"------评测口径变了。
4.3 三方权衡:把 Table 1 和 Table 6 叠在一起看

图 3:把 Table 1 的精度和 Table 6 的速度画到同一张图里。左图看右下角------只有 HMD-Poser 的三个红点同时占住"最快"和"最准",其余方法都在这两者之间二选一,AvatarJLM 尤其极端(精度不错但 1.9 Hz)。右图看边际收益------第一对小腿 IMU 把 L-PE 砍掉 38%,第三个腰部 IMU 只再砍 27%。来源:本文重绘自论文 Table 1 与 Table 6 的数据,论文中无对应图表。
右图那个边际收益递减,解释了为什么论文自己把 HMD+2IMUs 定为默认档,真机数据集也按这个配置采集:多戴一个腰部 IMU 的体验代价,未必换得回那 0.89 cm。
4.4 推理速度(Table 6)
| Method | FPS (GPU) ↑ | FPS (HMD) ↑ |
|---|---|---|
| AvatarPoser | 114.1 | -- |
| AGRoL | 60.8 | -- |
| AvatarJLM | 1.9 | -- |
| TransPose | 123.0 | -- |
| PIP | 62.5 | -- |
| HMD-Poser | 205.7 | 90.0 |
GPU 一栏统一在 NVIDIA GeForce RTX 3080 上测得,HMD 一栏是 PICO 4。注意所有基线的 HMD 列都是 "--"------它们根本没做端侧部署。
⚠️ 需要说明的是:论文全文没有给出模型参数量、FLOPs、模型体积或单帧延迟(ms) 。唯一和"模型大小"沾边的实验就是 TSFL block 数 N N N 的消融。如果你在别处看到"HMD-Poser 只有 XX M 参数"的说法,那不是出自这篇论文。同样,论文也没有提及 PICO 4 的 SoC 型号。
4.5 真机实验:合成数据和真实传感器之间的坑
这部分是我觉得这篇论文最有工程价值的地方。作者自建了 PICO-FreeDancing 数据集:
- 74 段自由舞蹈动作,8 名受试者(3 男 5 女),每段 120 秒(总时长约 2.5 小时,此为按 74×120s 推算,论文未直接给出)
- 佩戴 PICO 4(头显 + 两个手柄)+ 2 个小腿 PICO Motion Tracker,即 HMD+2IMUs 配置
- GT 由 OptiTrack 光学动捕 + Mosh++ 拟合 SMPL 参数;OptiTrack 120 Hz、IMU 500 Hz,统一下采样到 60 Hz
- 同步方案:头显顶部额外粘一个刚体,开录前做转头 / 点头 / 摇头的控制动作,用 IMU 姿态和 OptiTrack 刚体姿态对齐时间轴

图 4:真机采集装置。看图重点在三处标注------顶部红框的 OptiTrack 相机阵列、紫色圈出的头显顶部同步刚体、蓝色圈出的两个小腿 PICO Motion Tracker。来源:HMD-Poser 论文补充材料 Fig. A,仅供学习。
Table 5 是这套数据跑出来的结果(HMD+2IMUs):
| 配置 | MPJRE ↓ | MPJPE ↓ | MPJVE ↓ | Jitter ↓ | H-PE ↓ | U-PE ↓ | L-PE ↓ | R-PE ↓ |
|---|---|---|---|---|---|---|---|---|
| HMD-Poser(在 PICO 4 上跑,真实传感器) | 6.48 | 6.55 | 30.60 | 16.96 | 8.10 | 5.25 | 8.52 | 7.13 |
| HMD-Poser(离线跑,真实传感器) | 6.45 | 6.53 | 30.56 | 16.95 | 8.01 | 5.20 | 8.46 | 6.98 |
| HMD-Poser(离线跑,合成输入) | 4.77 | 4.75 | 22.30 | 15.25 | 2.09 | 3.35 | 6.77 | 6.06 |
两个结论:
- 端侧 vs 离线的差距可以忽略(6.48 vs 6.45),说明模型压到头显上跑没有掉点,量化/推理链路是干净的。
- 合成输入 vs 真实传感器的差距非常大 ,尤其 H-PE:2.09 → 8.01,涨了将近 4 倍。论文给的解释是手和手柄之间不是刚性连接,一旦有相对滑动,标定出的"手柄→手"变换矩阵就失效了。
第 2 点值得所有做这个方向的人记一下:AMASS 上所有方法都用合成输入,那套数字系统性地乐观。 真机上 MPJPE 从 2.27(Table 1)掉到 6.55,接近 3 倍劣化。这不是 HMD-Poser 的问题,是整个赛道的评测方式的问题------而这篇论文是第一个把它量出来的。
最终的实机 Avatar 驱动效果:

图 5:PICO 4 上的实时 Avatar 驱动。看图重点在下半身------坐姿、盘腿、躺地抬腿这些上半身观测高度相似而下半身差异极大的动作都跟得住,这正是纯 HMD 方案最容易崩的场景。来源:HMD-Poser 论文 Fig. 5,仅供学习。
对比图方面,论文分别给了 HMD 类方法和 6IMU 类方法的定性对比:

图 6:与 AGRoL / AvatarPoser / AvatarJLM 的定性对比(HMD-Poser 使用 HMD 单场景以保证公平)。看图重点在紫色虚线框标注的 "Incorrect" 和红色框的 "Penetration"------基线方法在 Seq4 出现明显的地面穿透,而 HMD-Poser 列没有标注。来源:HMD-Poser 论文 Fig. 3,仅供学习。
五、几处需要注意的信息差
整理时核对了论文、项目页和仓库三方,有几处不一致值得单独点出来:
1. GitHub 公布的预训练模型指标与论文表格对不上(protocol1):
| 输入配置 | 来源 | MPJRE | MPJPE | MPJVE |
|---|---|---|---|---|
| HMD | 论文 Table 1 | 2.28 | 3.19 | 17.47 |
| HMD | GitHub README | 2.29 | 3.15 | 17.52 |
| HMD+2IMUs | 论文 Table 1 | 1.83 | 2.27 | 13.28 |
| HMD+2IMUs | GitHub README | 1.88 | 2.30 | 13.34 |
| HMD+3IMUs | 论文 Table 1 | 1.73 | 1.89 | 11.03 |
| HMD+3IMUs | GitHub README | 1.79 | 2.01 | 12.70 |
大部分差异在小数点后第二位、属于训练随机性范围,但 HMD+3IMUs 的 MPJVE 差了 1.67(15%),这个幅度不太像单纯的种子差异。引用时务必注明是论文值还是复现值,两者不要混着用。
2. 论文里没有 HMD+6IMUs 这个配置。 HMD-Poser 只有 HMD / HMD+2IMUs / HMD+3IMUs 三档,6IMU 是 TransPose、PIP 这类基线自身的设定。
3. 作者单位的英文原文是 PICO, ByteDance (邮箱域名 @bytedance.com),项目页写 ByteDance Pico Inc.。论文中不存在任何中文法律实体名,看到具体中文公司名的说法都属于论文外的推断。
小结
这篇论文真正的贡献不在"又刷了个 SOTA",而在两个具体的工程决策:
第一,把 Transformer 的序列维从时间搬到分量,是一个非常划算的结构选择 。时序建模本来就是 RNN 的强项( O ( d 2 ) \mathcal{O}(d^2) O(d2) 且天然增量),跨分量相关性才是注意力真正擅长的。之前的方法把两件事都压给 Transformer,然后为长序列付平方级代价,还因为无隐状态每帧重算历史。HMD-Poser 拆开之后 M M M 从 40 降到 8,理论提速 5.6× 与实测的 205.7 Hz vs 1.9 Hz 完全自洽。这个思路可以迁移到任何"多路传感器 + 逐帧输出"的任务上。
第二,在线体型估计是被长期低估的一环。Table 4 里 MPJRE 几乎不动而 MPJPE 从 5.08 掉到 3.19,这组数字说明位置误差的主要来源根本不是姿态回归,而是骨长假设。而拿到体型信息的代价小得离谱------只是多喂两路"手相对头"的表示,多接一个 16 维输出的 MLP。
局限也很清楚:
- 零填充 + mask 的可伸缩方案本质是"一个模型将就三种场景",HMD 单场景下的性能必然被 IMU 场景的训练分布拖累。论文没有报告"专门为 HMD 场景单独训练"的对照,所以统一框架到底损失了多少精度,是个未回答的问题。
- protocol2 上并非全面领先,跨数据集泛化仍有欠缺,MPJVE 甚至明显差于 AGRoL。
- 真机 H-PE 从 2.09 涨到 8.01 这件事,论文归因于手柄非刚性连接,但没给出缓解方案。这是实际产品化时必须自己啃的骨头。
- 作者自己承认的局限:IMU 对"缓慢匀速垂直抬脚"这类动作天然难以区分------加速度信号接近零,测不出来。
对做 VR 全身追踪的团队,我的判断是:TSFL 的时空解耦结构和 Shape Head 这两个模块可以直接借鉴 ,它们既不依赖特定数据集也不依赖特定硬件;而 PICO-FreeDancing 数据集的价值可能比模型本身更高------它是目前少有的、同时含真实 HMD/IMU 传感器读数和光学动捕 GT 的公开数据,做 sim-to-real 研究绕不开。