前言
滤波是单片机开发中的重要操作,经常接触底层常常用到,比如ADC采样、控制系统、信号处理等场合,需要过滤掉一些干扰的参数。本文主要介绍常用的数值滤波算法,主要用于一些如ADC采样数据的滤波
1、限幅滤波法
方法:根据经验判断,确定两次采样允许的最大偏差值(设为FILTER_A),每次检测到新值时判断:
①如果本次值与上次值之差<=A,则本次值有效
②如果本次值与上次值之差>A,则本次值无效,放弃本次值,用上次值代替本次值
优缺点:能有效克服因偶然因素引起的脉冲干扰,但无法抑制那种周期性的干扰,平滑度差。
#define FILTER_A 1 //定义偏差值
int Filter(int NewValue,int Value) {
if(((NewValue - Value) > FILTER_A) || ((Value - NewValue) > FILTER_A))
return Value;
else
return NewValue;
}
2、中位值滤波法
方法:连续采样N次,只保存数据大小在中间的数值。
优缺点:对温度、液位的变化缓慢的被测参数有良好的滤波效果。对流量、速度等快速变化的参数不宜。
uint16_t GetMiddleValue( uint16_t *pusBuffer, uint8_t ucLength )
{
uint8_t i;
uint16_t usMiddle,usMax,usCount = 0,usPoint;
while( usCount <= (ucLength/2) )
{
usPoint = 0;
usMax = pusBuffer[0];
for( i=1;i<ucLength;i++ ) //取最大值
{
if( pusBuffer[i] > usMax )
{
usMax = pusBuffer[i];
usPoint = i;
}
}
usMiddle = usMax;
pusBuffer[usPoint] = 0; //将原来的最大值变为0
usCount++; //重新循环直到一半的数据为0
}
return usMiddle;
}
也可以先排序再取值
uint16_t GetMiddleValue( uint16_t *pusBuffer, uint8_t ucLength )
{
int i,j,filter_temp;
uint16_t usMiddle,usPoint;
//冒泡法
for(j = 0; j < ucLength - 1; j++)
{
for(i = 0; i < ucLength - 1 - j; i++)
{
if(pusBuffer[i] > pusBuffer[i + 1])
{
filter_temp = pusBuffer[i];
pusBuffer[i] = pusBuffer[i + 1];
pusBuffer[i + 1] = filter_temp;
}
}
}
return usMiddle;
}
3、算术平均滤波法
方法:连续取N个采样值累加后求平均值
优缺点:达到平滑的作用,适用性广泛,元素越多滤波效果越好时延越高。但是对于测量速度较慢或要求数据计算速度较快的实时控制不适用;
//data[]放入一段时间里的数值,length:data数组的长度
uint16_t Average(uint16_t data[],uint16_t length)
{
uint32_t add=0;
uint16_t result;
int i;
for(i=0;i<length;i++)
{
add += data[i];
}
result=add/length;
return result;
}
4、递推平均滤波法
方法:把连续取得的N个采样值看成一个队列,队列的长度固定为N,每次采样到一个新数据放入队尾,并扔掉原来队首的一次数据,把新的队列中的N个数据进行算术平均运算,获得新的滤波结果。
优缺点:对周期性干扰有良好的抑制作用,平滑度高;但是不易消除由于脉冲干扰所引起的采样值偏差,不适用于脉冲干扰比较严重的场合;
#define FILTER_N 12
int filter_buf[FILTER_N + 1];
int Filter(int NewValue) {
int i;
int filter_sum = 0;
filter_buf[FILTER_N] = NewValue; //新的采集值放入数组
for(i = 0; i < FILTER_N; i++) {
filter_buf[i] = filter_buf[i + 1]; // 所有数据左移,低位仍掉
filter_sum += filter_buf[i];
}
return (int)(filter_sum / FILTER_N);
}
5、中位值平均滤波法
方法: 相当于“中位值滤波法”+“算术平均滤波法”。先采N个数据去掉最大值和最小值后取平均值,然后计算这N-2个数据的算术平均值。
优缺点: 对于偶然出现的脉冲性干扰,可消除由其所引起的采样值偏差,对周期干扰有良好的抑制作用,平滑度高,适于高频振荡的系统。但是计算速度慢,加大CPU开销
①直接找极值
#define FILTER_N 100
int filter_buf[FILTER_N];
/*给filter_buf[]赋值*/
int Filter() {
int i;
int filter_sum = 0;
int filter_max, filter_min;
filter_max = filter_buf[0];
filter_min = filter_buf[0];
filter_sum = filter_buf[0];
for(i = FILTER_N - 1; i > 0; i--) {
if(filter_buf[i] > filter_max)
filter_max=filter_buf[i];
else if(filter_buf[i] < filter_min)
filter_min=filter_buf[i];
filter_sum = filter_sum + filter_buf[i];
filter_buf[i] = filter_buf[i - 1];
}
filter_sum = (filter_sum - filter_max - filter_min) / (FILTER_N - 2);
return filter_sum;
}
②先排序,后去极值
#define FILTER_N 100
int filter_buf[FILTER_N];
/*给filter_buf[]赋值*/
int Filter(int NewValue) {
int i, j;
int filter_temp, filter_sum = 0;
filter_buf[FILTER_N-1] = NewValue; //新数据给队列
// 采样值从小到大排列(冒泡法)
for(j = 0; j < FILTER_N - 1; j++) {
for(i = 0; i < FILTER_N - 1 - j; i++) {
if(filter_buf[i] > filter_buf[i + 1]) {
filter_temp = filter_buf[i];
filter_buf[i] = filter_buf[i + 1];
filter_buf[i + 1] = filter_temp;
}
}
}
// 去除最大最小极值后求平均
for(i = 1; i < FILTER_N - 1; i++) filter_sum += filter_buf;
return filter_sum / (FILTER_N - 2);
}
6、限幅平均滤波法
方法:相当于“限幅滤波法”+“递推平均滤波法”。每次采样到的新数据先进行限幅处理,再送入队列进行递推平均滤波处理。
优缺点: 对于偶然出现的脉冲性干扰,可消除由于脉冲干扰所引起的采样值偏差。但是CPU开销大
#define FILTER_A 1
int Filter(int NewValue) {
int i;
int filter_sum = 0;
filter_buf[FILTER_N - 1] = NewValue;
//限幅
if(((filter_buf[FILTER_N - 1] - filter_buf[FILTER_N - 2]) > FILTER_A) || ((filter_buf[FILTER_N - 2] - filter_buf[FILTER_N - 1]) > FILTER_A))
filter_buf[FILTER_N - 1] = filter_buf[FILTER_N - 2];
//求平均
for(i = 0; i < FILTER_N - 1; i++) {
filter_buf[i] = filter_buf[i + 1];
filter_sum += filter_buf[i];
}
return (int)filter_sum / (FILTER_N - 1);
}
7、一阶滞后滤波法
方法:取一个滤波系数a(范围0-1),本次滤波结果 = a×本次采样值 + (1-a)×上次滤波结果
优缺点:对周期性干扰具有良好的抑制作用,适用于波动频率较高的场合。但是相位滞后,灵敏度低,滞后程度取决于a值大小,不能消除滤波频率高于采样频率1/2的干扰信号。
#define FILTER_A 0.01 //滤波系数
int Value;
/*给Value赋值*/
int Filter(int NewValue) {
Value = (int)((float)NewValue * FILTER_A + (1.0 - FILTER_A) * (float)Value);
return Value;
}
8、加权递推平均滤波法
方法:在均值滤波的基础上,加上比例系数,最新的数据具有更大的比例,增加时效性。
优缺点:适用于有较大纯滞后时间常数的对象,和采样周期较短的系统。但是对于纯滞后时间常数较小、采样周期较长、变化缓慢的信号,不能迅速反应系统当前所受干扰的严重程度,滤波效果差。
#define PROPORTIONNEW 0.55
#define PROPORTIONMID 0.35
#define PROPORTIONLAST (1- PROPORTIONNEW -PROPORTIONMID )
double LowPassFilter_Silding(double dataNewest,double dataMiddle,double dataLast)
{
double result;
result = PROPORTIONNEW *dataNewest+ PROPORTIONMID *dataMiddle+PROPORTIONLAST *dataLast;
return result;
}
9、消抖滤波法
方法:设置一个滤波计数器,将每次采样值与当前有效值比较:
如果采样值=当前有效值,则计数器清零;
如果采样值<或>当前有效值,则计数器+1,并判断计数器是否>=上限N(溢出);
如果计数器溢出,则将本次值替换当前有效值,并清计数器。
优缺点: 对于变化缓慢的被测参数有较好的滤波效果,可避免在临界值附近控制器的反复开/关跳动或显示器上数值抖动。但是对于快速变化的参数不宜,如果在计数器溢出的那一次采样到的值恰好是干扰值,则会将干扰值当作有效值导入系统。
#define FILTER_N 12
int i = 0;
int value = 3;//设置有效值
int Filter(int new_value) {
if(Value != new_value) {
i++;
if(i > FILTER_N) {
i = 0;
Value = new_value;
}
}
else i = 0;
return Value;
}