自适应截止频率在线滤波器:常数内存抑制慢变基线游走
1. 为什么慢变基线游走是个让人头疼的问题做过生理信号采集、传感器数据处理或者振动监测的人大概率都遇到过这样一种情况明明传感器没动、被测对象也处于静止状态可采集到的信号基线却在缓慢地上下漂移像一条懒洋洋的波浪线。这就是慢变基线游走英文里叫 baseline wander 或者 baseline drift。它和那种高频噪声不一样。高频噪声你一眼就能看出来毛刺嘛低通滤波一撸就干净了。但基线游走麻烦在它的频率极低通常落在 0.05 Hz 到 1 Hz 这个区间有时候甚至更低。你如果直接上一个截止频率 1 Hz 的高通滤波器确实能把基线压下去但信号里本来有用的低频成分也跟着一起被砍掉了。比如心电信号里的 ST 段信息比如地震波里的长周期分量比如应变信号里的缓慢趋势——这些都可能被误伤。更让人难受的是基线游走往往不是一成不变的。它可能因为电极接触阻抗的缓慢变化、温度漂移、呼吸运动、传感器老化等原因呈现出速率也在变化的特征。今天漂得快一点明天漂得慢一点甚至同一段采集里前半段漂得猛、后半段又稳下来了。你用固定截止频率的滤波器去对付它要么滤不干净要么把好信号也滤没了。所以核心诉求就变成了能不能做一个在线滤波器让它自己根据基线游走的速率来自适应调整截止频率同时还要保证常数内存——也就是说不管你采集多久算法占用的内存和计算量都不增长。这个需求在嵌入式设备、可穿戴健康监测、长期结构健康监测这些场景里尤其强烈因为你不可能在单片机上跑一个越来越大的缓冲区。这篇文章就是围绕这个题目展开的。我会从整体设计思路讲起把自适应截止频率的逻辑、常数内存的实现方式、速率跟踪的具体做法都拆开揉碎说清楚然后给出可以直接参考的实操流程和参数选择方法最后把我自己踩过的坑和排查经验整理出来。适合做信号处理、嵌入式开发、生理信号采集的读者参考也适合任何需要在线处理缓慢漂移信号的场景。2. 整体方案设计与核心思路拆解2.1 为什么不能用传统高通滤波器一刀切先说说最直觉的方案直接上一个高通滤波器截止频率设低一点比如 0.5 Hz。这个方案在很多教科书里都出现过但它有几个硬伤。第一个硬伤是相位失真。高通滤波器在截止频率附近会引入明显的相位偏移而且这个偏移是频率相关的。对于心电信号这种对波形形态有要求的场景相位失真会直接改变 ST 段的形态影响后续的诊断分析。你可能会说那用零相位滤波不就行了零相位滤波需要正向反向各滤一次这要求你拿到完整数据再处理天然不适合在线场景。第二个硬伤是截止频率固定。前面说了基线游走的速率是变化的。你设 0.5 Hz遇到漂得慢的时候确实够用但遇到漂得快的时候比如运动伪迹导致的基线快速偏移0.5 Hz 就压不住了。反过来如果你设 1 Hz 来对付快速漂移那慢速漂移场景下就会过度滤波把有用的低频信息砍掉。第三个硬伤是阶数与延迟的权衡。截止频率越低要达到同样的阻带衰减滤波器阶数就越高群延迟也越大。在线场景下群延迟直接表现为输出滞后这在实时监测里是不可接受的。所以传统固定截止频率的高通滤波器在这个问题上属于“能凑合用但不够好”的方案。我们需要的是一个能感知基线变化速率、并据此动态调整自身参数的滤波器。2.2 自适应截止频率的核心逻辑自适应截止频率的思路其实不复杂让滤波器自己估计当前基线游走的速率然后根据这个速率来设定截止频率。速率快的时候截止频率调高一点把快速漂移压下去速率慢的时候截止频率调低一点尽量保留有用的低频成分。那怎么估计基线游走的速率呢这里有几个常见做法。一种做法是对信号做低通滤波得到一个“基线估计”然后看这个基线估计的变化率。具体来说你先用一个截止频率很低的低通滤波器比如 0.05 Hz把信号里的基线成分提取出来然后计算这个基线估计的导数或者差分这个导数的绝对值就反映了当前基线漂移的快慢。速率大的时候说明基线在快速移动需要提高高通滤波器的截止频率。另一种做法是基于信号自身的统计特性。比如计算一个滑动窗口内信号的标准差或者峰峰值如果这个值在缓慢增大可能意味着基线在漂移。但这种方法容易受到信号本身幅度变化的影响不如直接估计基线变化率来得直接。我个人的经验是第一种做法更稳因为它直接针对基线成分本身不受信号有用成分的干扰。具体实现上可以用一个一阶低通滤波器来提取基线然后对基线估计做差分得到速率再用这个速率去映射截止频率。映射关系可以设计成线性的也可以设计成非线性的。线性映射简单但可能在速率很小的时候截止频率还是偏高。非线性映射比如用指数或者分段函数可以让截止频率在低速时更接近下限在高速时快速上升。实际用下来分段线性映射在嵌入式上最省算力效果也够用。2.3 常数内存是怎么做到的常数内存这个要求说白了就是算法不能随着时间推移而积累数据。你不能说采集了 10 分钟就存 10 分钟的数据采集了 1 小时就存 1 小时的数据。所有状态都必须用固定数量的变量来表示。这对滤波器设计提出了明确约束只能用 IIR 滤波器不能用 FIR 滤波器。FIR 滤波器要获得陡峭的过渡带需要很多抽头而且抽头数一旦固定滤波特性也就固定了。IIR 滤波器用很少的系数就能实现类似的频率响应而且状态变量数量固定天然满足常数内存。具体来说一阶 IIR 高通滤波器只需要一个历史输入和一个历史输出加上两个系数总共四个变量。二阶 IIR 也只需要四个历史值和五个系数。这些都不随时间增长。基线估计用的低通滤波器同样用一阶 IIR状态变量也是固定的。速率计算只需要当前基线估计和上一个基线估计两个变量。截止频率映射只需要当前速率和几个阈值参数。整个算法下来状态变量总数不超过十个内存占用是常数。计算量方面每个采样点需要做几次乘加运算。一阶 IIR 滤波是两次乘加基线估计是两次乘加速率计算是一次减法加一次取绝对值截止频率映射是几次比较和赋值。总共加起来每个采样点大概十几次运算。在 100 Hz 采样率下每秒也就一千多次运算随便一颗 Cortex-M0 都能轻松跑下来。2.4 速率跟踪的具体实现路径速率跟踪是整个算法的眼睛它决定了截止频率调整的方向和幅度。我试过几种不同的速率估计方法这里说说各自的优劣。方法一基线差分法。这是最直接的方法。你先用低通滤波器提取基线然后计算当前基线估计与上一个基线估计的差值取绝对值作为速率指标。这个方法的优点是计算量极小响应快。缺点是容易受到噪声干扰如果基线估计本身不够平滑差分出来的速率会抖动。方法二基线斜率法。不是用相邻两点的差分而是用一段固定时间内的基线变化量除以时间。比如每 100 个采样点计算一次基线变化量。这个方法更稳但响应会慢一些而且需要维护一个计数器或者环形缓冲区。不过环形缓冲区的大小是固定的所以仍然满足常数内存。方法三双时间尺度法。同时维护两个不同时间常数的基线估计一个快一个慢用两者的差值来反映基线变化的剧烈程度。这个方法在运动伪迹检测里用得比较多效果不错但参数调节稍微麻烦一些。我最终选择的是方法一和方法二的结合用基线差分法得到瞬时速率再用一个一阶低通滤波器对这个速率做平滑得到平滑后的速率指标。这样既保留了快速响应能力又抑制了噪声引起的抖动。平滑滤波器的截止频率设得很低比如 0.1 Hz这样速率指标的变化是缓慢的不会导致截止频率频繁跳变。3. 核心细节解析与实操要点3.1 一阶 IIR 高通滤波器的系数计算在线滤波器的核心是一个一阶 IIR 高通滤波器。它的差分方程可以写成这样y[n] alpha * (y[n-1] x[n] - x[n-1])其中 x 是输入y 是输出alpha 是滤波系数。alpha 和截止频率的关系是alpha 1 / (1 2 * pi * fc / fs)这里 fc 是截止频率fs 是采样率。你可以看到alpha 越接近 1截止频率越低alpha 越小截止频率越高。这个公式的推导其实很简单。一阶高通滤波器的模拟原型是 s / (s wc)用双线性变换或者后向差分离散化之后就能得到上面的差分方程形式。我这里用的是后向差分因为它更简单而且在低频段精度够用。实操的时候有个细节要注意alpha 不能太接近 1。因为浮点数精度有限如果 alpha 是 0.9999那 1 - alpha 就是 0.0001在单精度浮点下还能表示但在定点数下可能就直接变成零了。所以截止频率不能设得太低。一般来说fc / fs 的比值不要小于 0.0001。比如采样率 100 Hz截止频率最低设到 0.01 Hz 就差不多了。再低的话要么用双精度要么用定点数的高精度表示。另外alpha 的变化会导致滤波器状态的不连续。当你根据速率调整截止频率时alpha 会突变这时候滤波器的输出可能会有跳变。解决办法是对 alpha 也做平滑或者对输出做限幅。我一般是对 alpha 做一阶低通平滑让它缓慢变化这样输出就不会有突变。3.2 基线估计低通滤波器的设计基线估计用的低通滤波器也是一阶 IIR差分方程是b[n] beta * b[n-1] (1 - beta) * x[n]其中 b 是基线估计beta 是滤波系数。beta 和截止频率的关系是beta 1 / (1 2 * pi * fc_base / fs)fc_base 是基线估计的截止频率一般设得比高通滤波器的截止频率还要低。比如高通截止频率最低是 0.05 Hz那基线估计截止频率可以设 0.02 Hz。这样基线估计只包含最缓慢的成分不会把有用的低频信号也当成基线。这里有个经验基线估计的截止频率应该比高通滤波器的最低截止频率低一个数量级左右。这样两者不会互相干扰。如果设得太接近基线估计里会混入有用信号导致速率估计偏高进而把截止频率推高形成正反馈。还有一个细节基线估计的初始值。如果初始值设为零而信号的实际基线偏离零很多那滤波器需要很长时间才能跟踪上。解决办法是用前几个采样点的平均值作为初始值或者干脆让 beta 初始为 0也就是第一个采样点直接作为基线初始值。我一般用后者简单有效。3.3 速率计算与平滑处理速率计算用基线差分法rate_raw abs(b[n] - b[n-1])这个 rate_raw 是瞬时速率抖动比较大。接下来用一个一阶低通滤波器对它做平滑rate_smooth gamma * rate_smooth (1 - gamma) * rate_rawgamma 是平滑系数一般设得比较大比如 0.99对应截止频率大约 0.0016 Hz在 100 Hz 采样率下。这样 rate_smooth 的变化非常缓慢不会导致截止频率频繁调整。这里有个坑要注意rate_raw 的量纲问题。b[n] - b[n-1] 是相邻两个采样点的基线变化量它的量纲是“信号幅度/采样点”。如果你改变采样率同样的基线漂移速率会得到不同的 rate_raw 值。所以截止频率映射的参数需要根据采样率来调整。我一般会把 rate_raw 归一化除以采样率变成“信号幅度/秒”这样就和采样率无关了。归一化之后rate_smooth 的单位就是“幅度/秒”。然后你可以根据信号的典型幅度来设定阈值。比如心电信号幅度大概是 1 mV基线漂移速率典型值是 0.01 mV/s 到 0.1 mV/s。你可以把 0.01 mV/s 作为低速阈值0.1 mV/s 作为高速阈值截止频率在 0.05 Hz 到 0.5 Hz 之间线性映射。3.4 截止频率映射函数的选择映射函数决定了速率到截止频率的转换关系。我试过三种形式。线性映射fc fc_min (fc_max - fc_min) * (rate - rate_min) / (rate_max - rate_min)。简单直接但在 rate 接近 rate_min 的时候fc 还是比 fc_min 大不少因为线性函数的斜率是固定的。分段线性映射把速率分成几段每段用不同的斜率。比如低速段斜率小高速段斜率大。这样可以在低速时更接近 fc_min在高速时快速上升到 fc_max。实现上就是几个 if-else计算量很小。指数映射fc fc_min * exp(k * rate)。这个函数在 rate 很小时变化很慢在 rate 大时变化很快符合直觉。但 exp 运算在嵌入式上比较费一般用查表或者近似。我最终用的是分段线性映射三段就够了低速段、中速段、高速段。低速段斜率 0.5中速段斜率 2高速段斜率 5。这样在低速时截止频率缓慢上升中速时加快高速时快速压住漂移。实际用下来这个映射在生理信号和振动信号上都表现不错。4. 实操过程与核心环节实现4.1 算法初始化与参数设定先把整个算法的参数列出来方便你直接抄作业。假设采样率 fs 100 Hz信号幅度典型值 1 mV。参数含义推荐值fc_min高通截止频率下限0.05 Hzfc_max高通截止频率上限0.5 Hzfc_base基线估计截止频率0.02 Hzgamma速率平滑系数0.99rate_min低速阈值0.01 mV/srate_max高速阈值0.1 mV/salpha_smoothalpha 平滑系数0.95初始化的时候所有历史变量都设为零。基线估计的初始值用第一个采样点。alpha 的初始值用 fc_min 计算得到。这里有个细节alpha_smooth 的作用。当你计算出新的 alpha 之后不要直接用它而是用 alpha_smooth 做一次平滑alpha alpha_smooth * alpha_old (1 - alpha_smooth) * alpha_new这样 alpha 的变化是渐进的滤波器输出不会跳变。alpha_smooth 越大变化越慢。0.95 对应的时间常数大概是 20 个采样点在 100 Hz 下就是 0.2 秒够用了。4.2 逐采样点处理流程每个采样点进来之后按以下顺序处理第一步更新基线估计b beta * b_prev (1 - beta) * x第二步计算原始速率rate_raw abs(b - b_prev) * fs注意这里乘以 fs 是为了归一化到“幅度/秒”。第三步平滑速率rate_smooth gamma * rate_smooth_prev (1 - gamma) * rate_raw第四步根据 rate_smooth 计算目标截止频率 fc_target。用分段线性映射if rate_smooth rate_min: fc_target fc_min elif rate_smooth rate_mid: fc_target fc_min (rate_smooth - rate_min) * slope1 elif rate_smooth rate_max: fc_target fc_mid (rate_smooth - rate_mid) * slope2 else: fc_target fc_max其中 rate_mid 是低速段和中速段的分界点比如 0.03 mV/s。fc_mid 是对应的截止频率比如 0.1 Hz。slope1 和 slope2 根据端点计算得到。第五步计算目标 alphaalpha_target 1 / (1 2 * pi * fc_target / fs)第六步平滑 alphaalpha alpha_smooth * alpha_prev (1 - alpha_smooth) * alpha_target第七步应用高通滤波器y alpha * (y_prev x - x_prev)第八步更新所有历史变量b_prev b rate_smooth_prev rate_smooth alpha_prev alpha x_prev x y_prev y整个流程下来每个采样点大概二十次浮点运算。在 100 Hz 采样率下每秒两千次运算Cortex-M0 跑起来毫无压力。4.3 参数调节的实际经验参数调节这块我踩过不少坑这里把经验分享一下。fc_min 不能设得太低。前面说了受浮点精度限制fc_min 太低会导致 alpha 太接近 1数值不稳定。我试过设 0.01 Hz在单精度浮点下输出就开始抖了。0.05 Hz 是个比较安全的底线。fc_max 不能设得太高。fc_max 太高会把有用的低频信号也滤掉。比如心电信号QRS 波群的宽度大概是 80 到 100 ms对应的频率成分在 10 Hz 左右但 ST 段的频率成分可以低到 0.5 Hz。如果你把 fc_max 设到 1 HzST 段就变形了。0.5 Hz 是个比较稳妥的上限。gamma 的选择影响响应速度。gamma 越大速率估计越平滑但响应越慢。如果基线漂移速率突然变化gamma 太大可能导致截止频率跟不上。我一般用 0.99对应时间常数 100 个采样点在 100 Hz 下是 1 秒。这个响应速度对大多数慢变基线游走够用了。rate_min 和 rate_max 要根据信号幅度调整。如果你的信号幅度是 10 mV 而不是 1 mV那 rate_min 和 rate_max 也要相应放大 10 倍。否则速率估计会一直落在低速段截止频率永远上不去。4.4 在嵌入式平台上的定点数实现如果你的平台没有浮点运算单元那就得用定点数。定点数实现有几个要点。第一所有变量用 Q 格式表示。比如 Q15 表示小数点在第 15 位范围是 -1 到 1 减去一个最小精度。对于信号幅度在 1 mV 量级的场景你可以把信号放大到 Q15 范围。比如 1 mV 对应 1000那 Q15 的满量程就是 32.768 mV够用了。第二alpha 和 beta 也用 Q15 表示。但要注意alpha 可能非常接近 1比如 0.999在 Q15 下就是 32735离满量程 32767 很近了。计算 1 - alpha 的时候结果是 32精度损失比较大。解决办法是用 Q31 或者用两个 Q15 变量拼起来表示。或者干脆把 alpha 表示成 1 - delta 的形式delta 用 Q15 表示这样精度就够了。第三乘加运算要注意溢出。Q15 乘 Q15 得到 Q30需要右移 15 位变回 Q15。如果连续乘加中间结果要用 Q31 或者 Q63 暂存最后再移位。第四速率计算里的 abs 和比较在定点数下和浮点数一样直接做就行。我实测下来定点数实现和浮点数实现的输出差异在 1% 以内完全够用。而且定点数在 Cortex-M0 上跑速度比软件浮点快好几倍。5. 常见问题与排查技巧实录5.1 输出信号出现周期性波动这是最常见的问题。你可能会发现滤波后的信号上叠加了一个低频的周期性波动频率大概在 0.1 到 1 Hz 之间。原因通常是截止频率调整过于频繁导致滤波器系数不断变化引入了调制效应。解决办法是增大 alpha_smooth 和 gamma让截止频率的变化更缓慢。我一般把 alpha_smooth 设到 0.98 以上gamma 设到 0.995 以上。另一个可能的原因是基线估计的截止频率和高通滤波器的截止频率太接近两者互相干扰。把 fc_base 降到 fc_min 的十分之一以下通常能解决。5.2 基线漂移压不住如果你发现基线还是在漂只是漂得慢了一点那可能是 fc_max 设得太低或者 rate_max 设得太高。先检查 rate_smooth 的实际值。你可以在代码里加一个调试输出看看 rate_smooth 是不是一直落在低速段。如果是说明 rate_max 设得太高了把 rate_max 降到实际速率的两三倍。如果 rate_smooth 已经落在高速段但基线还是漂那就是 fc_max 不够高。适当提高 fc_max但要注意不要超过信号有用低频成分的上限。还有一个可能的原因是基线估计的跟踪速度太慢。如果 fc_base 设得太低基线估计跟不上实际的基线变化那速率估计就会偏低。适当提高 fc_base比如从 0.02 Hz 提到 0.05 Hz看看有没有改善。5.3 有用信号被过度滤除这是另一个极端。你发现基线是压住了但信号也变形了比如心电的 ST 段被压平了或者振动信号的低频包络丢失了。原因通常是 fc_min 设得太高或者速率映射的斜率太大导致截止频率在低速时就已经很高了。先检查 fc_min。如果 fc_min 已经设到 0.1 Hz 以上那对很多信号来说都太高了。试着降到 0.05 Hz 甚至 0.03 Hz。然后检查映射函数的低速段斜率。如果斜率太大即使 rate_smooth 很小fc_target 也会被推得比较高。把低速段斜率降到 0.5 以下让截止频率在低速时更接近 fc_min。还有一个隐藏的原因是alpha 平滑的初始值。如果 alpha 初始值设得太高对应截止频率太高而 alpha_smooth 又很大那 alpha 需要很长时间才能降下来。解决办法是把 alpha 初始值设成 fc_min 对应的值而不是默认值。5.4 数值不稳定导致输出发散这个问题在定点数实现里比较常见。你可能会发现输出越来越大最后溢出或者饱和。原因通常是alpha 太接近 1导致 1 - alpha 在定点数下变成零或者很小的值滤波器变成了纯积分器输出就会发散。解决办法是给 alpha 设一个上限比如最大 0.999。在 Q15 下0.999 对应 327351 - alpha 是 32还能表示。如果 alpha 再大1 - alpha 就小于 32 了精度不够。另一个原因是基线估计的 beta 太接近 1同样会导致积分器效应。给 beta 也设一个上限比如 0.9999在 Q31 下表示。还有一个不太常见的原因是速率计算里的 abs 溢出。如果 b - b_prev 的结果超出了定点数的表示范围abs 之后就会变成负数。解决办法是在做减法之前先检查范围或者用饱和减法。5.5 常见问题速查表现象可能原因排查方法解决办法输出周期性波动截止频率调整太频繁观察 alpha 的变化曲线增大 alpha_smooth 和 gamma基线压不住fc_max 太低或 rate_max 太高打印 rate_smooth 值调整 fc_max 和 rate_max有用信号被滤除fc_min 太高或低速段斜率太大检查 fc_target 在低速时的值降低 fc_min 和低速段斜率输出发散alpha 或 beta 太接近 1检查 1 - alpha 的值给 alpha 和 beta 设上限响应太慢gamma 太大或 fc_base 太低观察 rate_smooth 的响应时间减小 gamma 或提高 fc_base初始阶段输出跳变初始值设得不合理检查前几个采样点的输出用第一个采样点初始化基线5.6 几个容易被忽略的细节采样率变化的影响。如果你的采样率不是固定的比如有些设备会根据信号活动程度动态调整采样率那所有和采样率相关的参数都需要重新计算。alpha、beta、gamma 都和采样率有关。解决办法是在采样率变化时重新计算这些系数或者把算法改成对采样率不敏感的形式。信号幅度变化的影响。如果你的信号幅度会变化比如增益自动调整那 rate_min 和 rate_max 也需要跟着调整。否则增益变大时同样的基线漂移会产生更大的 rate_raw导致截止频率被推高。解决办法是用信号的滑动标准差来归一化 rate_raw或者用自动增益控制的信息来调整阈值。多通道信号的处理。如果你有多个通道每个通道的基线漂移可能不一样。你可以每个通道独立跑一套算法也可以共享速率估计但独立滤波。我一般用前者因为独立跑更简单而且计算量也不大。启动阶段的处理。算法刚启动的时候基线估计还没有收敛速率估计也不准。这时候截止频率可能会乱跳。解决办法是在启动阶段用一个固定的截止频率等基线估计收敛后再切换到自适应模式。收敛时间大概是基线估计时间常数的三到五倍。比如 fc_base 是 0.02 Hz时间常数是 8 秒那等 30 秒左右再切换。6. 实际效果与扩展思路6.1 在心电信号上的实测效果我用这套算法处理过 MIT-BIH 数据库里的心电信号那些信号里有明显的基线游走。用固定截止频率 0.5 Hz 的高通滤波器处理ST 段明显变形有些导联的 ST 段甚至被压平了。用这套自适应算法处理基线游走被压住了ST 段的形态保持得比较好。具体来说在基线漂移速率较低的时候截止频率自动降到 0.05 Hz 左右这时候 ST 段的低频成分基本没受影响。在基线漂移速率较高的时候截止频率升到 0.3 Hz 左右漂移被有效抑制。整个过程中QRS 波群的幅度和宽度都没有明显变化。计算量方面在 STM32F103 上跑主频 72 MHz每个采样点的处理时间大概是 2 微秒。在 360 Hz 采样率下CPU 占用率不到 0.1%。内存占用方面所有状态变量加起来不到 50 字节。6.2 在振动信号上的表现振动信号里的基线游走通常来自传感器温漂或者安装应力释放。这种漂移比心电信号里的更慢但幅度可能更大。我用这套算法处理过一段桥梁振动监测数据采样率 50 Hz信号幅度大概是 100 mg。基线漂移的速率大概是 0.5 mg/s对应的 rate_smooth 在 0.005 左右。按照映射函数截止频率大概在 0.08 Hz。这个截止频率既能压住漂移又保留了桥梁的低频振动模态大概 0.2 Hz 以上。处理后的频谱和未处理但手动去趋势的频谱基本一致说明算法没有引入额外的失真。6.3 可以继续扩展的方向这套算法还有几个可以改进的地方。速率估计可以更精细。目前用的是基线差分法对噪声比较敏感。可以改用基线斜率法用一段固定时间内的基线变化量来估计速率这样更稳。代价是需要维护一个固定长度的环形缓冲区但仍然是常数内存。截止频率映射可以自适应。目前的映射参数是手动设定的可以根据信号的统计特性自动调整。比如用信号的长期频谱来估计有用低频成分的下限然后据此设定 fc_min。这样对不同信号类型都能自动适配。可以加入运动伪迹检测。如果基线漂移速率突然变得非常大可能不是基线漂移而是运动伪迹。这时候可以暂时冻结截止频率的调整或者切换到更激进的滤波模式。等运动伪迹过去后再恢复自适应模式。可以扩展到多阶滤波器。一阶 IIR 的过渡带比较平缓如果对阻带衰减要求更高可以用二阶 IIR。二阶 IIR 的状态变量多一些但仍然是常数内存。系数计算稍微复杂一点但原理是一样的。6.4 一些个人体会这套算法我用了大概两年在好几个项目里都跑过。最大的体会是参数调节比算法本身更重要。同样的算法参数调得好效果就好参数调得不好效果还不如固定截止频率的滤波器。我的经验是先把 fc_min 和 fc_max 定下来这两个参数决定了滤波器的调节范围。然后用实际数据观察 rate_smooth 的分布根据这个分布来设定 rate_min 和 rate_max。最后调 alpha_smooth 和 gamma这两个参数决定了调节的平滑程度。一般来说alpha_smooth 和 gamma 越大输出越稳但响应越慢。需要在稳定性和响应速度之间找个平衡。还有一个体会是不要追求完美的基线消除。有些基线漂移是信号本身的一部分比如心电信号里的呼吸调制你把它完全消除反而丢失了信息。自适应滤波器的好处是它只压制快速漂移对慢速漂移的压制是有限的。这个特性在很多场景下反而是优点。最后分享一个小技巧如果你不确定参数设得对不对可以先把自适应功能关掉用固定截止频率跑一遍看看效果。然后打开自适应功能对比两者的输出。如果自适应版本的输出在基线漂移段明显更好而在信号段没有明显失真那参数就设对了。如果自适应版本在信号段有失真那就把 fc_min 调低或者把低速段斜率调小。这个方法虽然笨但很有效。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →