前言
在嵌入式開發程式中涉及資料取樣如感測器取樣,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 }