C语言实现滑动平均滤波器

830 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天。

前言

使用电机速度做闭环控制时,发现传感器的数据受到了高频噪声的影响,于是想先对其进行滤波处理。

原理

滑动平均滤波器在数字信号处理中是最常见的滤波器,滑动平均滤波器非常适合用于减少随机噪声,同时保持清晰的阶跃响应,这使其成为时域编码信号的首选滤波器。但是,从频域看,滑动平均滤波器是对频域编码信号最不友好的滤波器,它几乎没有能力将一个频带与另一个频带分开。由滑动平均滤波器发展来的滤波器还包括高斯滤波,布莱克曼滤波和多次滑动平均滤波,这些改进的滑动平均滤波器在频域中具有稍好的性能,但以增加的计算时间为代价。

移动平均滤波器(Moving Average Filter)原理,移动平均滤波基于统计规律,将连续的采样数据看成一个长度固定为N的队列,在新的一次测量后,上述队列的首数据去掉,其余N-1个数据依次前移,并将新的采样数据插入,作为新队列的尾;然后对这个队列进行算术运算,并将其结果做为本次测量的结果。

对于第m(m>=N)次测量,其算术平均值为:1( n-1) ,N i="0" 式中Ym为本次采样数据,移动平均滤波器是一个低通滤波器,是对模拟滤波的补充,用于实时的检测,只要采样率足够高,就能得到较为理想的测量结果。滑动平均滤波器本质是一个低通滤波器,可以看成FIR滤波器的一个特例。其时域表达为

y(k)=1Ni=0N1x(k+i)y(k)=\frac{1}{N} \sum_{i=0}^{N-1} x(k+i)

,其中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;
}

讨论

滑动平均滤波器的长度选择也是个值得讨论的问题。太短的话消除了不了高频噪声,太长的话延迟比较严重。 参考文章里给出了滤波器长度与系统截止频率的经验公式: N=0.443fs/fco\mathrm{N}=0.443* \mathrm{f}_{\mathrm{s}} / \mathrm{fco}

其中,fs为采样频率,fco为截至频率。推导的过程简单讲就是假设输入信号为正弦信号,且将离散信号变为连续信号之后,对输出信号进行傅里叶变换,进而得到他们的关系。

在这里插入图片描述

滑动平均滤波器频率响应

图片来源:www.cnblogs.com/pingwen/p/6…

改进

最近又看到一个改进的计算公式,可以节省计算时间,原理很简单,学而不思则罔啊。

改进后的公式为:

y(k)=y(k1)+1Nx(k)x(kN)y(k)=y(k-1)+\frac{1}{N} (x(k)-x(k-N))

参考文章

截止频率和MAF滤波器长度关系