滑动窗口滤波算法C代码实现
滑动窗口算法实现
在数字滤波器中,滑动窗口滤波法和平均值滤波法是最为常见和最易实现的两种算法,其中平均值比较简单,实现起来比较容易,而滑动窗口理解起来很容易,但实现起来相对困难。本文的主要内容一是从原理上对比一下两种算法的差别,另外就是通过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)();
void (*update_data)(); //更新数据
void (*data_average)();
}LINKED_LIST;
typedef LINKED_LIST *List_Handle;
#define LIST_DEFAULT {\
{0,0,0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0,0,0,\
}, \
0,0,0,0.0,0.0,0.0,0.0, \
(void (*)(unsigned long))Init, \
(void (*)(unsigned long))Update_data, \
(void (*)(unsigned long))Data_average, \
}
void Init(List_Handle s);
void Update_data(List_Handle s);
void Data_average(List_Handle s);
extern LINKED_LIST list;
具体函数实现
#include ".\..\include\Alg_linked_list.h"
void Init(List_Handle s)
{
s->Tail = LINKED_LEN;
}
void Update_data(List_Handle s)
{
if(s->Head < s->Tail)
{
s->Sample_discarded = s->Sample[s->Head]; //记录丢弃倿
s->Sample[s->Head] = s->Sample_value; //新加入的数据
s->Head = s->Head +1;
}
else if(s->Head == s->Tail)
{
s->Head = 0;
s->Sample_discarded = s->Sample[s->Head]; //记录丢弃倿
s->Sample[s->Head] = s->Sample_value; //新加入的数据
s->Head = s->Head +1;
s->Win_full_flag = 1;
}
}
void Data_average(List_Handle s)
{
if(s->Win_full_flag == 0)
{
s->Sum = s->Sum + s->Sample[s->Head - 1];
s->Average = s->Sum/(s->Head);
}
else
{
s->Sum = s->Sum + s->Sample[(s->Head + s->Tail -1)%LINKED_LEN] - s->Sample_discarded;
s->Average = s->Sum/LINKED_LEN;
}
}
测试程序
#include "Alg_linked_list.c"
#include "stdlib.h"
#include "stdio.h"
LINKED_LIST list = LIST_DEFAULT;
void main()
{
list.init(&list);
float temp;
for(int i=0;i<201;i++)
{
temp = 20.0 + rand()/32767.0;
//printf("%7.6f\n",temp);
list.Sample_value = temp;
list.update_data(&list);
list.data_average(&list);
}
}
代码中的文件命名及变量命名有些不规范,读者可根据自己的喜好更改,队列英文单词应该是queue。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)