1. 从“看见”到“感知”:FMCW雷达如何捕捉心跳
你可能觉得心跳监测是智能手表或者医疗监护仪的专利,得贴着皮肤才能测。但你知道吗,现在有一种技术,可以让你隔着一米远,甚至隔着被子,就能精准地“听”到一个人的心跳。这听起来有点像科幻电影里的场景,但它背后的核心技术,其实就藏在很多我们身边的产品里,比如一些高端的智能家居传感器、睡眠监测仪,甚至是汽车里的驾驶员状态监测系统。这项技术,就是FMCW雷达。
FMCW,全称是调频连续波雷达。这个名字听起来有点唬人,但原理其实挺直观的。想象一下,你站在一个空旷的山谷里,对着远处的山崖大喊一声,然后听回声。通过回声回来的时间,你就能大概估算出山崖离你有多远。传统雷达就是这个原理。但FMCW雷达更“聪明”一点,它发出的不是一声固定的“啊——”,而是一段音调从低到高连续变化的“歌声”。当这段“歌声”碰到目标(比如人的胸口)反射回来时,由于目标在微微运动(心跳和呼吸),反射回来的“歌声”音调会和原来发出的“歌声”音调有一个微小的差异。这个差异,专业上叫“频差”,就包含了目标距离和运动速度的全部信息。
对于心跳监测来说,我们关心的就是这个由胸口起伏带来的、极其微小的运动。一次心跳引起的胸腔位移,可能只有零点几毫米,比一张纸还薄。FMCW雷达的厉害之处就在于,它能捕捉到这种毫米甚至亚毫米级别的微动。我刚开始接触这个领域时,也觉得不可思议:一个无线电波,怎么能“看”到这么细微的变化?但当你理解了它的信号处理流程,就会明白这其实是一系列精妙“算法魔术”的结果。整个过程,就是从原始的、充满噪声的无线电波中,一步步“提纯”出那个代表心跳的、有规律的微弱信号。接下来,我就带你深入这个“魔术”的后台,看看我们是怎么把心跳信号从一堆干扰里“揪”出来的。
2. 信号处理的起点:IQ基带信号解调
雷达天线接收到的信号,就像一盘录下了各种环境噪音、设备干扰和微弱心跳回声的“原始录音带”。我们的第一步,就是把这盘“录音带”转换成我们能处理的“数字音频文件”。这个转换过程,就是基带信号解调。FMCW雷达通常会输出两路信号,我们称之为I路和Q路。你可以把它们理解为一对双胞胎,它们携带的信息几乎一样,但彼此之间永远保持着90度的“相位差”,就像一个正弦波和一个余弦波的关系。
为什么需要两路?这其实是为了解决一个关键问题:盲点问题。如果只有一路信号,当目标的距离恰好使得回波信号的相位处于某个特定值时,解调出来的信号幅度可能会变得非常小,甚至为零,导致我们丢失目标信息。有了I和Q这两路正交的信号,就像有了两个观察角度,总能保证至少有一路信号是清晰的,从而避免了信息丢失。我在实际调试硬件时,就遇到过单路信号在某些距离上周期性衰减的问题,加上Q路后,信号就稳定多了。
拿到I、Q两路原始的基带信号后,它们还不能直接用,因为里面混杂着很多我们不想要的成分。最主要的是直流偏置和IQ失衡。直流偏置就像是录音里的底噪,是硬件电路本身产生的固定偏移。IQ失衡则像是双胞胎一个声音大、一个声音小,或者反应稍微有点不同步,这是由于两路模拟电路不可能做到完全一致导致的。如果不处理掉它们,后续的分析就会误差很大。
常用的解调方法主要有两种:反正切解调和复数解调。
反正切解调非常直观,它的核心公式是:相位 = arctan(Q / I)。通过计算I和Q比值的反正切,我们可以直接得到目标相位随时间的变化,而这个相位变化直接对应着胸腔的位移。这个方法的优点是直接、计算量相对小,能线性地还原出位移信号。但它对IQ失衡和噪声比较敏感,如果I、Q两路信号质量不高,解算出的相位会跳变,产生所谓的“相位缠绕”问题。
复数解调则更“数学”一些。它把I路信号当作复数的实部,Q路信号当作虚部,构造出一个复数信号:S(t) = I(t) + j * Q(t)(这里的j是虚数单位)。然后,我们计算这个复数信号的相位角,同样可以得到位移信息。复数解调在数学上更完备,能更好地处理信号,并且为后续的频谱分析提供了便利。在实际项目中,我通常更倾向于使用复数解调,因为它为滤波和频谱分析提供了更统一的框架,虽然计算上稍微复杂一点,但换来的是更好的鲁棒性。
提示:选择哪种解调方法,取决于你的具体应用和硬件条件。如果追求简单快速,且信号质量很好,反正切法够用。如果信号环境复杂,或者你需要做更精细的频谱分析,复数解调是更强大的工具。
3. 去芜存菁:滤波优化与生命体征分离
解调出来的相位信号(也就是胸腔位移信号),现在是一锅“大杂烩”。里面最强的信号通常是呼吸,因为呼吸的起伏幅度大(几毫米到一厘米),频率低(0.1-0.5 Hz)。而我们要找的心跳信号,则微弱得多(幅度不到1毫米),频率却高一些(0.8-3 Hz,通常是1-1.7 Hz)。除此之外,信号里还混杂着人体无意识的微动、环境物体的晃动、以及电路本身的各种噪声。我们的任务,就是设计一个高效的“筛子”,把心跳这颗“金砂”筛出来。
第一步,是滤除呼吸信号。这通常用一个高通滤波器来实现。比如,我们可以设置一个截止频率在0.7 Hz左右的高通滤波器,把频率更低的呼吸信号和一部分超低频漂移给滤掉。但这里有个坑:呼吸和心跳的频谱有时会靠得很近,特别是当呼吸急促时。如果高通滤波器的截止频率设得太高,可能会伤及心跳的低频部分;设得太低,又滤不干净呼吸残留。我常用的做法是采用一个过渡带比较陡峭的FIR(有限长单位冲激响应)滤波器。FIR滤波器没有反馈,是绝对稳定的,而且可以设计出严格的线性相位,这意味着它不会扭曲信号的时间关系,对于后续分析波形形态很重要。
第二步,是进一步净化心跳频带。滤掉呼吸后,信号里主要剩下心跳和噪声。这时我们需要一个带通滤波器,只让大概0.8 Hz到3 Hz这个频率范围(心跳可能出现的范围)的信号通过,把更高频的噪声和可能残留的极低频干扰进一步滤除。这个带通滤波器的设计也很有讲究。窗函数的选择(比如汉宁窗、汉明窗)、滤波器的阶数,都会影响效果。阶数太高,计算量大,还可能引入不必要的纹波;阶数太低,滤波效果又不好。我一般会先用一个阶数适中的滤波器做初步处理,观察频谱,再微调参数。
第三步,也是最具挑战性的一步:处理谐波和互调干扰。呼吸和心跳都不是完美的单一频率正弦波,它们会有谐波。呼吸的二次谐波(比如0.3 Hz呼吸的二次谐波是0.6 Hz)可能会落到心跳的频带内,被误认为是心跳。更麻烦的是,由于系统的非线性,呼吸和心跳信号可能会产生互调,生成新的频率成分,比如“呼吸频率+心跳频率”或“呼吸频率-心跳频率”,这些成分也可能干扰对真实心跳频率的判断。为了解决这个问题,仅仅靠时域滤波就不够了,我们需要结合频域分析和自适应算法。
一种有效的策略是频谱追踪与剔除。我们先对信号做短时傅里叶变换,在时频谱上找到能量最强的呼吸频率成分(假设是f_b),然后主动在信号中生成一个与f_b及其主要谐波(如2*f_b,3*f_b)相对应的正弦波,再从原始信号中减去这些成分。这个过程有点像“主动降噪”。实测下来,这个方法对于抑制呼吸谐波干扰非常有效。
4. 从时频域中锁定心跳:特征提取与算法优化
经过层层滤波,我们得到了一条相对干净的、主要包含心跳信息的信号。但这还不够,我们需要从中精确地提取出心率值,并尽可能还原出心跳波形(如心冲击图),这对健康分析更有价值。
心率提取最经典的方法是快速傅里叶变换。把一段时间的信号(比如30秒)从时域转换到频域,看看在0.8-3 Hz这个范围内,哪个频率点的能量最高,那个频率就对应着心率(需要乘以60转换成每分钟心跳次数,BPM)。但FFT有个缺点:它假设信号在这段时间内是平稳的,心率不变。实际上,人的心率是时刻变化的。为了解决这个问题,我们会用短时傅里叶变换或者小波变换。STFT就像用一个滑动的时间窗,一段一段地做FFT,从而得到一个“时频谱”,能看到心率随时间的变化趋势。小波变换则能提供更好的时频分辨率,尤其适合分析像心跳这样瞬变的信号。
仅仅找到主峰频率有时会出错,比如遇到前面提到的谐波干扰,或者人突然动了一下产生一个干扰脉冲。因此,我们需要一些后处理算法来“纠错”。常用的方法包括:
- 峰值检测:直接在时域信号上寻找每个心跳周期的峰值点,计算相邻峰值的间隔,再换算成瞬时心率。这个方法能捕捉心率的瞬时变化,但对信号质量要求高,容易受噪声影响产生误检或漏检。
- 频谱质心跟踪:不只看最高的峰,而是计算心跳频带内所有频率分量的加权平均频率,作为心率的估计。这种方法更稳健,抗单频干扰能力强。
- 卡尔曼滤波或粒子滤波:这是一种更高级的状态估计方法。我们把心率看作一个随时间变化的“状态”,建立状态方程和观测方程(即我们测到的信号)。滤波器会根据之前的估计和新的观测值,最优地估计出当前最可能的心率值。这种方法特别适合处理噪声大、心率变化快的情况,我在做运动状态下的心率监测项目时,就大量使用了卡尔曼滤波来平滑结果,效果比简单平均好很多。
为了让你更直观地理解这个过程,我写一段简化的Python处理流程的核心代码框架。这只是一个示意,真实的工程代码要复杂得多,会包含大量的参数调整和错误处理。
import numpy as np from scipy import signal, fft import matplotlib.pyplot as plt # 假设我们已经有了解调后的位移信号 `displacement_signal` 和采样率 `fs` # 1. 设计高通滤波器滤除呼吸(假设截止频率为0.7 Hz) b_high, a_high = signal.butter(4, 0.7/(fs/2), 'high') filtered_signal = signal.filtfilt(b_high, a_high, displacement_signal) # 使用filtfilt实现零相位滤波 # 2. 设计带通滤波器提取心跳频带(0.8 Hz - 3 Hz) b_band, a_band = signal.butter(4, [0.8/(fs/2), 3.0/(fs/2)], 'bandpass') heartbeat_signal = signal.filtfilt(b_band, a_band, filtered_signal) # 3. 计算频谱,寻找心率 N = len(heartbeat_signal) freqs = fft.rfftfreq(N, 1/fs) spectrum = np.abs(fft.rfft(heartbeat_signal)) # 将频率范围限制在心跳范围内 mask = (freqs >= 0.8) & (freqs <= 3.0) heartbeat_freqs = freqs[mask] heartbeat_spectrum = spectrum[mask] # 找到频谱峰值对应的频率 dominant_freq = heartbeat_freqs[np.argmax(heartbeat_spectrum)] heart_rate_bpm = dominant_freq * 60 # 转换为每分钟心跳次数 print(f"估计的心率为: {heart_rate_bpm:.1f} BPM") # 4. (进阶)简单的时域峰值检测 peaks, _ = signal.find_peaks(heartbeat_signal, distance=fs*0.4) # 假设心跳间隔至少0.4秒 if len(peaks) > 1: intervals = np.diff(peaks) / fs # 峰值间隔(秒) instant_hr = 60 / intervals # 瞬时心率 print(f"瞬时心率序列 (BPM): {instant_hr}")5. 实战挑战与性能提升技巧
理论很美好,但一上实际项目,坑就来了。我总结了几类最常见的挑战和我的应对“土方”。
第一类挑战:环境与运动干扰。这是非接触监测最大的敌人。除了人体自身的微动,风扇的转动、窗帘的飘动、甚至远处汽车的震动,都可能被雷达捕捉到。我的策略是多通道与自适应降噪。如果硬件条件允许,使用多天线雷达,通过比较不同天线接收信号的差异,可以一定程度上区分运动方向,抑制来自非监测方向的干扰。在算法上,可以引入参考通道,比如用另一个雷达或麦克风监测环境振动,然后从主信号中自适应地减去相关噪声。
第二类挑战:个体差异与姿势。不同体型的人(胖瘦)、不同的穿着(厚薄)、不同的睡姿(平躺、侧卧),对雷达波的反射特性影响巨大。侧卧时,胸腔的径向运动幅度会减小,信号变弱。解决这个问题需要算法具有鲁棒性。我们不能把滤波器的参数写死,而需要根据信号质量自适应调整。例如,可以先估计出当前信号的信噪比,如果信噪比低,就使用更强的平滑滤波;如果检测到姿势可能发生改变,可以短暂地提高更新率,重新锁定信号。
第三类挑战:硬件限制与校准。廉价的雷达芯片可能存在I/Q不平衡严重、相位噪声大、直流偏移不稳定等问题。这就需要在信号处理的最前端下功夫。上电后,先进行一段时间的空场景采集,估计并扣除直流偏移。对于I/Q不平衡,可以通过采集一个静止的强反射体(比如一个金属板)的回波,来校准两路的增益和相位差。这些校准步骤虽然增加了复杂度,但对于提升最终监测的准确性和稳定性是至关重要的。我踩过的坑就是,忽略了对新批次硬件的校准,导致同一套算法在不同设备上效果差异很大。
为了更系统地对比不同优化方法的效果,我整理了一个简单的表格,总结了在不同场景下的策略选择:
| 面临的挑战 | 可能的原因 | 推荐的优化策略 | 预期效果 |
|---|---|---|---|
| 心率读数不稳定,跳动大 | 1. 环境噪声干扰 2. 呼吸谐波干扰 3. 算法更新率过高 | 1. 增强带通滤波,降低带宽 2. 采用呼吸谐波抑制算法 3. 引入心率平滑算法(如移动平均、卡尔曼滤波) | 心率曲线更平滑, outlier(异常值)减少 |
| 侧卧时信号丢失 | 胸腔径向运动幅度减小,信噪比降低 | 1. 切换至复数解调分析相位变化 2. 降低检测阈值,提高接收增益(如果硬件支持) 3. 尝试从信号中提取次优特征 | 提高侧卧等非理想姿势下的检测成功率 |
| 不同人测量偏差大 | 个体生理结构差异,穿着影响 | 1. 采用自适应的信号幅度归一化 2. 在算法中引入基于体型的简单分类器,微调参数 | 提升算法对不同人群的普适性 |
| 存在周期性虚假心率 | 强周期性环境干扰(如风扇) | 1. 进行环境频谱学习与 notch filter(陷波滤波) 2. 使用多周期估计算法,选择最符合生理规律的频率 | 有效抑制特定频率的环境干扰 |
最后,我想分享一个很重要的心得:没有一劳永逸的“最优参数”。我在实验室里对着静止目标调得非常好的参数,一到真实的卧室环境,可能就不好用了。因此,构建一个参数可配置、甚至可在线学习的信号处理流水线非常重要。把滤波器的截止频率、谱分析的窗长、峰值检测的阈值等都做成可调节的,通过大量的实测数据去迭代优化。有时候,针对特定场景(比如专用于睡眠监测)做精细调优,比追求一个泛化能力强但精度一般的通用算法,更能做出好产品。信号处理既是科学,也是艺术,需要不断地在理论指导和实验反馈之间寻找平衡点。