滑动窗口滤波算法
滑动窗口算法实现
在数字滤波器中,滑动窗口滤波法和平均值滤波法是最为常见和最易实现的两种算法,其中平均值比较简单,实现起来比较容易,而滑动窗口理解起来很容易,但实现起来相对困难。本文的主要内容一是从原理上对比一下两种算法的差别,另外就是通过C代码实现滑动窗口滤波算法,并且提供对应的测试程序。本文所写的滑动窗口算法可用于数字电源中。
平均值滤波与滑动窗口滤波对比
请看上图中的平均值滤波和滑动窗口法,假设两种方法的采样周期都是Ts,平均值法以n为单位求取平均值,而滑动窗口的窗口长度为n,我们可以发现,在平均值法中,计算最小周期为n倍Ts,因为如图所示,假设我们在t1~tn这段时间内计算出一个平均值Avg1,则该时间段内的所有数据都是被用过了,是要丢弃的,而要得到下一个平均值Avg2,则需要等待n倍Ts的时间。
而滑动窗口法如右边所示,窗口长度是固定的,每滑动一次的时候,都会新加入一个新数据,舍弃最初始的数据,然后求取窗口内数据的均值,计算周期最小为Ts,也就是产生一个新数据都会被几十看到。
所以,我们可以从这两种方法中得出:
1、滑动窗口法的计算周期比均值法的计算周期短,每次产生的新数据变化都会对整体产生影响,而平均值法是要等变化达到一定的量时,才更新出新的数据,这就好像两个态度不同两个人,一个比较积极,一有变化,就积极响应,一个比较懒散,要有一段时间的变化后,才去响应。
2、在那种变化速率不太快的情况下,两种方法得到的结果其实是差不多的,但是在那种对变化速率很敏感的地方,滑动窗口还是比均值法优一些。
运用在实际中的一般写法
在数字电路的滤波中,实现的方法如下:
均值法:设定均值计算长度n,每次定时器或者AD刷新,都录入一个数据,计数器+1,把新数据加入累加和中,然后判断是否等于计算长度,如果等于计算长度,就求取均值,累加和清0,计数器清0,如果不等于计算长度,则将新的数据加入累加和中,示例代码如下:
//AD刷新或者定时器刷新函数中
{
//接收新数据
temp = sample;
//计数器+1
i=i+1;
//求和
sum = sum+temp;
if(i==n)
{
average = sum/n;
sum=0;
i=0;
}
}
而滑动窗口法也是类似,大体的实现思路如下:
1、接收新数据
2、丢弃最初始的数据
3、累加和-丢弃的最初始的数据+新的数据
4、均值=累加和/窗口长度
滑动窗口滤波实现
通过对滑动窗口的分析,我们可以发现,滑动窗口其实就是一个队列,先进先出,就像火车过隧道一样,隧道长度就是窗口,火车进出隧道遵循的就是火车头先进,火车头先出,火车尾部后进,火车尾部就后出,而隧道的长度始终保持不变。本文就是基于队列思想实现的,可直接在采样中断或者定时器中断函数中直接调用,另外,本文中的代码还对一种特殊情况做了处理,那就是窗口长度大于数据量时,这种情况在运用中是存在的,比如在数据采样的时候,刚开始的时候是没有数据,数据量得从0开始增加,这种情况下窗口长度是大于数据量的,针对这种情况,本文也完美地实现了即使数据量小于窗口长度,也能准确计算出窗口内有效数据的均值。详细代码见下文。
头文件实现
#ifndef ALG_LINKED_LIST_H
#define ALG_LINKED_LIST_H
#define LINKED_LEN 100 //循环链表长度
typedef struct{
float Sample[LINKED_LEN];
unsigned int Head; //表头
unsigned int Tail; //表尾
unsigned int Win_full_flag; //窗口是否满了标志
float Sample_value; //采样倿
float Average; //采样均忿
float Sample_discarded; //采样丢弃倿
float Sum; //窗口内数据濻和
void (*init)()