sum+=value_buf[count]*coe[count];
return(char)(sum/sum_coe);
}
9、消抖滤波法
#defineN12
charfilter()
{
charcount=0;
charnew_value;
new_value=get_ad();
while(value!
=new_value);
{
count++;
if(count>=N) returnnew_value;
delay();
new_value=get_ad();
}
returnvalue;
}
10、限幅消抖滤波法
/*
*/
一个非常适合单片机的滤波算法
一个非常适合单片机的滤波算法(附匠人分析)
2011-01-0809:
17
匠人按:
今天在论坛里看到一位网友发了个滤波算法。
开始以为是一种新算法,后来仔细分析,发现原来是一阶滤波(低通滤波)的变形。
原文及匠人的分析附录如下:
-------------------------------------------------以下为原文-------------------
连接:
bbs.21ic./icview-170880-1-1.html
单片机大多资源小,算法占用的资源越小越好,现在介绍就是一个占用很小资源的算法,这个算法是本人在进行扫描仪设计,实现灰度转二值时实现动态阈值,当时为了跟踪灰度等级的变化,需要一个灰度积分跟踪电路,开始使用一个电容积分电路,用灰度信号对电容充电,放电时以该电容电压的比例进行,实现对输入信号的跟踪,但用电容的电路设计比较复杂。
过后发现这种比例放电的思想用软件实现非常简单,且具有积分、微分的作用。
具体公式如下:
SUM=SUM-SUM/n+S
其中:
S为采样值,SUM为保存值,n是放电比例、最好选2的幂次数,单片机移位即可,不需要做除法,跟随后得到的值为SUM/n,SUM注意不溢出,预留的容量为采样数最大值的n倍,初始化时如果是跟踪一段时间后使用,可以是任何值,否则可以用采样值乘n初始化。
使用值为SUM/n(下文中SA),实现SUM/n对S的跟踪。
还有一个关键是计算周期T,即多长时间进行一次。
一、积分作用:
1.平滑滤波(滑动平均滤波)
由公式中可以看出,每次采样、计算后,当前采样的影响对SUM/n只有1/n,而且采到的值随次数的增加影响越来越小直至没有,相关性逐渐减弱,而且是连续相关。
如果计算周期与采样周期相同,使用计算后的值对干扰有n倍的抑制,即积分的平滑滤波作用,如1ms采样一次,同时运算一次,
则使用值SA=SUM/n为抑制干扰的结果,且同样是1ms给出一个结果,使用两个变量实现平滑滤波,并且是即时使用的,与采样几次平均的平滑不同。
2.动态阈值
在很多应用中需要动态阈值,比如触摸按键的键阈值门限,血压计的心率检出,前面提到的灰度转二值黑白图像等(灰度转二值因为扫描速度2.5Mbyte/S,不能使用软件运算,但可以使用可编程逻辑实现)。
动态阈值是对信号积分后得到的低频变化再与基本门限相加在触摸按键中增加动态阈值可以提高其适应性和可靠性。
关键是根据按键反应时间和按键间隔确定按键积分参数,跟踪速度,n、T越大跟踪的越慢,积分效果越好。
3.锁相作用:
把上边的积分运算,用于对时间上周期的信号,例如根据过零触发信号锁定交流电源周期,使用两次T时间不同,其它相同的运算,由于T不同,跟踪速度不同,当两次运算的结果相等时可以确认为锁定,这时得到的是准确的电源周期值,而相位偏差也很小。
二、微分作用:
公式中的SA趋近采样值S,如果S是线性的,SA的值是可控滞后于S,那么运算的间隔时间T不同,得到的跟踪曲线的滞后特性不同,这种滞后特性的差和间隔时间就是微分特性,表示曲线的变化规律。
如电热水壶,温度的变化相当于采样时间是还相当慢的,局部可以作为线性变化来处理。
下边以设计电热水壶的过程来说明微分作用。
电热水壶出口一直使用蒸汽开关这种需要交专利费的方式。
不使用蒸汽开关检测压力只能使用热敏器件检测温度。
温度检测的环境要求:
1. 海拔高度不同的地区水开的温度不同。
2. 热敏器件的误差较大,必须克服,否则可生产性不足。
3. 环境温度不同,电源电压不同,装水量不同。
由要求1、2决定检测温度不能判别水开与否,需要检测温度的变化率,但温度变化率的判别又和要求3相关,下边曲线图为热水器的加热曲线。
蓝线为即时温度,橙色为一次运算后的曲线。
图中加热过程中间添加了冷水,曲线有一段下降,过后的加热过程两个曲线有个差异滞后,同一个时间的两个曲线差表示了加热效率的变化,其中最大的加热效率体现了环境温度不同,电源电压不同,装水量不同的综合效果。
由于滞后的时间可以通过计算周期T来调整,知道滞后时间又有相减的差,这就是微分效应,加热过程整个就是效率的变化过程。
我们可以通过1秒钟计算一次,2秒钟计算一次,加上原始数据得到三个曲线,效率的变化一目了然。
第一次的水开检测使用效率的方法,同时也会得到水开时的温度检测值,微分特性本身是可以预知变化趋势的,如果1秒钟计算一次,用当前检测值减去这次计算的结果,这个差与当前值相加,就可以做为当前1秒后的结果,也就是预知1秒后加热的检测值,结合第一次得到的水开温度检测值,以后的水开检测就有两个判断条件。
----------------------------------------------以下为匠人的分析-----------------------------
拨开迷雾看真相,作者的这个算法,本质上,就是一阶滤波(低通滤波)。
引用作者原来的公式
SUM=SUM-SUM/n+S
首先点破一下,等号前面的SUM代表的是本次运算结果,而等号后面的SUM代表的是上次运算结果。
且看匠人如何推导:
设:
SUM=A
SUM/n=B=本次滤波结果
1/n=a(一阶滤波系数)
S=本次新采样值
则:
A=nB
B=A/n
另外:
A、B代表本次值
A’、B’代表上次值
作者原公式逐步推导:
原始:
SUM=SUM-SUM/n+S
第1步:
A=A’–A’/n+S
第2步:
nB=nB’–B’+S
第3步:
B=(nB’–B’+S)/n
第4步:
B=B’–B’/n+S/n
第5步:
B=(1-1/n)B’+(1/n)*S
第6步:
B=(1-a)B’+a*S
推导到最后一步,是不是很眼熟啦?
呵呵,这就是经典的一阶滤波(低通滤波)的标准公式了。
基于C8051F310的高灵敏车辆检测算法
2009-10-27 嵌入式在线 收藏|打印
1引言
弯路转弯处经常出现一段盲区,司机看不到弯路对面是否有车辆通过,因而引发大量的交通事故,因此,消除盲区造成的交通事故显得尤为重要。
为此,设计了基于C8051F310的山路转弯预警系统。
该系统当检测到弯路对面有车时可及时通过交通警示灯提前警示司机注意避让。
因此,准确判断是否有车辆经过是该系统设计的关键。
2系统设计
2.1系统设计方案
该系统设计的主要目的是警示司机在行驶时注意安全,预防事故。
在山路转弯处两边分别放置该系统,每边系统控制一警示灯。
当一方系统检测到车辆时,通过RF通讯发送至对方系统,对方系统接收到信号后,控制警示灯闪烁以提示司机。
图1为系统设计组成框图。
其中,车辆检测传感器采用正弦波振荡电路检测车辆,在检测电路中,输出信号频率由C8051F310采集得到,然后通过一阶滤波算法处理,滤除掉因环境因素等产生的频率干扰,并进一步计算验证C8051F310的采集精度。
2.2系统的硬件电路设计
图2为系统主要硬件电路。
车辆检测传感器的输入信号为U(t),该正弦信号通过比较器变为方波信号后,再输入到单片机C8051F310,然后单片机通过计数器采集信号频率。
2.3一阶滤波算法
一阶滤波,即一阶惯性滤波。
一阶低通滤波算法公式为:
式中,α为滤波系数,X(n)为本次采样值,Y(n-1)为上次滤波输出值,Y(n)为本次滤波输出值。
一阶低通滤波法采用本次采样值与上次滤波输出值进行加权,得到有效滤波值,使得输出对输入有反馈作用。
滤波系数为0~1;该系数决定新采样值在本次滤波结果中所占的权重。
一阶滤波系数可以是固定的,也可以按一定程序算法自动计算。
但一阶滤波算法无法完全兼顾灵敏度和平稳度。
只能寻找一个平衡点,在该系统设计可接受的灵敏度围选取尽可能好的平稳度。
即当数据快速变化时,滤波结果能及时跟进(灵敏度优先);而当数据趋于稳定,在固定点上下振荡时,滤波结果趋于平稳(平稳度优先)。
2.4车辆检测电路
图3为正弦波振荡电路。
该电路用于车辆检测电路传感器,能够感应出金属物体的存在。
采用涡流传感方式,将埋入地下的探测线罔直接接入正弦波振荡电路。
在未检测到车辆等金属物体时,振荡电路输出信号u0(t)的频率基本变化不大,但该值不是一直保持不变,而会在一定围漂移。
当检测到车辆等金属物体时,U0(t)的频率f0会突变为f。
频率差△f=f-f0,其中△f的围经大量的实验得出一般为几百赫兹到几千赫兹。
图3中电路的振荡频率为:
式中,f与电路中L、C1、C2有关。
当电感L数值变化时,f也会相应改变。
同样当电容容值发生变化时,f也随之变化。
一般情况下,电容值随环境温度变化而变化,因此振荡频率f也随温度变化而变化。
2.5检测电路频率算法
因为检测电路中信号频率随时在改变,这为检测机动车辆等金属物体带来一定困难,尤其在环境温度急剧变化时,信号自身频率值会大幅变化。
高温时在室外环境所采集的振荡电路数据分析得出:
f值在1h随温度变化几百赫兹,测量期间没有金属物体靠近。
因此该设计采用基准动态改变方法。
具体计算方法如下:
设定fz为基准频率;fc为参与计算和判断的采集频率;f为实际采集频率。
m和n为滤波因子。
系统没有上电时,fz初始化值为0;上电后,把第1次采集到的频率f作为fz的初始值,随后定时更换fz值。
先把实际采集到的频率f按式
(2)进行一阶滤波处理,然后计算fc的值:
式(3)、式(4)中的滤波因子m,n通过试验获得。
当f值快速变化时滤波结果及时跟进且数据变化越快,灵敏度越高。
在检测车辆时需定时更换fz,根据室外温度变化更换时间。
当有金属物体经过线圈时,采集的频率值为f,而这时基准频率为fz。
判断车辆的算法是南式(3)得出fc,再由式(5)得:
通过判断△f是否在同定围中得出车辆经过情况。
该同是通过大量实验得出的。
具体CPU算法流程见图4。
3实验及结果分析
该算法是经过大量验证试验而得出的。
在室,试验采用45cm×45cm的线圈(匝数n=12)。
模拟机动车辆为一辆长1.2m,宽0.8m的铁皮小推车。
当小车经过线圈时,采集频率fc与基准频率fz的差值约400Hz,CPU能够准确判断出有小车经过。
在室外试验则是把线圈埋入公路地面下,当汽车经过线圈时,采集频率fc与基准频率fz差值约400~2000Hz。
这个差值随着车辆型号不同和车辆底盘的高低而改变,而CPU也能准确判断出有机动车辆经过。
通过室和室外大量试验,及时调整了滤波算法中的滤波因子,提高了检测灵敏度,使其能够满足不同车辆的需求。
4结束语
采用滤波算法并实时更新振荡电路的基准频率可减少电路频率变化对于车辆检测的干扰。
这里采用c8051F310设计的山路转弯预防警示系统现已安装在盘山路上,该系统能够准确检测出车辆,并发送警示信息。
同时设计中充分考虑到环境因素和维护的不便,设计有上位机监控系统。
因此,该系统结构简单,性能可靠,价格低廉,已引起交通部门的广泛关注。
防脉冲干扰移动平均值法数字滤波器的C语言算法
在许多的数据采集系统中,现场的强电设备较多,不可避免
地会产生尖脉冲干扰,这种干扰一般持续时间短,峰值大,对这样
的数据进行数字滤波处理时,仅仅采用算术平均或移动平均滤波
时,尽管对脉冲干扰进行了1/n的处理,但,其剩余值仍然较大。
这种场合最好的策略是:
将被认为是受干扰的信号数据去掉,这
就是防脉冲干扰平均值滤波法的原理。
防脉冲干扰平均值滤波法的算法是:
对连续的n个数据进行排序,
去掉其中最大和最小的2个数据,将剩余数据示平均值。
在一般8051单片机的应用中为了加快数据处理速度,n可以取值6。
而对于具有较快速度的处理器,则n值可以适当取大一些。
但最好是
n=2^k+2, k为整数,因为这样在求平均值average=SUM/(n-2)=SUM/2^k时,
可以写成average=SUM>>k,用移位的方法,可以加快处理速度。
上述算法显然还存在一个不足之处,就是每采集一个数据就要进行一次排序,
这样会大量占用系统宝贵的时间。
这可以通过存储当前数据中的最大值和最小值来改进。
具体做法是:
系统中用两个变量来存储当前n个数据的最大值和最小值在这个数组中的
偏移量(也就是数组下标,存储数组下标而直接不存储数据本身是因为:
在一般的系统中,n不会超无符号短整形的表示围,因此用一个char形变量就可以存储了,而如果直接存储数据本身,则许多情况下要用int形变量,甚至更长的类型)。
这样只要在当前输入的数据将要覆盖的数据正好是当前的最大值或最小值时才在下个数组中查找最大值或最小值,而其他情况下则只要将输入的数据与最大值和最小值比较就可以修改下最大值和最小值了,而且不用进行数据排序。
这个算法很简单,下面是对应的C语言代码实现,可以很方便的应用的具体的51单片机,或其他处理器上,只须做少量的修改。
#include"stdio.h"
#definedtype unsignedint // 采集数据的数据类型
#defineuint8 char
#defineLEN 6 //移动算术平均的个数+2=SHIFT<<2+2
#defineSHIFT 2 //2^SHIFT
uint8pdata; //移动指针
uint8pmax,pmin; //记录数据表中最大值和最小值的位置,
//在一般的数据采集系统中,数据的长度>=8,
//因此用指针记录而不是直接记录最大值和最小值
dtypedatas[LEN];
dtypeszlb(dtype_data)
{
/****************************/
/* 在调用此子程序前必须对 */
/* pdata,datas[]数组, */
/* pmax,pmin进行初始化 */
/****************************/
uint8i;
dtypeaverage=0; //清零,用来计算平均值
pdata=(pdata+1)%LEN; //指针下标在0到LEN-1上滑动
datas[pdata]=_data; //采样所得数据存入数据表中
for(i=0;i average+=datas[i]; //求所有数据总和
/*******去除被认为是脉冲的数据******/
if(_data>datas[pmax])
pmax=pdata; //得到最大值的指针
elseif(_data pmin=pdata; //得到最小值的指针
if(pdata==pmax) //如果当前输入值将存入当前最大值的位置时
{ //由以上方法将不可行,必须从其他位置中查找极值
for(i=0;i if(datas[i]>datas[pmax])
pmax=i;
}
elseif(pdata==pmin)//如果当前输入值将存入当前最大值的位置时
{ //由以上方法将不可行,必须从其他位置中查找极值
for(i=0;i if(datas[i] pmin=i;
}
average=average-datas[pmax]-datas[pmin];//减去脉冲
return(average>>SHIFT); //求算术平均值
}
/******以下是在VC++6.0环境下运行的测试程序**/
/***通过手动输入来模拟数据采集过程****/
voidmain()
{
uint8i;
dtype_data;
pdata=0;
pmax=0;
pmin=0;
for(i=0