在嵌入式开发程序中涉及数据采样如传感器采样,AD采样等如果直接读取信号后,将信号值直接参与后续逻辑处理,若程序无软件滤波,会导致静态或者动态采样时:
1、信号曲线相对没有那么平滑。
2、同时可能存在脉冲干扰导致逻辑判断错误。
在信号处理系统中,输入信号通常含有各种噪声和干扰。为对信号进行准确的测量和控制,必须削弱或滤除被测信号中的噪声和干扰。因此在工程应用需要使用软件滤波,软件滤波也称数字滤波,是通过一定的算法削弱噪声的影响。在实际的开发过程中使用了滑动窗口滤波算法来对传感器的数据采样进行滤波处理。
一、图解滑动窗口滤波器的原理
1、建立采样窗口和滤波窗口,自定义各窗口长度大小。
2、当数据样本点数未填满采样窗口,对采样窗口内的数据累加做平均值计算。
例如此时采样窗口内数据采样点只有4个,小于定义的采样窗口长度,则将4个值累加后再做平均值计算。
3、当数据样本点数已填满采样窗口,进行冒泡排序后,去除n个最大值及最小值后,对滤波窗口内的数据累加做平均值运算。
例如此时采样窗口内数据采样点有7个,已填满定义长度大小的采样窗口,则分别去除自定义的1个最大值和1个最小值后,对剩余滤波窗口内的数据累加后再做平均值计算。
4、新的数据样本到来,移除采样窗口中时间最早的点(FIFO),重复上述3操作。
二、滑动窗口滤波器的特点
1、经过滤波处理后,滤除了噪声干扰,数据波动稳定平滑。
2、每采样一个新数据,就将最早采集的那个数据丢掉,因此每进行一次采样,就可计算出一个新的平均值,从而加快了数据处理的速度。
3、可以根据实时性的要求和需要滤波后数据的平滑度来设置采样窗口和滤波窗口的大小。
4、当采样的数据量非常大时,这时的采样窗口会设置很大,那么窗口需要消耗一定的内存空间。
5、当采样数据越多,数据实时性越差,当数据突然发生较大变化时,不能被立刻检测到,无法及时处理突发事件。
三、滑动窗口滤波器的C++代码实现
1、基本思路
1、在工程根目录下新建filter文件夹,将源文件filter.cpp和头filter.h放在文件夹*修改调用。
2、头文件:
(1)定义滤波算法函数中的可修改宏值;
(2)声明定义的滤波函数模板;
(3)使用extern的方式声明实例化后的模板函数。
3、源文件:
(1)定义需要使用滤波算法的函数模板;
(2)在定义的函数模板后进行函数实例化操作,通过这样的方法实现具体的模板函数。
2、头文件filter.h
1 #ifndef _FILTER_H_ 2 #define _FILTER_H_ 3 4 #define MAX_SENSOR_NUM 9 //使用滤波时的传感器数量 5 #define MAX_DATA_NUM 9 //最大采样点数量,即采样窗口长度 6 #define WINDOW_DATA_NUM 5 //滤波窗口长度 7 //去除采样窗口内最大最小值的数量,这里去除两个最大和两个最小 8 #define REMOVE_MAXMIN_NUM ((MAX_DATA_NUM - WINDOW_DATA_NUM)/2) 9 10 //extern double m_dataList[MAX_SENSOR_NUM][MAX_DATA_NUM]; 11 12 //声明定义的函数模板 13 template<typename InType> InType Filter_SlidingWindowAvg(int index, InType data); 14 //使用extern的方式声明实例化后的模板函数,根据实际需要自定义数据类型 15 extern template short Filter_SlidingWindowAvg(int index, short data); 16 extern template unsigned int Filter_SlidingWindowAvg(int index, unsigned int data); 17 18 #endif
3、源文件filter.c
1 #include <algorithm> 2 #include "Filter.h" 3 4 using namespace std; 5 //定义各个传感器的数据采样点列表 6 double m_dataList[MAX_SENSOR_NUM][MAX_DATA_NUM] = {0}; 7 //滑窗均值滤波,使用函数模板可用于适配不同数据类型的采样点 8 template<typename InType> 9 InType Filter_SlidingWindowAvg(int index, InType data) 10 { 11 static int dataNum[MAX_SENSOR_NUM] = {0}; //定义记录传感器的采样点个数 12 int i; 13 double sum = 0; 14 double out = 0; 15 double array[MAX_DATA_NUM] = {0}; 16 17 //数据采样点在采样窗口内移动,FIFO操作 18 for(i = MAX_DATA_NUM - 2; i >= 0; i--) 19 m_dataList[index][i+1] = m_dataList[index][i]; 20 21 m_dataList[index][0] = data; 22 //数据采样点数量小于采样窗口长度,对采样窗口数据累加后进行平均值运算 23 if(dataNum[index] < MAX_DATA_NUM) 24 { 25 dataNum[index]++; 26 for(i = 0; i < dataNum[index]; i++) 27 { 28 sum += m_dataList[index][i]; 29 } 30 out = sum / dataNum[index]; 31 } 32 //数据采样点已填满采样窗口,进行排序后,去除n个最大值及最小值后,对滤波窗口内的数据累加后进 33 //行平均值运算 34 else 35 { 36 for(i = 0; i < MAX_DATA_NUM; i++) 37 { 38 array[i] = m_dataList[index][i]; 39 } 40 //利用C++标准库的sort函数进行排序,这里使用默认的升序 41 sort(array, array + MAX_DATA_NUM); 42 43 int start = (MAX_DATA_NUM - WINDOW_DATA_NUM) / 2; //start = REMOVE_MAXMIN_NUM 44 45 for(i = start; i < start + WINDOW_DATA_NUM; i++) 46 { 47 sum += array[i]; 48 } 49 out = sum / WINDOW_DATA_NUM; 50 } 51 return out; 52 } 53 54 //对函数模板进行函数实例化操作,根据实际需要自定义数据类型 55 template short Filter_SlidingWindowAvg(int index, short data); 56 template unsigned int Filter_SlidingWindowAvg(int index, unsigned int data);
四、滑动窗口滤波器的C代码实现
1、基本思路
1、在工程根目录下新建filter文件夹,将头filter.h和源文件filter.c放在文件夹*修改调用。
2、头文件:
(1)定义滤波算法函数中的可修改宏值;
(2)声明定义的滤波算法函数;
3、源文件:
(1)定义冒泡排序功能函数;
(2)定义滤波算法功能函数。
2、头文件filter.h
1 #ifndef _FILTER_H_ 2 #define _FILTER_H_ 3 4 #define MAX_SENSOR_NUM 9 //使用滤波时的传感器数量 5 #define MAX_DATA_NUM 9 //最大采样点数量,即采样窗口长度 6 #define WINDOW_DATA_NUM 5 //滤波窗口长度 7 //去除采样窗口内最大最小值的数量,这里去除两个最大和两个最小 8 #define REMOVE_MAXMIN_NUM ((MAX_DATA_NUM - WINDOW_DATA_NUM)/2) 9 10 //extern double m_dataList[MAX_SENSOR_NUM][MAX_DATA_NUM]; 11 12 //声明定义的函数 13 double Filter_SlidingWindowAvg(int index, double data); 14 15 #endif
3、源文件filter.c
1 #include "Filter.h" 2 3 //定义各个传感器的数据采样点列表 4 double m_dataList[MAX_SENSOR_NUM][MAX_DATA_NUM] = {0}; 5 6 //冒泡排序 7 void BubbleSort(int array[], int len) 8 { 9 int temp; 10 //外层循环控制排序的趟数,n个元素排序需要循环n-1次 11 for(int i=0; i<len-1; i++) 12 { 13 //内层循环控制比较的次数,n个元素第i趟比较n-i次 14 for(int j=0; j<len-1-i; j++) 15 { 16 //比较相邻的元素大小 目的:将最大的元素选出到移动到最后 17 if(array[j] > array[j+1]) 18 { 19 temp = array[j]; 20 array[j] = array[j+1]; 21 array[j+1] = temp; 22 } 23 } 24 } 25 } 26 27 //滑窗均值滤波,这里采样点data数据类型和滤波后返回值数据类型都是double,实际使用可根据需要定义 28 //其他数据类型 29 double Filter_SlidingWindowAvg(int index, double data) 30 { 31 static int dataNum[MAX_SENSOR_NUM] = {0}; //定义记录传感器的采样点个数 32 int i; 33 double sum = 0; 34 double out = 0; 35 double array[MAX_DATA_NUM] = {0}; 36 37 //数据采样点在采样窗口内移动,FIFO操作 38 for(i = MAX_DATA_NUM - 2; i >= 0; i--) 39 m_dataList[index][i+1] = m_dataList[index][i]; 40 41 m_dataList[index][0] = data; 42 //数据采样点数量小于采样窗口长度,对采样窗口数据累加后进行平均值运算 43 if(dataNum[index] < MAX_DATA_NUM) 44 { 45 dataNum[index]++; 46 for(i = 0; i < dataNum[index]; i++) 47 { 48 sum += m_dataList[index][i]; 49 } 50 out = sum / dataNum[index]; 51 } 52 //数据采样点已填满采样窗口,进行排序后,去除n个最大值及最小值后,对滤波窗口内的数据累加后进 53 //行平均值运算 54 else 55 { 56 for(i = 0; i < MAX_DATA_NUM; i++) 57 { 58 array[i] = m_dataList[index][i]; 59 } 60 //调用冒泡排序函数 61 BubbleSort(array, MAX_DATA_NUM); 62 63 int start = (MAX_DATA_NUM - WINDOW_DATA_NUM) / 2; //start = REMOVE_MAXMIN_NUM 64 65 for(i = start; i < start + WINDOW_DATA_NUM; i++) 66 { 67 sum += array[i]; 68 } 69 out = sum / WINDOW_DATA_NUM; 70 } 71 return out; 72 }