基于稀疏毫米波雷达点云,使用空间-时间双阶段 Transformer 预测人体 17 关节三维骨架坐标。
输入:雷达点云序列,每帧 (x, y, z, v) 四维特征
输出:17 关节骨架坐标序列,单位为米
| 摄像头 | 毫米波雷达 | |
|---|---|---|
| 隐私 | 采集图像,隐私敏感 | 只有点云,无法识别身份 |
| 光照 | 暗光/强光下失效 | 全天候工作 |
| 穿透 | 墙壁遮挡即失效 | 可穿透薄墙/衣物 |
| 数据量 | 每帧百万像素 | 每帧几十~几百个 3D 点 |
核心挑战:雷达点云极其稀疏且噪声大。
输入 (B, T, N, 4)
↓ Input Embedding: Linear(4→256) + LayerNorm + GELU
↓ Spatial Encoder: 点云内 Self-Attention + 空间位置编码 (MLP 3→256)
↓ Learnable Query Pooling: (B*T, N, 256) → (B*T, 256)
↓ Temporal Encoder: 帧间 Self-Attention + RoPE 位置编码
↓ Head: Linear → (B, T, 17, 3)
输出:预测骨架坐标
损失函数(4 项加权 L1):
L_pos:关节绝对位置(四肢权重 ×2)L_root_vel:根节点(Pelvis)帧间位移L_local_vel:以 Pelvis 为中心的局部姿态变化L_bone:骨段长度一致性约束
pip install torch>=2.0 numpy scipy tqdm tensorboard pandas matplotlib pillow需要 CUDA 11.8+(BF16 支持)、PyTorch 2.0+(torch.compile 与 Flash Attention)
将原始 JSON 序列切分为固定长度的训练窗口(.npz 格式):
python data_split.py --root ../datapreprocess/ --outdir dataset_split/ \
--window 75 --trim 15 --step 5 --fix-flip| 参数 | 说明 | 默认 |
|---|---|---|
--window |
窗口帧数 | 75 |
--trim |
剔除首尾不稳定帧数 | 15 |
--step |
滑窗步长 | 5 |
--split |
train/val/test 比例 | 0.8 0.1 0.1 |
--fix-flip |
启用骨架翻转修复 | 否 |
--seed |
随机种子 | 42 |
归一化变体:
data_split.py:以最后一帧有效点云质心为原点(主版本)data_split_firstpoint.py:以第一帧为原点data_split_lastframe.py:以最后一帧 Pelvis 骨架坐标为原点
用随机数据验证模型能否正常运行(不需要真实数据):
python smoke_test.py# v1:RAM Cache 模式,预加载全部数据到内存,训练速度更快
python train_zd_v1.py
# v2:Lazy Loading 模式,内存占用低,支持 Flash Attention
python train_zd_v2.py训练产物:
best_model.pth:验证 MPJPE 最优的模型权重runs/run_YYYYMMDD_HHMMSS/:TensorBoard 日志logs/run_YYYYMMDD_HHMMSS_joints.csv:各关节逐 Epoch MPJPE
# TensorBoard(实时监控损失、MPJPE、3D 可视化)
tensorboard --logdir runs/
# 分析 CSV 结果(修改文件内路径后运行)
python res_show.py原始数据(../datapreprocess/):按子文件夹组织的 JSON 序列文件。
{
"pointcloud_data": {
"points": [{"x": 1.23, "y": 0.45, "z": 0.78, "v": -0.5}, ...]
},
"skeleton_data": {
"joints": {
"HEAD": {"x": ..., "y": ..., "z": ...},
"NECK": {"x": ..., "y": ..., "z": ...},
"...共17个关节..."
}
}
}处理后数据(dataset_split/train|val|test/*.npz):
pointcloud:object array,shape(T,)→ 每帧(N, 4),N 可变skeleton:float32,shape(T, 17, 3),以参考帧点云质心归一化
17 关节定义(索引 0-16):
[0] Head
│
[1] Neck
╱ │ ╲
[5]L_Sho [2]Chest [8]R_Sho
│ │ │
[6]L_Elb [3]Navel [9]R_Elb
│ │ │
[7]L_Wri [4]Pelvis [10]R_Wri ← 根节点
╱ ╲
[11]L_Hip [14]R_Hip
│ │
[12]L_Kne [15]R_Kne
│ │
[13]L_Ank [16]R_Ank
| v1 | v2 | |
|---|---|---|
| 数据加载 | RAM Cache(全量预加载) | Lazy Loading(按需读取) |
| 内存占用 | 高 | 低 |
| 训练速度 | 更快(无 I/O 开销) | 受磁盘 I/O 限制 |
| Spatial Attention | nn.MultiheadAttention |
F.scaled_dot_product_attention(Flash Attention) |
| 空间编码器层数 | 1 层 | 2 层 |
| 每帧点数 | 150 | 120 |
| 适用场景 | 内存 ≥ 32GB,追求速度 | 内存受限或数据集很大 |
| 参数 | v1 | v2 | 说明 |
|---|---|---|---|
points_per_frame |
150 | 120 | 每帧采样/补零到固定点数 |
dim_model |
256 | 256 | Transformer 隐层维度 |
num_spatial_layers |
1 | 2 | 空间编码器层数 |
num_temporal_layers |
5 | 5 | 时序编码器层数 |
batch_size |
128 | 124 | |
lr |
5e-4 | 8e-4 | AdamW 学习率 |
epochs |
300 | 300 | EarlyStopping patience=20 |
调度器:CosineAnnealingWarmRestarts(T_0=10, T_mult=2)
精度:bfloat16
MPJPE(Mean Per Joint Position Error):各关节预测与真实坐标的平均欧氏距离,单位毫米(mm),数值越小越好。
代码同时记录全部 17 个关节的独立 MPJPE,可用于分析各部位的预测难度。
motion_estimate/
├── data_split.py # 数据预处理(主版本)
├── data_split_firstpoint.py # 预处理变体(第一帧归一化)
├── data_split_lastframe.py # 预处理变体(骨架归一化)
├── train_zd_v1.py # 训练脚本(RAM Cache)
├── train_zd_v2.py # 训练脚本(Lazy + Flash Attn)
├── smoke_test.py # 冒烟测试
├── res_show.py # 结果分析可视化
└── docs/
└── project_technical_guide.md # 详细技术文档
完整的架构原理、代码对应关系和参数调优建议,请参考 docs/project_technical_guide.md。