一阶低通滤波

前言:
  在使用单片机开发中,常常会用到的外设包括ADC采样。而采样必然会伴随这随机干扰引起的毛刺噪声,对于需要捕捉采样值突变的系统来说尤其需要减小毛刺突变的影响。从硬件电路和软件算法上都能一定程度的减少噪声达到滤波的目的,本文主要讲解软件使用低通滤波算法来滤波ADC采样值的方法。

1 一阶低通滤波(又叫惯性滤波)算法

1.1 算法原理

  滤波算法公式:
Y(n) = a * X(n) + (1 - a) * Y(n - 1)

Y(n):本次滤波结果。
a:滤波系数。取值范围为0~1, 值越小越稳定,越大越灵敏。
X(n):本次采样值。
Y(n - 1):上次滤波结果。

1.2 C代码实现

1
2
3
4
5
6
7
8
9
10
11
//一阶低通滤波
#define FO_LOW_PASS_FILTER_SENSITIVE_a 0.8f //一阶低通滤波系数取值范围为(0,1)。值越小越稳定,越大越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_STEADY_a 0.15f

//输入Adc采样值,输出低通滤波值
//优点:调节精细,稳定度和灵敏度偏向分明。缺点:带有浮点运算
void FOLowPassFilter(AdcSample, a)
{
g_u16FOLowPassFilterResult = (uint16_t)(a * AdcSample + (1 - a) * g_u16FOLowPassFilterResultL);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

1.3 一阶低通滤波算法特点

  使用一阶低通滤波算法的优点:

  • 相比于硬件滤波,软件滤波节省成本,可靠性高,滤波范围可以由软件任意修改(硬件需要考虑阻抗匹配问题)。
  • 一阶低通滤波运算量小,需要调节的参数少,易于软件实现和单片机快速处理,是实时性强的滤波。

  使用一阶低通滤波算法的缺点:

  • 滤波系数越小,滤波结果越稳定,滤波系数越大结果越灵敏。稳定性和灵敏性二者难兼顾。

2 算法改进

2.1 系数a取较小值时(偏稳定)

一阶低通滤波

前言:
  在使用单片机开发中,常常会用到的外设包括ADC采样。而采样必然会伴随这随机干扰引起的毛刺噪声,对于需要捕捉采样值突变的系统来说尤其需要减小毛刺突变的影响。从硬件电路和软件算法上都能一定程度的减少噪声达到滤波的目的,本文主要讲解软件使用低通滤波算法来滤波ADC采样值的方法。

1 一阶低通滤波(又叫惯性滤波)算法

1.1 算法原理

  滤波算法公式:
Y(n) = a * X(n) + (1 - a) * Y(n - 1)

Y(n):本次滤波结果。
a:滤波系数。取值范围为0~1, 值越小越稳定,越大越灵敏。
X(n):本次采样值。
Y(n - 1):上次滤波结果。

1.2 C代码实现

1
2
3
4
5
6
7
8
9
10
11
//一阶低通滤波
#define FO_LOW_PASS_FILTER_SENSITIVE_a 0.8f //一阶低通滤波系数取值范围为(0,1)。值越小越稳定,越大越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_STEADY_a 0.15f

//输入Adc采样值,输出低通滤波值
//优点:调节精细,稳定度和灵敏度偏向分明。缺点:带有浮点运算
void FOLowPassFilter(AdcSample, a)
{
g_u16FOLowPassFilterResult = (uint16_t)(a * AdcSample + (1 - a) * g_u16FOLowPassFilterResultL);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

1.3 一阶低通滤波算法特点

  使用一阶低通滤波算法的优点:

  • 相比于硬件滤波,软件滤波节省成本,可靠性高,滤波范围可以由软件任意修改(硬件需要考虑阻抗匹配问题)。
  • 一阶低通滤波运算量小,需要调节的参数少,易于软件实现和单片机快速处理,是实时性强的滤波。

  使用一阶低通滤波算法的缺点:

  • 滤波系数越小,滤波结果越稳定,滤波系数越大结果越灵敏。稳定性和灵敏性二者难兼顾。

2 算法改进

2.1 系数a取较小值时(偏稳定)

a0.15细节图.png

  图中蓝色表示原始ADC采样值,橙色为滤波后的值,其他图片也这样。当电压不变化时采集到的ADC采样值实际存在很多毛刺噪声,选取较小的a值则能有效减小毛刺的幅度,使信号趋于稳定,防止误判。

a0.1.png

a0.15.png

  选取较小a值时,稳定段过滤很多毛刺噪声,但灵敏性(跟随性)较差,信号幅度变化明显时滤波结果明显滞后,且无法达到波峰波谷值。

2.2 系数a取较大时(偏灵敏)

  特点与a较小时相反,滤波结果有较好的跟随性,但消除毛刺噪声能力差。

2.3 使用动态系数

  既然一阶低通滤波无法同时兼容滤波的稳定性和灵敏性, 那么我们可以根据实际情况,在采样值较低且变换幅度较小时使用小系数偏稳定性,当检测到采样值大于一定范围且增幅明显时立刻且大系数偏灵敏性。

  代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Filter(AdcSample)
{
if(g_u16AdcSampleVal > g_u16AdcInitBaseVal)
{
if((DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValL) <= ADC_SAMPLE_LARGE_JUMP) //采样值无大幅度跳变
&& (DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValLL) <= ADC_SAMPLE_LARGE_JUMP))
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_SENSITIVE_a); //一阶低通滤波,偏灵敏
}
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
}

效果展示:

动态系数兼顾稳定性和灵敏度.png

a0.3.png

2.4 算法改进

  以下算法时一阶低通滤波的变形,可以避免单片机做浮点运算,如果你的单片机浮点运算的性能较差可以使用该变形公式。这个公式不是我发明的,是由一个大佬传授给我的,我将其拿出来分享。

1
2
3
4
5
6
7
8
9
10
11
12
//一阶低通滤波变形
#define FO_LOW_PASS_FILTER_EXTERN_SENSITIVE_a 1u //值越大越稳定,越小越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_EXTERN_STEADY_a 7u

//一阶低通滤波变形,此方法为网上借鉴
//优点:无浮点运算。缺点:调节粗糙。
void FOLowPassFilterExtern(AdcSample, a)
{
g_u16FOLowPassFilterResult = AdcSample + g_u16FOLowPassFilterResultL * a;
g_u16FOLowPassFilterResult = g_u16FOLowPassFilterResult / (a + 1);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

  图中蓝色表示原始ADC采样值,橙色为滤波后的值,其他图片也这样。当电压不变化时采集到的ADC采样值实际存在很多毛刺噪声,选取较小的a值则能有效减小毛刺的幅度,使信号趋于稳定,防止误判。

一阶低通滤波

前言:
  在使用单片机开发中,常常会用到的外设包括ADC采样。而采样必然会伴随这随机干扰引起的毛刺噪声,对于需要捕捉采样值突变的系统来说尤其需要减小毛刺突变的影响。从硬件电路和软件算法上都能一定程度的减少噪声达到滤波的目的,本文主要讲解软件使用低通滤波算法来滤波ADC采样值的方法。

1 一阶低通滤波(又叫惯性滤波)算法

1.1 算法原理

  滤波算法公式:
Y(n) = a * X(n) + (1 - a) * Y(n - 1)

Y(n):本次滤波结果。
a:滤波系数。取值范围为0~1, 值越小越稳定,越大越灵敏。
X(n):本次采样值。
Y(n - 1):上次滤波结果。

1.2 C代码实现

1
2
3
4
5
6
7
8
9
10
11
//一阶低通滤波
#define FO_LOW_PASS_FILTER_SENSITIVE_a 0.8f //一阶低通滤波系数取值范围为(0,1)。值越小越稳定,越大越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_STEADY_a 0.15f

//输入Adc采样值,输出低通滤波值
//优点:调节精细,稳定度和灵敏度偏向分明。缺点:带有浮点运算
void FOLowPassFilter(AdcSample, a)
{
g_u16FOLowPassFilterResult = (uint16_t)(a * AdcSample + (1 - a) * g_u16FOLowPassFilterResultL);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

1.3 一阶低通滤波算法特点

  使用一阶低通滤波算法的优点:

  • 相比于硬件滤波,软件滤波节省成本,可靠性高,滤波范围可以由软件任意修改(硬件需要考虑阻抗匹配问题)。
  • 一阶低通滤波运算量小,需要调节的参数少,易于软件实现和单片机快速处理,是实时性强的滤波。

  使用一阶低通滤波算法的缺点:

  • 滤波系数越小,滤波结果越稳定,滤波系数越大结果越灵敏。稳定性和灵敏性二者难兼顾。

2 算法改进

2.1 系数a取较小值时(偏稳定)

a0.15细节图.png

  图中蓝色表示原始ADC采样值,橙色为滤波后的值,其他图片也这样。当电压不变化时采集到的ADC采样值实际存在很多毛刺噪声,选取较小的a值则能有效减小毛刺的幅度,使信号趋于稳定,防止误判。

a0.1.png

a0.15.png

  选取较小a值时,稳定段过滤很多毛刺噪声,但灵敏性(跟随性)较差,信号幅度变化明显时滤波结果明显滞后,且无法达到波峰波谷值。

2.2 系数a取较大时(偏灵敏)

  特点与a较小时相反,滤波结果有较好的跟随性,但消除毛刺噪声能力差。

2.3 使用动态系数

  既然一阶低通滤波无法同时兼容滤波的稳定性和灵敏性, 那么我们可以根据实际情况,在采样值较低且变换幅度较小时使用小系数偏稳定性,当检测到采样值大于一定范围且增幅明显时立刻且大系数偏灵敏性。

  代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Filter(AdcSample)
{
if(g_u16AdcSampleVal > g_u16AdcInitBaseVal)
{
if((DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValL) <= ADC_SAMPLE_LARGE_JUMP) //采样值无大幅度跳变
&& (DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValLL) <= ADC_SAMPLE_LARGE_JUMP))
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_SENSITIVE_a); //一阶低通滤波,偏灵敏
}
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
}

效果展示:

动态系数兼顾稳定性和灵敏度.png

a0.3.png

2.4 算法改进

  以下算法时一阶低通滤波的变形,可以避免单片机做浮点运算,如果你的单片机浮点运算的性能较差可以使用该变形公式。这个公式不是我发明的,是由一个大佬传授给我的,我将其拿出来分享。

1
2
3
4
5
6
7
8
9
10
11
12
//一阶低通滤波变形
#define FO_LOW_PASS_FILTER_EXTERN_SENSITIVE_a 1u //值越大越稳定,越小越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_EXTERN_STEADY_a 7u

//一阶低通滤波变形,此方法为网上借鉴
//优点:无浮点运算。缺点:调节粗糙。
void FOLowPassFilterExtern(AdcSample, a)
{
g_u16FOLowPassFilterResult = AdcSample + g_u16FOLowPassFilterResultL * a;
g_u16FOLowPassFilterResult = g_u16FOLowPassFilterResult / (a + 1);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

一阶低通滤波

前言:
  在使用单片机开发中,常常会用到的外设包括ADC采样。而采样必然会伴随这随机干扰引起的毛刺噪声,对于需要捕捉采样值突变的系统来说尤其需要减小毛刺突变的影响。从硬件电路和软件算法上都能一定程度的减少噪声达到滤波的目的,本文主要讲解软件使用低通滤波算法来滤波ADC采样值的方法。

1 一阶低通滤波(又叫惯性滤波)算法

1.1 算法原理

  滤波算法公式:
Y(n) = a * X(n) + (1 - a) * Y(n - 1)

Y(n):本次滤波结果。
a:滤波系数。取值范围为0~1, 值越小越稳定,越大越灵敏。
X(n):本次采样值。
Y(n - 1):上次滤波结果。

1.2 C代码实现

1
2
3
4
5
6
7
8
9
10
11
//一阶低通滤波
#define FO_LOW_PASS_FILTER_SENSITIVE_a 0.8f //一阶低通滤波系数取值范围为(0,1)。值越小越稳定,越大越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_STEADY_a 0.15f

//输入Adc采样值,输出低通滤波值
//优点:调节精细,稳定度和灵敏度偏向分明。缺点:带有浮点运算
void FOLowPassFilter(AdcSample, a)
{
g_u16FOLowPassFilterResult = (uint16_t)(a * AdcSample + (1 - a) * g_u16FOLowPassFilterResultL);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

1.3 一阶低通滤波算法特点

  使用一阶低通滤波算法的优点:

  • 相比于硬件滤波,软件滤波节省成本,可靠性高,滤波范围可以由软件任意修改(硬件需要考虑阻抗匹配问题)。
  • 一阶低通滤波运算量小,需要调节的参数少,易于软件实现和单片机快速处理,是实时性强的滤波。

  使用一阶低通滤波算法的缺点:

  • 滤波系数越小,滤波结果越稳定,滤波系数越大结果越灵敏。稳定性和灵敏性二者难兼顾。

2 算法改进

2.1 系数a取较小值时(偏稳定)

a0.15细节图.png

  图中蓝色表示原始ADC采样值,橙色为滤波后的值,其他图片也这样。当电压不变化时采集到的ADC采样值实际存在很多毛刺噪声,选取较小的a值则能有效减小毛刺的幅度,使信号趋于稳定,防止误判。

a0.1.png

a0.15.png

  选取较小a值时,稳定段过滤很多毛刺噪声,但灵敏性(跟随性)较差,信号幅度变化明显时滤波结果明显滞后,且无法达到波峰波谷值。

2.2 系数a取较大时(偏灵敏)

  特点与a较小时相反,滤波结果有较好的跟随性,但消除毛刺噪声能力差。

2.3 使用动态系数

  既然一阶低通滤波无法同时兼容滤波的稳定性和灵敏性, 那么我们可以根据实际情况,在采样值较低且变换幅度较小时使用小系数偏稳定性,当检测到采样值大于一定范围且增幅明显时立刻且大系数偏灵敏性。

  代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Filter(AdcSample)
{
if(g_u16AdcSampleVal > g_u16AdcInitBaseVal)
{
if((DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValL) <= ADC_SAMPLE_LARGE_JUMP) //采样值无大幅度跳变
&& (DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValLL) <= ADC_SAMPLE_LARGE_JUMP))
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_SENSITIVE_a); //一阶低通滤波,偏灵敏
}
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
}

效果展示:

动态系数兼顾稳定性和灵敏度.png

a0.3.png

2.4 算法改进

  以下算法时一阶低通滤波的变形,可以避免单片机做浮点运算,如果你的单片机浮点运算的性能较差可以使用该变形公式。这个公式不是我发明的,是由一个大佬传授给我的,我将其拿出来分享。

1
2
3
4
5
6
7
8
9
10
11
12
//一阶低通滤波变形
#define FO_LOW_PASS_FILTER_EXTERN_SENSITIVE_a 1u //值越大越稳定,越小越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_EXTERN_STEADY_a 7u

//一阶低通滤波变形,此方法为网上借鉴
//优点:无浮点运算。缺点:调节粗糙。
void FOLowPassFilterExtern(AdcSample, a)
{
g_u16FOLowPassFilterResult = AdcSample + g_u16FOLowPassFilterResultL * a;
g_u16FOLowPassFilterResult = g_u16FOLowPassFilterResult / (a + 1);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

  选取较小a值时,稳定段过滤很多毛刺噪声,但灵敏性(跟随性)较差,信号幅度变化明显时滤波结果明显滞后,且无法达到波峰波谷值。

2.2 系数a取较大时(偏灵敏)

  特点与a较小时相反,滤波结果有较好的跟随性,但消除毛刺噪声能力差。

2.3 使用动态系数

  既然一阶低通滤波无法同时兼容滤波的稳定性和灵敏性, 那么我们可以根据实际情况,在采样值较低且变换幅度较小时使用小系数偏稳定性,当检测到采样值大于一定范围且增幅明显时立刻且大系数偏灵敏性。

  代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Filter(AdcSample)
{
if(g_u16AdcSampleVal > g_u16AdcInitBaseVal)
{
if((DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValL) <= ADC_SAMPLE_LARGE_JUMP) //采样值无大幅度跳变
&& (DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValLL) <= ADC_SAMPLE_LARGE_JUMP))
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_SENSITIVE_a); //一阶低通滤波,偏灵敏
}
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
}

效果展示:

一阶低通滤波

前言:
  在使用单片机开发中,常常会用到的外设包括ADC采样。而采样必然会伴随这随机干扰引起的毛刺噪声,对于需要捕捉采样值突变的系统来说尤其需要减小毛刺突变的影响。从硬件电路和软件算法上都能一定程度的减少噪声达到滤波的目的,本文主要讲解软件使用低通滤波算法来滤波ADC采样值的方法。

1 一阶低通滤波(又叫惯性滤波)算法

1.1 算法原理

  滤波算法公式:
Y(n) = a * X(n) + (1 - a) * Y(n - 1)

Y(n):本次滤波结果。
a:滤波系数。取值范围为0~1, 值越小越稳定,越大越灵敏。
X(n):本次采样值。
Y(n - 1):上次滤波结果。

1.2 C代码实现

1
2
3
4
5
6
7
8
9
10
11
//一阶低通滤波
#define FO_LOW_PASS_FILTER_SENSITIVE_a 0.8f //一阶低通滤波系数取值范围为(0,1)。值越小越稳定,越大越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_STEADY_a 0.15f

//输入Adc采样值,输出低通滤波值
//优点:调节精细,稳定度和灵敏度偏向分明。缺点:带有浮点运算
void FOLowPassFilter(AdcSample, a)
{
g_u16FOLowPassFilterResult = (uint16_t)(a * AdcSample + (1 - a) * g_u16FOLowPassFilterResultL);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

1.3 一阶低通滤波算法特点

  使用一阶低通滤波算法的优点:

  • 相比于硬件滤波,软件滤波节省成本,可靠性高,滤波范围可以由软件任意修改(硬件需要考虑阻抗匹配问题)。
  • 一阶低通滤波运算量小,需要调节的参数少,易于软件实现和单片机快速处理,是实时性强的滤波。

  使用一阶低通滤波算法的缺点:

  • 滤波系数越小,滤波结果越稳定,滤波系数越大结果越灵敏。稳定性和灵敏性二者难兼顾。

2 算法改进

2.1 系数a取较小值时(偏稳定)

a0.15细节图.png

  图中蓝色表示原始ADC采样值,橙色为滤波后的值,其他图片也这样。当电压不变化时采集到的ADC采样值实际存在很多毛刺噪声,选取较小的a值则能有效减小毛刺的幅度,使信号趋于稳定,防止误判。

a0.1.png

a0.15.png

  选取较小a值时,稳定段过滤很多毛刺噪声,但灵敏性(跟随性)较差,信号幅度变化明显时滤波结果明显滞后,且无法达到波峰波谷值。

2.2 系数a取较大时(偏灵敏)

  特点与a较小时相反,滤波结果有较好的跟随性,但消除毛刺噪声能力差。

2.3 使用动态系数

  既然一阶低通滤波无法同时兼容滤波的稳定性和灵敏性, 那么我们可以根据实际情况,在采样值较低且变换幅度较小时使用小系数偏稳定性,当检测到采样值大于一定范围且增幅明显时立刻且大系数偏灵敏性。

  代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Filter(AdcSample)
{
if(g_u16AdcSampleVal > g_u16AdcInitBaseVal)
{
if((DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValL) <= ADC_SAMPLE_LARGE_JUMP) //采样值无大幅度跳变
&& (DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValLL) <= ADC_SAMPLE_LARGE_JUMP))
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_SENSITIVE_a); //一阶低通滤波,偏灵敏
}
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
}

效果展示:

动态系数兼顾稳定性和灵敏度.png

a0.3.png

2.4 算法改进

  以下算法时一阶低通滤波的变形,可以避免单片机做浮点运算,如果你的单片机浮点运算的性能较差可以使用该变形公式。这个公式不是我发明的,是由一个大佬传授给我的,我将其拿出来分享。

1
2
3
4
5
6
7
8
9
10
11
12
//一阶低通滤波变形
#define FO_LOW_PASS_FILTER_EXTERN_SENSITIVE_a 1u //值越大越稳定,越小越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_EXTERN_STEADY_a 7u

//一阶低通滤波变形,此方法为网上借鉴
//优点:无浮点运算。缺点:调节粗糙。
void FOLowPassFilterExtern(AdcSample, a)
{
g_u16FOLowPassFilterResult = AdcSample + g_u16FOLowPassFilterResultL * a;
g_u16FOLowPassFilterResult = g_u16FOLowPassFilterResult / (a + 1);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

一阶低通滤波

前言:
  在使用单片机开发中,常常会用到的外设包括ADC采样。而采样必然会伴随这随机干扰引起的毛刺噪声,对于需要捕捉采样值突变的系统来说尤其需要减小毛刺突变的影响。从硬件电路和软件算法上都能一定程度的减少噪声达到滤波的目的,本文主要讲解软件使用低通滤波算法来滤波ADC采样值的方法。

1 一阶低通滤波(又叫惯性滤波)算法

1.1 算法原理

  滤波算法公式:
Y(n) = a * X(n) + (1 - a) * Y(n - 1)

Y(n):本次滤波结果。
a:滤波系数。取值范围为0~1, 值越小越稳定,越大越灵敏。
X(n):本次采样值。
Y(n - 1):上次滤波结果。

1.2 C代码实现

1
2
3
4
5
6
7
8
9
10
11
//一阶低通滤波
#define FO_LOW_PASS_FILTER_SENSITIVE_a 0.8f //一阶低通滤波系数取值范围为(0,1)。值越小越稳定,越大越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_STEADY_a 0.15f

//输入Adc采样值,输出低通滤波值
//优点:调节精细,稳定度和灵敏度偏向分明。缺点:带有浮点运算
void FOLowPassFilter(AdcSample, a)
{
g_u16FOLowPassFilterResult = (uint16_t)(a * AdcSample + (1 - a) * g_u16FOLowPassFilterResultL);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

1.3 一阶低通滤波算法特点

  使用一阶低通滤波算法的优点:

  • 相比于硬件滤波,软件滤波节省成本,可靠性高,滤波范围可以由软件任意修改(硬件需要考虑阻抗匹配问题)。
  • 一阶低通滤波运算量小,需要调节的参数少,易于软件实现和单片机快速处理,是实时性强的滤波。

  使用一阶低通滤波算法的缺点:

  • 滤波系数越小,滤波结果越稳定,滤波系数越大结果越灵敏。稳定性和灵敏性二者难兼顾。

2 算法改进

2.1 系数a取较小值时(偏稳定)

a0.15细节图.png

  图中蓝色表示原始ADC采样值,橙色为滤波后的值,其他图片也这样。当电压不变化时采集到的ADC采样值实际存在很多毛刺噪声,选取较小的a值则能有效减小毛刺的幅度,使信号趋于稳定,防止误判。

a0.1.png

a0.15.png

  选取较小a值时,稳定段过滤很多毛刺噪声,但灵敏性(跟随性)较差,信号幅度变化明显时滤波结果明显滞后,且无法达到波峰波谷值。

2.2 系数a取较大时(偏灵敏)

  特点与a较小时相反,滤波结果有较好的跟随性,但消除毛刺噪声能力差。

2.3 使用动态系数

  既然一阶低通滤波无法同时兼容滤波的稳定性和灵敏性, 那么我们可以根据实际情况,在采样值较低且变换幅度较小时使用小系数偏稳定性,当检测到采样值大于一定范围且增幅明显时立刻且大系数偏灵敏性。

  代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Filter(AdcSample)
{
if(g_u16AdcSampleVal > g_u16AdcInitBaseVal)
{
if((DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValL) <= ADC_SAMPLE_LARGE_JUMP) //采样值无大幅度跳变
&& (DifferenceAbs(g_u16AdcSampleVal, g_u16AdcSampleValLL) <= ADC_SAMPLE_LARGE_JUMP))
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_SENSITIVE_a); //一阶低通滤波,偏灵敏
}
}
else
{
FOLowPassFilter(AdcSample, FO_LOW_PASS_FILTER_STEADY_a); //一阶低通滤波,偏稳定
}
}

效果展示:

动态系数兼顾稳定性和灵敏度.png

a0.3.png

2.4 算法改进

  以下算法时一阶低通滤波的变形,可以避免单片机做浮点运算,如果你的单片机浮点运算的性能较差可以使用该变形公式。这个公式不是我发明的,是由一个大佬传授给我的,我将其拿出来分享。

1
2
3
4
5
6
7
8
9
10
11
12
//一阶低通滤波变形
#define FO_LOW_PASS_FILTER_EXTERN_SENSITIVE_a 1u //值越大越稳定,越小越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_EXTERN_STEADY_a 7u

//一阶低通滤波变形,此方法为网上借鉴
//优点:无浮点运算。缺点:调节粗糙。
void FOLowPassFilterExtern(AdcSample, a)
{
g_u16FOLowPassFilterResult = AdcSample + g_u16FOLowPassFilterResultL * a;
g_u16FOLowPassFilterResult = g_u16FOLowPassFilterResult / (a + 1);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}

2.4 算法改进

  以下算法时一阶低通滤波的变形,可以避免单片机做浮点运算,如果你的单片机浮点运算的性能较差可以使用该变形公式。这个公式不是我发明的,是由一个大佬传授给我的,我将其拿出来分享。

1
2
3
4
5
6
7
8
9
10
11
12
//一阶低通滤波变形
#define FO_LOW_PASS_FILTER_EXTERN_SENSITIVE_a 1u //值越大越稳定,越小越灵敏,二者难兼顾。
#define FO_LOW_PASS_FILTER_EXTERN_STEADY_a 7u

//一阶低通滤波变形,此方法为网上借鉴
//优点:无浮点运算。缺点:调节粗糙。
void FOLowPassFilterExtern(AdcSample, a)
{
g_u16FOLowPassFilterResult = AdcSample + g_u16FOLowPassFilterResultL * a;
g_u16FOLowPassFilterResult = g_u16FOLowPassFilterResult / (a + 1);
g_u16FOLowPassFilterResultL = g_u16FOLowPassFilterResult;
}