滤波算法

来源:互联网 发布:it管理软件 编辑:程序博客网 时间:2024/05/20 03:46

该算法主要用于消除尖脉冲,同时又有很好的阶跃响应能力。
尖脉冲干扰和阶跃响应是数据采集中的两个很矛盾的问题。往往为了消除尖脉冲,会损失阶跃响应能力,造成很大的延时。该算法在这个问题上有所改善。

实例场景:在很短的时间 T 内,连续测量某一电压值 n 次。得到一个集合 Vset = {v1, v2, … , vn};
假设前提:如果我们相信所用电压表正常,那么通过这一组数据应该能得到一个最接近真实值的结果 Vreal;并且理想情况下 Vreal ≈ v1, v2, … , vn;
实际情况:在集合 Vset 中,很可能出现若干个 Verror 与 Vreal 偏差太大。如果把这些值参与运算,会对结果造成很大的影响
算法思想:如果 Vset 中的数据真实可靠,那么一定存在一个子集 Vright ,满足 Vright 中的所有元素都相近。换句话说,如果 Vset 中的所有数据都千差万别,那么 Vset 所代表的这组测量数据就不可靠。
算法目标:找到上述 Vright 集合,对其所有元素求算术平均数。注意,这里的 Verror 不会被立即丢弃,它们有的将会参与到后续的计算中

下面的代码实现了上述思想;
1、理想情况下:输出结果是除尖脉冲以外的采样值的算术平均值
2、最糟糕的的情况下:输出结果是所有采样值的算术平均值

/** File: Filter_Statistics.h* Date: 2016-10-09* Author: Hy*/#ifndef _FILTER_STATISTICS_H#define _FILTER_STATISTICS_H// 队列元素结构体typedef struct _FILTER_STATISTICS_ITEM{    u32 Value;     // 采样数值    // 可靠度    u32 Matrix  : 27; // 和其它元素之间的关系:投票名单;        // 一个位表示一个元素:某一位为 1 ,表示这两个数据相近;为 0 ,表示相差很大    u32 Counter : 5;  // 与此元素相似的元素的个数:票数        // 也就是 Matrix 中 1 的个数}FILTER_STATISTICS_ITEM, *PFILTER_STATISTICS_ITEM;typedef struct _FILTER_STATISTICS_OBJECT{    PFILTER_STATISTICS_ITEM QueBody;       // 队列    u8  QueLength;     // 队列长度(小于等于 27)    u8  Probability;   // 概率的期望值(在 QueLength 次采样中,有 Probability 个是可靠的)    u16 Deviation;     // 允许偏差(按误差类型不同表达的意义不同)        // 绝对误差 - 两个采样值之间的差值小于此数,则被认定为相近关系;         // 相对误差 - 两个采样值之间的差值与此数之积小于较大的采样值,则被认定为相近关系    u8  DeviationType; // 误差类型:0 - 绝对误差; 1 - 相对误差    u8  Cursor;        // 游标的位置;        // 游标指示哪一个采样值进入队列时间最早(最早的采样值将会被新的采样值替换)    u8  Counter;       // 可靠数据的个数:入选数量    u8  QueSize;       // 队列大小    u32 CredibleSum;   // 可靠数据的和    u32 SuspectSum;    // 不可靠数据的和}FILTER_STATISTICS_OBJECT, *PFILTER_STATISTICS_OBJECT;// 初始化滤波器对象void Filter_Statistics_Init(PFILTER_STATISTICS_OBJECT pd);// 滤波:输入一个采样数据,返回一个结果u32  Filter_Statistics_Pass(PFILTER_STATISTICS_OBJECT pd, u32 i);// 读取当前结果的可信度u32  Filter_Statistics_Counter(PFILTER_STATISTICS_OBJECT pd);#endif/** File: Filter_Statistics.c* Date: 2016-10-09* Author: Hy*/#include "Filter_Statistics.h"/** 初始化滤波器对象*/void Filter_Statistics_Init(PFILTER_STATISTICS_OBJECT pobj){    u32 i;// 队列长度限制    if(pobj->QueLength > 27)        pobj->QueLength = 27;// 初始化队列    for(i = 0; i < pobj->QueLength; i ++)    {        pobj->QueBody[i].Value = 0;        pobj->QueBody[i].Matrix = 0x07FFFFFF;     // 所有的元素都是 0 ,因此都相似        pobj->QueBody[i].Counter = pobj->QueLength; // 所有的元素都是 0 ,因此相似个数为 pobj->QueLength    }// 默认参数    //     if(pobj->Probability > pobj->QueLength)        pobj->Probability = pobj->QueLength;    // 为了避免浮点数运算提高效率(STM32没有硬件浮点数单元),我一般用小单位,比如:毫伏、毫安、毫秒    // 这里的 1000 就代表 1 了,既默认偏差为 1 (伏、安、秒)    if(pobj->DeviationType && pobj->Deviation > 1000)        pobj->Deviation = 1000;// 内部寄存器    pobj->Cursor = 0; //     pobj->Counter = pobj->QueLength;    pobj->CredibleSum = 0;    pobj->SuspectSum = 0;    pobj->QueSize = 0;}/** 功能:滤波* 输入:新的采样数据* 输出:滤波后的结果* 说明:该函数维护一个循环队列;新的数据会覆盖最早的数据*/u32 Filter_Statistics_Pass(PFILTER_STATISTICS_OBJECT pobj, u32 i){    u32 dev, thr;    FILTER_STATISTICS_ITEM fi = {0};    fi.Value = i;// 统计新采样值、将被替换的采样值与其它采样值之间的相似度    for(i = 0; i < pobj->QueLength; i ++)    {        // 旧采样值与其它采样值之间的相似        if(pobj->QueBody[pobj->Cursor].Matrix & (1 << i))        {            // 在 i 的名单上去掉 Cursor             pobj->QueBody[i].Matrix &= (~(1 << pobj->Cursor));            // 与编号为 i 的采样值相近的值少了一个            pobj->QueBody[i].Counter --;            // 票数减少导致出局(i 变得不可信)            if(pobj->QueBody[i].Counter + 1 == pobj->Probability)            {                // 整体可信度降低                pobj->Counter --;                //                 pobj->CredibleSum -= pobj->QueBody[i].Value;                pobj->SuspectSum += pobj->QueBody[i].Value;            }        }        // 计算新采样值与其它采样值之间的偏差        if(pobj->QueBody[i].Value > fi.Value)        {            thr = pobj->QueBody[i].Value;            dev = (thr - fi.Value);        }        else        {            thr = fi.Value;            dev = (thr - pobj->QueBody[i].Value);        }        // 初始化时,误差类型为“相对误差”        if(pobj->DeviationType)            dev *= pobj->Deviation;        // 初始化时,误差类型为“绝对误差”        else            thr = pobj->Deviation;        // 两个采样值之间的差值在允许误差范围内        if(dev < thr)        {            // 将采样值 i 记录到新采样值的名单中            fi.Matrix |= (1 << i);            // 新采样值可信度增加            fi.Counter ++;            // 将新采样值记录到 i 的名单中            pobj->QueBody[i].Matrix |= (1 << pobj->Cursor);            // 采样值 i 的可信度增加            pobj->QueBody[i].Counter ++;            // 票数增加导致入局            if(pobj->QueBody[i].Counter == pobj->Probability)            {                // 整体可信度增加                pobj->Counter ++;                pobj->CredibleSum += pobj->QueBody[i].Value;                pobj->SuspectSum -= pobj->QueBody[i].Value;            }        }    }// 清理旧采样值    // 旧采样值是可信的    if(pobj->QueBody[pobj->Cursor].Counter >= pobj->Probability)    {        // 整体可信度减小        pobj->Counter --;        pobj->CredibleSum -= pobj->QueBody[pobj->Cursor].Value;    }    else        pobj->SuspectSum -= pobj->QueBody[pobj->Cursor].Value;// 新采样值    // 新采样值可信度较高    if(fi.Counter >= pobj->Probability)    {        // 整体可信度增加        pobj->Counter ++;        pobj->CredibleSum += fi.Value;    }    else        pobj->SuspectSum += fi.Value;    //     pobj->QueBody[pobj->Cursor] = fi;// 游标、计数器    pobj->Cursor ++; // 后面的一个采样值将变成最老的值,在下一次将会被替换    if(pobj->Cursor == pobj->QueLength)        pobj->Cursor = 0;// 结果    if(pobj->QueSize == pobj->QueLength)    {        // 有可靠数据存在;求可靠数据的平均数        if(pobj->Counter)            return pobj->CredibleSum / pobj->Counter;        // 所有数据都不太可靠;求所有数据的平均值        else            return pobj->SuspectSum / pobj->QueLength;    }    else    {        pobj->QueSize ++;        return (pobj->CredibleSum + pobj->SuspectSum) / pobj->QueSize;    }}/** 读取当前结果的可信度*/u32  Filter_Statistics_Counter(PFILTER_STATISTICS_OBJECT pobj){    return pobj->Counter;}// ----------------- END -----------------------------------------------

使用实例:

#define FILTER_VOLTAGE_QUELENGTH 10#define FILTER_VOLTAGE_DEVIATION 100#define FILTER_VOLTAGE_PROBABILITY 5// 定义队列FILTER_STATISTICS_ITEM   l_fi_Voltage[FILTER_VOLTAGE_QUELENGTH] = {0};// 定义滤波对象FILTER_STATISTICS_OBJECT l_fo_Voltage = {0};// 初始化l_fo_Voltage.QueBody = l_fi_Voltage;l_fo_Voltage.QueLength   = FILTER_VOLTAGE_QUELENGTH;l_fo_Voltage.Deviation   = FILTER_VOLTAGE_DEVIATION; // 40mAl_fo_Voltage.Probability = FILTER_VOLTAGE_PROBABILITY; // 40%Filter_Statistics_Init((PFILTER_STATISTICS_OBJECT)(&l_fo_Voltage));// u32 l_Voltage_Sample; // 原始值u32 l_Voltage_Filter; // 滤波后的值// 周期性地调用滤波函数l_Voltage_Sample = ADC_GET_ANALOG(ADC_VOLTAGE); // 读取原始采样值l_Voltage_Filter = Filter_Statistics_Pass((PFILTER_STATISTICS_OBJECT)(&l_fo_Voltage), l_Voltage_Sample); // 滤波

延伸探讨:
关于滤波,一般都是单通道多采样几次然后进行分析,这样总会有延迟;高速滤波可采用多个 ADC 通道同时采集同一个电压然后放入滤波器。

0 0
原创粉丝点击