【转】单片机系统中数字滤波的算法【C程序整理】
随机误差是有随机干搅引起的,其特点是在相同条件下测量同一个量时,其大小和符号做无规则变化而无法预测,但多次测量结果符合统计规律。为克服随机干搅引入的误差,硬件上可采用滤波技术,软件上可以采用软件算法实现数字滤波,其算法往往是系统测控算法的一个重要组成部分,实时性很强,采用汇编语言来编写。
采用数字滤波算法克服随机干搅引入的误差具有以下几个优点:
(1)数字滤波无须硬件,只用一个计算过程,可靠性高,不存在阻抗匹配问题,尤其是数字滤波可以对
频率很高或很低的信号进行滤波,这是模拟滤波器做不到的。
(2)数字滤波是用软件算法实现的,多输入通道可用一个软件“滤波器”从而降低系统开支。
(3)只要适当改变软件滤波器的滤波程序或运行参数,就能方便地改变其滤波特性,这个对于低频、脉冲
干搅、随机噪声等特别有效。
常用的数字滤波器算法有程序判断法、中值判断法、算术平均值法、加权滤波法、滑动滤波法、低通滤波法和复合滤波法。
1. 程序判断法:
程序判断法又称限副滤波法,其方法是把两次相邻的采样值相减,求出其增量(以绝对值表示)。然后与两次采样允许的最大差值△Y进行比较,△Y的大小由被测对象的具体情况而定,若小于或等于△Y,则取本次采样的值;若大于△Y,则取上次采样值作为本次采样值,即
yn - yn-1|≤△Y,则yn有效,
yn -yn-1|>△Y,则yn-1有效。
式中 yn ——第n次采样的值;
Yn-1——第(n-1)次采样的值;
△Y——相邻两次采样值允许的最大偏差。
设R1和R2为内部RAM单元,分别存放yn-1和yn,滤波值也存放在R2单元,采用MCS-51单片机指令编写的程序判断法子程序如下:付表
2. 中值滤波法即对某一参数连续采样N次(一般N为奇数),然后把N次采样值按从小到大排队,再取中间值作为本次采样值。
设DATA为存放采样值的内存单元首地址,SAMP为存放滤波值的内存单元地址,N为采样值个数,用MCS-51指令编写的中值滤波子程序如下:副表
3.算术平均值滤波算法
算术平均滤波法就是连续取N次采样值进行算术平均,其数学表达式是:
Y=∑yi
~y=1/N ∑ yi
i=1……N
式中 ~y——N个采样值的算术平均值;
Yi ——第i个采样值;
设8次采样值依次存放在地址DATA开始的连续单元中,滤波结果保留在累加器A中,程序如下:副表
4.加权平均滤波法
算术平均滤波法存在前面所说的平滑和灵敏度之间的矛盾。采样次数太少,平滑效果差,次数太多,灵敏度下降,对参数的变化趋势不敏感。协调两者关系,可采用加权平均滤波,对连续N次采样值,分别乘上不同的加权系数之后再求累加和,加权系数一般先小后大,以突出后面若干采样的效果,加强系统对参数的变化趋势的辩识,各个加权系数均为小于1的小数,且满足总和等于1的约束条件,这样,加权运算之后的累加和即为有效采样值。为方便计算,可取各个加权系数均为整数,且总和为256,加权运算后的累加和除以256(即舍去低字节)后便是有效采样值。
设每批采样8个数据,依次存放在地址DATA开始的单元中,各加权系数是用一个表格存放在ROM中,MCS-51指令编写的算术平均滤波程序如下:副表
5.滑动平均滤波法:
以上介绍的各种平均滤波算法有一个共同点,即每取得一个有效采样值必须连续进行若干次采样,当采样速度较慢(如双积分型A/D转换)或目标参数变化较快时,系统的实时性不能保证,滑动平均滤波算法只采样一次,将这一次采样值和过去的若干次采样值一起求平均,得到的有效采样值即可投入使用,如果取N个采样值求平均,RAM中必须开辟N个数据的暂存区。每新采样一个数据便存入暂存区,同时去掉一个最老的数据,保持这N个数据始终是最近的数据,这种数据存放方式可以用环行队列结构方便的实现。设环行队列为40H-4FH连续16个单元,RO作为队尾指针,滤波程序如下:副表
6.低通滤波法:
将普通硬件RC低通滤波器的微分方程用差分方程来表求,变可以采用软件算法来模拟硬件滤波的功能,经推导,低通滤波算法如下:
Yn=a* Xn+ (1-a) *Yn-1
式中 Xn——本次采样值
Yn-1——上次的滤波输出值;
a——滤波系数,其值通常远小于1;
Yn——本次滤波的输出值。
由上式可以看出,本次滤波的输出值主要取决于上次滤波的输出值(注意不是上次的采样值,这和加权平均滤波是有本质区别的),本次采样值对滤波输出的贡献是比较小的,但多少有些修正作用,这种算法便模拟了具体有教大惯性的低通滤波器功能。滤波算法的截止频率可用以下式计算:
fL= a/2Pit pi为圆周率 3.14…
式中 a——滤波系数;
t——采样间隔时间;
例如:当t=0.5s(即每秒2次),a=1/32时;
fL=(1/32)/(2*3.14*0.5)=0.01Hz
当目标参数为变化很慢的物理量时,这是很有效的。另外一方面,它不能滤除高于1/2采样频率的干搅信号,本例中采样频率为2Hz,故对1Hz以上的干搅信号应采用其他方式滤除,
低通滤波算法程序于加权平均滤波相似,但加权系数只有两个:a和1-a。为计算方便,a取一整数,1-a用256-a,来代替,计算结果舍去最低字节即可,因为只有两项,a和1-a,均以立即数的形式编入程序中,不另外设表格。虽然采样值为单元字节(8位A/D)。为保证运算精度,滤波输出值用双字节表示,其中一个字节整数,一字节小数,否则有可能因为每次舍去尾数而使输出不会变化。
设Yn-1存放在30H(整数)和31H(小数)两单元中,Yn存放在32H(整数)和33H(小数)中。滤波程序如下:副表
结束语:
微型计算机在仪器仪表系统中的成功应用,使传统的电子仪器以及复杂的跟踪测量装置发生了许多革命性变化。其中一个突出表现就是一个系统中包含了智能性运作。微机具有很强的分析和运算能力,智能系统可完成复杂的数据处理,智能系统采用软件硬件想结合的方法进行随机误差的数字滤波和系统误差的修正,可以实现实时修正,较准测量数据,这些都是传统仪器难以比拟的。
#include <stdio.h>
#include <absacc.h>
#include <intrins.h>
#include <./Atmel/at89x52.h>
#include "source.h"
main()
{
filter_1();
filter_2();
filter_3();
filter_4();
filter_5();
filter_6();
filter_7();
filter_8();
filter_9();
filter_10();
}
unsigned char get_ad(void){
static unsigned char i;
return i++;
}
void delay(void){
unsigned char i=0;
while(1){
i++;
if(i>20) return;
}
}
#define A 10 //设置两次采样允许的最大偏差值
char value; //上次采用后的有效值变量
char filter_1(void){
char new_value; //本次采样值变量
new_value=get_ad(); //读入本次采样值
if((new_value-value>A)||(value-new_value>A)) //比较是否超出最大偏差值
return value; //如果超出,返回上次的有效值作为本次的有效值
return new_value;// 如果没有超出,返回本次的采样值作为本次的有效值
}
#define N 11 //设置连续采样的次数
char filter_2(void){
char value_buf[N]; //缓存N次采样值的存储变量
char count,i,j,temp; //i,j是冒泡排序的下标变量,count是采样数据读入的下标变量
//temp是临时变量
for(count=0;count<N;count++) //连续读入N个采样值
{
value_buf[count]=get_ad();
delay();
}
for(j=0;j<N;j++) //气泡排序,由小到大
{
for(i=0;i<N-j;i++)
{
if(value_buf[i]>value_buf[i+1])
{
temp=value_buf[i];
value_buf[i]=value_buf[i+1];
value_buf[i+1]=temp;
}
}
}
return value_buf[(N-1)/2]; //将排序后N个采样值的中间值作为最后结果返回
}
#undef N
#define N 12 //设置每组参与平均运算的采样值个数
char filter_3(){
int sum=0; //求和变量,用于存储采样值的累加值
char count;//采样数据读入的下标变量
for(count=0;count<N;count++) //连续读入N个采样值,并累加
{
sum+=get_ad();
delay();
}
return (char)(sum/N); //讲累加值进行平均计算作为返回值
}
#undef N
#define N 12 //设置FIFO队列的长度
char value_buf[N];//FIFO队列变量
char i=0; //队列的下标变量
char filter_4(){
char count;
int sum=0;
value_buf[i++]=get_ad();
if(i==N) i=0;
for(count=0;count<N;count++)
sum+=value_buf[count];
return(char)(sum/N);
}
#undef N
#define N 12 //设置每组采样值的数量
char filter_5()
{
char count,i,j,temp; //i,j是冒泡排序的下标变量,count是采样数据读入的下标变量
char value_buf[N]; // 缓冲N个采样值的存储变量
int sum=0; //求和变量,用于存储采样值的累加值
for (count=0;count<N;count++) //连续读入N个采样值
{
value_buf[count] = get_ad();
delay();
}
for (j=0;j<N-1;j++) //气泡排序,由小到大
{
for (i=0;i<N-j;i++)
{
if ( value_buf[i]>value_buf[i+1] )
{
temp = value_buf[i];
value_buf[i] = value_buf[i+1];
value_buf[i+1] = temp;
}
}
}
for(count=1;count<N-1;count++)
sum += value_buf[count]; //去掉两端的最小和最大采样值,对中间的N-2个采样值求和
return (char)(sum/(N-2));// 返回中间N-2个采样值的平均值
}
#undef A
#undef N
#define A 10 //设置两次采样允许的最大偏差值
#define N 12 //设置每组参与平均运算的采样值个数
char value; //上次采用后的有效值变量
char filter_6()
{
char new_value; //本次采样值变量
int sum=0; //求和变量,用于存储采样值的累加值
char count;//采样数据读入的下标变量
for(count=0;count<N;count++)
{
new_value=get_ad(); //读入本次采样值
if((new_value-value>A)||(value-new_value>A)) //比较是否超出最大偏差值
new_value=value; //如果超出,返回上次的有效值作为本次的有效值
sum+=new_value; //累加采样的有效值
value=new_value;
delay();
}
return (char)(sum/N); //将累加值进行平均计算作为返回值
}
#define COE 50 //定义加权系数
char value; //上一个采样值变量
char filter_7()
{
char new_value; //本次采样值变量
new_value = get_ad();
return (100-COE)*value + COE*new_value; //返回的本次滤波结果
}
#undef N
#define N 12 //设置FIFO队列的长度
char code coe[N] = {1,2,3,4,5,6,7,8,9,10,11,12}; //加权系数
char code sum_coe = 1+2+3+4+5+6+7+8+9+10+11+12;
char filter_8()
{
char count; //采样数据读入的下标变量
char value_buf[N]; //缓存N个采样值的存储变量
int sum=0; //求和变量,用于存储采样值的累加值
for (count=0;count<N;count++)
{
value_buf[count] = get_ad(); //读入采样值
delay();
}
for (count=0;count<N;count++)
sum += value_buf[count]*coe[count]; //累加采样值和系数的乘积
return (char)(sum/sum_coe); //累加值与系数和相除作为返回结果
}
#undef N
#define N 12 //设置计数器溢出值
char filter_9()
{
char count=0; //计数变量
char new_value; //本次采样值变量
new_value = get_ad(); //读入本次采样值
while (value !=new_value);
{
count++; //计数器加1
if (count>=N) return new_value; //如果本次采样值与当前有效值不相等,
//且计数器溢出,返回本次采样值
delay();
new_value = get_ad();
}
return value; //如果本次采样值与当前有效值相等,则返回当前有效值
}
#undef A
#undef N
#define A 10 //设置两次采样允许的最大偏差值
#define N 12 //设置计数器溢出值
char value; //有效值变量
char filter_10()
{
char count=0; //计数变量
char new_value; //本次采样值变量
new_value = get_ad(); //读入本次采样值
if((new_value-value>A)||(value-new_value>A)) //比较是否超出最大偏差值
new_value=value; //如果超出,返回有效值作为本次的采样有效值
while (value !=new_value);
{
count++; //计数器加1
if (count>=N) return new_value; //如果本次采样值与当前有效值不相等,
//且计数器溢出,返回本次采样值
delay();
new_value = get_ad();
}
return value; //如果本次采样值与当前有效值相等,则返回当前有效值
}
【转】单片机系统中数字滤波的算法【C程序整理】的更多相关文章
- 硬件知识整理part3--电阻在单片机系统中的应用
邦有道,如矢:邦无道,如矢. --孔子 电阻在电路中主要功能是限流和分压等等.在单片机系统中自然也是. 电阻作为限流应该是最常用的应用之一,对于单片机外围设计来说,电阻的应用非常重要,在很多时候,我 ...
- 命令行翻译 推荐一个linux系统中可用的终端小程序
程序的github地址:https://github.com/fanbrightup/fanyi 使用起来非常简单,同时支持中英文互译甚至是整句. 步骤一:首先你需要安装node,参见我的node安装 ...
- 单片机系统与标准PC键盘的接口模块设计
转自单片机系统与标准PC键盘的接口模块设计 概述 在单片机系统中,当输入按键较多时,在硬件设计和软件编程之间总存在着矛盾.对于不同的单片机系统需要进行专用的键盘硬件设计和编程调试,通用性差,使 ...
- VB6单片机编程中的汉字处理
在DOS时代,拥有一个华丽的汉字菜单几乎是每个高档中文应用程序必须的包装.中文Windows操作系统的出现使得高级开发平台实现全中文的提示和界面非常容易和方便.在一般的应用程序中已经很少需要去专门考虑 ...
- 64位系统中连接Access数据库文件的一个问题
近日在windows 7 64位系统中编译以前写的程序,发现在连接Access数据库时总是出现异常,提示“Microsoft.Jet.OLEDB.4.0”未在本机注册,同样的代码在32位的xp系统中却 ...
- 西门子PLC在自动浇灌系统中的应用
西门子PLC在自动浇灌系统中的应用(鸿控整理) 2020-02-07 22:50:48 1 自动浇灌系统简介 系统采用自行研制的湿度传感器监测土壤的湿度情况,当土壤湿度低于所要求的值后,自动开启水泵电 ...
- 广告系统中weak-and算法原理及编码验证
wand(weak and)算法基本思路 一般搜索的query比较短,但如果query比较长,如是一段文本,需要搜索相似的文本,这时候一般就需要wand算法,该算法在广告系统中有比较成熟的应 该,主要 ...
- 分布式数据库中的Paxos 算法
分布式数据库中的Paxos 算法 http://baike.baidu.com/link?url=ChmfvtXRZQl7X1VmRU6ypsmZ4b4MbQX1pelw_VenRLnFpq7rMvY ...
- (分享)Paxos在大型系统中常见的应用场景
原帖http://timyang.net/distributed/paxos-scenarios/ 在分布式算法领域,有个非常重要的算法叫Paxos, 它的重要性有多高呢,Google的Chubby ...
随机推荐
- centos安装redis并设置开机启动
1.通过yum安装: yum install redis 2.设置redis.conf中daemonize为yes.设置密码: requirepass 3.安装完后的启动脚本是完善的,/etc/ini ...
- flask登录注册简单的例子
1.主程序 # app.py # Auther: hhh5460 # Time: 2018/10/05 # Address: DongGuan YueHua from functools import ...
- EAS_BI(扩展报表)
case when 的使用 1. 扩展报表,一张收费单据中,下面分为分录 问题描述: 收费单中有一个分录用于记录检测的项目名称以及标准费用.收费单有自己的主键,分录中的外键即是收费单的主键,然后分录表 ...
- idea 迁移maven项目出现导入仓库半天没反应的问题解决
可以先参考: https://www.cnblogs.com/kinome/p/10289212.html 然后再看看maven配置文件是否正确,项目进行迁移时,如果环境不同,比如一个是使用的自定义m ...
- [LOJ#6044]. 「雅礼集训 2017 Day8」共[二分图、prufer序列]
题意 题目链接 分析 钦定 \(k\) 个点作为深度为奇数的点,有 \(\binom{n-1}{k-1}\) 种方案. 将树黑白染色,这张完全二分图的生成树的个数就是我们钦定 \(k\) 个点之后合法 ...
- 您需要来自XXX的权限才能对此文件夹进行更改
解决办法: cmd命令:del/f/s/q 文件夹
- python中eval函数作用
eval函数就是实现list.dict.tuple与str之间的转化str函数把list,dict,tuple转为为字符串 一.字符串转换成列表 a = "[[1,2], [3,4], [5 ...
- 行级安全(Row-Level Security)
通过授予和拒绝(Grant/Deny)命令控制用户的权限,只能控制用户对数据库对象的访问权限,这意味着,用户访问的粒度是对象整体,可以是一个数据表,或视图等,用户要么能够访问数据库对象,要么没有权限访 ...
- Python对Selenium调用浏览器进行封装包括启用无头浏览器,及对应的浏览器配置文件
""" 获取浏览器 打开本地浏览器 打开远程浏览器 关闭浏览器 打开网址 最大化 最小化 标题 url 刷新 Python对Selenium封装浏览器调用 ------b ...
- Go语言实现数据结构(一)单链表
1.基本释义 2.结构体设计 3.基本方法设计 4.Main函数测试 1. 基本释义 线性表包含两种存储方法:顺序存储结构和链式存储结构,其中顺序表的缺点是不便插入与删除数据:接下来我们重点实现基于G ...