一阶低通滤波:从原理到实践,平滑数据的艺术
在信号处理、嵌入式系统甚至是数据处理中,我们常常会遇到一些包含“毛刺”或高频噪声的数据。直接使用这些数据可能会导致系统误动作、控制震荡或者绘图难看。这时,一阶低通滤波器(First-Order Low-Pass Filter, 1st-order LPF)就派上了用场。它犹如一个“数据平滑器”,允许低频信号通过,同时抑制高频干扰。今天,我们就来彻底搞懂它。
一、原理:它是如何“平滑”数据的?
想象一个场景:用一个笨重的水泥桶通过一个非常细的管子给另一个容器加水。即使你突然改变水泥桶的倾斜角度(输入信号突变),细管子也只能让水流缓慢地变化(输出信号缓慢跟随)。这个“细管子”就是低通滤波效应的来源。
1. 模拟世界的原理:
在模拟电路中,一个简单的RC电路(一个电阻 + 一个电容)就构成了一阶低通滤波器。
- 电阻 R:阻碍电流的变化,相当于“不情愿”因子。
- 电容 C:通过充放电来平滑电压的变化,相当于“惯性”因子。
这个电路有一个关键参数:截止频率 (Fc)。计算公式为:
Fc = 1 / (2π * R * C)
频率高于 Fc
的信号会被显著衰减,而低于 Fc
的信号则能较好地通过。
它的行为可以用一个一阶微分方程来描述,但我们更关心如何在数字世界中使用它。
二、公式:从连续到离散
我们要在单片机、Python、C++等程序中实现滤波,就必须将连续的模拟公式离散化。
1. 微分方程到差分方程
模拟一阶低通滤波器的行为可以用以下微分方程描述:
τ * (dy(t)/dt) + y(t) = x(t)
其中,τ = R*C
是时间常数(τ = 1 / (2π * Fc)
),x(t)
是输入,y(t)
是输出。
我们对它进行离散化处理,用差分近似微分:
dy(t)/dt ≈ (y[n] - y[n-1]) / ΔT
其中 ΔT
是采样周期(两次采样之间的时间间隔)。代入上面的方程:
τ * (y[n] - y[n-1])/ΔT + y[n] = x[n]
整理后,我们得到离散形式的一阶低通滤波公式:
2. 核心滤波公式
y[n] = α * x[n] + (1 - α) * y[n-1]
这就是我们编程实现的核心公式!
其中:
y[n]
:本次滤波后的输出值y[n-1]
:上一次滤波后的输出值x[n]
:本次采样到的输入值α
:滤波系数,它是一个介于 0 到 1 之间的常数,是决定滤波平滑程度的关键。
3. 滤波系数 α 与截止频率 Fc 的关系
α
并不是凭空设定的,它与模拟世界中的截止频率 Fc
和采样周期 ΔT
密切相关:
α = ΔT / (τ + ΔT) = ΔT / (1/(2π * Fc) + ΔT)
更常用的简化形式(当 ΔT 远小于 τ 时):
α ≈ 2π * Fc * ΔT
如何理解 α?
- α → 1(例如
α=0.9
):(1-α)
很小,y[n]
几乎等于x[n]
。滤波器“信任”新数据,滞后小,但平滑效果差。 - α → 0(例如
α=0.1
):(1-α)
很大,y[n]
很大程度上是旧的y[n-1]
的自我重复。滤波器“信任”历史数据,平滑效果好,但滞后非常严重。
编程时,通常直接根据想要的平滑效果来经验性地设置 α(如 0.1~0.3),或者通过已知的 Fc
和 ΔT
计算出来。
三、程序应用与实现
这个公式的优点是极其高效,只涉及一次乘法和一次加法运算,非常适合在资源受限的微控制器(如Arduino, STM32)上运行。
程序步骤:
- 定义并初始化滤波系数
alpha
和上一次的输出值lpf_prev_value
。 - 在定时采样中断或循环中,读取新的采样值
adc_value
。 - 应用公式:
lpf_output = alpha * adc_value + (1 - alpha) * lpf_prev_value;
- 更新
lpf_prev_value = lpf_output;
,为下一次计算做准备。 - 使用平滑后的
lpf_output
进行后续控制或显示。
C 语言实现(适用于单片机/嵌入式)
// First order low pass filter implementation in C// 定义滤波器结构体,方便管理多个滤波器
typedef struct {float alpha; // 滤波系数float prev_value; // 上一次的输出值
} FirstOrderLPF;// 初始化滤波器
// - alpha: 滤波系数
// - init_value: 滤波器的初始输出值(通常设为第一次的采样值)
FirstOrderLPF lpf_init(float alpha, float init_value) {FirstOrderLPF filter;filter.alpha = alpha;filter.prev_value = init_value;return filter;
}// 执行一步滤波计算
// - filter: 指向滤波器对象的指针
// - new_value: 新的采样值
float lpf_update(FirstOrderLPF* filter, float new_value) {// 应用公式: y[n] = α * x[n] + (1 - α) * y[n-1]float output = filter->alpha * new_value + (1.0f - filter->alpha) * filter->prev_value;// 更新上一次的输出值为本次结果filter->prev_value = output;return output;
}/****************** 使用示例 ******************/
#include <stdio.h>int main() {// 假设采样周期 ΔT = 0.01s (100Hz), 期望截止频率 Fc = 1Hz// α ≈ 2π * Fc * ΔT = 2*3.14*1*0.01 ≈ 0.0628float alpha = 0.0628f;// 初始化滤波器,假设初始采样值为 0.0FirstOrderLPF my_filter = lpf_init(alpha, 0.0f);// 模拟一些含噪声的输入数据(例如:正弦波 + 随机噪声)float input_samples[] = {0.1, 0.5, 0.8, 1.2, 0.9, 0.7, 0.3, ...};int num_samples = sizeof(input_samples) / sizeof(input_samples[0]);printf("Raw Data, Filtered Data\n");for (int i = 0; i < num_samples; i++) {float raw_data = input_samples[i];float filtered_data = lpf_update(&my_filter, raw_data);printf("%.3f, %.3f\n", raw_data, filtered_data);}return 0;
}
Python 实现(适用于数据分析、仿真)
# First order low pass filter implementation in Python
import numpy as np
import matplotlib.pyplot as pltdef first_order_lpf(new_value, prev_value, alpha):"""一阶低通滤波函数:param new_value: 本次采样值:param prev_value: 上一次的滤波输出值:param alpha: 滤波系数:return: 本次滤波输出值"""return alpha * new_value + (1 - alpha) * prev_value# 生成示例数据:一个干净的正弦波 + 高频噪声
sample_count = 500
t = np.linspace(0, 5, sample_count)
clean_signal = np.sin(2 * np.pi * 1 * t) # 1Hz 正弦波
noise = 0.5 * np.random.normal(size=sample_count) # 随机噪声
raw_signal = clean_signal + noise# 滤波参数设置
alpha = 0.1 # 滤波系数,越小越平滑,滞后也越大# 初始化
filtered_signal = np.zeros_like(raw_signal)
filtered_signal[0] = raw_signal[0] # 用第一个采样值初始化# 执行滤波
for i in range(1, sample_count):filtered_signal[i] = first_order_lpf(raw_signal[i], filtered_signal[i-1], alpha)# 绘图对比
plt.figure(figsize=(12, 6))
plt.plot(t, raw_signal, label='Raw Data (Noisy)', alpha=0.7)
plt.plot(t, clean_signal, label='Ground Truth (Clean Sin)')
plt.plot(t, filtered_signal, label=f'Filtered Data (α={alpha})', linewidth=2)
plt.legend()
plt.xlabel('Time [s]')
plt.ylabel('Amplitude')
plt.title('First Order Low Pass Filter Demo')
plt.grid(True)
plt.show()
四、应用场景与注意事项
典型应用场景:
- 传感器数据平滑:ADC读取的电压、陀螺仪/加速度计数据、温度传感器数据等。
- 按键消抖:虽然硬件消抖更常见,但软件滤波可以进一步确保稳定性。
- 控制系统:对反馈信号进行滤波,防止控制指令过于激进和高频震荡。
- 信号处理:作为更复杂滤波器的基础单元。
注意事项:
- 相位滞后:这是最大的缺点。滤波后的信号在时间上会滞后于原始信号,
α
越小,滞后越严重。在闭环控制系统中,这可能影响稳定性,需要谨慎设计。 - 初始值:初始值
y[0]
的设置会影响滤波器开始一段时间的输出。通常设置为第一次的采样值。 - 选择 α:需要在平滑度和响应速度之间做权衡。通过实际效果测试来选择最适合的
α
值。