在信号采集系统中,除了我们感兴趣的数据外,难免会有一些来自于环境的干扰信号。但我们总希望我们得到的数据是纯净而真实的,为了达到这个目标,我们不得不想办法去除这些干扰信号,于是滤波器就成为我们必不可少的帮手。这一篇我们就来讨论如何通过软件实现基于中值平均算术平均算法的中值数字滤波器。
1、问题的提出
在我们通过AD采集获取数据时,不可避免会受到干扰信号的影响,而且很多时候我们希望尽可能的将这种影响减到最小。为实现这一目的,人们想了很多办法,有硬件方面的,也有软件方面的。在硬件难以改变或者软件能够达到相应效果时,我们一般采用软件方法来实现,通常称之为数字滤波。
在我们的项目中,要求100毫秒采集100个数据,由于信号非常微弱,很容易收到干扰,所以实际采集到的数据中起伏很大,这其中有很多数据数叠加了干扰项的数据。通过示波器观察,我们发现在这些干扰项中,有周期性的频率相对低一些的干扰,也有无规律的频率很高的干扰。这些干扰信号对我们的测量结果的影响非常之大,几乎让测量结果完全不可信,所以我们必须想办法将其去除。
我们考虑并测试了多种滤波算法,我们发现单一的滤波手段很难同时滤掉几种不同的干扰信号。所以我们尝试组合多种滤波算法以实现我们的目标。
2、算法设计
前面我们已经描述了问题的来源,所以为了去除不同种类的干扰信号,我们必须设计一个针对多种干扰信号的滤波算法。我们知道主要的干扰信号是相对频率较低的周期干扰和相对频率较高的非周期干扰,我们将分析这两种信号的特点并针对性的采取相应的滤波手段。
首先我们来考虑相对频率较低的周期干扰,这种干扰来自于环境并且很难避免,但这种干扰信号具有一定的规律,所以它对正常信号造成的影响也是有一定规律的。我们可以图示如下:
如果只存在这一种周期性的低频率的干扰信号,那么我们很容易想到采用算术平局算法就能够去除,在前面我们也确实是这么做的。事实上如果存在多种频率的周期性干扰信号,只要采集到的数据样本数量足够,采用算数平均算法基本都是可以得到比较理想的结果。在我们的项目中,我们的采集频率达到了1KHz,而我们每100毫秒出一个数,所以从理论上讲,10Hz以上的周期性干扰都可以通过算术平均率波来消除。
接下来我们来考虑相对频率较高的非周期干扰,这种干扰具有较大的随机性,有可能对信号的影响较大,也有可能对信号的影响较小,其频率和幅值都是随机的,测量结果存在很大的偶然性。我们可以简单的图示如下:
对于这种干扰我们前面的方法对他是没有效果的,但我们的ADC采用的是积分方式来检测信号的,所以在两个采样点之间,无论这类干扰信号在何时出现都会叠加到紧接着的这个采样数据上,致使最终的采样数据比周期性干扰叠加的情况下要么大一些,要么小一些。这就存在两种情况,如果是正向干扰就会是数据变大一些,如果是反向干扰就会是数据变小一些。使得最终的测量数据更加背离原始数据或者更加接近原始数据。
对于更加接近我们需要的数据的变化,我们先不用理会它,毕竟它更加接近我们想要的数据。对于更加偏离的那一部分数据,我们有什么办法将其去除掉呢?办法是有的,我们借鉴比赛积分中去掉偶然性的方式,去掉最高和最低的数,中间的数应该更接近与真实值。具体如下图所示:
这样去掉最大的一些数和最小的一些数后,并不能保证得到的就是真实的信号值,但有一点我们时刻以肯定的就是,余下的值都更为接近真实的信号值。然后我们在对余下的数采取算术平均操作,得到的就是接近真实值的一个采集值了。
3、编码实现
我们已经设计了采用中值及算术平均的方式的算法来处理这些数据。但具体该如何实现呢?这一节中我们就来讨论算法的实现。
首先我们来分析一下实现这里算法需要涉及到哪些问题。第一,首先我们必须采集到一定数量的数据,这个数据量不可太小,理论上来讲3个数据可以实现,但事实上你会发现对此等规模的数据量使用这一算法其实没有多大意义。一般一次采集几十个数然后再做此滤波是比较有效果的。第二,因为采用丢弃一定数量的极大值和极小值减少干扰项的方法,所以在几十上百的数据量时,我们最好是将其先排序在去除比较方便。第三,我们需要选择合理的基础数据量和需要去除的极大极小数量。这一点对最终的结果和滤波的效果都会有影响。明白了上述几点后,我们就可以来实现这一滤波算法了。
/*中值平均滤波算法*/
static uint32_t MedianMeanFilter(uint32_t *pData,uint16_t aSize,uint16_t eSize)
{uint32_t tData;uint32_t result=0;if(aSize<=2*eSize){return 0;}for (int i=0; i<aSize-1; i++) //比较n-1轮{for (int j=0; j<aSize-1-i; j++) //每轮比较n-1-i次,{if (pData[j] < pData[j+1]){tData = pData[j];pData[j] = pData[j+1];pData[j+1] = tData;}}}for(int j=eSize;j<(aSize-eSize);j++){result=result+pData[j];}result = result/(aSize-2*eSize);return result;
}
在上述实现中,我们先对输入的数据进行了排序。在上述代码中我们考虑到数据量不大,采用了冒泡排序的方式实现。然后我们去除了一定数量的极大值和极小值,并对余下的值取算术平均就得到了本次我们的采集值。
对于函数中的三个参数:uint32_t *pData是需要滤波的原始采集数据;uint16_t aSize是需要滤波的原始采集数据的数量;uint16_t eSize是需要丢弃的极大值和极小值的数量。其中aSize要远大于eSize的2倍,否则大部分被舍弃,滤波的意义就不大了。
函数的使用也很简单。比如在我们的应用中,我们以1KHz的速度采集原始值,每采集100个数出一个测量结果,去掉10个极大值和10个极小值,于是我们就可以调用函数如下:
temp[i]=MedianMeanFilter(rDatas[i],100,10);
在这个应用中,我们测试去掉10个极大值和10个极小值可以得到比较理想的结果,当然也可以将eSize的值增大一些以强化滤波的效果,但不要太大。
4、应用总结
我们实现了基于算术平均的中值滤波器。该滤波器对一定频率以上的周期性干扰和随机性的噪声干扰均有较好的效果。通过修改丢弃的极大值和极小值的数量可以应对在不同环境下的滤波要求。
这一滤波算法适用于高速采集,大数据量的情况下滤波。所以必须采集到一定数量的数据,这个数据量不可太小。一般一次采集几十个数然后再做此滤波是比较有效果的。
这一滤波算法对一定频率以上的周期性干扰和随机性的高频干扰有显著的效果。而对其他类型的干扰只具有有限的改善。