数值滤波算法


前言

  滤波是单片机开发中的重要操作,经常接触底层常常用到,比如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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

别问,问就是全会

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值