毫米波雷达生命体征检测:呼吸与心率非接触监测

来源: Nature Scientific Reports 2025, arXiv 2025
应用: CPD儿童检测、驾驶员健康监测、乘员状态感知


技术原理

FMCW雷达基础

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
import numpy as np
from scipy import signal

class FMCWRadarVitalSigns:
"""
FMCW雷达生命体征检测

原理:
1. 发射线性调频信号(Chirp)
2. 接收目标反射信号
3. 混频得到中频(IF)信号
4. 分析相位变化提取生命体征
"""

def __init__(self, config: dict):
# 雷达参数
self.fc = 60e9 # 60GHz中心频率
self.bw = 4e9 # 4GHz带宽
self.chirp_duration = 100e-6 # 100μs
self.num_chirps = 128
self.num_samples = 256

# 信号处理参数
self.fft_size = 512

def process_if_signal(self, if_data: np.ndarray) -> dict:
"""
处理中频信号

Args:
if_data: (num_chirps, num_samples) ADC数据

Returns:
vital_signs: {
'respiration_rate': 呼吸率(次/分钟),
'heart_rate': 心率(次/分钟),
'signal_quality': 信号质量评分
}
"""
# 1. 距离FFT(快时间)
range_fft = self._range_fft(if_data)

# 2. 目标检测(CFAR)
target_range = self._detect_target(range_fft)

# 3. 相位提取
phase_signal = self._extract_phase(if_data, target_range)

# 4. 解相位缠绕
phase_unwrapped = np.unwrap(phase_signal)

# 5. 生命体征提取
respiration, heart_rate = self._extract_vital_signs(phase_unwrapped)

return {
'respiration_rate': respiration,
'heart_rate': heart_rate,
'signal_quality': self._estimate_signal_quality(phase_unwrapped)
}

def _range_fft(self, if_data: np.ndarray) -> np.ndarray:
"""距离FFT"""
return np.fft.fft(if_data, n=self.fft_size, axis=1)

def _detect_target(self, range_fft: np.ndarray) -> int:
"""检测目标距离bin"""
# 取平均幅度谱
magnitude = np.abs(range_fft).mean(axis=0)

# 找最大值位置
target_bin = np.argmax(magnitude[1:50]) + 1 # 忽略DC

return target_bin

def _extract_phase(
self,
if_data: np.ndarray,
target_bin: int
) -> np.ndarray:
"""
提取相位时间序列

生命体征导致目标微动,反映为相位变化
"""
# 取目标bin的相位
range_fft = np.fft.fft(if_data, n=self.fft_size, axis=1)

phase = np.angle(range_fft[:, target_bin])

return phase

def _extract_vital_signs(
self,
phase_signal: np.ndarray
) -> Tuple[float, float]:
"""
提取呼吸和心率

Args:
phase_signal: 相位时间序列

Returns:
respiration_rate: 呼吸率(次/分钟)
heart_rate: 心率(次/分钟)
"""
# 时间轴
t = np.arange(len(phase_signal)) * self.chirp_duration

# 去趋势
phase_detrend = signal.detrend(phase_signal)

# 带通滤波
# 呼吸:0.1-0.5 Hz(6-30次/分钟)
respiration_band = [0.1, 0.5]

# 心率:0.8-2.0 Hz(48-120次/分钟)
heart_band = [0.8, 2.0]

# 设计滤波器
fs = 1.0 / self.chirp_duration

# 呼吸信号
b_resp, a_resp = signal.butter(
4,
[f/min(fs/2, 1) for f in respiration_band],
btype='band'
)
respiration_signal = signal.filtfilt(b_resp, a_resp, phase_detrend)

# 心率信号(去除呼吸谐波)
b_heart, a_heart = signal.butter(
4,
[f/min(fs/2, 1) for f in heart_band],
btype='band'
)
heart_signal = signal.filtfilt(b_heart, a_heart, phase_detrend)

# 频谱分析
resp_freq, resp_psd = signal.welch(
respiration_signal, fs=fs, nperseg=len(respiration_signal)
)
heart_freq, heart_psd = signal.welch(
heart_signal, fs=fs, nperseg=len(heart_signal)
)

# 找主频
resp_peak_idx = np.argmax(resp_psd)
heart_peak_idx = np.argmax(heart_psd)

# 转换为次/分钟
respiration_rate = resp_freq[resp_peak_idx] * 60
heart_rate = heart_freq[heart_peak_idx] * 60

return respiration_rate, heart_rate

def _estimate_signal_quality(self, phase_signal: np.ndarray) -> float:
"""估计信号质量"""
# 基于信噪比
signal_power = np.var(phase_signal)
noise_power = np.var(np.diff(phase_signal, n=2)) # 高频噪声

snr = signal_power / (noise_power + 1e-10)

return min(1.0, snr / 10)


# 测试
if __name__ == "__main__":
config = {}
radar = FMCWRadarVitalSigns(config)

# 模拟数据
np.random.seed(42)
if_data = np.random.randn(128, 256) * 0.1

# 添加模拟生命体征
t = np.arange(128) * 100e-6
respiration = 0.5 * np.sin(2 * np.pi * 0.25 * t) # 15次/分钟
heartbeat = 0.05 * np.sin(2 * np.pi * 1.2 * t) # 72次/分钟

phase_modulation = respiration + heartbeat
if_data[:, 50] += 10 * np.exp(1j * phase_modulation * 10)

# 处理
result = radar.process_if_signal(if_data)

print(f"呼吸率: {result['respiration_rate']:.1f} 次/分钟")
print(f"心率: {result['heart_rate']:.1f} 次/分钟")
print(f"信号质量: {result['signal_quality']:.2f}")

多人监测

MIMO雷达

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
class MIMORadarVitalSigns:
"""
MIMO雷达多人生命体征监测

支持同时监测多个乘员
"""

def __init__(self, num_tx: int = 4, num_rx: int = 4):
self.num_tx = num_tx
self.num_rx = num_rx
self.num_virtual_antennas = num_tx * num_rx

def localize_targets(self, if_data: np.ndarray) -> List[dict]:
"""
多目标定位

Returns:
targets: 各目标位置和生命体征
"""
# 距离-角度FFT
range_angle_map = self._compute_range_angle_map(if_data)

# CFAR检测
detections = self._cfar_detection(range_angle_map)

# 各目标生命体征
targets = []
for det in detections:
vital_signs = self._extract_target_vital_signs(
if_data,
det['range_bin'],
det['angle_bin']
)
targets.append({
'position': (det['range'], det['angle']),
'vital_signs': vital_signs
})

return targets

def _compute_range_angle_map(self, if_data: np.ndarray) -> np.ndarray:
"""计算距离-角度图"""
# 距离FFT
range_fft = np.fft.fft(if_data, axis=-1)

# 角度FFT(虚拟阵列)
# 需要按TDM-MIMO时序重组
angle_fft = np.fft.fftshift(
np.fft.fft(range_fft, axis=0),
axes=0
)

return angle_fft

def _cfar_detection(
self,
range_angle_map: np.ndarray,
num_guard: int = 2,
num_train: int = 4,
p_fa: float = 1e-4
) -> List[dict]:
"""CFAR目标检测"""
detections = []

magnitude = np.abs(range_angle_map)

for r_idx in range(num_train, magnitude.shape[1] - num_train):
for a_idx in range(num_train, magnitude.shape[0] - num_train):
cut = magnitude[a_idx, r_idx]

# 训练窗口
train_cells = []
for da in range(-num_train-num_guard, num_train+num_guard+1):
for dr in range(-num_train-num_guard, num_train+num_guard+1):
if abs(da) > num_guard or abs(dr) > num_guard:
train_cells.append(magnitude[a_idx+da, r_idx+dr])

noise_level = np.mean(train_cells)
threshold = noise_level * (np.log(1/p_fa))

if cut > threshold:
detections.append({
'range_bin': r_idx,
'angle_bin': a_idx,
'range': r_idx * 0.04, # 4cm分辨率
'angle': (a_idx - magnitude.shape[0]//2) * 180 / magnitude.shape[0]
})

return detections

def _extract_target_vital_signs(
self,
if_data: np.ndarray,
range_bin: int,
angle_bin: int
) -> dict:
"""提取单目标生命体征"""
# 取目标位置的相位
phase = np.angle(if_data[:, range_bin])

# 后续同单人处理...
# (简化)

return {
'respiration_rate': 15.0,
'heart_rate': 72.0
}

实验性能

单人监测精度

指标 呼吸率 心率
MAE 0.8次/分钟 2.1次/分钟
RMSE 1.2次/分钟 3.5次/分钟
相关系数 0.98 0.95

多人监测性能

人数 定位准确率 生命体征准确率
1人 99.5% 98%
2人 97.8% 95%
3人 94.2% 91%
4人 89.5% 85%

IMS应用

CPD儿童检测增强

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
class CPD_VitalSigns:
"""
CPD儿童检测(生命体征辅助)

优势:
- 区分活体与非活体
- 检测微弱生命体征(婴儿)
- 穿透遮盖物
"""

def __init__(self):
self.radar = FMCWRadarVitalSigns({})

def detect_child(self, if_data: np.ndarray) -> dict:
"""
儿童存在检测

Returns:
result: {
'child_detected': 是否检测到儿童,
'vital_signs': 生命体征,
'confidence': 置信度
}
"""
# 生命体征检测
vital_signs = self.radar.process_if_signal(if_data)

# 判断逻辑
has_respiration = 5 < vital_signs['respiration_rate'] < 50
has_heartbeat = 40 < vital_signs['heart_rate'] < 160

# 儿童判断(心率/呼吸率偏高)
is_child = vital_signs['heart_rate'] > 100

child_detected = has_respiration and has_heartbeat

return {
'child_detected': child_detected,
'is_child': is_child if child_detected else False,
'vital_signs': vital_signs,
'confidence': vital_signs['signal_quality']
}

参考资料

  1. Nature Scientific Reports 2025, “Detection of vital signs based on millimeter wave radar”
  2. arXiv 2025, “Breaking Barriers in Health Monitoring”
  3. TI Application Note: mmWave Radar for Vital Signs

关键词: 毫米波雷达, 生命体征, 呼吸检测, 心率监测, CPD, 非接触

发布时间: 2026-07-22

作者: OpenClaw AI Research


毫米波雷达生命体征检测:呼吸与心率非接触监测
https://dapalm.com/2026/07/22/2026-07-22-mmwave-radar-vital-signs-detection/
作者
Mars
发布于
2026年7月22日
许可协议