脈衝濾波算法
本文博客鏈接:http://blog.csdn.net/jdh99,作者:jdh,轉載請註明.
說明:
有一些傳感器採集的數據基本準確,但有偶發性的錯誤。本文設計的脈衝濾波算法可以過濾掉這些脈衝干擾。
算法原理:
建一個數組保存數據,每個數據都有一個有效性標誌。有新數據時,跟前一個有效數據比較差值,小於閾值則有效,大於閾值則無效。如果無效數據過多,則算法失效,認爲所有數據都有效。
算法提供讀取接口,可以返回最新的有效數據。
算法優勢:
實時性很高,數據正確時是0延時,數據錯誤或者劇烈變化時會造成延時。算法適用於採集的數據可靠性很高,僅有偶發性錯誤的場景。
源碼:
pulse_filter.h:
/**
* Copyright (c), 2015-2025
* @file pulse_filter.c
* @brief 脈衝過濾器模塊頭文件
* @verbatim
* Change Logs:
* Date Author Notes
* 2017-10-27 jdh 新建
* 2018-10-09 jdh 時間校準用週期數的機制進行處理
* 2019-02-26 jdh 標準化
* @endverbatim
*/
#ifndef _PULSE_FILTER_H_
#define _PULSE_FILTER_H_
#include "stdlib.h"
#include "stdio.h"
/**
* @brief 創建脈衝濾波器
* @param num_filter_pulse: 過濾的脈衝數
* @param max_delta: 最大偏差值
* @param cycle: 鍵值週期.如果爲0表示沒有周期
* @return 過濾器索引
*/
int pulse_filter_create(int num_filter_pulse, int max_delta, int cycle);
/**
* @brief 寫入數據
* @note 針對本項目進行定製.輸入的數是一個週期數
* @param key: 鍵值
* @param cycle: 週期
*/
void pulse_filter_write(int filter_index, int key);
/**
* @brief 讀取數據
*/
int pulse_filter_read(int filter_index);
/**
* @brief 是否包含數據
* @retval true:有.false:沒有
*/
bool pulse_filter_is_contain_data(int filter_index);
/**
* @brief 打印過濾器
*/
void pulse_filter_print(int filter_index);
#endif
pulse_filter.c:
/**
* Copyright (c), 2015-2025
* @file pulse_filter.c
* @brief 脈衝過濾器模塊主文件
* @verbatim
* Change Logs:
* Date Author Notes
* 2017-10-27 jdh 新建
* 2018-10-09 jdh 時間校準用週期數的機制進行處理
* 2019-02-26 jdh 標準化
* @endverbatim
*/
#include "pulse_filter.h"
/**
* @brief 濾波器結構
*/
typedef struct
{
// 過濾連續脈衝數
int num_filter_pulse;
// 週期
int cycle;
// 最大偏差值
int max_delta;
// 緩存節點數
int num_node;
// key數組.基於key進行濾波
int *key;
// 數據有效標誌數組
bool *valid;
} Filter;
static void append(Filter *filter, int key, bool is_valid);
/**
* @brief 得到最近的有效數據下表
* @note 不考慮第一個節點.因爲第一個節點需要被刪除
* @param filter: 濾波器
* @return -1:無有效數據.其他:下標
*/
static int get_newest_valid_index(Filter *filter);
static void pop_first(Filter *filter);
static void set_all_node_valid(Filter *filter);
static bool key_is_valid(Filter *filter, int valid_index, int key);
/**
* @brief 計算兩個週期數的最小差值
* @note 比如兩個數1, 9.都是在週期10以內循環的。所以最小差值應該爲2而不是8
* @param x: 數1
* @param y: 數2
* @return 差值
*/
int calc_min_delta_in_cycle(int x, int y, int cycle);
/**
* @brief 創建脈衝濾波器
* @param num_filter_pulse: 過濾的脈衝數
* @param max_delta: 最大偏差值
* @param cycle: 鍵值週期.如果爲0表示沒有周期
* @return 過濾器索引
*/
int pulse_filter_create(int num_filter_pulse, int max_delta, int cycle)
{
Filter *filter = (Filter *)malloc(sizeof(Filter));
filter->num_filter_pulse = num_filter_pulse;
filter->max_delta = max_delta;
filter->cycle = cycle;
int item_num = filter->num_filter_pulse + 2;
filter->key = (int *)malloc(item_num * sizeof(int));
filter->valid = (bool *)malloc(item_num * sizeof(bool));
filter->num_node = 0;
return (int)filter;
}
/**
* @brief 寫入數據
* @note 針對本項目進行定製.輸入的數是一個週期數
* @param key: 鍵值
* @param cycle: 週期
*/
void pulse_filter_write(int filter_index, int key)
{
Filter *filter = (Filter *)filter_index;
int item_num = filter->num_filter_pulse + 2;
if (filter->num_node < item_num)
{
append(filter, key, true);
return;
}
pop_first(filter);
int index = get_newest_valid_index(filter);
if (index == -1)
{
append(filter, key, true);
set_all_node_valid(filter);
return;
}
append(filter, key, key_is_valid(filter, index, key));
}
static void append(Filter *filter, int key, bool is_valid)
{
int item_num = filter->num_filter_pulse + 2;
if (filter->num_node < item_num)
{
filter->key[filter->num_node] = key;
filter->valid[filter->num_node] = is_valid;
filter->num_node++;
return;
}
}
/**
* @brief 得到最近的有效數據下表
* @note 不考慮第一個節點.因爲第一個節點需要被刪除
* @param filter: 濾波器
* @return -1:無有效數據.其他:下標
*/
static int get_newest_valid_index(Filter *filter)
{
for (int i = 0; i < filter->num_node; i++)
{
if (filter->valid[filter->num_node - 1 - i])
{
return filter->num_node - 1 - i;
}
}
return -1;
}
static void pop_first(Filter *filter)
{
int item_num = filter->num_filter_pulse + 2;
for (int i = 0; i < item_num - 1; i++)
{
filter->key[i] = filter->key[i + 1];
filter->valid[i] = filter->valid[i + 1];
}
filter->num_node--;
}
static void set_all_node_valid(Filter *filter)
{
int item_num = filter->num_filter_pulse + 2;
for (int i = 0; i < item_num; i++)
{
filter->valid[i] = true;
}
}
/**
* @brief 計算兩個週期數的最小差值
* @note 比如兩個數1, 9.都是在週期10以內循環的。所以最小差值應該爲2而不是8
* @param x: 數1
* @param y: 數2
* @return 差值
*/
int calc_min_delta_in_cycle(int x, int y, int cycle)
{
if (cycle == 0)
{
if (x > y)
{
return x - y;
}
else
{
return y - x;
}
}
int a = (x - y + cycle) % cycle;
int b = cycle - a;
if (a > b)
{
return b;
}
else
{
return a;
}
}
static bool key_is_valid(Filter *filter, int valid_index, int key)
{
return (calc_min_delta_in_cycle(filter->key[valid_index], key, filter->cycle) <= filter->max_delta);
}
/**
* @brief 讀取數據
*/
int pulse_filter_read(int filter_index)
{
Filter *filter = (Filter *)filter_index;
if (filter->num_node == 0)
{
return 0;
}
int index = get_newest_valid_index(filter);
if (index == -1)
{
return 0;
}
return filter->key[index];
}
/**
* @brief 是否包含數據
* @retval true:有.false:沒有
*/
bool pulse_filter_is_contain_data(int filter_index)
{
Filter *filter = (Filter *)filter_index;
return (filter->num_node > 0);
}
/**
* @brief 打印過濾器
*/
void pulse_filter_print(int filter_index)
{
Filter *filter = (Filter *)filter_index;
printf("--------------\n");
for (int i = 0; i < filter->num_node; i++)
{
printf("key = %d valid = %d\n", filter->key[i], filter->valid[i]);
}
}
測試代碼:
#include "pulse_filter.h"
int main()
{
int filter = pulse_filter_create(2, 10, 0);
int data[10] = {205, 205, 206, 207, 250, 238, 209, 210, 210, 211};
printf("最大過濾2個脈衝, 限幅爲10\n");
int output = 0;
for (int i = 0; i < 10; i++)
{
pulse_filter_write(filter, data[i]);
output = pulse_filter_read(filter);
if (data[i] == output)
{
printf("input:%d output:%d\n", data[i], output);
}
else
{
printf("input:%d -->filter output:%d\n", data[i], output);
}
}
printf("-----------------------\n");
int data1[10] = {205, 205, 206, 207, 250, 245, 240, 242, 243, 242};
output = 0;
for (int i = 0; i < 10; i++)
{
pulse_filter_write(filter, data1[i]);
output = pulse_filter_read(filter);
if (data1[i] == output)
{
printf("input:%d output:%d\n", data1[i], output);
}
else
{
printf("input:%d -->filter output:%d\n", data1[i], output);
}
}
getchar();
return 0;
}
測試輸出:
最大過濾2個脈衝, 限幅爲10
input:205 output:205
input:205 output:205
input:206 output:206
input:207 output:207
input:250 -->filter output:207
input:238 -->filter output:207
input:209 output:209
input:210 output:210
input:210 output:210
input:211 output:211
-----------------------
input:205 output:205
input:205 output:205
input:206 output:206
input:207 output:207
input:250 -->filter output:207
input:245 -->filter output:207
input:240 -->filter output:207
input:242 output:242
input:243 output:243
input:242 output:242
創建濾波器時需要寫入一個週期值:
/**
* @brief 創建脈衝濾波器
* @param num_filter_pulse: 過濾的脈衝數
* @param max_delta: 最大偏差值
* @param cycle: 鍵值週期.如果爲0表示沒有周期
* @return 過濾器索引
*/
int pulse_filter_create(int num_filter_pulse, int max_delta, int cycle);
cycle用於週期數時使用。比如在一個週期爲8s的校時系統中,則1s和7s的最小差值是2s而不是6s。當cycle爲0時,表示週期無效。