各大滤波算法的学习

前言

人生如逆旅,我亦是行人。


一、限幅滤波法(又称程序判断滤波法)

  • 方法:

  1. 比较相邻 n 和 n - 1 时刻的两个采样值 y(n) 和 y(n – 1) ,根据经验确定两次采样允许的 最大偏差,如果 两次采样值的差值超过最大偏差范围,认为发生 可随机干扰,并认为后一次采样值 y(n) 为非法值,应予删除,删除 y(n) 后,可用 y(n – 1) 代替 y(n) ;若未超过所允许的最大偏差范围,则认为本次采样值有效。

    :通俗地来讲,就是先确定一个最大的偏值差(设为A)
    每次检测到新值时判断:
    如果本次值与上次值之差<=A,则本次值有效,
    如果本次值与上次值之差>A,则本次值无效,放弃本次值,用上次值代替本次值。


  • 优点:

    对于随机干扰 , 限幅滤波是一种有效的方法 ,对于随机干扰 , 限幅滤波是一种有效的方法。
  • 缺点:

    无法抑制那种周期性的干扰。 平滑度差。
  • 程序:

#include <stdio.h>
 
// 声明最大误差:可根据实际情况进行调整
#define DELAT_MAX 10
// 定义滤波数据类型
typedef	int filter_type;
// 函数声明
filter_type filter(filter_type effective_value, filter_type new_value, filter_type delat_max);
// 限幅滤波算法函数
filter_type filter(filter_type effective_value, filter_type new_value, filter_type delat_max)
{
	if (( new_value - effective_value > delat_max ) || ( effective_value - new_value > delat_max ))
		return effective_value;		//effective_value为有效值,用有效值代替新值,舍去
	else
		return new_value;	//new_value 为当前采样值滤波程序返回有效的实际值
}
// 打印测试
int main()
{
	filter_type a = 8;
	filter_type b = -3;
	filter_type c;
 
	c = filter(a, b, DELAT_MAX);
	printf("%d", c);

	return 0;
}
int Filter_Value;
int Value;

void setup() {
  Serial.begin(9600);       // 初始化串口通信
  randomSeed(analogRead(0)); // 产生随机种子
  Value = 300;
}

// 用于随机产生一个300左右的当前值
int Get_AD() {
  return random(295, 305);
}

// 限幅滤波法(又称程序判断滤波法)
#define FILTER_A 1
int Filter() {
  int NewValue;
  NewValue = Get_AD();
  if(((NewValue - Value) > FILTER_A) || ((Value - NewValue) > FILTER_A))
    return Value;
  else
    return NewValue;
}

void loop() {
  Filter_Value = Filter();       // 获得滤波器输出值
  Value = Filter_Value;          // 最近一次有效采样的值,该变量为全局变量
  Serial.println(Filter_Value); // 串口输出
  delay(50);
}

二、中值滤波算法

  • 中值滤波是对一个滑动窗口内的诸像素灰度值排序,用其中值代替窗口中心象素的原来灰度值,它是一种非线性的图像平滑法,它对脉冲干扰级椒盐噪声的抑制效果好,在抑制随机噪声的同时能有效保护边缘少受模糊。

  • 中值滤波可以过滤尖峰脉冲。目的在于我们对于滤波后的数据更感兴趣。滤波后的数据保留的原图像的变化趋势,同时去除了尖峰脉冲对分析造成的影响。

  • 方法

    连续采样 N 次(N 取整数),然后把 N 个采样值按大小顺序排序,取中间值作为本次的有效值。

  • 优点

    能克服因偶然因素引起的波动干扰;对温度、液位的变化缓慢的被测参数有良好的滤波效果。

  • 缺点

    对流量、速度等快速变化的参数不宜。

  • 程序

int Filter_Value;

void setup()
{
    Serial.begin(9600);        // 初始化串口通信
    randomSeed(analogRead(0)); // 产生随机种子
}

// 用于随机产生一个300左右的当前值
int Get_AD()
{
    return random(295, 305);
}

// 中位值滤波法
#define FILTER_N 101
int Filter()
{
    int filter_buf[FILTER_N];
    int i, j;
    int filter_temp;
    for (i = 0; i < FILTER_N; i++)
    {
        filter_buf = Get_AD();
        delay(1);
    }
    // 采样值从小到大排列(冒泡排序法)
    for (j = 0; j < FILTER_N - 1; j++)
    {
        for (i = 0; i < FILTER_N - 1 - j; i++)
        {
            if (filter_buf > filter_buf[i + 1])
            {
                filter_temp = filter_buf;
                filter_buf = filter_buf[i + 1];
                filter_buf[i + 1] = filter_temp;
            }
        }
    }

    //算出平均值作为最后的有效值
    return filter_buf[(FILTER_N - 1) / 2];
}

void loop()
{
	Filter_Value = Filter();	//获得滤波器输出值
	Serial.println(Filter_Value);	//串口输出
	delay(50);
}
  • bArray 是一个整形指针,我们传入的一般是一个数组,用来存储待排序的数据 ;iFilterLen 是滤波器的长度 ;
  • 用在图像处理中时,由于像素的取值范围是0~255,刚好是 unsigned char 的范围,所以函数的返回值是 unsigned char ,如果我们要处理的数是 float 型,或其他类型,返回值也可以更改返回值是 temp ,也即是我们想得到的中值
#include<stdio.h>

unsigned char GetMedianNum(int * bArray, int iFilterLen)
{
    unsigned char temp;

    //利用冒泡排序进行排序
    for (int i = 0; i < iFilterLen - 1; i++)
    {
        /* code */
        for (int j = 0; i < iFilterLen - 1 - i; j++)
        {
            /* code */
            if (bArray[i] > bArray[i+1])
            {
                /* 互换 */
                temp        = bArray[i];
                bArray[i]   = bArray[i+1];
                bArray[i+1] = temp;
            }
        }
    }

    //计算中值
    if ((iFilterLen & 1) > 0)
    {
        //数组中有奇数个元素,返回中间一个元素
        temp = bArray[(iFilterLen+1) / 2];
    }
    else
    {
        // 数组有偶数个元素,返回中间两个元素平均值
        temp = (bArray[iFilterLen/2] + bArray[iFilterLen/2 + 1]) / 2;
    }
    return temp;
}



三、算术平均滤波法

  • 方法

    连续取 N 个采样值进行算术平均值运算:

    • N 值较大时:信号平滑度较高,但灵敏度较低;
    • N 值较小时:信号平滑度较低,但灵敏度较高;
    • N值的选取:一般流量,N=12;压力:N=4。
  • 优点

    适用于对一般具有随机干扰的信号进行滤波;

    这种信号的特点是有一个平均值,信号在某一数值范围附近上下波动

  • 缺点:

    • 对于测量速度较慢或要求数据计算速度较快的实时控制不适用;比较浪费RAM、
    • 对于偶发异常,比如某次的采样值明显偏大,经过算数平均值导致最终值存在较大偏差。针对这种极大值或极小值的问题,可以在算数平均前先进行剔除。
    • 在实际如现场比赛打分中,就是去掉最高分和最低分之后的平均值。但是对于一次采样周期中出现多个异常数据则无法排除,正如比赛中,恰好某选手有2个以上的评委干爹,这是无解的。不过可加大采样量,1分钟内采集4次改为采集10次,排序后去掉最大的2个值和最小的2个值,再对剩下的6个值求算数平均值。
  • 程序:

int Filter_Value;

void setup()
{
	Serial.begin(9600);		//初始化串口通信
	randomSeed(analogRead(0));	//产生随机种子
}

//用于随机产生一个300左右的当前值
int Get_AD()
{
	return random(295,305);
}

//算术平均滤波
#define FILTER_N	12
int Filter(void)
{
	int filter_sum = 0;
	for(int i=0; i<FILTER_N; i++)
	{
		filter_sum += Get_AD();
		delay(1);	//延时一毫秒
	}
	return (int)(filter_sum/FILTER_N);
}

void loop()
{
	Filter_Value = Filter();	//获得滤波器输出值
	Serial.println(Filter_Value);	//串口输出
	delay(50);
}
  • 改进后的算法:剔除极大值和极小值;
int filter(void)
{
    int sum=0;
    int min=0,max=0;

    for(int i=0; i<FILTER_N; i++)
    {
        if(vBat[i]>max)
        {
            max=vBat[i];
        }
        if(vBat[i]<min)
        {
            min==vBat[i];
        }
        sum+=vBat[i];
    }

    sum=sum-min-max;
    
    return sum/SAMPLE_NUM;
}


四、递推平均滤波法(又称滑动平均滤波法)

  • 方法

    • 把连续取得的N个采样值看成一个队列,队列的长度固定为N
    • 每次采样到一个新数据放入队尾,并扔掉原来队首的一次数据(先进先出FIFO
    • 把队列中的N个数据进行算术平均运算,获得新的滤波结果
    • N 值的选取:流量,N = 12;压力,N = 4;液面,N = 4~12;温度,N = 1~4
  • 优点

    • 周期性干扰 有良好的抑制作用,平滑度高
    • 适用于 高频振荡的系统
  • 缺点

    • 灵敏度低,对偶然出现的脉冲性干扰的抑制作用较差;
    • 不易消除由于脉冲干扰所引起的采样值偏差;
    • 不适用于脉冲干扰比较严重的场合;
    • 比较浪费 RAM ;
  • 程序

int Filter_Value;

void setup() {
  Serial.begin(9600);       // 初始化串口通信
  randomSeed(analogRead(0)); // 产生随机种子
}

// 用于随机产生一个300左右的当前值
int Get_AD() {
  return random(295, 305);
}

// 递推平均滤波法(又称滑动平均滤波法)
#define FILTER_N 12

int filter_buf[FILTER_N + 1];

int Filter() {
  int filter_sum = 0;
  filter_buf[FILTER_N] = Get_AD();
  for(int i = 0; i < FILTER_N; i++) {
    filter_buf[i] = filter_buf[i + 1]; // 所有数据左移,低位仍掉
    filter_sum += filter_buf;
  }
  return (int)(filter_sum / FILTER_N);
}

void loop() {
  Filter_Value = Filter();       // 获得滤波器输出值
  Serial.println(Filter_Value); // 串口输出
  delay(50);
}
  • 参考代码
/*定义滤波数据类型*/
typedef	int filter_type;
// 函数声明
filter_type filter(filter_type value_buf[], filter_type new_value, int num);

/* 递推平均滤波函数 */
filter_type filter(filter_type value_buf[], filter_type new_value, int num)
{
	static int i;
	int count;
	filter_type sum = 0;
 
	value_buf[i++] = new_value;

	if (i == num)
		i = 0;
	for (count=0; count<num; count++)
		sum += value_buf[count];
 
	return (filter_type)(sum/num);
}


五、中位值平均滤波法(又称防脉冲干扰平均滤波法)

  • 方法:(相当于 “中位值滤波法” + “算术平均滤波法” )

    • 采一组队列去掉最大值和最小值后取平均值
    • 连续采样N个数据,去掉一个最大值和一个最小值
    • 然后计算N-2个数据的算术平均值
    • N 值的选取:3~14
  • 优点

    • 融合了“中位值滤波法”+“算术平均滤波法”两种滤波法的优点
    • 对于偶然出现的脉冲性干扰,可消除由其所引起的采样值偏差
    • 对周期干扰有良好的抑制作用
    • 平滑度高,适于高频振荡的系统
  • 缺点

    • 计算速度较慢,和算术平均滤波法一样;
    • 比较浪费 RAM ;
  • 程序

方法一

int Filter_Value;

void setup() {
  Serial.begin(9600);       // 初始化串口通信
  randomSeed(analogRead(0)); // 产生随机种子
}

// 用于随机产生一个300左右的当前值
int Get_AD() {
  return random(295, 305);
}

#define FILTER_N 100

int Filter() {
  int i, j;
  int filter_temp, filter_sum = 0;
  int filter_buf[FILTER_N];
  for(i = 0; i < FILTER_N; i++) {
    filter_buf = Get_AD();
    delay(1);
  }
  // 采样值从小到大排列(冒泡法)
  for(j = 0; j < FILTER_N - 1; j++) {
    for(i = 0; i < FILTER_N - 1 - j; i++) {
      if(filter_buf > filter_buf[i + 1]) {
        filter_temp = filter_buf;
        filter_buf = 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);
}

void loop() {
  Filter_Value = Filter();       // 获得滤波器输出值
  Serial.println(Filter_Value); // 串口输出
  delay(50);
}

方法二

#define FILTER_N 100

int Filter()
{
    int i;
    int filter_sum = 0;
    int filter_max, filter_min;
    int filter_buf[FILTER_N];
    for (i = 0; i < FILTER_N; i++)
    {
        filter_buf = Get_AD();
        delay(1);
    }
    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 > filter_max)
            filter_max = filter_buf;
        else if (filter_buf < filter_min)
            filter_min = filter_buf;
        filter_sum = filter_sum + filter_buf;
        filter_buf = filter_buf[i - 1];
    }
    i = FILTER_N - 2;
    filter_sum = filter_sum - filter_max - filter_min + i / 2; // +i/2 的目的是为了四舍五入
    filter_sum = filter_sum / i;
    return filter_sum;
}

六、限幅平均滤波算法法

  • 方法:(相当于“限幅滤波法”+“递推平均滤波法”)

    • 每次采样到的新数据先进行限幅处理
    • 再送入队列进行递推平均滤波处理
  • 优点

    • 融合了 “限幅滤波法” + “递推平均滤波法” 两种滤波法的优点
    • 对于偶然出现的脉冲性干扰,可消除由于脉冲干扰所引起的采样值偏差
  • 缺点

    • 比较浪费 RAM
  • 程序

#define FILTER_N 12
int Filter_Value;
int filter_buf[FILTER_N];

void setup() 
{
  Serial.begin(9600);       // 初始化串口通信
  randomSeed(analogRead(0)); // 产生随机种子
  filter_buf[FILTER_N - 2] = 300;
}

// 用于随机产生一个300左右的当前值
int Get_AD() 
{
  return random(295, 305);
}

//限幅平均滤波法
#define FILTER_A	1
int Filter()
{
	int filter_sum = 0;
	filter_buf[FILTER_N - 1] = Get_AD();
	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(int i=0; i<FILTER_N; i++)
	{
		filter_buf = filter_buf[i + 1];
		filter_sum += filter_buf;
	}
	return (int)filter_sum/(FILTER_N - 1);
}
  • 参考代码
#define LIMIT 200
#define SIZE 20
 
//返回两个数差
typedef  unsigned int filter_type;
filter_type num_sub( filter_type a, filter_type b )
{
    return ( a >= b ? ( a - b ) : ( b - a ) );
}
 
filter_type filter( void )                            
{
    static filter_type value_buf[SIZE];
    static unsigned int i = 0;
    filter_type count;
    filter_type  new_value = 0;
    static filter_type last_value = 0;
    int  sum = 0;
    new_value = ReadVol_CH2();
    if( num_sub( new_value, last_value ) < LIMIT )
    {
        value_buf[i++] = new_value;
        last_value = new_value;
    }
    else
    {
        value_buf[i++] = last_value;
    }
    if( i == SIZE )
    {
        i = 0;
    }
    for( count = 0; count < SIZE; count++ )
    {
        sum += value_buf[count];
    }
    return ( last_value )( sum / SIZE );
}

七、一阶滞后滤波法(又叫一阶惯性滤波)

  • 算法公式:
    Y ( n ) = a ∗ Y ( n − 1 ) + ( 1 − a ) ∗ X ( n ) Y(n) = a * Y(n - 1) + (1 - a) * X(n) Y(n)=aY(n1)+(1a)X(n)
    :a 为滤波系数,X(n)= 本次采样值,Y(n-1) = 上次滤波输出值,Y(n)= 本次滤波输出值。

  • 方法

    • 取 a = 0 ~ 1;
    • 本次滤波结果 = (1 - a)* 本次采样值 + a * 上次滤波结果
  • 优点

    • 对周期性干扰具有良好的抑制作用;
    • 适用于波动频率较高的场合
  • 缺点

    • 相位滞后,灵敏度低;
    • 滞后程度取决于 a 值大小;
    • 不能消除滤波频率高于采样频率 1/2 的干扰信号。
  • 程序

int Filter_Value;
int Value;

void setup()
{
	Serial.begin(9600);	//初始化串口通信
	randomSeed(analogRead(0)); // 产生随机种子
  	Value = 300;
}

// 用于随机产生一个300左右的当前值
int Get_AD() {
  return random(295, 305);
}
// 一阶滞后滤波法
#define FILTER_A 0.01
int Filter() {
  int NewValue;
  NewValue = Get_AD();
  Value = (int)((float)NewValue * FILTER_A + (1.0 - FILTER_A) * (float)Value);
  return Value;
}

void loop() {
  Filter_Value = Filter();       // 获得滤波器输出值
  Serial.println(Filter_Value); // 串口输出
  delay(50);
}


八、加权递推平均滤波法

  • 方法

    • 是对 递推平均滤波法 的改进,即不同时刻的数据加以不同的权
    • 通常是,越接近现时刻 的数据,权取得越大
    • 给予新采样值的权系数越大,则灵敏度越高,但信号平滑度越低
  • 优点

    • 适用于有较大纯滞后时间常数的对象,和采样周期较短的系统
  • 缺点

    • 对于纯滞后时间常数较小、采样周期较长、变化缓慢的信号
    • 不能迅速反应系统当前所受干扰的严重程度,滤波效果差
  • 程序

int Filter_Value;

void setup()
{
	Serial.begin(9600); 		// 初始化串口通信
  	randomSeed(analogRead(0));  // 产生随机种子
}

// 用于随机产生一个300左右的当前值
int Get_AD() {
  return random(295, 305);
}

// 加权递推平均滤波法
#define FILTER_N 12
int coe[FILTER_N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};    // 加权系数表
int sum_coe = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12; // 加权系数和
int filter_buf[FILTER_N + 1];
int Filter() {
  int i;
  int filter_sum = 0;
  filter_buf[FILTER_N] = Get_AD();
  for(i = 0; i < FILTER_N; i++) {
    filter_buf = filter_buf[i + 1]; // 所有数据左移,低位仍掉
    filter_sum += filter_buf * coe;
  }
  filter_sum /= sum_coe;
  return filter_sum;
}

void loop() {
  Filter_Value = Filter();       // 获得滤波器输出值
  Serial.println(Filter_Value); // 串口输出
  delay(50);
}
  • 参考代码
define N 12
typedef  unsigned int filter_type;

filter_type  coe[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
filter_type  sum_coe = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12;
extern filter_type  Read[N];	/采样得到的数组/

filter_type filter( filter_type * read )                    
{
    filter_type count;
    filter_type value_buf[N];
    unsigned long  sum = 0;
    for( count = 0; count < N; count++ )
    {
        value_buf[count] =  Read[count];
    }
    for( count = 0; count < N; count++ )
    {
        sum += value_buf[count] * coe[count];
    }
    return ( filter_type )( sum / sum_coe );
}
 

注:coe 为数组加权系数表,存在程序存储区,sum_coe 为加权系数和。


九、消抖滤波算法

  • 方法

    • 设置一个滤波计数器,将每次采样值与当前有效值比较
    • 如果采样值 = 当前有效值,则计数器清零
    • 如果采样值 != 当前有效值,则计数器 +1 ,并判断计数器是否 >= 上限 N(溢出)
    • 如果计数器溢出,则将本次采样值替换为当前有效值,并清计数器
  • 优点

    • 对于变化缓慢的被测参数有较好的滤波效果
    • 可避免在临界值附近控制器的反复开/关跳动或显示器上数值抖动
  • 缺点

    • 对于快速变化的参数不宜
    • 如果在计数器溢出的那一次采样到的值恰好是干扰值,则会将干扰值当作有效值导入系统
  • 程序

int Filter_Value;
int Value;

void setup() {
  Serial.begin(9600);       // 初始化串口通信
  randomSeed(analogRead(0)); // 产生随机种子
  Value = 300;
}


// 用于随机产生一个300左右的当前值
int Get_AD() {
  return random(295, 305);
}

// 消抖滤波法
#define FILTER_N 12
int count = 0;
int Filter() {
  int new_value;
  new_value = Get_AD();
  if(Value != new_value) {
    count ++;
    if(count > FILTER_N) {
      count = 0;
      Value = new_value;
    }
  }
  else
    count = 0;
  return Value;
}

void loop() {
  Filter_Value = Filter();       // 获得滤波器输出值
  Serial.println(Filter_Value); // 串口输出
  delay(50);
}

十、限幅消抖滤波法

  • 方法

    • 相当于 “限幅滤波法” + “消抖滤波法”
    • 先限幅,后消抖。
  • 优点

    • 继承了“限幅”和“消抖”的优点
    • 改进了“消抖滤波法”中的某些缺陷,避免将干扰值导入系统。
  • 缺点

    • 对于快速变化的参数不宜
  • 程序

int Filter_Value;
int Value;

void setup() {
  Serial.begin(9600);       // 初始化串口通信
  randomSeed(analogRead(0)); // 产生随机种子
  Value = 300;
}

// 用于随机产生一个300左右的当前值
int Get_AD() {
  return random(295, 305);
}

// 限幅消抖滤波法
#define FILTER_A 1
#define FILTER_N 5
int count = 0;
int Filter() {
  int NewValue;
  int new_value;
  NewValue = Get_AD();
  if(((NewValue - Value) > FILTER_A) || ((Value - NewValue) > FILTER_A))
    new_value = Value;
  else
    new_value = NewValue;
  if(Value != new_value) {
    count ++;
    if(count > FILTER_N) {
      count = 0;
      Value = new_value;
    }
  }
  else
    count = 0;
  return Value;
}

void loop() {
  Filter_Value = Filter();       // 获得滤波器输出值
  Serial.println(Filter_Value); // 串口输出
  delay(50);
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

W_oilpicture

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

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

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

打赏作者

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

抵扣说明:

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

余额充值