雷达心率估计的呼吸谐波干扰消除:从毫米波到座舱生命体征

本文深度解读 MDPI Sensors 2026 论文《Radar-Based Heart Rate Estimation Method Under Respiratory Harmonic Interference》,该论文解决了非接触雷达心率估计的核心挑战——呼吸谐波对心脏信号的掩盖问题,并探讨其在 IMS 座舱生命体征监测中的应用。

1. 论文信息

要素 内容
标题 Radar-Based Heart Rate Estimation Method Under Respiratory Harmonic Interference
作者 Xu Didi, Ying Li, Zinan Wu, Tingting Xie, Pengwei Gong
期刊 Sensors, 26(17), 5587
发表 2026 年 9 月
DOI 10.3390/s26175587
核心问题 心脏引起的胸壁位移比呼吸小一个数量级,心率基频常被呼吸谐波掩盖

2. 核心挑战

2.1 为什么雷达测心率难?

1
2
3
4
5
6
7
8
9
胸壁位移来源:
呼吸运动: ~4-12 mm (主要)
心脏搏动: ~0.2-0.5 mm (微弱,~10x更小)

频率关系:
呼吸频率: 0.2-0.5 Hz (12-30 次/分)
心率基频: 1.0-2.0 Hz (60-120 次/分)
呼吸2次谐波: 0.4-1.0 Hz ← 与心率基频重叠!
呼吸3次谐波: 0.6-1.5 Hz ← 严重重叠!

2.2 信号混叠示意

3. 方法概述

3.1 论文核心方法

论文提出了一种在呼吸谐波干扰下提取心率信号的方法,核心步骤:

  1. 雷达回波 → 提取胸壁位移波形
  2. 识别呼吸基频及其谐波
  3. 自适应滤波消除呼吸谐波
  4. 从残余信号中提取心率

3.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
import numpy as np
from scipy.signal import butter, filtfilt, find_peaks

class RadarHeartRateEstimator:
"""
雷达心率估计器(基于论文方法概念实现)

解决呼吸谐波对心率信号的干扰
"""

def __init__(self, fs: float = 20.0):
"""
Args:
fs: 采样率 (Hz),雷达帧率
"""
self.fs = fs
# 呼吸频率范围
self.resp_range = (0.15, 0.5) # 9-30 次/分
# 心率频率范围
self.hr_range = (1.0, 2.0) # 60-120 次/分

def estimate_heart_rate(self,
chest_displacement: np.ndarray) -> dict:
"""
从胸壁位移信号估计心率

Args:
chest_displacement: 雷达提取的胸壁位移, shape=(N,)

Returns:
{
'heart_rate_bpm': float,
'respiratory_rate_bpm': float,
'confidence': float,
}
"""
# 1. FFT 频谱分析
freqs, spectrum = self._compute_spectrum(chest_displacement)

# 2. 识别呼吸基频
resp_freq = self._find_respiratory_freq(freqs, spectrum)

# 3. 识别呼吸谐波
harmonics = self._find_harmonics(freqs, spectrum, resp_freq)

# 4. 自适应消除呼吸谐波
clean_spectrum = self._remove_harmonics(
spectrum, freqs, resp_freq, harmonics
)

# 5. 从残余信号提取心率
hr_freq = self._find_heart_rate(freqs, clean_spectrum)

return {
'heart_rate_bpm': hr_freq * 60,
'respiratory_rate_bpm': resp_freq * 60,
'confidence': self._compute_confidence(
clean_spectrum, freqs, hr_freq
),
}

def _compute_spectrum(self, signal):
"""计算频谱"""
n = len(signal)
freqs = np.fft.rfftfreq(n, 1/self.fs)
spectrum = np.abs(np.fft.rfft(signal))
return freqs, spectrum

def _find_respiratory_freq(self, freqs, spectrum):
"""识别呼吸基频"""
mask = (freqs >= self.resp_range[0]) & (freqs <= self.resp_range[1])
resp_band = spectrum.copy()
resp_band[~mask] = 0
peak_idx = np.argmax(resp_band)
return freqs[peak_idx]

def _find_harmonics(self, freqs, spectrum, fundamental):
"""识别呼吸谐波"""
harmonics = []
for n in range(2, 6): # 2-5次谐波
harmonic_freq = fundamental * n
if harmonic_freq > self.fs / 2:
break
# 在谐波频率附近找峰值
mask = (freqs >= harmonic_freq * 0.9) & \
(freqs <= harmonic_freq * 1.1)
if np.any(mask):
peak_idx = np.argmax(spectrum[mask])
harmonics.append({
'order': n,
'freq': freqs[mask][peak_idx],
'amplitude': spectrum[mask][peak_idx],
})
return harmonics

def _remove_harmonics(self, spectrum, freqs,
fundamental, harmonics):
"""消除呼吸谐波"""
clean_spectrum = spectrum.copy()

# 消除基频
for freq_target in [fundamental] + [h['freq'] for h in harmonics]:
mask = (freqs >= freq_target * 0.95) & \
(freqs <= freq_target * 1.05)
clean_spectrum[mask] *= 0.1 # 衰减到10%

return clean_spectrum

def _find_heart_rate(self, freqs, clean_spectrum):
"""从清理后的频谱提取心率"""
mask = (freqs >= self.hr_range[0]) & (freqs <= self.hr_range[1])
hr_band = clean_spectrum.copy()
hr_band[~mask] = 0
peak_idx = np.argmax(hr_band)
return freqs[peak_idx]

def _compute_confidence(self, spectrum, freqs, hr_freq):
"""计算置信度"""
mask = (freqs >= hr_freq * 0.95) & (freqs <= hr_freq * 1.05)
hr_power = np.max(spectrum[mask])
total_power = np.sum(spectrum[(freqs >= self.hr_range[0]) &
(freqs <= self.hr_range[1])])
return min(1.0, hr_power / (total_power + 1e-6))


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

estimator = RadarHeartRateEstimator(fs=20.0)

# 模拟雷达信号:呼吸 + 心跳 + 噪声
duration = 30 # 30秒
t = np.arange(duration * 20) / 20.0

# 呼吸:0.3 Hz (18次/分)
resp_signal = 4.0 * np.sin(2 * np.pi * 0.3 * t)
# 呼吸2次谐波
resp_h2 = 1.0 * np.sin(2 * np.pi * 0.6 * t)
# 呼吸3次谐波
resp_h3 = 0.5 * np.sin(2 * np.pi * 0.9 * t)
# 心跳:1.2 Hz (72次/分)
heart_signal = 0.3 * np.sin(2 * np.pi * 1.2 * t)
# 噪声
noise = np.random.randn(len(t)) * 0.1

chest_displacement = resp_signal + resp_h2 + resp_h3 + heart_signal + noise

result = estimator.estimate_heart_rate(chest_displacement)

print("=== 雷达心率估计 ===")
print(f"实际心率: 72.0 次/分")
print(f"估计心率: {result['heart_rate_bpm']:.1f} 次/分")
print(f"实际呼吸: 18.0 次/分")
print(f"估计呼吸: {result['respiratory_rate_bpm']:.1f} 次/分")
print(f"置信度: {result['confidence']:.2f}")

error = abs(result['heart_rate_bpm'] - 72.0)
print(f"\n心率误差: {error:.1f} 次/分")
print(f"准确率: {(1 - error/72)*100:.1f}%")

4. 对 IMS 座舱生命体征监测的启示

4.1 应用场景

场景 传感器 检测目标 挑战
驾驶员健康监测 座舱雷达 心率+呼吸 运动伪影
CPD 儿童检测 UWB 雷达 呼吸频率 呼吸谐波
乘员安全验证 座舱雷达 生命体征 多人分离
疲劳评估 DMS+雷达 心率变异性 心跳微弱

4.2 UWB vs mmWave 对比

参数 UWB (6-8GHz) mmWave (60GHz)
穿透性 ✅ 穿透毛毯/衣物 ⚠️ 衣物衰减
分辨率 ~1.5cm ~0.5cm
心率检测 ⚠️ 挑战大 ✅ 较好
呼吸检测 ✅ 良好 ✅ 良好
成本 低 中
部署 简单 中等

4.3 技术路线建议

优先级 建议 依据
🔴 高 先实现呼吸检测(UWB) 信号强,可靠
🟡 中 再增加心率检测 需消除呼吸谐波
🟡 中 多位置雷达融合 提高信噪比
🟢 低 心率变异性分析 需高精度心率

5. 总结

该论文解决了非接触雷达心率估计的核心障碍——呼吸谐波干扰。对 IMS 的启示:

  1. 呼吸检测优先:信号强、可靠,先做呼吸
  2. 心率需谐波消除:不能直接滤波,需自适应方法
  3. UWB 适合 CPD:穿透性好,信号虽弱但够检测呼吸
  4. 多传感器融合:UWB+mmWave+摄像头互补

参考来源:

  • Xu et al., “Radar-Based Heart Rate Estimation Method Under Respiratory Harmonic Interference”, Sensors, 26(17), 5587 (2026)