返回系列

滤波背景:采样率、BPM、频域、噪声和基线漂移

先不看代码,先理解呼吸信号为什么适合带通滤波,采样率和 BPM 如何映射到频率,以及实时滤波为什么天然会有延迟。

背景问题

RespiraScope 里的滤波不是为了让曲线“看起来更顺”。它的目标是把原始传感器信号变成一个更适合识别呼吸周期的波形。原始数据里通常混有三类东西:真正的呼吸起伏、低频基线漂移、高频噪声或体动尖峰。

如果直接在原始数据上找波峰波谷,噪声会制造伪峰,基线漂移会让阈值失效,咳嗽或体动会让 BPM 瞬间乱跳。所以在峰谷识别之前,需要先把“呼吸可能出现的频段”保留下来,把明显不属于呼吸的部分压掉。

背景概念

采样率决定每秒有多少个点。项目默认 sampling_rate = 50,也就是每秒 50 个采样点。BPM 是每分钟呼吸次数。频率 Hz 是每秒周期数,所以换算关系是:

Hz = BPM / 60
6 BPM  = 0.10 Hz
40 BPM = 0.67 Hz

这就是项目默认把 low_bpm = 6、high_bpm = 40 转成 0.10 Hz 到 0.67 Hz 的原因。对工程验证和普通呼吸监测演示来说,这个频段能覆盖慢呼吸、正常呼吸、浅快呼吸和多数异常演示场景。

0 Hz 0.10 Hz 0.67 Hz 高频 保留呼吸频段 6 到 40 BPM 基线漂移 过低频 随机噪声 高频抖动 尖峰干扰 突变成分

带通滤波的意思是只保留一个频率区间。低于区间的慢变化被削弱,高于区间的快速抖动也被削弱。呼吸信号天然适合这种方法,因为呼吸周期通常有明确的生理频率范围。

设计方案对比

方案能解决什么主要问题项目取舍
不滤波,直接找峰实现最简单噪声、漂移、体动都会制造伪峰只适合干净仿真数据
移动平均压掉高频噪声,计算便宜对基线漂移帮助有限,窗口选择影响大前端回看可做轻量视觉平滑
低通滤波压高频噪声保留了基线漂移,峰谷阈值仍会受影响不够完整
带通滤波同时压低频漂移和高频噪声参数要和采样率、BPM 匹配当前核心方案
小波或更复杂模型能处理非平稳信号参数、解释和维护成本高当前项目不需要

RespiraScope 不是做复杂生理建模,而是做工程验证和可解释实时监测。所以项目选择 Butterworth 带通滤波加 Gaussian 平滑。这个组合足够成熟、容易解释,也能用 SciPy 稳定实现。

当前方案

项目的核心滤波参数在 FilterConfig:

参数默认值作用
sampling_rate50每秒采样点数,影响频率换算和峰间距
low_bpm6呼吸频率下界,会转成 0.10 Hz
high_bpm40呼吸频率上界,会转成 0.67 Hz
order1Butterworth 滤波器阶数
gaussian_sigma1高斯平滑强度
restore_baselinetrue滤波后把曲线恢复到原始中位数附近

normalize_filter_config() 会把 BPM 转成 Hz:

config.lowpass_cutoff = config.low_bpm / 60
config.highpass_cutoff = config.high_bpm / 60

字段名 lowpass_cutoff 和 highpass_cutoff 沿用了旧接口,实际进入 butterworth_filter() 时被当作带通区间的低频和高频边界。

为什么实时滤波会延迟

实时滤波只能用已经到来的数据。它不能知道未来波形会怎么走,所以对峰谷位置的判断天然更保守。sosfilt 是因果滤波,适合流式处理,但会产生相位延迟:曲线形状被平滑后,峰值可能看起来稍晚。

离线滤波不同。Record End 后,系统已经拿到了完整片段,sosfiltfilt 可以先正向滤波再反向滤波,抵消相位延迟。这就是项目为什么区分实时和离线:实时页面优先低延迟,记录复盘优先质量。

真实峰 实时峰稍晚 原始趋势 实时因果滤波 离线双向滤波

代价和边界

带通滤波依赖参数。low_bpm 太高会削弱慢呼吸,high_bpm 太低会削弱快呼吸,gaussian_sigma 太大会抹平真实峰谷。RespiraScope 的默认值适合常见演示和工程验证,但不是所有患者、所有传感器、所有体位的通用参数。

滤波也不能“修复”坏数据。传感器断流、线缆松动、体动强干扰和饱和信号,需要状态提示和质量事件,而不是靠滤波硬压。项目里 SignalQualityAnalyzer 正是为这些情况提供额外信号。

关键函数表

函数或类来源在滤波背景中的意义
np.median()NumPy用原始中位数恢复基线,使滤波曲线回到可读值域
np.round()NumPy输出保留 1 位小数,减少前端数据抖动
signal.butter()SciPy设计 Butterworth 带通滤波器
signal.sosfilt()SciPy实时因果滤波,低延迟但有相位延迟
signal.sosfiltfilt()SciPy离线双向滤波,抵消相位延迟
gaussian_filter1d()SciPy对滤波后波形做一维高斯平滑

读源码建议

先读 http/schemas.py 的 FilterConfig,理解每个参数的范围和默认值。再读 normalize_filter_config(),确认 BPM 到 Hz 的转换。最后读 filter_strategies.py,重点看 butterworth_filter() 和 _filter_common(),你会看到项目里的滤波链路其实就是“带通滤波、恢复基线、高斯平滑、四舍五入”四步。