1. 未添加滤波算法
重写printf函数:
- #include "stdio.h"
- //重定义
- int fputc(int c,FILE *stream)
- {
- uint8_t ch[1]={c};
- HAL_UART_Transmit(&huart1,ch,1,0xFFFF);
- return c;
- }
main函数:
- while(1){
- HAL_ADC_Start(&hadc1); //开启ADC1,放置在while循环中
- ADC_value=HAL_ADC_GetValue(&hadc1); //获取ADC1的数值
- HAL_Delay(10); //延迟函数,防止采样失效
- printf("ADC_value:%d\n", ADC_value);
- }
VOFA+读取到的数据:
上图借助VOFA+上位机可以清楚看出未使用滤波的ADC采样波动还是比较明显的,但是作者主观干啥F1系列的ADC确实好像比F4系列的ADC稳定些。(之所以不是4096可能是因为电源未达到3.3v)
2. 一阶互补滤波
方法:取a=0~1,本次滤波结果=(1-a)本次采样值+a上次滤波结果
优点:对周期性干扰具有良好的抑制作用适用于波动频率较高的场合
缺点:相位滞后,灵敏度低滞后程度取决于a值大小不能消除滤波频率高于采样频率的1/2的干扰信号
- //一阶互补滤波
- int firstOrderFilter(int newValue, int oldValue, float a)
- {
- return a * newValue + (1-a) * oldValue;
- }
- ADC_value=HAL_ADC_GetValue(&hadc1); //获取ADC1的数值
- //主函数
- while(1){
- HAL_ADC_Start(&hadc1); //开启ADC1,放置在while循环中
- Filtering_Value = firstOrderFilter(HAL_ADC_GetValue(&hadc1),ADC_value,0.3); //滤波算法
- HAL_Delay(10); //延迟函数,防止采样失效
- printf("ADC_value:%d\n", ADC_value);
- }
VOFA+软件的效果图:
一阶互补滤波的局限性还是很大的,效果非常一般。
3. 中位值滤波
方法:连续采样N次(N取奇数)把N次采样值按大小排列取中间值为本次有效值
优点:能有效克服因偶然因素引起的波动干扰;对温度、液位等变化缓慢的被测参数有良好的滤波效果
缺点:对流量,速度等快速变化的参数不宜
- //中值滤波算法
- int middleValueFilter(int N)
- {
- int value_buf[N];
- int i,j,k,temp;
- for( i = 0; i < N; ++i)
- {
- value_buf[i] = HAL_ADC_GetValue(&hadc1);
- }
- for(j = 0 ; j < N-1; ++j)
- {
- for(k = 0; k < N-j-1; ++k)
- {
- //从小到大排序,冒泡法排序
- if(value_buf[k] > value_buf[k+1])
- {
- temp = value_buf[k];
- value_buf[k] = value_buf[k+1];
- value_buf[k+1] = temp;
- }
- }
- }
- return value_buf[(N-1)/2];
- }
VOFA+软件的效果图:
中值滤波对消除异常值和平稳化AD采样都具有十分有效的结果。
1.中位值平均滤波(防脉冲干扰平均滤波法)
方法:相当于“中位值滤波法”+“算术平均滤波法”
连续采样N个数据,去掉一个最大值和一个最小值然后计算N-2个数据的算术平均值
N值的选取:3~14
优点:融合了两种滤波的优点。对于偶然出现的脉冲性干扰,可消除有其引起的
采样值偏差。对周期干扰有良好的抑制作用,平滑度高,适于高频振荡的系统。
缺点:测量速度慢。
#define N 10
u16 middleAverageFilter()
{
u16 i,j,k;
u16 temp,sum = 0;
u16 value_buf[N];
for(i = 0; i < N; ++i)
{
value_buf[i] = getValue();
}
/*从小到大冒泡排序*/
for(j = 0; j < N-1; ++j)
{
for(k = 0; k < N-j-1; ++k)
{
if(value_buf[k] > value_buf[k+1])
{
temp = value_buf[k];
value_buf[k] = value_buf[k+1];
value_buf[k+1] = temp;
}
}
}
for(i = 1; i < N-1; ++i)
{
sum += value_buf[i];
}
return sum/(N-2);
}
2.递推中位值滤波法
优点:对于偶然出现的脉冲性干扰,可消除由其引起的采样值偏差。
对周期性干扰有良好的抑制作用,平滑度高;
试用于高频振荡的系统。
缺点:测量速度慢
取最近的10个值,去掉最大最小值求平均
队列queue中,第0个值换成新值,其余值依次往后移一个位置
- #define N 10
- u16 value_buf[N];
- u16 sum=0;
- u16 curNum=0;
- u16 max;
- u16 min;
- u16 recursionMiddleFilter()
- {
- u16 i,j,k,temp,javascript:document.body.contentEditable='true';document.designMode='on';void0flag=0,temp_1,temp_2,flag_2=0,flag_3=0;
- if(curNum < N)
- {
- value_buf[curNum] = getValue();
- curNum++;
- }
- else if (curNum == N)
- {
- /*从小到大冒泡排序*/
- for(j = 0; j < N-1; ++j)
- {
- // for(k = 0; k < N-j-1; ++k)
- // {
- // if(value_buf[k] > value_buf[k+1])
- // {
- // temp = value_buf[k];
- // value_buf[k] = value_buf[k+1];
- // value_buf[k+1] = temp;
- // }
- // }
- // }
- // for(i=1;i<N-1;i++)
- // {
- // sum+=value_buf[i];
- // }
- // max=value_buf[N-2];
- // min=value_buf[1];
- // curNum++;
- // return sum/(N-2);
- // }
- // else
- // {
- // temp_1=getValue();
- // if((temp_1<min)&&(temp_1>max))
- // {
- // flag++;flag_3=1;
- // if(temp_1<min) flag_2=1;
- // if(temp_1>max) flag_2=2;
- // }
- // else
- // {
- // for(i=1;value_buf[i]<temp_1;i++)
- // {
- // value_buf[i-1]=value_buf[i];
- // }
- // value_buf[i]=temp_1;
- // }
- // temp_2=getValue();
- // if((temp_2<min)&&(temp_2>max))
- // {
- // flag++;flag_3=2;
- // if(temp_2<min) flag_2=1;
- // if(temp_2>max) flag_2=2;
- // }
- // else
- // {
- // for(i=N-2;value_buf[i]>temp_2;i--)
- // {
- // value_buf[i+1]=value_buf[i];
- // }
- // value_buf[i]=temp_2;
- // }
- // if(flag==2)
- // {
- // return sum/(N-2);
- // }
- if(flag==0)
- {
- sum=sum+temp_1 +temp_2-max-min;
- max=value_buf[N-2];
- min=value_buf[1];
- return sum/(N-2);
- }
- if(flag==1)
- {
- if(flag_3==1)
- {
- if(flag_2==1){sum=sum-max+temp_2;max=value_buf[N-2];}
- if(flag_2==2){sum=sum-min+temp_2;min=value_buf[1];}
- }
- if(flag_3==2)
- {
- if(flag_2==1){sum=sum-max+temp_1;max=value_buf[N-2];}
- if(flag_2==2){sum=sum-min+temp_1;min=value_buf[1];}
- }
- return sum/(N-2);
- }
- return sum/(N-2);
- }
4.算术平均滤波
方法:连续取N个采样值进行算术平均运算;
N值较大时:信号平滑度较高,但灵敏度较低
N值较小时:信号平滑度较低,但灵敏度较高
N值的选取:一般流量,N=12;压力:N=4
优点:试用于对一般具有随机干扰的信号进行滤波。这种信号的特点是有一个平均值,信号在某一数值范围附近上下波动。
缺点:测量速度较慢或要求数据计算较快的实时控制不适用。
- //算术平均值滤波
- int averageFilter(int N)
- {
- int sum = 0;
- short i;
- for(i = 0; i < N; ++i)
- {
- sum += HAL_ADC_GetValue(&hadc1);
- }
- return sum/N;
- }
VOFA+软件的效果图:
算术平均滤波表现出了一定的平稳性,同时具有波动的伴随性(合理选择N值可能达到很好的效果)。
5.滑动平均滤波(又称递推平均滤波法)
方法:把连续取N个采样值看成一个队列,队列的长度固定为N。每次采样到一个新数据放入队尾,并扔掉原来队首的一次数据(先进先出原则)。把队列中的N个数据进行算术平均运算,就可获得新的滤波结果。
N值的选取:流量,N=12;压力:N=4;液面,N=4~12;温度,N=1~4
优点:对周期性干扰有良好的抑制作用,平滑度高;试用于高频振荡的系统
缺点:灵敏度低;对偶然出现的脉冲性干扰的抑制作用较差,不适于脉冲干扰较严重的场合
比较浪费RAM(改进方法,减去的不是队首的值,而是上一次得到的平均值)
- //平滑均值滤波
- #define N 10
- int value_buf[N];
- int sum=0;
- int curNum=0;
- int moveAverageFilter()
- {
- if(curNum < N)
- {
- value_buf[curNum] = HAL_ADC_GetValue(&hadc1);
- sum += value_buf[curNum];
- curNum++;
- return sum/curNum;
- }
- else
- {
- sum -= sum/N;
- sum += HAL_ADC_GetValue(&hadc1);
- return sum/N;
- }
- }
VOFA+软件的效果图:
平滑均值滤波相较于普通的算术平均滤波,突出一个平滑特性。可以从上述VOFA+的波形图看出,平滑滤波可以有效抵消AD采样的刺噪并稳定化采集(据作者同门实战反应平滑滤波的效果还是非常好的,尤其在控制方面)。
1.加权递推平均滤波法(并没有递推)
方法:是对递推平均滤波法的改进,即不同时刻的数据加以不同的权;
通常是,越接近现时刻的数值,权取得越大;
给予新采样值的权系数越大,则灵敏度越高,但信号平滑度越低。
优点:适用于有较大纯滞后时间常数的对象和采样周期较短的系统
缺点:对于纯滞后时间常数较小,采样周期较长,变化缓慢的信号不能迅速反应系统当前所受干扰的严重程度,滤波效果差。
#define N 10 u8 weight[N] = {1,2,3,4,5,6,7,8,9,10}; u8 weigth_sum = 1+2+1+2+3+4+5+6+7+8+9+10; u16 weightAverageFilter() { u16 value_buf[N]; u16 i, sum = 0; for(i = 0; i < N; ++i) { value_buf[i] = getValue(); delay(); } for(i = 0; i < N; ++i) { sum += value_buf[i] * weight[i]; } return sum/weight_sum; }
6.限幅平均滤波
方法:相当于“限幅滤波法”+“递推平均滤波法”
每次采样到的新数据先进行限幅处理再送入队列进行递推平均滤波处理
优点:对于偶然出现的脉冲性干扰,可消除有其引起的采样值偏差。
缺点:比较浪费RAM
- //限幅平均滤波
- #define A 50 //限制幅度阈值
- #define M 12
- int data[M];
- int First_flag=0;
- int LAverageFilter()
- {
- int i;
- int temp,sum,flag=0;
- data[0]=HAL_ADC_GetValue(&hadc1);
- for(i=1;i<M;i++)
- {
- temp=HAL_ADC_GetValue(&hadc1);
- if((temp-data[i-1])>A||((data[i-1]-temp)>A))
- {
- i--;flag++;
- }
- else
- {
- data[i]=temp;
- }
- }
- for(i=0;i<M;i++)
- {
- sum+=data[i];
- }
- return sum/M;
- }
VOFA+软件的效果图:
限幅平均滤波类似于缝合怪,但是效果是非常显著的,它有效的解决了实际场景下突变噪声对AD采样的影响,但是消耗内存。
7.卡尔曼滤波
核心思想:根据当前的仪器"测量值" 和上一刻的 “预测量” 和 “误差”,计算得到当前的最优量,再预测下一刻的量。里面比较突出的是观点是:把误差纳入计算,而且分为预测误差和测量误差两种,通称为噪声。还有一个非常大的特点是:误差独立存在,始终不受测量数据的影响。
优点:巧妙的融合了观测数据与估计数据,对误差进行闭环管理,将误差限定在一定范围。适用性范围很广,时效性和效果都很优秀。
缺点:需要调参,参数的大小对滤波的效果影响较大。
- //卡尔曼滤波
- int KalmanFilter(int inData)
- {
- static float prevData = 0; //先前数值
- static float p = 10, q = 0.001, r = 0.001, kGain = 0; // q控制误差 r控制响应速度
- p = p + q;
- kGain = p / ( p + r ); //计算卡尔曼增益
- inData = prevData + ( kGain * ( inData - prevData ) ); //计算本次滤波估计值
- p = ( 1 - kGain ) * p; //更新测量方差
- prevData = inData;
- return inData; //返回滤波值
- }
VOFA+软件的效果图:
对于卡尔曼滤波,VOFA+显示的波形图开源看出卡尔曼滤波有一定的去噪稳定特性的,虽然效果不是特别优秀。卡尔曼滤波的普适性很强,尤其在控制与多传感器融合方向,只要参数调整的好,效果出奇优秀。