搜索
bottom↓
回复: 65

[经验分享]【求平均值的简捷方法】

  [复制链接]

出0入0汤圆

发表于 2014-5-7 21:54:58 | 显示全部楼层 |阅读模式
本帖最后由 FSL_TICS_ZJJ 于 2014-6-4 13:09 编辑

前些日子做项目用到了PIC单片机,无意中接触一个求平均值的小算法,稍加整理,共享给大家,欢迎拍砖...

首先说明,这是前人所推荐的求平均值的方法,只是经过我的一点点加工整理
这里开头先说一个重要的概念,在二进制数值表示方法中,一个无限长的二进制数顺序向左移动一位就是原值乘以二,而如果二进制数顺序向右移一位就是原值除以二。我们利用这个特性在PIC单片机中可以很方便地求得两个数的平均值:将两个数相加,然后将和右移一位便是两个数的平均值,如果和是奇数,那么余数就在C里面。
更进一步的方法,比如我们要求一段时间内AD值的平均值,通常需要把几十次的和再除以几十,而利用移位其实非常方便,甚至求和后都可以不用再作任何运算就可以得到平均值。举一个很特殊的例子说明:
我们要为8位的AD结果做平均值运算,如果我们一次做256次(注意刚好是2的8次方哦)AD转换,将每次的AD结果相加,这样256次AD转换之后我们得到一个16位的和,这时我们就不用再把和除以256了,实际上我们已经得到了8位的平均值整数部分,那就是16位和的高8位!怎么证明?很简单:我们求得的和要除以256,将256拆开,就是这样一个方程(假设平均值为X,和为Y):
X=Y/(2*2*2*2*2*2*2*2)
也就是将Y除以8次2,换句话说按照上面的说法就是要将“和”右移8位————刚好将16位和的高8位移入低8位,而原低8位如果作为小数舍去的话,那么原高8位就是平均值的整数部分!既然如此简单就没有理由再去自找麻烦移位8次了,直接取16位和的高8位多简单?若是有人拿了那个16位的和直接去冒充16位精度的AD结果,我也不反对。
让我们再来思考一下10位AD值的平均值如何求:再做256次?太麻烦了,取得的和要18位占用3个字节,而且时间太长不允许,怎么样才更简便呢?右对齐的10位AD值占用两个字节,高字节前面还有6位空的,6位就是2的6次方=64,这就是我们需要做AD的次数,做完64次AD之后结果相加的和刚好是10位的左对齐值!当然要舍去低字节的低6位,那是小数,我们取整的时候并不关心的。有人说:我还是需要结果是右对齐的以方便计算————那就右移6位…………有必要吗?其实还有更简单的:那就是只需要左移2位!呵呵大家都是明眼人不用我多说为什么了,可以看得出来这有多方便。
推而广之,如果我们要作若干整数的平均值,只需要先求2的n次方次的和然后右移n次或者左移(8-n)次就可得到它们的平均值整数了。往哪移,看往哪近喽。
附:10位AD转换求64次平均值程序:

        MOVLW        D'64'
        MOVWF        COUNT
        MOVLW        ADRESL
        MOVWF        FSR
        CLRF        ADMEANH
        CLRF        ADMEANL                ;这个不要忘了
LOOPAD
        CALL        AD转换
        MOVF        ADRESH,W
        ADDWF        ADMEANL,F            ;加高字节,因为高字节只有最低两位,64次求和不会溢出,所以不用判断是否有进位。
        MOVF        INDF,W
        ADDWF        ADMEANH,F            ;加低字节
        BTFSC        STATUS,C            ;是否有进位
        INCF        ADMEANL
        DECFSZ    COUNT
        GOTO        LOOPAD
平均值调整
        BCF            STATUS,C            ;这是一个移位之前的好习惯,许多找不到原因的错误就源于此
        RLF            ADMEANH
        RLF            ADMEANL
        RLF            ADMEANH
        RLF            ADMEANL
        RLF            ADMEANH                ;思考:为什么要做两次半左移?为什么先前加的时候AD结果高字节加到暂存低字节?
        MOVLW        B'11'
        ANDLW        ADMEANH                ;舍掉高字节高六位的余数。
;至此,64次AD的结果平均值高字节两位结果在ADMEANH中,低字节8位在ADMEANL中
;=============================================================
AD转换
        重新选择AD通道
        重新开启AD模块                ;这两项比较重要,在同一个通道反复采样中,必须每次都重新采样才能得到准确的AD结果
        CALL        采样延时            ;大约几十微秒使采样电容充饱,此延时视AD口输入阻抗而定。若阻抗大于10K,应适当延长采样时间。
        BSF            ADCON0,GO        
        BTFSC        ADCON0,GO
        GOTO        $-1
        AD返回

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2014-5-7 22:33:13 | 显示全部楼层
好像是第一次沙发了。

出0入0汤圆

发表于 2014-5-7 23:41:32 来自手机 | 显示全部楼层
帮你顶起来嘿嘿

出0入0汤圆

发表于 2014-5-8 06:46:06 来自手机 | 显示全部楼层
Very good!

出0入0汤圆

发表于 2014-5-8 06:54:19 来自手机 | 显示全部楼层
不错                                 

出10入10汤圆

发表于 2014-5-8 07:34:56 | 显示全部楼层
51的时候就学过了。左移乘2,右移除2.

但是如果乘除不是2的N次方,怎么才能快一些?

出0入0汤圆

发表于 2014-5-8 09:36:19 | 显示全部楼层
值得学习借鉴,支持一下。

出0入0汤圆

发表于 2014-5-8 09:40:55 | 显示全部楼层
右移6位…………有必要吗?其实还有更简单的:那就是只需要左移2位!呵呵大家都是明眼人不用我多说为什么了,可以看得出来这有多方便。


这个左移2跟右移6我真没看懂什么原理,楼主详细深入浅出下~

出0入0汤圆

发表于 2014-5-8 09:41:00 | 显示全部楼层

Very good!

出0入0汤圆

发表于 2014-5-8 10:32:31 | 显示全部楼层
不错。。。

出0入0汤圆

发表于 2014-5-8 10:32:59 | 显示全部楼层
cmheia 发表于 2014-5-8 09:40
这个左移2跟右移6我真没看懂什么原理,楼主详细深入浅出下~

假如是uint16型的,左移2位,取的是高8位,右移6位,取的是低8位,其实结果是一样的!

出0入0汤圆

发表于 2014-5-8 10:40:29 | 显示全部楼层
wangpengcheng 发表于 2014-5-8 10:32
假如是uint16型的,左移2位,取的是高8位,右移6位,取的是低8位,其实结果是一样的! ...

没……看……懂……

出0入0汤圆

发表于 2014-5-8 10:46:10 | 显示全部楼层
cmheia 发表于 2014-5-8 10:40
没……看……懂……

自己试一下就知道了,比如0xC0对应的2进制数据是0000 0000 1100 0000,左移6位:0000 0000 0000 0011,低8位的结果是3,右移2位:0000 0011 0000 0000,高8位的结果也是3,现在明白了吧?

出0入8汤圆

发表于 2014-5-8 11:08:55 | 显示全部楼层
非常好,学习了,

出0入0汤圆

发表于 2014-5-8 12:16:34 | 显示全部楼层
顶楼主, 非常好   

出0入0汤圆

发表于 2014-5-8 14:13:26 | 显示全部楼层
wangpengcheng 发表于 2014-5-8 10:46
自己试一下就知道了,比如0xC0对应的2进制数据是0000 0000 1100 0000,左移6位:0000 0000 0000 0011,低 ...

看了你这个我更不懂了,感觉我的左右跟你的镜像了……
我前面不懂的是你移来移去,最终都是要取得较长的数据中的一段,我想等谁说出“因为每次只能移一bit,所以移动2bit比移动6bit更快”这样的说法(虽然我不知道哪些处理器只能这么做,呢些处理器移多少bit都是同数量汇编、同指令周期的事)。

出0入0汤圆

发表于 2014-5-8 14:39:44 | 显示全部楼层
cmheia 发表于 2014-5-8 14:13
看了你这个我更不懂了,感觉我的左右跟你的镜像了……
我前面不懂的是你移来移去,最终都是要 ...

是他妈搞错了,哈哈,我左右不分

出0入0汤圆

发表于 2014-5-8 16:09:37 | 显示全部楼层
wangpengcheng 发表于 2014-5-8 14:39
是他妈搞错了,哈哈,我左右不分

你的飞币真多  是怎么做到的

出0入0汤圆

发表于 2014-5-8 16:12:47 | 显示全部楼层
话说楼主的真是厉害  汇编只是在学校学过,还没编过汇编程序  建议玩玩C语言,汇编用着多累

出0入0汤圆

发表于 2014-5-8 16:20:50 | 显示全部楼层
原来是汇编,有c的没

出0入0汤圆

发表于 2014-5-8 16:36:54 | 显示全部楼层
霸气侧漏 发表于 2014-5-8 16:09
你的飞币真多  是怎么做到的

多回复啊!

出0入0汤圆

发表于 2014-8-10 21:24:47 | 显示全部楼层
本帖最后由 klxx68 于 2014-8-10 23:52 编辑

“让我们再来思考一下10位AD值的平均值如何求:再做256次?太麻烦了,取得的和要18位占用3个字节,而且时间太长不允许,怎么样才更简便呢?”

取得的和要18位占用3个字节,没看懂,为什么呢?


补充:仔细看了下,取得的和要18位占用3个字节,应该是指256个10位AD转换结果之和最大可能需要18位二进制位来表示,但这里应该是4字节而不是三字节,数据类型中的整型量没有占用3个字节的吧。

出0入0汤圆

发表于 2014-8-10 21:32:08 | 显示全部楼层
“我们要为8位的AD结果做平均值运算,如果我们一次做256次(注意刚好是2的8次方哦)AD转换,将每次的AD结果相加,这样256次AD转换之后我们得到一个16位的和,这时我们就不用再把和除以256了,实际上我们已经得到了8位的平均值整数部分,那就是16位和的高8位!怎么证明?很简单:我们求得的和要除以256,将256拆开,就是这样一个方程(假设平均值为X,和为Y):
X=Y/(2*2*2*2*2*2*2*2)”


感觉总是有问题,比如采集256次,256次中一次结果是0,255次结果是1 ,加起来结果也没有16位,按你这种算法出来的平均值就是0?

出0入0汤圆

发表于 2014-8-10 22:57:16 | 显示全部楼层
klxx68 发表于 2014-8-10 21:32
“我们要为8位的AD结果做平均值运算,如果我们一次做256次(注意刚好是2的8次方哦)AD转换,将每次的AD结果 ...

因为按照你的这个举例,最后的平均值是小于1的,所以按照楼主的算法就出现了等于0的现象了。因此楼主的算法直接放弃小数了,他也说了得到平均值的整数位。

出0入0汤圆

发表于 2014-8-10 22:59:32 | 显示全部楼层
步之道 发表于 2014-8-10 22:57
因为按照你的这个举例,最后的平均值是小于1的,所以按照楼主的算法就出现了等于0的现象了。因此楼主的算 ...

那实际的结果就不对了,没意义啊

出0入0汤圆

发表于 2014-8-10 23:40:55 | 显示全部楼层
klxx68 发表于 2014-8-10 22:59
那实际的结果就不对了,没意义啊

具体算法没研究,把int改为float,或者double,能解决这个问题,不过还是有一个问题,就是精度问题,绝对精度是达不到的,只能有一个相对精度。

/*****来源网络****/

根据国际标准IEEE 754,任意一个二进制浮点数V可以表示成下面的形式:

  V = (-1)^s×M×2^E

  (1)(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。

  (2)M表示有效数字,大于等于1,小于2。

  (3)2^E表示指数位
IEEE 754规定,对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。
                     对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

/**************END****/
也就是说不管怎么搞,如果你用float型那么你求和后平均到2^8次方后就会产生算法误差。double是11位哦。
那么现在就到楼主去改算法的时间了。

个人理解,不对请指正。如果各位要拍砖啥的就拍屁股,千万别打脸哦。

出0入0汤圆

发表于 2014-8-10 23:42:36 | 显示全部楼层
步之道 发表于 2014-8-10 23:40
具体算法没研究,把int改为float,或者double,能解决这个问题,不过还是有一个问题,就是精度问题,绝对 ...

忘了说了,楼主的算法只能是2的次方,要是谁较真说我就要采样9次算平均,好像比较闹心。

出0入0汤圆

发表于 2014-8-25 19:03:56 | 显示全部楼层
个人表示对汇编无能为力啊

出0入0汤圆

发表于 2014-8-25 19:15:33 | 显示全部楼层
表示有点看不懂汇编!!

出0入0汤圆

发表于 2014-8-25 19:22:58 | 显示全部楼层
这个是经典方法了

出0入0汤圆

发表于 2014-8-25 21:08:07 | 显示全部楼层
汇编的,领悟楼主精神。

出0入0汤圆

发表于 2014-8-25 22:24:47 | 显示全部楼层
常用的简捷算法,但对汇编不感冒

出0入0汤圆

发表于 2014-8-25 22:28:38 | 显示全部楼层
这个好.......支持啊............

出0入0汤圆

发表于 2014-8-25 22:47:51 | 显示全部楼层
汇编还不懂

出0入0汤圆

发表于 2014-8-25 22:49:07 | 显示全部楼层
先收藏起来                           

出0入0汤圆

发表于 2014-8-25 22:56:52 | 显示全部楼层
高大啊,汇编的程序

出100入101汤圆

发表于 2014-8-26 07:20:28 | 显示全部楼层
汇编,只懂一点51

出0入0汤圆

发表于 2014-8-26 07:29:02 | 显示全部楼层
试试,试试

出0入0汤圆

发表于 2014-8-26 08:13:18 | 显示全部楼层
klxx68 发表于 2014-8-10 22:59
那实际的结果就不对了,没意义啊

这个问题只能增加“四舍五入”判断条件才能解决。
1. 移位平均后得到平均值的整数位和小数位,
2. 判断小数位的最高位是否为1,若是,则整数位加1。

出0入0汤圆

发表于 2014-8-26 08:18:41 | 显示全部楼层
不错,学习了

出0入0汤圆

发表于 2014-8-26 08:32:35 | 显示全部楼层
本帖最后由 xizi 于 2014-8-26 08:41 编辑
wangpengcheng 发表于 2014-5-8 14:39
是他妈搞错了,哈哈,我左右不分


我也看不懂,楼主说的是10位AD采集64次:
假设每次采集都是0000 0011 1111 0000 (0x03f0), 64次总和为1111 1100 0000 0000(0xfc00).
右移6位,仍为0000 0011 1111 0000 (0x03f0);可是左移2位变为1111 0000 0000 0000(0xf000) ,就算舍弃低字节也为1111 0000(0xf0), 明显错了.

其实楼主没有讲明白,10位AD采集64次后在16位内右移正确,16位内左移可能错误(如果9,10位为1,就会出错)。只有扩展一个高字节,在24位内左移,然后舍弃低字节,由次高字节和新扩展的最高字节组成新的16位,才会永远正确。

出0入0汤圆

发表于 2014-8-26 08:45:47 | 显示全部楼层
引出新的问题,10位AD采集64次,由于需要在24位内左移2位才会正确。那么到地是24位数左移2位快呢,还是16位数右移6位快呢?

出0入0汤圆

发表于 2014-8-26 09:04:36 | 显示全部楼层
如果和是奇数,那么余数就在C里面。

楼主,什么叫余数就在C里面

出0入0汤圆

发表于 2014-8-26 09:13:59 | 显示全部楼层
还是汇编,这个离飞思卡尔好远了吧!

出0入0汤圆

发表于 2014-8-26 09:40:32 | 显示全部楼层
霸气侧漏 发表于 2014-5-8 16:09
你的飞币真多  是怎么做到的

多回复,多发贴,多发精品!

出0入93汤圆

发表于 2014-8-26 09:44:36 | 显示全部楼层
貌似某些C编译器会在可以的情况下自动采用移位方式计算的

出0入0汤圆

发表于 2014-8-28 06:36:32 | 显示全部楼层
汇编不会用啦,呵呵

出0入0汤圆

发表于 2014-8-28 09:20:46 | 显示全部楼层
天哪,汇编,我慢慢看。

出0入0汤圆

发表于 2014-8-29 10:45:19 | 显示全部楼层
我一般是把4个采样结果累加,然后除以4,
单片机做除以4的运算很方便,右移动2位就可以,

楼主把64个结果累加,累加的数据不是越多就越好,在时间上造成延迟滞后

出0入0汤圆

发表于 2014-8-29 11:05:19 | 显示全部楼层
学习啦   

出0入0汤圆

发表于 2014-8-30 17:33:24 | 显示全部楼层
学习了  对算法一直很少研究   其实这才是核心竞争力啊

出0入0汤圆

发表于 2014-8-30 20:03:24 来自手机 | 显示全部楼层
这是个老常识了,不过没人像楼主这样好好整理出来。

出0入0汤圆

发表于 2014-8-31 02:33:56 来自手机 | 显示全部楼层
好,一直在用

出0入0汤圆

发表于 2014-8-31 06:48:19 | 显示全部楼层
单纯的累加求和再除以采集次数是不准确的,类似这样的,好歹将采样结果冒泡排序一下,然后分别去掉结果比较低和结果比较高的几个再去求平均值
这叫中值平均值滤波,不然就像楼上有人说的一旦来一个扰动采到一个极小的值,结果就相差十万八千里了

出0入476汤圆

发表于 2014-8-31 07:48:03 | 显示全部楼层
汇编需要好好规划,用C的不用考虑这么多。

出0入0汤圆

发表于 2014-8-31 11:54:16 | 显示全部楼层
汇编一看就心慌,c到是容易不过代码空间大了

出0入0汤圆

发表于 2014-8-31 12:15:41 | 显示全部楼层
汇编看起来好费劲

出0入0汤圆

发表于 2014-8-31 13:45:11 | 显示全部楼层
顶一个,支持一下

出0入0汤圆

发表于 2014-8-31 22:29:36 | 显示全部楼层
真的学无止境

出0入0汤圆

发表于 2014-9-3 21:51:18 | 显示全部楼层
是汇编,厉害

出500入203汤圆

发表于 2014-9-3 21:59:58 | 显示全部楼层
在arm核心中,单周期硬件乘除法器秒杀

出0入0汤圆

发表于 2014-9-3 23:41:17 来自手机 | 显示全部楼层
不错。。。。

出0入0汤圆

发表于 2014-9-4 06:47:39 来自手机 | 显示全部楼层
思想不错,我就是这么做的

出0入0汤圆

发表于 2014-9-4 09:12:17 | 显示全部楼层
很好的总结

出0入8汤圆

发表于 2014-9-4 15:09:40 | 显示全部楼层
汇编看不懂

出0入0汤圆

发表于 2014-9-4 17:13:23 | 显示全部楼层
细细品味下原理。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-23 11:19

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表