PhysDrive:多模态远程生理测量数据集——驾驶员健康监测的”真实驾驶”基准

论文信息

  • 标题: PhysDrive: A Multimodal Remote Physiological Measurement Dataset for In-vehicle Driver Monitoring
  • 来源: Lacuna / Tiptree Systems
  • 领域: 非接触式生理测量 / 驾驶员监测 / 智能座舱
  • 时间: 2026年9月
  • 核心贡献: 首个大规模、多模态、真实驾驶环境下的远程生理测量数据集

核心创新

PhysDrive 是首个在真实驾驶环境中同步采集 RGB、近红外(NIR)和毫米波雷达(mmWave)三模态生理数据的数据集,涵盖 48 名受试者、1.5M+ 同步帧,跨三种车型、四种光照条件、三种路况。解决了现有 RPM 数据集只在受控室内环境采集、无法泛化到真实驾驶场景的核心问题。

方法详解

1. 多模态传感器架构

PhysDrive 的传感器配置直接对应智能座舱量产方案:

模态 传感器 采样率 用途 优势 局限
RGB 车载摄像头 30fps rPPG 肤肤色变化提取心率 白天精度高 夜间失效
NIR 近红外摄像头 30fps 低光环境 rPPG 全天候工作 SNR 较低
mmWave 77GHz 毫米波雷达 ~20fps 胸部位移检测心跳/呼吸 隐私保护+抗光照 路面震动敏感
Ground Truth ECG/BVP/呼吸带 多通道 参考标准 医疗级精度 需接触式

2. 数据采集设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# PhysDrive 数据采集配置复现
import numpy as np
from dataclasses import dataclass
from typing import List, Tuple

@dataclass
class PhysDriveConfig:
"""PhysDrive 数据集采集配置"""
# 受试者
n_subjects: int = 48
# 车辆类型
vehicle_types: List[str] = None # ['Sedan', 'SUV', 'Hatchback']
# 光照条件
lighting_conditions: List[str] = None # ['Daylight', 'Tunnel', 'Night', 'Mixed']
# 路况
road_types: List[str] = None # ['Flat', 'Congested', 'Bumpy']
# 同步帧数
total_frames: int = 1_500_000
# 采样率
rgb_fps: int = 30
nir_fps: int = 30
radar_freq: float = 77e9 # 77 GHz
# Ground Truth 通道
gt_channels: List[str] = None # ['ECG', 'BVP', 'Resp', 'PPG', 'GSR', 'Temp']

# 实验矩阵设计
def generate_experiment_matrix():
"""生成 12 组实验条件矩阵"""
vehicles = ['Sedan', 'SUV', 'Hatchback']
lightings = ['Daylight', 'Tunnel', 'Night', 'Mixed']
roads = ['Flat', 'Congested', 'Bumpy']

experiments = []
for v in vehicles:
for l in lightings:
experiments.append({
'vehicle': v,
'lighting': l,
'road': 'Flat', # 基线路况
'subject_set': 'group_{}{}'.format(v, l)
})
return experiments

# STMap 时空图提取
def extract_stmap(video_frames: np.ndarray,
face_boxes: List[Tuple[int,int,int,int]]) -> np.ndarray:
"""
从视频帧提取 Spatial-Temporal Map (STMap)

将面部 ROI 的肤色调变随时间展开为 2D 图,
使生理信号与背景噪声分离。

Args:
video_frames: 视频帧序列, shape=(T, H, W, 3)
face_boxes: 每帧人脸框 [(x1,y1,x2,y2), ...]

Returns:
stmap: 时空图, shape=(T, num_patches, 3)

Example:
>>> frames = np.random.randint(0, 255, (300, 480, 640, 3), dtype=np.uint8)
>>> boxes = [(100, 120, 300, 350)] * 300
>>> stmap = extract_stmap(frames, boxes)
>>> print(f"STMap shape: {stmap.shape}")
"""
T = len(video_frames)
# 提取面部 ROI 均值
stmap = np.zeros((T, 6, 3)) # 6 个面部 patch
patch_names = ['Forehead', 'Left_Cheek', 'Right_Cheek',
'Nose', 'Chin', 'Neck']

for t in range(T):
x1, y1, x2, y2 = face_boxes[t]
face = video_frames[t, y1:y2, x1:x2, :]

# 分割为 6 个 patch
h, w = face.shape[:2]
patches = {
'Forehead': face[0:h//3, w//4:3*w//4],
'Left_Cheek': face[h//3:2*h//3, 0:w//2],
'Right_Cheek': face[h//3:2*h//3, w//2:],
'Nose': face[h//3:2*h//3, 2*w//5:3*w//5],
'Chin': face[2*h//3:, w//3:2*w//3],
'Neck': face[2*h//3:, :]
}

for i, (name, patch) in enumerate(patches.items()):
stmap[t, i] = patch.mean(axis=(0, 1))

return stmap

if __name__ == "__main__":
config = PhysDriveConfig()
config.vehicle_types = ['Sedan', 'SUV', 'Hatchback']
config.lighting_conditions = ['Daylight', 'Tunnel', 'Night', 'Mixed']
config.road_types = ['Flat', 'Congested', 'Bumpy']

print(f"受试者数: {config.n_subjects}")
print(f"总帧数: {config.total_frames:,}")
print(f"模态: RGB + NIR + mmWave(77GHz)")
print(f"GT通道: {len(config.gt_channels)} 通道")

experiments = generate_experiment_matrix()
print(f"实验组数: {len(experiments)}")

3. 关键发现:模态互补性

指标 RGB (PhysNet) NIR mmWave (mmFormer) 最优组合
心率 MAE (bpm) 6.29 8.5 3.65 mmWave > RGB > NIR
呼吸率 MAE 2.8 3.1 1.9 mmWave 最优
白天性能 ✅ 最佳 ✅ 良好 ✅ 良好 RGB 白天足够
夜间性能 ❌ 失效 ✅ 最佳 ✅ 最佳 NIR + mmWave
隧道场景 ⚠️ 震动+光照 ✅ 良好 ⚠️ 震动干扰 NIR 最稳定
隐私保护 ❌ 图像泄露 ⚠️ 部分 ✅ 完全 mmWave 最安全

4. 域适应问题

论文关键发现:在室内数据集(如 PURE)上训练的模型,在 PhysDrive 上性能显著下降

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 域适应性能衰减分析
def domain_shift_analysis():
"""
室内→驾驶环境域适应分析

论文发现:PURE 数据集训练的 PhysNet 在 PhysDrive 上
心率 MAE 从 5.8 bpm → 12.3 bpm(性能下降 53%)
"""
results = {
'PURE (室内)': {'HR_MAE': 5.8, 'RR_MAE': 1.5},
'PhysDrive (驾驶)': {'HR_MAE': 12.3, 'RR_MAE': 3.2},
'性能下降': {'HR_MAE': '53%', 'RR_MAE': '53%'}
}

for domain, metrics in results.items():
print(f"{domain}: HR={metrics['HR_MAE']:.1f} bpm, RR={metrics['RR_MAE']:.1f} rpm")

return results

domain_shift_analysis()

代码复现:多模态心率估计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
"""
PhysDrive 多模态心率估计基线复现

基于 PhysNet (RGB) 和 mmFormer (mmWave) 的融合方案
"""

import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Tuple

class PhysNetBackbone(nn.Module):
"""
PhysNet rPPG 提取网络
输入: STMap (B, T, 6, 3)
输出: rPPG 信号 (B, T)
"""
def __init__(self, input_dim: int = 18, hidden_dim: int = 128):
super().__init__()
# 时序编码器
self.encoder = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim // 2)
)
# 解码器
self.decoder = nn.Sequential(
nn.Conv1d(hidden_dim // 2, 32, kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv1d(32, 1, kernel_size=3, padding=1)
)

def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
Args:
x: STMap, shape=(B, T, 6, 3) → flatten to (B, T, 18)
Returns:
rppg: rPPG 信号, shape=(B, T)
"""
B, T, H, W = x.shape
x_flat = x.reshape(B, T, H * W) # (B, T, 18)

# 编码
encoded = self.encoder(x_flat) # (B, T, hidden_dim//2)
encoded = encoded.permute(0, 2, 1) # (B, hidden_dim//2, T)

# 解码
rppg = self.decoder(encoded) # (B, 1, T)
return rppg.squeeze(1) # (B, T)


class mmFormerBackbone(nn.Module):
"""
mmFormer: mmWave 雷达心率提取网络
输入: mmWave range-time signal (B, 1, T, R)
输出: 心率信号 (B, T)
"""
def __init__(self, in_channels: int = 1, base_ch: int = 32):
super().__init__()
# 特征提取
self.feature_extractor = nn.Sequential(
nn.Conv2d(in_channels, base_ch, kernel_size=(3, 3), padding=1),
nn.BatchNorm2d(base_ch),
nn.ReLU(),
nn.MaxPool2d((2, 1)),

nn.Conv2d(base_ch, base_ch * 2, kernel_size=(3, 3), padding=1),
nn.BatchNorm2d(base_ch * 2),
nn.ReLU(),
nn.MaxPool2d((2, 1)),

nn.Conv2d(base_ch * 2, base_ch * 4, kernel_size=(3, 3), padding=1),
nn.BatchNorm2d(base_ch * 4),
nn.ReLU(),
)
# 时序重建
self.temporal_decoder = nn.LSTM(
input_size=base_ch * 4,
hidden_size=64,
num_layers=2,
batch_first=True,
bidirectional=True
)
self.output_layer = nn.Linear(128, 1)

def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
Args:
x: mmWave 信号, shape=(B, 1, T, R)
Returns:
heart_signal: shape=(B, T)
"""
# 空间特征
features = self.feature_extractor(x) # (B, C, T', R')
B, C, T, R = features.shape
features = features.mean(dim=3) # range 维度池化 → (B, C, T)
features = features.permute(0, 2, 1) # (B, T, C)

# 时序建模
output, _ = self.temporal_decoder(features) # (B, T, 128)
heart_signal = self.output_layer(output).squeeze(-1) # (B, T)
return heart_signal


class MultiModalFusion(nn.Module):
"""
PhysDrive 多模态融合心率估计

融合 RGB rPPG + mmWave 雷达信号
自适应权重根据光照条件动态调整
"""
def __init__(self):
super().__init__()
self.physnet = PhysNetBackbone()
self.mmformer = mmFormerBackbone()

# 光照条件自适应权重
self.lighting_classifier = nn.Sequential(
nn.AdaptiveAvgPool2d(1),
nn.Flatten(),
nn.Linear(18, 16),
nn.ReLU(),
nn.Linear(16, 3), # [Day, Night, Mixed]
nn.Softmax(dim=1)
)

# 模态融合
self.fusion = nn.Sequential(
nn.Linear(2, 16),
nn.ReLU(),
nn.Linear(16, 2),
nn.Softmax(dim=1) # [rgb_weight, radar_weight]
)

def forward(self, rgb_stmap: torch.Tensor,
mmwave_signal: torch.Tensor) -> Tuple[torch.Tensor, dict]:
"""
Args:
rgb_stmap: RGB 时空图, shape=(B, T, 6, 3)
mmwave_signal: mmWave 信号, shape=(B, 1, T, R)

Returns:
fused_hr: 融合心率信号
weights: 各模态权重
"""
# 各模态独立估计
rppg = self.physnet(rgb_stmap) # (B, T)
radar_hr = self.mmformer(mmwave_signal) # (B, T)

# 光照条件判断
lighting = self.lighting_classifier(rgb_stmap.mean(dim=1, keepdim=True))

# 自适应融合权重
concat = torch.stack([rppg, radar_hr], dim=-1) # (B, T, 2)
weights = self.fusion(concat.mean(dim=1)) # (B, 2)
weights = weights.unsqueeze(1) # (B, 1, 2)

# 加权融合
stacked = torch.stack([rppg, radar_hr], dim=-1) # (B, T, 2)
fused = (stacked * weights).sum(dim=-1) # (B, T)

info = {
'rgb_rppg': rppg,
'radar_hr': radar_hr,
'lighting': lighting,
'fusion_weights': weights.squeeze(1)
}
return fused, info


# 性能评估
if __name__ == "__main__":
# 模拟输入
batch_size, seq_len = 2, 300
rgb_stmap = torch.randn(batch_size, seq_len, 6, 3)
mmwave = torch.randn(batch_size, 1, seq_len, 64) # 64 range bins

model = MultiModalFusion()
fused_hr, info = model(rgb_stmap, mmwave)

print(f"融合心率信号 shape: {fused_hr.shape}")
print(f"光照条件: {info['lighting']}")
print(f"融合权重 (RGB, mmWave): {info['fusion_weights']}")

# 性能对比
print("\n=== PhysDrive 基准性能 ===")
print(f"{'模态':<20} {'HR MAE (bpm)':<15} {'RR MAE (rpm)':<15}")
print(f"{'RGB (PhysNet)':<20} {'6.29':<15} {'2.8':<15}")
print(f"{'mmWave (mmFormer)':<20} {'3.65':<15} {'1.9':<15}")
print(f"{'NIR (PhysNet)':<20} {'8.5':<15} {'3.1':<15}")
print(f"{'融合方案':<20} {'3.2 (期望)':<15} {'1.7 (期望)':<15}")

实验结果

指标 论文结果 复现预期 说明
mmWave HR MAE 3.65 bpm ~3.8 bpm 77GHz,震动补偿后
RGB HR MAE 6.29 bpm ~6.5 bpm 白天场景
室内→驾驶域适应 性能下降 53% ~50% 需驾驶数据微调
多任务 (HR+RR) 优于单任务 确认 生理耦合利用

IMS 应用启示

1. 毫米波雷达是驾驶员生理监测的量产首选

对比项 摄像头 rPPG mmWave 雷达
心率精度 6.29 bpm 3.65 bpm
夜间工作
隐私保护
路况鲁棒 ⚠️ 震动 ⚠️ 震动
量产成本 摄像头已有 增加雷达模块
ASIL 等级 B B-D 可达

建议: IMS 3.0+ 架构应将 77GHz mmWave 作为生理监测主模态,RGB 作为辅助/白天优化。

2. STMap 是轻量化 rPPG 的关键

  • 将 3D 视频压缩为 2D 时序图,计算量降低 90%+
  • 适合高通 8255 等 NPU 部署
  • 输入仅需面部 ROI 均值,隐私友好

3. 域适应是量产必须

  • 室内训练的模型直接用于驾驶场景性能减半
  • 需要 100-500 段真实驾驶数据微调
  • 关键域差异:震动、光照变化、头部运动

4. 路况对 mmWave 的影响不可忽视

路况 mmWave HR MAE RGB HR MAE 主因
平路 3.65 bpm 6.29 bpm 基线
拥堵 4.2 bpm 7.1 bpm 频繁启停+头部运动
颠簸 7.8 bpm 9.5 bpm 胸部震动伪运动

对策: 需要加速度计辅助的震动补偿模块。

5. 多任务学习优于单任务

同时预测心率和呼吸率比单独预测各提升 5-8%,说明生理信号间的耦合关系值得利用。

硬件方案建议

量产级 PhysDrive 方案

组件 型号 参数 用途
RGB 摄像头 OV2311 2MP, 1600×1200, 全局快门 白天 rPPG
NIR 摄像头 OV9282 1MP, 1280×800, 940nm 夜间 rPPG
mmWave 雷达 TI AWR2944 77GHz, 4Tx4Rx, FMCW 心率/呼吸
加速度计 BMI270 6轴, 1.6kHz 震动补偿
处理器 QCS8255 Hexagon NPU, 26 TOPS 模型推理
总成本 ~$45 BOM 量产可行

开发优先级

优先级 模块 工作量 预期收益
P0 mmWave 心率提取 4 周 MAE < 4 bpm
P0 STMap 提取+优化 2 周 计算量降低 90%
P1 震动补偿 3 周 颠簸路面 MAE < 6
P1 域适应微调 2 周 室内→驾驶泛化
P2 多模态融合 3 周 全场景覆盖
P2 多任务学习 2 周 HR+RR 联合提升

测试场景

PT-01 白天心率监测

前置条件:

  • 驾驶员正常坐姿,面部无遮挡
  • 光照:白天 500±100 lux
  • 平路行驶,车速 60±10 km/h
  • 红外摄像头 + mmWave 雷达正常工作

测试步骤:

  1. 启动多模态采集系统
  2. ECG 参考设备同步记录
  3. 驾驶 5 分钟(建立基线)
  4. 记录各模态心率估计值
  5. 对比 ECG 参考值

判定条件:

检测项 通过条件 失败条件
mmWave HR MAE ≤ 4 bpm > 4 bpm
RGB HR MAE ≤ 7 bpm > 7 bpm
融合 HR MAE ≤ 3.5 bpm > 4 bpm
检测延迟 ≤ 5s > 5s

PT-02 夜间心率监测

前置条件:

  • 光照:< 5 lux(夜间)
  • 仅 NIR + mmWave 可用

判定条件:

检测项 通过条件
NIR HR MAE ≤ 10 bpm
mmWave HR MAE ≤ 5 bpm
融合 HR MAE ≤ 4 bpm

总结

PhysDrive 数据集揭示了一个关键事实:室内环境下训练的生理监测模型在真实驾驶场景中性能下降超过 50%。这意味着 IMS 团队不能依赖现有公开数据集直接量产,必须:

  1. 建立真实驾驶场景数据采集能力
  2. 以 mmWave 雷达为主模态(MAE 3.65 bpm,隐私+全天候)
  3. 使用 STMap 轻量化方案适配边缘部署
  4. 开发震动补偿算法应对颠簸路况
  5. 利用多任务学习提升生理信号提取精度

PhysDrive 的 1.5M 同步帧、48 受试者、三模态数据为我们提供了首个真实驾驶场景基准,IMS 3.0 架构应以此为目标建立数据闭环。


https://dapalm.com/2026/09/14/2026-09-14-physdrive-multimodal-rpm-dataset-driver-vitals-ims/
作者
Mars
发布于
2026年9月14日
许可协议