欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一阶RC低通滤波算法原理与实现

程序员文章站 2024-03-25 21:58:04
...

本文整理自网络,参考文献附在文末,侵权请联系!


1. 一阶低通滤波算法原理

一阶滤波,又叫一阶惯性滤波,或一阶低通滤波,软件实现RC低通滤波器的功能。

Y(n)=αX(n)+(1α)Y(n1)Y(n)=αX(n) + (1-α)Y(n-1)

式中:αα为滤波系数,X(n)X(n)为本次采样值,Y(n1)Y(n-1)为上次滤波输出值,Y(n)Y(n)为本次滤波输出值
一阶RC低通滤波算法原理与实现

2. 一阶滤波算法的特点

  • 对于周期干扰有良好的抑制作用(优)

  • 带来了相位滞后,导致灵敏度低(缺)

  • 不能滤除频率高于采样频率的二分之一(称为奈奎斯特频率)的干扰(例如采样频率为100Hz,则它不能滤除50Hz以上的干扰信号)(缺)

  • 滤波系数越小,滤波结果越平稳,灵敏度越低

  • 滤波系数越大,灵敏度越高,但滤波结果越不稳定

一阶滤波无法完美地兼顾灵敏度和平稳度。有时,我们只能寻找一个平衡,在可接受的灵敏度范围内取得尽可能好的平稳度。而在一些场合,我们希望拥有这样一种接近理想状态的滤波算法。即:当数据快速变化时,滤波结果能及时跟进(灵敏度优先);当数据趋于稳定,在一个固定的点上下振荡时,滤波结果能趋于平稳(平稳度优先)。

3. 基本算法的例程

案例1:油门数据滤波

// 油门滤波
thr_lpf+=(1/(1+1/(2.0f*3.14f*T)))*(height_thr-thr_lpf)

案例2:

#define a   0.01                // 滤波系数a(0-1) 

static float oldOutData = 0;

char filter(void)
{
    nowData  = get_Data(); 
    nowOutData = a * nowData  + (1.0f - a) * oldOutData;
    oldOutData = nowOutData;
    return nowOutData;  
}

////////////////////////////////////////////////////////////////////
/*
程序中整数运算比小数运算快,为加快程序的处理速度,
为计算方便,a取一整数,1-a用256-a来代替,
a则取0~255,代表新采样值在滤波结果中的权重
(也可将1-a的基数改为100-a,计算结果做相应处理,这里不做说明)
*/

#define a 128 

char value; //上次滤波值
char filter()
{
    char new_value;
    new_value=get_ad();//本次采样值
    return(256-a)*value/256+a*new_value/256}

案例3:MATLAB测试

% 一阶低通滤波器测试
close all;
t = 0.003;
A = 1/(1+(1/(2*pi*t)));
tt = 0:t:25;
y = sin(0.5*tt);
y_noise = awgn(y,35);
y_proce = y_noise;
for i = 2:length(y)
    y_proce(i) = (1-A) * y_proce(i-1) + A * y_noise(i);
end

plot(tt,y,'r');
figure(2)
plot(tt,y_noise,'g');hold on;
plot(tt,y_proce,'b');
hold off;

4. 优化:减少乘、除的运算次数以提高运算速度

思路:先将新采样值与上次滤波结果进行比较,然后根据比较采用不同的公式计算,这样程序的运算效率提高了一倍

一阶RC低通滤波算法原理与实现
一阶RC低通滤波算法原理与实现

/*入口:NEW_DATA 新采样值
       OLD_DATA 上次滤波结果
       k        滤波系数(0~255)(代表在滤波结果中的权重)
  出口:         本次滤波结果
 */
 char filter_1(char NEW_DATA,char OLD_DATA,char k)
{
    int result;
    if(NEW_DATA<OLD_DATA)
    {
        result=OLD_DATA-NEW_DATA;
        result=result*k;
        result=result+128;//+128是为了四色五入
        result=result/256;
        result=OLD_DATA-result;
    }
    else if(NEW_DATA>OLD_DATA)
    {
        result=NEW_DATA-OLD_DATA;
        result=result*k;
        result=result+128;//+128是为了四色五入
        result=result/256;
        result=OLD_DATA-result;
    }
    else result=OLD_DATA;
    return((char)result);
}

分析:

  • 仍然存在灵敏度与平温度之间的矛盾
  • 小数舍弃带来的误差(单片机很少采用浮点数,小数位要么舍弃要么四舍五入)

5. 改进:动态调整滤波系数

实现功能:

  • 当数据快速变化时,滤波结果能及时跟进,并且数据的变化越快,灵敏度应该越高(灵敏度优先原则)
  • 当数据趋于稳定,并在一个范围内振荡时,滤波结果能趋于平稳(平稳度优先原则)
  • 当数据稳定后,滤波结果能逼近并最终等于采样数据(消除因计算中小数带来的误差)
    调整前判断:
  • 数据变化方向是否为同一个方向(如当连续两次的采样值都比其上次滤波结果大时,视为变化方向一致,否则视为不一致)
  • 数据变化是否较快(主要是判断采样值和上一次滤波结果之间的差值)
    调整原则:
  • 当两次数据变化不一致时,说明有抖动,将滤波系数清零,忽略本次新采样值
  • 当数据持续向一个方向变化时,逐渐提高滤波系数,提供本次采样值得权;
  • 当数据变化较快(差值>消抖计数加速反应阈值)时,要加速提高滤波系数

一阶RC低通滤波算法原理与实现
几个常量参数及其取值范围(不同的取值会影响滤波的灵敏度和稳定度):

  1. 消抖计数加速反应阈值,取值根据数据情况确定
  2. 消抖计数最大值,一般取值10;
  3. 滤波系数增量,一般取值范围为10~30
  4. 滤波系数最大值,一般取值255;

一阶RC低通滤波算法原理与实现

在调用一阶滤波程序前,先调用调整滤波系数程序,对系数进行即时调整。滤波效果:

  1. 当采样数据偶然受到干扰,滤波结果中的干扰完全被滤除
  2. 当数据在一个范围内振荡时,滤波结果曲线非常平滑,几乎是一根直线
  3. 当采样数据发生真实的变化时,滤波结果也能比较及时地跟进
  4. 当采样数据趋于稳定时,滤波结果逐渐逼近并最终等于采样数据
  • 最终改进算法,兼顾了灵敏度和平稳度的要求;同时又不太消耗系统的RAM;
  • 只要合理调整几个常量,以使得算法更合适实际应用;

动态调整滤波例程

//用MPU6050测得数据;对x轴滤波处理

#define Threshold_1     8       //阈值1用于一阶带参滤波器,变化角度大于此值时,计数增加
#define Threshold_2     30      //阈值2用于一阶带参滤波器,计数值大于此值时,增大参数,增强滤波跟随

float K_x=0; //滤波系数
u8 new_flag_x=0;//本次数据变化方向
u8 num_x=0;//滤波计数器


/*****带系数修改的一阶滤波函数

入口: NEW_DATA    新采样的角度值
      OLD_DATA    上次滤波获得的角度结果
      k           滤波系数(代表在滤波结果中的权重)
      flag        上次数据变化方向
出口: result      本次滤波角度结果
 */
float filter_1_x(float NEW_DATA,float OLD_DATA,float k,u8 flag)
{


    //角度变化方向,new_flag=1表示角度增加,=0表示角度正在减小
    if((NEW_DATA-OLD_DATA)>0)
        new_flag_x=1;
    else if((NEW_DATA-OLD_DATA)<0)
        new_flag_x=0;


    if(new_flag_x==flag)  //此次变化与前一次变化方向是否一致,相等表示角度变化方向一致
        {
            num_x++;
            if(fabs((NEW_DATA-OLD_DATA))>Threshold_1)
        //当变化角度大于Threshold_1度的时候,进行计数器num快速增加,以达到快速增大K值,提高跟随性
                num_x+=5;                           

            if(num_x>Threshold_2)   //计数阈值设置,当角度递增或递减速度达到一定速率时,增大K值
            {
                K_x=k+0.2;          //0.2为K_x的增长值,看实际需要修改
                num_x=0;
            }
        }
    else 
        {
            num_x=0;
            K_x=0.01;     //角度变化稳定时K_x值,看实际修改
        }

    OLD_DATA=(1-K_x)*OLD_DATA+K_x*NEW_DATA;
    return OLD_DATA;
}


参考文献: