基于 PerceptAlign 框架,适配 MMFi 数据集,实现跨环境(跨域)WiFi CSI 3D 人体姿态估计。
WiFi CSI 3D 人体姿态估计的核心挑战在于跨环境泛化:不同房间的多径环境导致 CSI 分布差异巨大,在源域(如 E01-E03)训练的模型直接迁移到新环境(E04)时性能会大幅下降。
PerceptAlign 是一个基于 Geometry-Conditioned Transformer 的 WiFi 姿态估计框架,原始设计针对自采数据集(5 个场景,含几何标定信息)。本项目将其适配到 MMFi 公开数据集,用于系统研究跨环境域自适应问题。
任务定义:
- 输入:WiFi CSI 信号(3 接收天线 × 114 子载波 × 10 packets/帧)
- 输出:17 个人体关节的 3D 坐标(H36M 骨架)
- 源域:E01、E02、E03(有 CSI + 姿态标注)
- 目标域:E04(用于测试泛化能力)
WiFi CSI (per frame, per receiver)
│
▼
┌───────────────────────────────────┐
│ CSI → 3-Channel Image (224×224) │ Phase / Amplitude / Doppler
│ × 3 receivers × T frames │
└───────────────────────────────────┘
│
▼
┌───────────────────────────────────┐
│ ResNet-34 CNN Backbone │ 每帧每天线提取 256-d 特征
│ (GroupNorm, no BatchNorm) │
└───────────────────────────────────┘
│
▼
┌───────────────────────────────────┐
│ Token Construction (Eq. 24) │ u_{n,t} = LN(W_f·f + W_e·e + r_t + s_n)
│ ├── f: CNN 特征 │
│ ├── e: 空间嵌入 (NeRF PE) │
│ ├── r_t: 时间嵌入 │
│ └── s_n: 天线偏置 │
└───────────────────────────────────┘
│ T×Nr = 29×3 = 87 tokens
▼
┌───────────────────────────────────┐
│ Transformer Encoder (4 layers) │ 联合时空注意力
│ d_model=512, 8 heads │
└───────────────────────────────────┘
│
▼
┌───────────────────────────────────┐
│ Keypoint Decoder (MLP) │ 每帧输出 17×3 坐标
│ Nr×512 → 1024 → 512 → 51 │
└───────────────────────────────────┘
MMFi 的每帧 .mat 文件(3 天线 × 114 子载波 × 10 packets)通过以下方式转换为 PerceptAlign 需要的 3 通道图像:
| 通道 | 内容 | 物理意义 |
|---|---|---|
| Channel 0 | 归一化振幅 | 信号强度,反映人体遮挡和多径衰落 |
| Channel 1 | Unwrap 相位 | 携带距离和运动信息 |
| Channel 2 | 子载波相位差 | Doppler 频移近似,对运动敏感 |
每 seg_len=10 帧合并后,CSI 的时频矩阵 (114, 100) 通过双线性插值 resize 到 (224, 224) 以适配 ResNet-34 输入。
PerceptAlign/
├── perceptalign/ # PerceptAlign 源码(不修改)
│ ├── models/
│ │ └── perceptalign.py # posenet 模型定义
│ ├── data/
│ │ └── actions.py # 数据加载器
│ └── config.py # 默认配置
│
├── tools/
│ ├── train.py # 训练脚本(原始)
│ ├── eval.py # 评估脚本(原始)
│ ├── preprocess.py # 原始数据预处理
│ ├── preprocess_mmfi.py # ✨ MMFi 数据预处理
│ └── verify_mmfi_pt.py # ✨ 预处理结果验证
│
├── configs/
│ ├── cross_env_mmfi_E04.yaml # ✨ 跨环境: E01+E02+E03 → E04
│ ├── cross_env_mmfi_E01.yaml # ✨ 跨环境: E02+E03+E04 → E01
│ ├── within_env_E04.yaml # ✨ 域内基线 (上界)
│ └── cross_layout_scene3_*.yaml # 原始配置
│
├── data/
│ └── preprocessed_actions/ # 预处理输出目录
│ ├── MMFi/pt/*.pt # 转换后的 .pt 文件
│ ├── manifest.json # 数据清单
│ └── manifest.jsonl # JSONL 版本
│
├── weights/ # 模型权重保存目录
├── MMFi/ # MMFi 原始数据(需自行放置)
│ ├── E01/S01/A01/wifi-csi/...
│ ├── E02/...
│ ├── E03/...
│ └── E04/...
│
└── README.md # 本文件
标注 ✨ 的文件为 MMFi 适配新增,其余为 PerceptAlign 原始代码。
- Python 3.7+
- PyTorch >= 1.12(已测试 PyTorch 1.13 + CUDA 11.7)
- torchvision(与 PyTorch 版本匹配)
- scipy, h5py, numpy, tqdm, pyyaml, matplotlib
# 创建虚拟环境(可选,已有环境可跳过)
conda create -n perceptalign python=3.7
conda activate perceptalign
# 安装 PyTorch(根据 CUDA 版本选择)
pip install torch==1.13.0+cu117 torchvision==0.14.0+cu117 \
--extra-index-url https://download.pytorch.org/whl/cu117
# 安装其他依赖
pip install scipy h5py tqdm pyyaml matplotlibpython -c "import torch; print(torch.__version__, torch.cuda.is_available())"
python -c "import scipy; import h5py; import yaml; print('OK')"MMFi 数据集需要自行下载,放置在项目目录或任意位置,目录结构如下:
MMFi/
├── E01/ # 环境 1
│ ├── S01/ # 被试 1
│ │ ├── A01/ # 动作 1
│ │ │ ├── wifi-csi/ # CSI 数据
│ │ │ │ ├── frame_000001.mat
│ │ │ │ ├── frame_000002.mat
│ │ │ │ └── ... # ~297 帧
│ │ │ └── ground_truth.npy # 姿态标注 (F, 17, 3)
│ │ ├── A02/
│ │ └── ... # A01-A27,共 27 种动作
│ ├── S02/
│ └── ... # S01-S10
├── E02/ # S11-S20
├── E03/ # S21-S30
└── E04/ # S31-S40
数据规模:
| 环境 | 被试 | 动作 | 帧数/动作 | 总样本数 |
|---|---|---|---|---|
| E01 | S01-S10 | A01-A27 | ~297 | 270 sequences |
| E02 | S11-S20 | A01-A27 | ~297 | 270 sequences |
| E03 | S21-S30 | A01-A27 | ~297 | 270 sequences |
| E04 | S31-S40 | A01-A27 | ~297 | 270 sequences |
| 合计 | 40 人 | 27 种 | 1080 sequences |
每个 .mat 文件包含:
CSIamp:float32 (3, 114, 10)— 振幅(3 接收天线 × 114 子载波 × 10 packets)CSIphase:float32 (3, 114, 10)— 相位
ground_truth.npy: float32 (F, 17, 3) — F 帧的 17 关节 3D 坐标(米),遵循 H36M 骨架定义。
将 MMFi 原始数据转换为 PerceptAlign 的 .pt 格式。
cd ~/PerceptAlign
python tools/preprocess_mmfi.py \
--mmfi_root /path/to/MMFi \
--out_root data/preprocessed_actions \
--envs E01 E02 E03 E04 \
--seg_len 10参数说明:
| 参数 | 默认值 | 说明 |
|---|---|---|
--mmfi_root |
(必填) | MMFi 数据集根目录 |
--out_root |
(必填) | 预处理输出目录 |
--envs |
E01 E02 E03 E04 | 要处理的环境列表 |
--seg_len |
10 | 每输出帧聚合的原始帧数 |
--image_size |
224 | 输出图像分辨率 |
--overwrite |
False | 是否覆盖已有 .pt 文件 |
预期输出:
[progress] saved=270 skipped=0 current=E01/S10/A27 T_out=29
[progress] saved=540 skipped=0 current=E02/S20/A27 T_out=29
[progress] saved=810 skipped=0 current=E03/S30/A27 T_out=29
[progress] saved=1080 skipped=0 current=E04/S40/A27 T_out=29
[done] saved=1080 skipped=0
[stats] per-environment: {'E01': 270, 'E02': 270, 'E03': 270, 'E04': 270}
每个 .pt 文件约 30-50MB,总计约 30-50GB 磁盘空间。
python tools/verify_mmfi_pt.py \
--pt_dir data/preprocessed_actions/MMFi/pt \
--max_files 5预期输出:
[OK] E01_S01_A01.pt
csi_data: (29, 3, 3, 224, 224) dtype=torch.float32 range=[0.000, 1.000]
keypoints: (29, 17, 3) dtype=torch.float32 range=[-0.xxx, 0.xxx]
scene=MMFi user=S01 action=A01 layout=E01
[ALL OK] 所有文件格式正确, 可以开始训练。
cd ~/PerceptAlign
# E01 + E02 + E03 → E04
PYTHONPATH=. python tools/train.py --config configs/cross_env_mmfi_E04.yaml如果配置中使用相对路径报错,请修改为绝对路径:
# 自动修改配置为绝对路径
sed -i "s|preprocessed_root: data/|preprocessed_root: $(pwd)/data/|" configs/cross_env_mmfi_E04.yaml
sed -i "s|manifest: data/|manifest: $(pwd)/data/|" configs/cross_env_mmfi_E04.yaml
sed -i "s|weights_dir: weights|weights_dir: $(pwd)/weights|" configs/cross_env_mmfi_E04.yaml
sed -i "s|outputs_dir: outputs|outputs_dir: $(pwd)/outputs|" configs/cross_env_mmfi_E04.yamlPYTHONPATH=. torchrun --nproc_per_node=2 tools/train.py \
--config configs/cross_env_mmfi_E04.yaml编辑 configs/cross_env_mmfi_E04.yaml:
data:
batch_size: 1 # 从 2 降到 1
train:
grad_accum: 16 # 从 8 增到 16,保持等效 batch size[epoch 1/200] val: MPJPE=xxx.xxmm PCK@20=xx.xx% PCK@50=xx.xx% lr=1.000e-04
[epoch 2/200] val: MPJPE=xxx.xxmm PCK@20=xx.xx% PCK@50=xx.xx% lr=1.000e-04
[save] best -> weights/cross_env_mmfi_E04_best.pth
...
模型权重自动保存在 weights/ 目录,文件名为 {experiment_name}_best.pth。
PYTHONPATH=. python tools/eval.py \
--config configs/cross_env_mmfi_E04.yaml \
--checkpoint weights/cross_env_mmfi_E04_best.pth \
--split test \
--amp输出格式:
[test] MPJPE=xxx.xx mm | PCK@20=xx.xx% | PCK@50=xx.xx%
# 测试集 (目标域 E04)
PYTHONPATH=. python tools/eval.py --config ... --split test
# 验证集 (源域的 10% 留出)
PYTHONPATH=. python tools/eval.py --config ... --split val
# 训练集 (检查过拟合)
PYTHONPATH=. python tools/eval.py --config ... --split train| 配置文件 | 训练域 | 测试域 | 用途 |
|---|---|---|---|
cross_env_mmfi_E04.yaml |
E01, E02, E03 | E04 | 主实验:跨环境泛化 |
cross_env_mmfi_E01.yaml |
E02, E03, E04 | E01 | 消融实验 |
within_env_E04.yaml |
E04 | E04 | 域内基线(性能上界) |
以 "E02+E04 训练 → E03 测试" 为例:
# configs/cross_env_mmfi_E03.yaml
experiment:
name: cross_env_mmfi_E03
protocol:
type: cross_layout
scene: MMFi
train_layouts: [E02, E04]
test_layouts: [E03]
# 其余配置与 cross_env_mmfi_E04.yaml 相同| 参数 | 默认值 | 说明 |
|---|---|---|
model.num_keypoints |
17 | H36M 关节数(不要改) |
model.num_layers |
4 | Transformer 层数 |
model.num_heads |
8 | 注意力头数 |
model.max_seq_len |
60 | 最大序列长度(>29 帧即可) |
train.lr |
1e-4 | 初始学习率 |
train.epochs |
200 | 训练轮数 |
train.grad_accum |
8 | 梯度累积步数 |
data.batch_size |
2 | 批大小(受显存限制) |
| 指标 | 说明 | 方向 |
|---|---|---|
| MPJPE | 平均关节位置误差(mm),预测与 GT 的欧氏距离均值 | 越小越好 |
| PCK@20mm | 关节误差 < 20mm 的比例 | 越大越好 |
| PCK@50mm | 关节误差 < 50mm 的比例 | 越大越好 |
注:PerceptAlign 的 MPJPE 在根节点中心化的坐标系中计算,已消除全局位置偏移。
| MMFi 概念 | PerceptAlign 对应 | 适配方式 |
|---|---|---|
| 环境 (E01-E04) | Layout (A/B/C) | layout 字段映射环境编号 |
| 被试 (S01-S40) | User | user 字段直接对应 |
| 动作 (A01-A27) | Action | action 字段直接对应 |
| 3 接收天线 | 3 Receivers | 天线 = 接收器,物理对应 |
| 114 子载波 | 57 subcarriers | MMFi 频率分辨率更高 |
| 17 关节 (H36M) | 25 关节 (SMPLX) | config 中设置 num_keypoints: 17 |
| 无几何标定 | tx/rx 坐标 | 不使用几何条件化,receiver_bias 自动学习 |
-
几何条件化是可选的:当
.pt中没有tx_coords/rx_coords时,train.py自动跳过几何计算,receiver_bias参数学习天线间差异。 -
关节数通过 config 控制:
num_keypoints: 17传入posenet构造函数,自动调整 decoder 输出维度。 -
数据格式完全兼容:预处理脚本生成的
.pt与 PerceptAlign 的ManifestPTDataset完全兼容。 -
协议框架复用:MMFi 的跨环境设定映射为
cross_layout协议,环境编号作为 layout 标签。
CSI 帧聚合:每 seg_len=10 帧合并为 1 个输出帧。297 原始帧 → 29 输出帧,每帧的 CSI 包含 100 个 packets 的信息(10 帧 × 10 packets)。
根节点中心化:预处理时对 keypoints 做根节点中心化(第一帧 Hip 关节 = 原点),与 PerceptAlign 原始流程一致。
存储开销:每个 .pt 约 30-50MB(主要是 224×224 图像),1080 个文件共约 30-50GB。
Q: ModuleNotFoundError: No module named 'perceptalign'
项目未安装到 Python 路径。使用 PYTHONPATH 运行:
PYTHONPATH=. python tools/train.py --config ...或安装为可编辑包(如果项目有 setup.py):
pip install -e .Q: FileNotFoundError: ... manifest.json
配置中的路径是相对于 config 文件所在目录解析的。改为绝对路径:
# 自动修改
sed -i "s|preprocessed_root: data/|preprocessed_root: $(pwd)/data/|" configs/cross_env_mmfi_E04.yaml
sed -i "s|manifest: data/|manifest: $(pwd)/data/|" configs/cross_env_mmfi_E04.yaml
sed -i "s|weights_dir: weights|weights_dir: $(pwd)/weights|" configs/cross_env_mmfi_E04.yamlQ: 预处理时所有样本都 skip(saved=0 skipped=1080)
检查报错信息。如果是 _stack_dispatcher() got an unexpected keyword argument 'dim',需要修正 numpy 调用:
sed -i 's/np.stack(keypoints, dim=0)/np.stack(keypoints, axis=0)/g' tools/preprocess_mmfi.pyQ: 训练时 GPU 显存不足(OOM)
调小 batch_size 并增大 grad_accum 以保持等效 batch size:
data:
batch_size: 1
train:
grad_accum: 16Q: 训练速度很慢
- 开启 AMP 混合精度:确保配置中
data.amp: true - 减少
num_workers如果 CPU 是瓶颈 - 降低图像分辨率(需重新预处理):
--image_size 112
Q: 如何添加新的跨环境实验?
复制 cross_env_mmfi_E04.yaml,修改 train_layouts 和 test_layouts:
protocol:
train_layouts: [E01, E03]
test_layouts: [E02]Q: 预处理后磁盘空间不够
可以减小图像尺寸和增大帧聚合数:
python tools/preprocess_mmfi.py \
--mmfi_root /path/to/MMFi \
--out_root data/preprocessed_actions \
--seg_len 15 --image_size 112 --overwrite这会将存储从 ~40GB 降至 ~5GB,但可能影响精度。
- PerceptAlign — Geometry-Aware WiFi Sensing 框架
- MMFi — 多模态 WiFi 感知数据集