毫米波雷达心率估计:呼吸谐波干扰抑制方法深度解析

毫米波雷达心率估计:呼吸谐波干扰抑制方法深度解析

MDPI Sensors 2026 发表雷达心率估计新方法。核心挑战:心搏引起的胸壁位移仅为呼吸的1/10,且心搏频率常被呼吸高次谐波掩盖。本文深度解析谐波干扰抑制算法、性能对比及对 IMS 座舱雷达的启示。

1 研究背景

1.1 问题定义

非接触毫米波雷达生命体征监测中,心率估计是最难的子问题:

挑战 量化 说明
信号幅度 心搏0.5mm,呼吸5mm 心搏比呼吸弱10倍
频率重叠 心率60-100bpm(1-1.67Hz),呼吸谐波2-3次(0.4-1.5Hz) 频谱混叠
谐波干扰 呼吸2次谐波常落在心率频段 最大干扰源
个体差异 心率随情绪/活动变化 非平稳信号
多人场景 多目标反射叠加 空间分离难题

1.2 论文信息

项目 内容
期刊 MDPI Sensors 2026, 26(17):5587
核心方法 呼吸谐波干扰抑制
应用场景 医疗监护/养老/座舱

2 呼吸谐波问题

2.1 频谱分析

1
2
3
4
5
6
7
8
频率 (Hz)
0.2 ─── 呼吸基频 (12bpm)
0.4 ─── 呼吸2次谐波 ← 与心率重叠!
0.6 ─── 呼吸3次谐波
0.8 ─── 呼吸4次谐波
1.0 ─── 心率基频 (60bpm)
1.2 ─── 心率2次谐波
1.4 ─── 心率3次谐波

核心问题: 呼吸频率 0.2Hz 时,2次谐波 0.4Hz 可能与心率(1.0Hz)不直接重叠。但呼吸频率 0.4-0.5Hz 时,2次谐波 0.8-1.0Hz 直接落在心率频段。

2.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
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
import numpy as np
from scipy.signal import butter, filtfilt, fft
from typing import Tuple

class RadarHeartRateEstimator:
"""
毫米波雷达心率估计器

核心算法: 呼吸谐波干扰抑制

参考: MDPI Sensors 2026, 26(17):5587
"""

def __init__(self, sample_rate: float = 100.0):
self.sample_rate = sample_rate

# 频率范围
self.breath_freq_range = (0.15, 0.5) # 9-30 bpm
self.heart_freq_range = (0.8, 2.0) # 48-120 bpm

def estimate_heart_rate(self, radar_signal: np.ndarray) -> dict:
"""
从雷达回波信号估计心率

Args:
radar_signal: 雷达相位信号, shape=(N,)

Returns:
心率估计结果
"""
# 1. 预处理:去除DC和线性趋势
signal = self._detrend(radar_signal)

# 2. 估计呼吸频率
breath_freq = self._estimate_breath_freq(signal)

# 3. 计算呼吸谐波位置
harmonics = self._compute_harmonics(breath_freq, n_harmonics=5)

# 4. 自适应陷波滤波去除谐波
cleaned = signal
for h_freq in harmonics:
if self.heart_freq_range[0] <= h_freq <= self.heart_freq_range[1]:
cleaned = self._notch_filter(cleaned, h_freq, Q=30)

# 5. 带通滤波提取心率频段
heart_band = self._bandpass_filter(
cleaned, self.heart_freq_range[0], self.heart_freq_range[1]
)

# 6. FFT 找峰值频率
freqs, spectrum = self._compute_spectrum(heart_band)
heart_freq = freqs[np.argmax(spectrum)]
heart_rate_bpm = heart_freq * 60

# 7. 置信度评估
snr = self._compute_snr(spectrum, heart_freq)
confidence = self._estimate_confidence(snr, heart_freq, breath_freq)

return {
'heart_rate_bpm': heart_rate_bpm,
'heart_freq_hz': heart_freq,
'breath_freq_hz': breath_freq,
'breath_rate_bpm': breath_freq * 60,
'harmonics_removed': len([h for h in harmonics
if self.heart_freq_range[0] <= h <= self.heart_freq_range[1]]),
'snr_db': snr,
'confidence': confidence,
}

def _detrend(self, signal: np.ndarray) -> np.ndarray:
"""去趋势"""
n = len(signal)
t = np.arange(n)
coeffs = np.polyfit(t, signal, 1)
return signal - np.polyval(coeffs, t)

def _estimate_breath_freq(self, signal: np.ndarray) -> float:
"""估计呼吸基频"""
# 带通滤波到呼吸频段
filtered = self._bandpass_filter(signal, 0.1, 0.6)

# FFT
freqs, spectrum = self._compute_spectrum(filtered)

# 找峰值
breath_mask = (freqs >= self.breath_freq_range[0]) & \
(freqs <= self.breath_freq_range[1])
breath_freqs = freqs[breath_mask]
breath_spec = spectrum[breath_mask]

return breath_freqs[np.argmax(breath_spec)]

def _compute_harmonics(self, base_freq: float, n_harmonics: int) -> list:
"""计算谐波频率"""
return [base_freq * (i + 1) for i in range(n_harmonics)]

def _notch_filter(self, signal: np.ndarray, freq: float, Q: int = 30) -> np.ndarray:
"""陷波滤波器"""
nyq = self.sample_rate / 2
w0 = freq / nyq
bandwidth = w0 / Q
low = max(w0 - bandwidth / 2, 0.001)
high = min(w0 + bandwidth / 2, 0.999)
b, a = butter(4, [low, high], btype='bandstop')
return filtfilt(b, a, signal)

def _bandpass_filter(self, signal: np.ndarray, low: float, high: float) -> np.ndarray:
"""带通滤波"""
nyq = self.sample_rate / 2
b, a = butter(4, [low / nyq, high / nyq], btype='band')
return filtfilt(b, a, signal)

def _compute_spectrum(self, signal: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""计算功率谱"""
n = len(signal)
freqs = np.fft.rfftfreq(n, 1.0 / self.sample_rate)
spectrum = np.abs(np.fft.rfft(signal)) ** 2
return freqs, spectrum

def _compute_snr(self, spectrum: np.ndarray, peak_freq: float) -> float:
"""计算峰值信噪比"""
peak_idx = np.argmax(spectrum)
peak_power = spectrum[peak_idx]

# 排除峰值周围的噪声功率
exclude_range = 5
noise_mask = np.ones(len(spectrum), dtype=bool)
noise_mask[max(0, peak_idx - exclude_range):peak_idx + exclude_range + 1] = False
noise_power = np.mean(spectrum[noise_mask]) if np.any(noise_mask) else 1e-10

if noise_power == 0:
return 60.0 # 极高SNR

return 10 * np.log10(peak_power / noise_power)

def _estimate_confidence(self, snr: float, heart_freq: float,
breath_freq: float) -> float:
"""置信度评估"""
# SNR 贡献
snr_score = min(snr / 15.0, 1.0) # 15dB 为高置信

# 频率合理性
freq_score = 1.0 if 0.8 <= heart_freq <= 2.0 else 0.5

# 与呼吸谐波的接近度
harmonic_proximity = min(
abs(heart_freq - breath_freq * i) for i in range(2, 6)
) if breath_freq > 0 else 1.0
harmonic_score = min(harmonic_proximity / 0.1, 1.0) # >0.1Hz 远离为好

return 0.4 * snr_score + 0.3 * freq_score + 0.3 * harmonic_score


# 测试
if __name__ == "__main__":
np.random.seed(42)

estimator = RadarHeartRateEstimator(sample_rate=100.0)

# 模拟雷达信号
t = np.arange(10000) / 100.0 # 100秒

# 呼吸信号 (0.3 Hz = 18bpm)
breath = 5.0 * np.sin(2 * np.pi * 0.3 * t)

# 心搏信号 (1.2 Hz = 72bpm)
heartbeat = 0.5 * np.sin(2 * np.pi * 1.2 * t)

# 呼吸谐波 (干扰)
breath_h2 = 2.0 * np.sin(2 * np.pi * 0.6 * t) # 2次
breath_h3 = 1.0 * np.sin(2 * np.pi * 0.9 * t) # 3次 ← 接近心率!
breath_h4 = 0.5 * np.sin(2 * np.pi * 1.2 * t) # 4次 ← 直接重叠!

# 合成信号
signal = breath + heartbeat + breath_h2 + breath_h3 + breath_h4 + \
0.1 * np.random.randn(len(t))

result = estimator.estimate_heart_rate(signal)

print("=== 雷达心率估计 ===")
print(f"真实心率: 72 bpm")
print(f"估计心率: {result['heart_rate_bpm']:.1f} bpm")
print(f"呼吸频率: {result['breath_rate_bpm']:.1f} bpm")
print(f"去除谐波数: {result['harmonics_removed']}")
print(f"SNR: {result['snr_db']:.1f} dB")
print(f"置信度: {result['confidence']:.2%}")

3 对 IMS 座舱雷达的启示

3.1 应用场景

场景 价值 难度
驾驶员心率监测 压力/疲劳评估
乘客生命体征 CPD 儿童检测 低(只需检测有无)
安全驾驶评估 HRV→认知负荷
健康监测 长期趋势

3.2 雷达选型对比

参数 UWB 雷达 60GHz 毫米波 24GHz 毫米波
心率精度 ⚠️ 低 ✅ 高 ⚠️ 中
呼吸精度 ✅ 高 ✅ 高 ✅ 高
穿透力 ✅ 强 ⚠️ 中 ✅ 强
成本 $3-5 $8-15 $5-10
推荐用途 CPD HRV/心率 呼吸/CPD

3.3 部署建议

优先级 任务 价值
P0 呼吸检测(CPD基础) Euro NCAP 必需
P1 心率估计(谐波抑制) 压力/疲劳评估
P2 HRV 分析 认知负荷代理
P3 多人心率分离 全舱监测

4 总结

雷达心率估计的核心挑战是呼吸谐波干扰。本文解析的谐波抑制方法通过自适应陷波滤波+带通提取+SNR置信度评估的三步策略,可在呼吸4次谐波直接重叠场景下仍实现可靠心率估计。

对 IMS 座舱,建议先用 UWB 雷达实现呼吸级 CPD(满足 Euro NCAP),再升级到 60GHz 毫米波实现心率/HRV 级驾驶员状态监测。


参考来源:


毫米波雷达心率估计:呼吸谐波干扰抑制方法深度解析
https://dapalm.com/2026/09/09/2026-09-09-radar-heart-rate-estimation-respiratory-harmonic-suppression-mmwave-ims/
作者
Mars
发布于
2026年9月9日
许可协议