指数滑动滤波器
- 作用
- 原理
- 特点
- 公式
- 代码
- 优化升级
作用
首先这个滤波器能够将一些突变的信号对系统的影响降低,能够平滑输入信号,滤除噪声,减少测量数据的瞬间波动和干扰,就是实现输入信号不能不变,数值不会突然变大,比如你根据编码器的值控制呼吸灯的亮度,如果编码器突然拉的值很大,呼吸灯不会立刻变得很亮,会慢慢亮。
原理
原理就是不完全采样当前的输入信号,而是将输入信号的值和上一刻滤波后的输入信号做一个权重划分,然后得到最终滤波后的信号值,比如前一刻滤波后是10V,当前采样的电压是5V,假如设置前一刻的权重是90%,当前采样的信号权重是10%,则将100.9+50.1 = 9.5V,这就是滤波后的结果,不会说因为10V掉到5V,我对应的输出也立刻改变,而是先按照9.5V对应输出,如果后面保持5V,那么根据权重划分输出信号也会慢慢降低的,只是响应没那么快。
特点
特点就是响应没那么快,但是输出信号会平滑,所以这个权重的设定也非常的重要,如果历史值(也就是上一刻的滤波结果)占的权重比较大,就会导致响应非常的缓慢,如果历史值的权重比较小,就会导致信号平滑的效果不是那么好。
所以需要注意系统刚开始运行的时候,输出信号也是要缓慢变化才能达到目标值,这是因为系统第一次滤波时上一刻的滤波结果为0,如果想要在系统刚开始运行时就达到目标值附近在开始滤波,可以在进行第一次滤波时给上一刻滤波结果赋当前输入值,让系统更快速达到目标值附件。
公式
y[n]=a×x[n]+(1−a)×y[n−1]y[n] = a \times x[n] + (1 - a) \times y[n-1]y[n]=a×x[n]+(1−a)×y[n−1]
- x[n] 是最新的测量值
- y[n] 是滤波后的输出值
- a 是滤波器“记忆权重”,越小说明当前输入影响越小,滤波越平滑(但响应慢)
代码
下面给出一段Python的案例代码:
import numpy as npdef exponential_filter(input_signal, a):"""单一输入信号的指数滑动滤波:param input_signal: 输入信号列表:param a: 滤波系数(本次输入权重),0 < a < 1:return: 滤波后的输出信号列表"""output_signal = [input_signal[0]] # 以第一个输入作为初始滤波值for n in range(1, len(input_signal)):y_prev = output_signal[-1]x_curr = input_signal[n]y_curr = a * x_curr + (1 - a) * y_prevoutput_signal.append(y_curr)return output_signal# 模拟你的滤波系数
a_vdc = 1 / 16 # vdc权重# 示例输入信号,替换成你的采样数据
np.random.seed(0)
input_vdc = 17 + 2 * np.random.randn(100) # 模拟带噪声电压信号filtered_vdc = exponential_filter(input_vdc, a_vdc)# 输出前10个滤波值查看
for i in range(10):print(f"原始vdc: {input_vdc[i]:.3f}, 滤波vdc: {filtered_vdc[i]:.3f}")
结果如下:
原始vdc: 20.528, 滤波vdc: 20.528
原始vdc: 17.800, 滤波vdc: 20.358
原始vdc: 18.957, 滤波vdc: 20.270
原始vdc: 21.482, 滤波vdc: 20.346
原始vdc: 20.735, 滤波vdc: 20.370
原始vdc: 15.045, 滤波vdc: 20.037
原始vdc: 18.900, 滤波vdc: 19.966
原始vdc: 16.697, 滤波vdc: 19.762
原始vdc: 16.794, 滤波vdc: 19.576
原始vdc: 17.821, 滤波vdc: 19.467
这里可以看出滤波后的值并不会由于原始vdc的值的变化而发生比较大的突变,接下来给出在单片机中C语言的实现代码:
u16 DataFilter(u16 dat) {static u16 s_u16LastDat; // 记录历史值float a = 0.9; // 历史值权重u16 result;// 进行指数滤波计算result = a * s_u16LastDat + (1 - a) * dat;// 记录当前滤波结果用于下次计算s_u16LastDat = result;return result;
}
优化升级
虽然这样写可以,但是可以考虑一下单片机的性能问题,对单片机来说浮点运算是非常耗时的,因为有些单片机没有浮点单元(大部分都没有把),所以接下来对这段代码进行优化升级:
u16 DataFilter(u16 dat) {static u16 s_u16LastDat; // 记录历史值u16 result;// 进行指数滤波计算,历史权重比为0.9375result = (dat >> 4) + ((s_u16LastDat * 15) >> 4);// 记录当前滤波结果用于下次计算s_u16LastDat = result;return result;
}
通过右移的方式,把乘除运算变成位移能够大幅提高运算速度,这里是如何设置权重的呢,>>4相当于➗16,所以系数a = 1/16 = 0.0625,而*15>>4相当于✖15在➗16,系数a=15/16 = 0.9375;最终的权重和还是为1的,只是将权重刚好设置成2的倍数,就能够通过位移完成计算。
最后需要提醒一下这个权重系数的设置关系整个系统的性能,需要根据实际情况选择合适的权重比。