技术论坛

平均值滤波之鬼斧神工算法

作者 主题
侠士

经验值: 1355
发帖数: 725
精华帖: 25
平均值滤波之鬼斧神工算法
精华帖


只看楼主 只看精华 2008-01-03 00:11:55

在十种经典软件滤波算法中,可以看到很多算法都是平均值滤波算法变种,事实上最常用的也还是平均值滤波算法。但传统的平均值滤波算法很占内存,每次运算都要求累加和,再求平均值,导致运算效率不高。
今天介绍一种超简洁超高效的平均滤波算法,此算法是以前搞单片机时一老师所创(单片机上的内存简直是寸土寸金),仅仅用三个变量,就完成了平均值滤波的计算。刚开始看到这个算法是只觉得很佩服,后来用了各种各样的算法后,才感到此算法简直到了鬼斧神工的地步(别以为看完后觉得太简单没啥大不了的,正是因为太简单才突出了它的了不起,最开始能想到将一个复杂的算法简化到无法再简的地步非一般功力所能做到的)。

在该基础上,我们再演变出一种带死区和限幅控制的队列平均值实用算法。
采样值 C、累加器 S,平均值 A,采样次数 N

传统的平均值滤波算法
S = C(1) + C(2) + ... + C(N)
A = S / N
需要用循环来计算累加和,比较耗时,C(1~N)是缓存,随采样数N增大,所需内存量也增大

向队列平均值算法推进
S = C(1) + C(2) + ... + C(N) (第一次)
C(x) = C(x + 1) (队列前移)
C(N) = C
S = S - C(1) + C(N)
A = S / N
运算量有所改进(用指针维护循环队列,不实际移动数据),占用内存问题不变

鬼斧神工算法
初始化:A=初始值,S=A*N
S = S - A + C
A = S / N
就这么简单,三个变量(N可以是常数),只要 S 的量程足够,N可以任意调整。
可以看出,此算法是从队列平均值算法演变而来,因没有了队列,每次计算时不知道该丢弃的最老的一个采样值是多少,这里用了个替代的办法,丢弃上次计算出的平均值。
去掉了缓存维护,节省内存空间,同时也将运算量压缩到了最小,执行效率非常高。调试时容易修改采样数。

优化算法
此算法的核心思想还是平均值滤波,虽然改进了运算量和内存占用,但同样继承了平均值滤波法 N 值较大时平滑度好,反应迟钝的特点。
为此,对算法引入 S7-200 系统滤波程序中死区的概念:采样值偏差在死区范围内时,进行滤波计算,采样值偏差在死区范围以外时直接使用采样值,达到快速反应的效果。
再溶合限幅滤波法去掉偶然的干扰脉冲:采样值偏差在限幅范围内时,进行滤波计算,采样值偏差在限幅范围以外时直接丢弃,使用上次滤波输出值。显然,限幅值应该大于死区值。

将此算法写成两个子函数(也可以做成库)
主滤波程序 AveFilter


入口参数:
EN  : 调用使能位
bType :采样值类型,'W'=整型、'F'=浮点型、'D'(或其它)=长整型,参数类型:字节
wHi : 采样值高位字(采样值为整型是,实参必须为0),参数类型,2字节
wLo : 采样值低位字,参数类型,2字节
rDie : 滤波死区,参数类型:浮点数
rMaxErr : 最大允许偏差,参数类型:浮点数
rLen :滤波队列长度,参数类型:浮点数
出/入口参数:
rSum :累加和,参数类型:浮点数
rAve :滤波输出平均值,参数类型:浮点数
命令行:CALL   AveFilter, 'W', 0, SMW28, 640.0, 32000.0, 4.0, VD0, VD4

注意:本程序采样值是参数类型可适应的,用 wHi/wLo 的组合来适应整型、长整型、浮点型的参数类型输入,避免使用多个相同的子程序来适应不同类型的输入参数。由 bType 来指定输入的参数类型。

滤波器初始化程序 InitFilter


入口参数:
EN  : 调用使能位
rInit :初始值(一般为0),参数类型:浮点数
rLen : 滤波队列长度,参数类型:浮点数
出/入口参数:
rSum :累加和,参数类型:浮点数
rAve :滤波输出平均值,参数类型:浮点数
命令行:CALL   InitFilter, 0.0, 4.0, VD0, VD4


生命存在的方式只有两种:腐烂或燃烧
以下网友喜欢您的帖子:

  
重要声明:

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

帖子链接:https://www.ad.siemens.com.cn/club/bbs/post.aspx?a_id=392528&b_id=3&s_id=0&num=16

侠士

经验值: 1743
发帖数: 1247
精华帖: 0
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-03 08:21:44
有这个必要吗?
一般人的思维就是:
初始化和S=0,次数N=0,采集数据C,平均值C=0
S=S+A
N=N+1
10次取平均
C=S/N
完成后将S=0,次数N=0,平均值输出
我想恐怕没人笨到要把10次的值都取过来,再做平均处理吧

志存高远,追求卓越!
以下网友喜欢您的帖子:

  
侠圣

经验值: 4693
发帖数: 757
精华帖: 1
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-03 09:27:03
我同意楼上的说法。我在使用中也是这样做。没必要把10次的值都取过来,再做平均处理
 
以下网友喜欢您的帖子:

  
侠士

经验值: 1355
发帖数: 725
精华帖: 25
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-03 14:41:22
本文只是提出一种算法而已,仅供参考
二楼的方法当然也是一种好方法,但那只是代码上的技巧(只是优化了内存占用,运算量跟先采集再用循环计算是相当的),而不是算法本身的优劣。即使按二楼的方法进行代码优化,大概也不会比下面的再简洁多少:
+* C, S
LD*>= n, 10
mov* S, A
/* n, A
mov* 0, n
mov* 0, S
not
inc* n

而本文的算法:
+* C, S
-* A, S
mov* S, A
/* 10, A
只需要3个变量,4条语句,不需做采样计数与比较,效率不言自明
而且更重要的区别是算法本身,本文的方法是基于队列平均值,它用相对简洁的代码利用了队列平均值算法的精髓。假如采样时间为100mS,二楼的方法要每隔1S才得到一个有效的平均值,而本算法每100mS就可以得到一个有效的平均值。
本文主要是比较算法本身的优劣,而不是代码,首先要算法好,代码才可能更简洁。
生命存在的方式只有两种:腐烂或燃烧
以下网友喜欢您的帖子:

  
侠士

经验值: 1743
发帖数: 1247
精华帖: 0
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-03 17:00:00
举一个简单的例子来检测一下你的算法
采集N=2次,每次采集假设为A=10,数值总和为S=20,
假设下次采集数据为11,根据你的公式:
S = S - A + C
A = S / N
S=20-10+11=21
A=21/2=11.5
再一次采集为9,再次计算:
S=21-10.5+9=19.5
A=19.5/2=9.75
但是实际上两次采集平均值为(9+11)/2=10,好象与你公式差了好多?
还是不明白你的算法!
志存高远,追求卓越!
以下网友喜欢您的帖子:

  
侠士

经验值: 1355
发帖数: 725
精华帖: 25
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-03 18:04:40
你这个例子举得非常好,用一个特例一下子点出了本算的关键缺点
队列平均值滤波要求维护一个队列,每采到一个新值入队时,最老的一个采样值要出列。本算法改进后去掉了队列后,就不知道该丢弃的最老的采样值是多少,采取的替代办法是丢弃上次的平均值。所以它会存在一个理论误差:
(C[1] - A) / N
就你这个特例来说,最老的采样值是10,而我们丢弃的是10.5,所以误差为:
(10-10.5) / 2 = -0.25
所以此算法中 N 值不能太小,当N值取得足够大时,这个误差就可以忽略,而由此带来的运算量精简是值得的。实际应用中平均值滤波一般也不会只采2次是吧。而现实中模拟量采入也不可能跟举例中的数字一样精准,它总是存在误差的,所以最终的理论误差在实际误差面前完全可以忽略不计(理论还要结合实践嘛,呵呵)。
本算法基于队列平均值算法,增大N值不会影响稳定时的采样实时性。但增大N值会导致从不稳定到稳定状态的时间变长,所以加入了死区的概念。
而死区的引入同时又导致对尖峰的滤波效果变差,所以最后又加入限幅控制。
经过综合改进后,最终本算法用相对简单的代码,具有队列平均值滤波法+死区法+限幅滤波法综合优点。

生命存在的方式只有两种:腐烂或燃烧
以下网友喜欢您的帖子:

  
侠士

经验值: 1743
发帖数: 1247
精华帖: 0
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-04 08:16:59
程序还没看呢,看完了程序再和你讨论
不过还是要谢谢楼主的无私
志存高远,追求卓越!
以下网友喜欢您的帖子:

  
至圣

经验值: 10684
发帖数: 1561
精华帖: 33
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-04 12:51:42
首先要算法好,代码才可能更简洁。


支持!
工控爱好者
以下网友喜欢您的帖子:

  
奇侠

经验值: 6879
发帖数: 5350
精华帖: 25
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-05 09:33:26
对于PLC 维护一个队列的代价也不是很高

怎么论坛改成这样了??
以下网友喜欢您的帖子:

  
侠士

经验值: 1460
发帖数: 120
精华帖: 2
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-07 12:39:03
确实鬼斧神工!好!
不过对于PLC 维护一个队列的代价也不是很高,现在速度快内存大了。

 
以下网友喜欢您的帖子:

  
侠士

经验值: 1355
发帖数: 725
精华帖: 25
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-07 15:57:03
确实,用指针维护一个环形队列时间代价并不高(但如果用FIFO指令来维护队列,代价是相当高的,这也是S7-200中表长有限制的一个重要原因),而且在S7-200上节省一点内存也不是很有必要。该算法关键是要减少代码量,提高执行速度。
对于基础算法来说,能减少一条指令,节省一个微秒都是有用的。当只有一二路模拟量需要滤波时,可能看不出什么好处,但需要滤波的量很多时,效率就体现出来了
生命存在的方式只有两种:腐烂或燃烧
以下网友喜欢您的帖子:

  
新手

经验值: 2
发帖数: 1
精华帖: 0
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-07 16:10:18
"该算法关键是要减少代码量,提高执行速度。" 支持!!
 
以下网友喜欢您的帖子:

  
游民

经验值: 70
发帖数: 23
精华帖: 0
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-01-07 18:58:36
收藏了,下了程序看看先
s7-300
以下网友喜欢您的帖子:

  
版主

经验值: 25990
发帖数: 11808
精华帖: 43
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-02-07 10:16:24
如果是整数运算,这个算法有一个天然的死区。当采样值C和平均值A的差小于N时,会在/N时被吃掉。
 
以下网友喜欢您的帖子:

  
侠士

经验值: 1203
发帖数: 912
精华帖: 5
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-02-07 21:34:29
谢过了
 
以下网友喜欢您的帖子:

  
侠士

经验值: 1429
发帖数: 896
精华帖: 5
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-02-11 15:35:28
首先觉得这样做在算法上有点不妥,踢出旧值踢得有点问题,再者对现在PLC来说,平均值这些队列空间还不是什么问题
Palm:IIIXE->TW->TX CellPhone:SE T102->SE K700c->SIEMENS SK65->Dopod 838->SE M608C->Treo680(Old)-〉Treo680(New)->Samsung I780
以下网友喜欢您的帖子:

  
游侠

经验值: 398
发帖数: 204
精华帖: 0
回复:平均值滤波之鬼斧神工算法


只看楼主 只看精华 2008-02-15 11:26:23
没详细看,但直觉“鬼斧神工”来形容可能言过其实了,学过数据结构的人
都能明白其中的道理
1)传统的:当N比较大时,内存的确是个问题,好比int iLen[1000000],没人会这么傻,哪怕是在PC端
2)队列的:合乎逻辑的通用的算法
3)楼主的:比较特殊,比较1)2)内存空间效率是提高了,时间复杂度方面是否有优化不太清楚,但N值固定。可2)链表结构的N可动态变化,而且对于节点也可add和delete(但相比较会增加CPU运算量)

但我有些不明白:采样值是串行队列式采集,还是一次采集M多个点?楼主讲的去掉了缓存维护是什么意思?CPU的缓存还是。。。?

没做过数据采集方面的东东,所以胡乱讲了,有不对的大家指出


成功离你很近,只需你跨出一小步而已!
以下网友喜欢您的帖子:

  
  • 上一页
  • 1
  • 下一页
收起
平均值滤波之鬼斧神工算法
您收到0封站内信:
×
×
信息提示
很抱歉!您所访问的页面不存在,或网址发生了变化,请稍后再试。