滤波器开发之五:基于算术平均的限幅滤波器

  通过AD采集数据时,我们总是希望采集到的数据是纯净而真实的,而实际上环境中存在太多的干扰信号,为了让我们得到的数据尽可能地接近实际值,我们需要降低这些干扰信号的影响。所以软件实现的数字滤波器应运而生,这一篇我们就来讨论基于中值算术平均的平滑滤波器。

1、问题的提出

  在我们通过AD采集获取数据时,不可避免会受到干扰信号的影响,而且很多时候我们希望尽可能的将这种影响减到最小。为实现这一目的,人们想了很多办法,有硬件方面的,也有软件方面的。在硬件难以改变或者软件能够达到相应效果时,我们一般采用软件方法来实现,通常称之为数字滤波。

  前面我们实现了基于算术平均的中值平均滤波器。这一滤波器可以解决我们一定频率范围内的周期性干扰和随机性的高频干扰。但是随机性的干扰出现的频次我们是不知道的,所以我们采用去掉固定数量的极大值和极小值时,虽然可以去除掉随机干扰的部分影响,但有两种情况还是会对我们的最终计算产生影响。其一是当随机干扰很频繁,我们去掉固定数量的极大值和极小值时,还会有一些受干扰的数据影响到最后的结果。其二是当随机干扰不频繁时,我们去掉固定数量的极大值和极小值就可能会去掉一些周期干扰所影响的数据,那么我们采用平均值的方法就不能很好的消除周期性干扰的影响。

  为了消除上述两种情况造成的影响,我们需要改进前述的基于算术平均的中值滤波算法。我们注意到我们的每一次的测量与上一次的测量相距时间很短,数据不会有大幅度的变化,超过一定幅度的数据我们就可以认为它是受到干扰的数据,去除这些受到干扰的数据,我们就可以得到相对理想的结果。

2、算法设计

  前面我们已经描述了问题的来源,这个问题分为两个层次。第一,我们需要去除不同种类的干扰信号,所以我们必须设计一个针对多种干扰信号的滤波算法。第二,我们需要为丢弃固定数量极大值和极小值,而造成的周期干扰的影响不平衡,导致的算术平均算法不能完全消除周期干扰。

  对于第一个层面的问题,其实与上一篇中所描述的问题是一致的。我们知道主要的干扰信号是相对频率较低的周期干扰和相对频率较高的非周期干扰,我们将分析这两种信号的特点并针对性的采取相应的滤波手段。

  首先我们来考虑相对频率较低的周期干扰,这种干扰来自于环境并且很难避免,但这种干扰信号具有一定的规律,所以它对正常信号造成的影响也是有一定规律的。我们可以图示如下:

  如果只存在这一种周期性的低频率的干扰信号,那么我们很容易想到采用算术平局算法就能够去除,在前面我们也确实是这么做的。事实上如果存在多种频率的周期性干扰信号,只要采集到的数据样本数量足够,采用算数平均算法基本都是可以得到比较理想的结果。在我们的项目中,我们的采集频率达到了1KHz,而我们每100毫秒出一个数,所以从理论上讲,10Hz以上的周期性干扰都可以通过算术平均率波来消除。

  接下来我们来考虑相对频率较高的非周期干扰,这种干扰具有较大的随机性,有可能对信号的影响较大,也有可能对信号的影响较小,其频率和幅值都是随机的,测量结果存在很大的偶然性。我们可以简单的图示如下:

  对于这种干扰我们前面的方法对它是没有效果的,但我们的ADC采用的是积分方式来检测信号的,所以在两个采样点之间,无论这类干扰信号在何时出现都会叠加到紧接着的这个采样数据上,致使最终的采样数据比周期性干扰叠加的情况下要么大一些,要么小一些。这就存在两种情况,如果是正向干扰就会是数据变大一些,如果是反向干扰就会是数据变小一些。使得最终的测量数据更加背离原始数据或者更加接近原始数据。

  对于更加接近我们需要的数据的变化,我们先不用理会它,毕竟它更加接近我们想要的数据。对于更加偏离的那一部分数据,我们有什么办法将其去除掉呢?办法是有的,我们借鉴比赛积分中去掉偶然性的方式,去掉最高和最低的数,中间的数应该更接近与真实值。具体如下图所示:

  这样去掉最大的一些数和最小的一些数后,并不能保证得到的就是真实的信号值,但有一点我们时刻以肯定的就是,余下的值都更为接近真实的信号值。然后我们在对余下的数采取算术平均操作,得到的就是接近真实值的一个采集值了。

  而对于第二个层面的问题,我们考虑到我们的测量对象并不会在两次测量之间发生剧烈的变化,所以如果某一个原始数据与上一次的测量结果偏离较大,我们就认为它是一个受到了干扰的数据,我们就将其舍弃。也就是说,以上一次的测量结果为基础,超过上限或者下线的数据我们都认为是异常数据,具体操作图示如下:

  事实上,这一算法不仅可以剔除剧烈变化的异常数据,对于超长周期的干扰也会有一定的抑制作用。

3、编码实现

  上一节,我们描述了基于算术平均的限幅滤波算法,接下来我们看看具体该如何实现这一算法。根据前述的经验,我们可以将算法的实现分为三个层次:第一,采集到足够多的数据,并将数据排序;第二,将数据中一定数量的极大值和极小值剔除;第三,将超越限幅值的数据剔除并使用算术平均值得到最后结果。

  首先来考虑数据采集和排序的问题。数据采集速度不能太低,数据量必须达到一定的数据,约几十至几百的规模。考虑到数据的规模,我们依然采用简单直接的冒泡排序实现数据的极大值和极小值的查找。

  其次我们考虑剔除极大值和极小值的问题。我们实现了对数据的排序后,剔除极大值和极小值是非常容易的,关键是提出的数量怎么设置。

  最后我们考虑限幅滤波的问题。我们将其超过限幅值的数据去除,并对余下的数据取算术平均。这里存在一个问题,就是如果超出限幅的数据量非常之多,远远超过了没有超限的数据量该怎么办呢?我们认为这使得数据也许真的是因为某些原因而出现了较大的变化。此时我们将对全体数据取算术平均值,以快速响应检测对象的变化。

  根据上述的描述,我们可以实现算法如下:

/*限幅平均滤波算法*/
static uint32_t LimitedMeanFilter(uint32_t *pData,uint16_t aSize,uint16_t eSize,uint32_t rData,uint32_t lValue)
{uint32_t tData;uint32_t uResult=0;uint32_t mResult=0;uint32_t lResult=0;uint16_t uNumber=0;uint16_t mNumber=0;uint16_t lNumber=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++){if(pData[j]>(rData+lValue)){uResult=uResult+pData[j];uNumber++;}else if(pData[j]<(rData-lValue)){lResult=lResult+pData[j];lNumber++;}else{mResult=mResult+pData[j];mNumber++;}}if((mNumber>uNumber)&&(mNumber>lNumber)){mResult = mResult/mNumber;}else{mResult = (uResult+mResult+lResult)/(uNumber+mNumber+lNumber);}return mResult;
}

  在上述实现中,我们先对输入的数据进行了排序。然后我们去除了一定数量的极大值和极小值,并检测余下的值是否超越了限幅值。并对限幅之内、超越上限及超越下限的数据分别求和。然后判断三类数据的数量,当限幅内数据的数量超过三分之一时,对其取算术平均,否则对所有数据取算术平均。

  对于函数中的五个参数:uint32_t *pData是需要滤波的原始采集数据;uint16_t aSize是需要滤波的原始采集数据的数量;uint16_t eSize是需要丢弃的极大值和极小值的数量。其中aSize要远大于eSize的2倍,否则大部分被舍弃,滤波的意义就不大了。uint32_t rData参数是参考值;uint32_t lValue参数是偏离参考值的限幅。

  函数的使用也很简单。比如在我们的应用中,我们以1KHz的速度采集原始值,每采集100个数出一个测量结果,去掉10个极大值和10个极小值,于是我们就可以调用函数如下:

temp[i]=LimitedMeanFilter(rDatas[i],100,10,refData[aPara.phyPara.waveband][i],150);

  在这个应用中,我们测试去掉10个极大值和10个极小值,并将限幅的偏差设置为了150,当然这些数值的取值根据具体的应用而定。特别是参考数据的选择非常关键,一般可以根据不同的情况选取上一个测量结果、一定数量的之前的测量结果的算术平均或加权平均,或者采用累计的平均值等。

4、应用总结

  这一篇中,我们实现了基于算术平均的限幅滤波器。该滤波器对一定频率以上的周期性干扰和随机性的噪声干扰均有较好的效果。通过修改丢弃的极大值和极小值的数量可以应对在不同环境下的滤波要求。也可以对超长周期的干扰和其它原因造成的剧变数据拥有较好的抑制作用。

  对于限幅的取值一般只能根据采集系统的特点或者工程师的经验来判断,但并非是盲目的,因为很多情况下我们是能够判断出干扰信号的大致判断范围的。所以限幅值的选取,以及剔除的极大极小值的数量都需要根据集体的应用场景来设置。

  此外参考值的选取也会对滤波效果有决定性影响。一般根据具体的应用场景我们可以选取上一个测量结果、一定数量的之前的测量结果的算术平均或加权平均,或者采用累计的平均值等作为参考值。

欢迎关注:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499313.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

外设驱动库开发笔记34:OLED显示屏驱动

现在OLED显示屏在嵌入式系统中应用的越来越多。对于一些显示信息不太复杂&#xff0c;以显示信息为主的需求&#xff0c;我们一般会选择OLED显示屏。在这一篇中&#xff0c;我们将讨论OLED显示屏驱动的设计与实现。 1、功能概述 从使用的情况来说&#xff0c;较为常用的是0.96…

外设驱动库开发笔记35:迪文触摸屏驱动

有些时候嵌入式系统也需要显示更为复杂的图形&#xff0c;需要更丰富的数据展示。为此&#xff0c;我们需要更大&#xff0c;色彩更丰富&#xff0c;带触屏的显示屏&#xff0c;当然性价比更高就最好了。在我们的项目中遇到此类需求&#xff0c;我们有时会选择DWIN触摸屏。在本…

快速实现一个室内空气质量检测仪

冬天我们大多会关闭门窗&#xff0c;而依靠暖通空调设备来维持室内温度。而在保证居室温度的同时&#xff0c;我们也希望保持居室内大气环境的健康度。鉴于此&#xff0c;我们设计了一个简单的室内空气质量检测器。 1、系统概述 我们依靠暖通空调设备来维持室内温度、湿度和通…

外设驱动库开发笔记36:NTC负温度系数热电阻测温驱动

在嵌入式产品中&#xff0c;温度检测非常常见。在成本比较敏感而精度要求较低时&#xff0c;NTC电阻是个不错的选择。在这一篇中&#xff0c;我们将讨论如何和设计并实现一个通用的NTC驱动&#xff0c;以便在后续的项目中更方便的复用。 1、功能概述 NTC是指随温度上升电阻呈指…

外设驱动库开发笔记37:S1336-5BQ光敏二极管作为光度计驱动

光敏二极管能够实现很多应用&#xff0c;用于光度检测即是其一。我们在一些产品中就曾使用S1336-5BQ光敏二极管进行光度值检测。所以在本篇中&#xff0c;我们将讨论如何设计并实现S1336-5BQ光敏二极管用于光度检测的驱动。 1、功能概述 根据相关的资料&#xff0c;光电二极管…

PID控制器改进笔记之六:改进PID控制器之参数设定

前面我们发布了一系列PID控制器相关的文章&#xff0c;包括经典PID控制器以及参数自适应的PID控制器。这一系列PID控制器虽说实现了主要功能&#xff0c;也在实际使用中取得了良好效果&#xff0c;但还有很多的细节部分可以改进以提高性能和灵活性。这篇中我们来讨论改进PID控制…

软件设计开发笔记1:基于状态机的程序设计

在编码实现的过程中&#xff0c;我们会经常使用到条件判断结构&#xff0c;而且使用起来很方便。但是在需要转移的状态比较多&#xff0c;或是条件比较复杂时&#xff0c;我们就可能需要很长的条件判断结构来处理。不过&#xff0c;过于复杂的条件判断结构会给代码的编写和维护…

外设驱动库开发笔记38:RTD热电阻测温驱动

我们已经讨论过多种温度检测方式&#xff0c;但我们尚未关注热电阻温度检测&#xff0c;但热电阻测温在工业环境中是非常常见的。尽管有很多集成的数字式的热电阻接口元器件&#xff0c;但这些器件不但成本较高&#xff0c;灵活性也大打折扣。所以我们有时会使用更简单灵活的电…

外设驱动库开发笔记39:按键操作驱动

按键在我们的项目中是经常使用到的组件。一般来说&#xff0c;我们都是在用到按键时直接针对编码&#xff0c;但这样每次都做很多重复性的工作。所以在这里我们考虑做一般性抽象得到一个可应用于按键操作的通用性驱动程序。 1、功能概述 按键操作在我们的产品种经常用到&#…

外设驱动库开发笔记40:AT25xxx外部存储器驱动

我们在前面开发过AT24CXX系列EEPROM存储器&#xff0c;它使用的是I2C接口。不过有时候我们也会使用SPI接口的EEPROM存储器。在这一篇我们将来讨论AT25XXX系列EEPROM存储器的驱动设计、实现及使用。 1、功能概述 AT25XXX系列EEPROM存储器采用SPI接口&#xff0c;因其操作简单且…

闪存中的键值对:无文件系统 minINI

许多嵌入式系统应用需要以持久的方式存储某种数据&#xff1a;校准值、设置或日志信息。对于较少的数据量&#xff0c;使用外部存储器或文件系统是一种过度大材小用。在许多系统中&#xff0c;我使用minINI以“ini-file”的方式存储键值解析&#xff0c;但它需要使用某种文件系…

外设驱动库开发笔记41:ADS1256 ADC驱动

我们经常会碰到多通道AD采集的需求&#xff0c;有时候甚至需要高精度的ADC器件。本篇我们将来设计并实现ADS1256模数转换器的驱动。并简单讨论该驱动使用方式。 1、功能概述 ADS1256是TI公司推出的一款低噪声高分辨率的24位Sigma-Delta(E-v)模数转换器(ADC)。E-vADC与传统的逐…

PID参数自整定库之一:继电反馈整定算法

在前述的篇章中&#xff0c;我们实现了PID控制器并在后续对其进行了改进。但作为经典PID控制器还存在PID参数整定的问题。通常我们可以采取人工整定的办法&#xff0c;但人工整定涉及到比较专业的知识&#xff0c;而且找到合适的参数本身也不是一件容易的事&#xff0c;所以人们…

外设驱动库开发笔记42:DAC8552 DAC驱动

模拟信号输出是经常会遇到的应用需求&#xff0c;解决的办法应多种&#xff0c;但我们使用最多的还是数模转换。对于不同的数模转换器我们需要为其编写适用的驱动程序&#xff0c;在这一篇中我们就来考虑如何实现DAC8552高精度模数转换器的驱动程序。 1、功能概述 该DAC8552是…

软件设计开发笔记2:基于QT设计串口调试工具

串口通信是我们经常会遇到的问题。很多时候当我们设计一个串口应用时&#xff0c;我们希望有一个简便的、可视的方式来验证它。这一篇中我们就来基于QT设计一个串口调试工具。 1、概述 在开始软件设计之前&#xff0c;我们来简略地分析一下这样一个小软件其要包含的主要内容有…

外设驱动库开发笔记43:GPIO模拟SPI驱动

SPI总线是我们常用的串行设备接口&#xff0c;一般情况下我们都会适应硬件SPI接口&#xff0c;但有些时候当硬件端口不足时&#xff0c;我们也希望可以使用软件来模拟SPI硬件接口&#xff0c;特别是要求不是很高的时候。在这一篇中我们将来讨论如何使用GPIO和软件来模拟SPI通讯…

外设驱动库开发笔记44:DDC114 ADC驱动

在产品设计过程中&#xff0c;很多时候都会用到ADC器件&#xff0c;而在一些特殊场合还需要一些特别的ADC器件。我们在这篇中将讨论常用于医疗器件方面的&#xff0c;DDC114这款电流输入ADC&#xff0c;并为其设计一个驱动程序。 1、功能概述 模数转换器DDC114是一款电流输入型…

PID控制器改进笔记之七:改进PID控制器之防超调设定

我们已经设计了PID控制器&#xff0c;并根据实际使用的情况对器进行了诸多的改进。在这一篇中我们将讨论如何改进PID控制器超调的问题。 1、问题提出 在前面的文章中&#xff0c;我们曾推导过增量式PID控制器的公式&#xff0c;并且对其进行了离散化以适用于程序实现&#xff…

软件设计开发笔记3:基于QT的Modbus RTU主站

Modbus是一种常见的工业系统通讯协议。在我们的设计开发工作中经常使用到它。在这一篇中我们将简单实现一个基于QT的Modbus RTU主站上位工具。 1、概述 Modbus RTU主站应用很常见&#xff0c;有一些是通用的&#xff0c;有一些是专用的。而这里我们希望实现一个主要针对我们的…

外设驱动库开发笔记45:MS4515DO压力传感器驱动

很多时候我们需要检测流量和压力这些参数&#xff0c;比如我们要检测大气压&#xff0c;或者通过测量差压来获得输送流体的流量等&#xff0c;都需要用到压力传感器。这一篇我们就来讨论MS4515DO压力传感器的数据获取。 1、功能概述 MS4515DO是TE公司推出的一款基于PCB安装的小…