脈衝濾波算法

脈衝濾波算法
 

本文博客鏈接: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時,表示週期無效。

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章