携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天。
前言
使用电机速度做闭环控制时,发现传感器的数据受到了高频噪声的影响,于是想先对其进行滤波处理。
原理
滑动平均滤波器在数字信号处理中是最常见的滤波器,滑动平均滤波器非常适合用于减少随机噪声,同时保持清晰的阶跃响应,这使其成为时域编码信号的首选滤波器。但是,从频域看,滑动平均滤波器是对频域编码信号最不友好的滤波器,它几乎没有能力将一个频带与另一个频带分开。由滑动平均滤波器发展来的滤波器还包括高斯滤波,布莱克曼滤波和多次滑动平均滤波,这些改进的滑动平均滤波器在频域中具有稍好的性能,但以增加的计算时间为代价。
移动平均滤波器(Moving Average Filter)原理,移动平均滤波基于统计规律,将连续的采样数据看成一个长度固定为N的队列,在新的一次测量后,上述队列的首数据去掉,其余N-1个数据依次前移,并将新的采样数据插入,作为新队列的尾;然后对这个队列进行算术运算,并将其结果做为本次测量的结果。
对于第m(m>=N)次测量,其算术平均值为:1( n-1) ,N i="0" 式中Ym为本次采样数据,移动平均滤波器是一个低通滤波器,是对模拟滤波的补充,用于实时的检测,只要采样率足够高,就能得到较为理想的测量结果。滑动平均滤波器本质是一个低通滤波器,可以看成FIR滤波器的一个特例。其时域表达为
,其中N为滤波器长度。
代码
代码比较简单,我就没有注释。大概思想就是设置一个滤波器长度的数组,然后不断更新数组内容,再求平均就可以了。写的时候发现其实用队列写应该会更快,但是我懒了一把,因为单片机性能够用所以就算了,如果有写出来的,欢迎一起讨论哈。
/*滑动平均滤波器长度*/
#define MVF_LENGTH 8
float moving_average_filtre(float xn)
{
static int index = -1;
static float buffer[MVF_LENGTH];
static float sum = 0;
float yn = 0;
int i = 0;
if(index == -1)
{
//初始化
for(i = 0; i <MVF_LENGTH; i++)
{
buffer[i] = xn;
}
sum = xn*MVF_LENGTH;
index = 0;
}
else
{
sum -= buffer[index];
buffer[index] = xn;
sum += xn;
index++;
if(index >= MVF_LENGTH)
{
index = 0;
}
}
yn = sum/MVF_LENGTH;
return yn;
}
讨论
滑动平均滤波器的长度选择也是个值得讨论的问题。太短的话消除了不了高频噪声,太长的话延迟比较严重。 参考文章里给出了滤波器长度与系统截止频率的经验公式:
其中,fs为采样频率,fco为截至频率。推导的过程简单讲就是假设输入信号为正弦信号,且将离散信号变为连续信号之后,对输出信号进行傅里叶变换,进而得到他们的关系。
滑动平均滤波器频率响应
图片来源:www.cnblogs.com/pingwen/p/6…
改进
最近又看到一个改进的计算公式,可以节省计算时间,原理很简单,学而不思则罔啊。
改进后的公式为: