搜索
bottom↓
回复: 57

请问,下面的代码如何简化。

  [复制链接]

出0入0汤圆

发表于 2017-3-7 20:43:05 | 显示全部楼层 |阅读模式
总共有32路adc采样,每一路都要做滤波,而且每采样一次就要与历史数据平均。
u16 filter_1(u16 dat)
{
        static u8 len=0;
        static u8 iter=0;
        static u16 oldDat;
        static s32 sum=0;
        static u16 temp[10]={0};
       
        oldDat = temp[iter];
        temp[iter] = dat;
        iter = ++iter%10;
        sum = sum + dat - oldDat;
        if (len<10) len++;
        return (u16)(sum/len);
}
u16 filter_2(u16 dat)
{
        static u8 len=0;
        static u8 iter=0;
        static u16 oldDat;
        static s32 sum=0;
        static u16 temp[10]={0};
       
        oldDat = temp[iter];
        temp[iter] = dat;
        iter = ++iter%10;
        sum = sum + dat - oldDat;
        if (len<10) len++;
        return (u16)(sum/len);
}
u16 filter_3(u16 dat)
{
        static u8 len=0;
        static u8 iter=0;
        static u16 oldDat;
        static s32 sum=0;
        static u16 temp[10];
       
        oldDat = temp[iter];
        temp[iter] = dat;
        iter = ++iter%10;
        sum = sum + dat - oldDat;
        if (len<10) len++;
        return (u16)(sum/len);
}
.....
上述一共32个滤波函数,每采集一次要调用一次下面对应的函数,能不能有简单的方法,不用写32个滤波函数,不用写32次调用。用for循环如何做?
voltage[0] =filter_1(adc[0]);
voltage[1] =filter_2(adc[1]);
voltage[2] =filter_3(adc[2]);

.....

出0入0汤圆

 楼主| 发表于 2017-3-7 20:47:47 | 显示全部楼层
主要是一个采样周期,32路都要采样一次,马上进行滤波。

出0入0汤圆

发表于 2017-3-7 20:48:20 | 显示全部楼层
粗看了一下仿佛每个函数完全一样,就是因为要用static保存历史数据从而做了16个函数么?如果是这样的话,按照现有思路就是把你的变量换成数组,数组换成二维数组,应该就可以只用一个函数了。

出0入9汤圆

发表于 2017-3-7 20:53:13 | 显示全部楼层
  1. typedef struct {
  2.     s16 *flbuf;     // 缓冲区指针
  3.     s32 datsum;     // 数据和
  4.     u16 cnt;        // 当前缓冲区数据个数
  5.     u16 size;       // 缓冲区大小
  6.     u16 ptr;        // 循环指针
  7. }FilterDef;         // 平移滤波类型定义

  8. s32 SlideFilter(FilterDef *filter,s32 datin)
  9. {
  10.     if(filter->cnt < filter->size)
  11.         filter->cnt++;
  12.     filter->datsum = filter->datsum+datin-filter->flbuf[filter->ptr];
  13.     filter->flbuf[filter->ptr] = datin;
  14.     filter->ptr = (filter->ptr + 1)%filter->size;

  15.     return filter->datsum/filter->cnt;
  16. }
复制代码

出0入0汤圆

 楼主| 发表于 2017-3-7 20:54:03 | 显示全部楼层
starsnow 发表于 2017-3-7 20:48
粗看了一下仿佛每个函数完全一样,就是因为要用static保存历史数据从而做了16个函数么?如果是这样的话,按 ...

是的,就是要用static保存每个通道的历史数据,所以才无奈做32个滤波函数。

出0入0汤圆

 楼主| 发表于 2017-3-7 21:26:01 | 显示全部楼层
4楼真乃高人,这个方法太好了,马上验证。

出0入17汤圆

发表于 2017-3-7 21:53:53 来自手机 | 显示全部楼层
楼主好好看看面向对象编程方法吧

出0入0汤圆

发表于 2017-3-7 21:59:56 | 显示全部楼层
标记,很快将用到此算法

出330入0汤圆

发表于 2017-3-7 23:57:34 | 显示全部楼层
本帖最后由 zcllom 于 2017-3-8 14:58 编辑

4楼的思路很好,可以延伸到很多地方。
搂主做的过程中,觉得不对劲,善于提问,也值得称道。

把后面的发现问题的回复移到前面来:

楼主的思路有一处小小的错误,那就是sum = sum + dat - oldDat;这个地方!
在len<10的时候不应该-olddat,会导致平均值的结果不对。
举例:
第1次采集: sum=0  oldDat=0  这时候来了个新DAT=10,那么sum = sum + dat - oldDat = 0+10-0=10 。if (len<10) len++;  那么len就是1 。sum/len = 10/1 =10;
第2次采集: sum=10  oldDat=10  这时候来了个新DAT=10,那么sum = sum + dat - oldDat = 10+10-10=10 。if (len<10) len++;  那么len就是2 。sum/len = 10/2 = 5;  而实际上,这两次采集到的数据都是10,应该平均值结果仍然是10!
……………………………………………………
……………………………………………………

出0入0汤圆

发表于 2017-3-8 06:12:18 | 显示全部楼层
环形队列结构变形

出0入0汤圆

发表于 2017-3-8 07:04:07 来自手机 | 显示全部楼层
4楼高手!!

出0入0汤圆

发表于 2017-3-8 07:18:31 | 显示全部楼层
ring buffer

出0入42汤圆

发表于 2017-3-8 07:47:14 来自手机 | 显示全部楼层
4楼高手

出0入0汤圆

发表于 2017-3-8 08:19:07 | 显示全部楼层
typedef struct
{
        uint16_t Index;
        uint16_t Count;
        uint32_t Buf[FILTER_LENGTH];
        uint32_t Sum;
        uint32_t In;
        uint32_t Out;
}Filter_TypeDef;
void Moving_Filter(Filter_TypeDef *filter)
{
        filter->Sum+=filter->In;
        filter->Buf[filter->Index]=filter->In;
        if(++filter->Count>=FILTER_LENGTH)
        {
                filter->Out=filter->Sum/FILTER_LENGTH;
                filter->Sum-=filter->Buf[(filter->Index==FILTER_LENGTH-1)?(0):(filter->Index+1)];
                filter->Count--;
        }
        filter->Index=(filter->Index>=FILTER_LENGTH-1)?(0):(filter->Index+1);
}

出0入0汤圆

发表于 2017-3-8 08:34:24 | 显示全部楼层
结构体,指针。。。。想到这两个。

出0入0汤圆

发表于 2017-3-8 08:58:21 来自手机 | 显示全部楼层
最多32xn的数组吧,怎么能32个函数(///▽///)

出0入0汤圆

发表于 2017-3-8 09:16:18 | 显示全部楼层
这种讨论 便是我逛论坛的乐趣所在,4楼 高手

出0入0汤圆

发表于 2017-3-8 09:22:43 | 显示全部楼层
赞一下4L,多用结构体跟指针,可以避免大量的参数传递,易理解又好用。

出0入0汤圆

发表于 2017-3-8 09:31:51 来自手机 | 显示全部楼层
记号记号

出0入0汤圆

发表于 2017-3-8 09:33:57 | 显示全部楼层
jia_xuan 发表于 2017-3-7 20:47
主要是一个采样周期,32路都要采样一次,马上进行滤波。

每个采立即完单独滤波?那要调用32次了。   如果采完一轮滤波一次,一次循环里再搞32次操作就结了。

出0入0汤圆

 楼主| 发表于 2017-3-8 09:52:30 | 显示全部楼层
huangqi412 发表于 2017-3-8 09:33
每个采立即完单独滤波?那要调用32次了。   如果采完一轮滤波一次,一次循环里再搞32次操作就结了。 ...

是的,每个通道都要单独滤波。

出0入0汤圆

 楼主| 发表于 2017-3-8 09:55:50 | 显示全部楼层
采完一轮数据,每个通道都要单独滤波一次,滤波过程中分别要用到本身通道的历史数据。

出0入0汤圆

 楼主| 发表于 2017-3-8 10:01:43 | 显示全部楼层
我在写代码的过程中,觉得用结构体配合指针应该能简化代码,但没有思路,所以提出来问题,期待高手解答,同时也给更多的朋友参考学习。

出0入0汤圆

 楼主| 发表于 2017-3-8 10:20:59 | 显示全部楼层
在实际应用中,这种滤波算法很好,思路就是:用数组保存最近的N次采样值,每采样一次就去掉最老的采样值,加入新值,然后平均。在采样次数小于N时,也能计算出正确的平均值。克服了采样次数要大于N以后,才能调用滤波算法的弊端。

出0入0汤圆

发表于 2017-3-8 10:38:34 来自手机 | 显示全部楼层
不用指针烦死人!乱用指针坑死人…反正我被坑过n次…溢出没发现…囧

出0入0汤圆

发表于 2017-3-8 13:10:01 | 显示全部楼层
4楼高手,结构体加指针用的好事半功倍

出0入0汤圆

发表于 2017-3-8 13:38:21 | 显示全部楼层
楼主用4楼的方法试验了吗?

出0入0汤圆

 楼主| 发表于 2017-3-8 14:00:14 | 显示全部楼层
正在调试,稍等。

出0入0汤圆

发表于 2017-3-8 14:28:06 | 显示全部楼层
4樓方法不錯.有用到時再試試。

出330入0汤圆

发表于 2017-3-8 14:39:27 来自手机 | 显示全部楼层
本帖最后由 zcllom 于 2017-3-8 14:56 编辑

楼主的思路有一处小小的错误,那就是sum = sum + dat - oldDat;这个地方!
在len<10的时候不应该-olddat,会导致平均值的结果不对。
举例:
第1次采集: sum=0  oldDat=0  这时候来了个新DAT=10,那么sum = sum + dat - oldDat = 0+10-0=10 。if (len<10) len++;  那么len就是1 。sum/len = 10/1 =10;
第2次采集: sum=10  oldDat=10  这时候来了个新DAT=10,那么sum = sum + dat - oldDat = 10+10-10=10 。if (len<10) len++;  那么len就是2 。sum/len = 10/2 = 5;  而实际上,这两次采集到的数据都是10,应该平均值结果仍然是10!
……………………………………………………
……………………………………………………

出0入0汤圆

发表于 2017-3-8 14:46:18 | 显示全部楼层

4楼高手!!

出0入0汤圆

发表于 2017-3-8 14:58:36 | 显示全部楼层
4楼的代码有问题,和值要除完以后再减,先减后除结果会偏小,缓冲区越小越明显

出0入0汤圆

 楼主| 发表于 2017-3-8 15:48:05 | 显示全部楼层
本帖最后由 jia_xuan 于 2017-3-8 15:54 编辑
zcllom 发表于 2017-3-8 14:39
楼主的思路有一处小小的错误,那就是sum = sum + dat - oldDat;这个地方!
在len


此处没错,是这样的:
第1次采集: sum=0   oldDat=temp[0]=0;   这时候来了个新DAT=10,那么sum = sum + dat - oldDat = 0+10-0=10 。if (len<10) len++;  那么len就是1 。sum/len = 10/1 =10;
第2次采集: sum=10  oldDat=temp[1]=0;  这时候来了个新DAT=10,那么sum = sum + dat - oldDat = 10+10-0=20 。if (len<10) len++;  那么len就是2 。sum/len = 20/2 = 10;
第3次采集: sum=20  oldDat=temp[2]=0;  这时候来了个新DAT=10,那么sum = sum + dat - oldDat = 20+10-0=30 。if (len<10) len++;  那么len就是3 。sum/len = 30/3 = 10;

因为temp[]数组初始化都为0,所以oldDat的数值在不够10次之前都=0

出0入0汤圆

 楼主| 发表于 2017-3-8 15:54:06 | 显示全部楼层
        iter = ++iter%10;这行代码,可以保证oldDat在采集10次之前,oldDat一直等于0

出0入0汤圆

发表于 2017-3-8 16:04:31 | 显示全部楼层
正在学结构体和指针。不错。

出0入0汤圆

发表于 2017-3-8 16:51:40 | 显示全部楼层
int32_t SUM16_AVE(int32_t input)
    {
        static unsigned char idx=0;
        static int32_t sum=0;
                  static int32_t array[16];
                               
        sum-=array[idx];
        sum+=input;        
        array[idx]=input;
        idx++;
        idx&=0x0F;
        
        return sum/16;
    }
有问题再讨论。

出0入76汤圆

发表于 2017-3-8 19:44:16 | 显示全部楼层
本帖最后由 foxpro2005 于 2017-3-8 19:52 编辑

上面4楼与14楼的会比较费内存了, 不就为了实现滑动平均吗, 我来开源分享两个更省更精简的:

滤波器数据结构类型
  1. /* 滤波器数据结构类型 */
  2. typedef struct _filter_t {
  3.     unsigned char  chFinish;
  4.     unsigned char  chCount;
  5.     unsigned short hwSum;
  6.     unsigned short hwMin;
  7.     unsigned short hwMax;
  8.     unsigned short hwValue;
  9.    
  10. } filter_t;
复制代码


1) 滑动平均值滤波(一阶滤波)
  1. void FLT_IIR(filter_t *ptObj, unsigned short hwNewValue, unsigned char n)
  2. {
  3.     if (ptObj->chCount) {
  4.         ptObj->hwSum += hwNewValue;
  5.     }
  6.     else {
  7.         ptObj->hwSum = hwNewValue * n + (n >> 1);
  8.         ptObj->chCount = 1;
  9.     }
  10.     ptObj->hwValue = ptObj->hwSum / n;
  11.     ptObj->hwSum -= ptObj->hwValue;
  12. }
复制代码



2) 去极值平均值滤波
  1. void FLT_ExtremeAverage(filter_t *ptObj, unsigned short hwNewValue, unsigned char n)
  2. {
  3.     if (0 == ptObj->chCount) {
  4.         ptObj->hwMax = ptObj->hwMin = hwNewValue;
  5.         ptObj->hwSum = hwNewValue + (n >> 1);
  6.         ptObj->chCount++;
  7.     }
  8.     else {
  9.         ptObj->hwSum += hwNewValue;
  10.         
  11.         if (ptObj->hwMin > hwNewValue) ptObj->hwMin = hwNewValue;
  12.         if (ptObj->hwMax < hwNewValue) ptObj->hwMax = hwNewValue;
  13.         
  14.         if (++ptObj->chCount >= n) {
  15.             ptObj->chCount = 0;
  16.             if (2 < n) {
  17.                 ptObj->hwValue = (ptObj->hwSum - ptObj->hwMax - ptObj->hwMin) / (n - 2);
  18.             }
  19.             else {
  20.                 ptObj->hwValue = ptObj->hwSum / n;
  21.             }
  22.             ptObj->chFinish = 1;
  23.         }
  24.     }
  25. }
复制代码


这两个都是一直在用的, 这个滑动平均值滤波是改进过的,不会出现初始(窗口长度)n未满时, 缓慢上升变化的过程。
欢迎大家讨论...

出0入0汤圆

 楼主| 发表于 2017-3-8 22:37:04 | 显示全部楼层
楼上朋友,没太看懂你的滑动平均滤波,能讲解一下吗,及如何调用。

出0入0汤圆

 楼主| 发表于 2017-3-8 22:45:07 | 显示全部楼层
本帖最后由 jia_xuan 于 2017-3-8 23:12 编辑

4楼朋友的那个结构体内指针成员如何初始化?

FilterDef adc[32]; //定义
for(i=0;i<32;i++) //初始化
{
        adc [ i ].flbuf=(s16*)malloc(10*sizeof(s16));//因为是10次平均,是不是要*10
}
这样对不?

出330入0汤圆

发表于 2017-3-10 00:42:12 | 显示全部楼层
jia_xuan 发表于 2017-3-8 15:54
iter = ++iter%10;这行代码,可以保证oldDat在采集10次之前,oldDat一直等于0

仔细看确实是这样。
你赶紧做测试吧。

出0入0汤圆

发表于 2017-3-10 07:10:26 来自手机 | 显示全部楼层
foxpro2005 发表于 2017-3-8 19:44
上面4楼与14楼的会比较费内存了, 不就为了实现滑动平均吗, 我来开源分享两个更省更精简的:

滤波器数据 ...

好方法,学习了,谢谢

出0入0汤圆

 楼主| 发表于 2017-3-10 08:57:45 | 显示全部楼层
昨天测试了,4楼的方法是正确的,可惜我结构体指针成员不知道如何初始化,我做了个小改动,改为数组成员。
typedef struct {
    s16 flbuf[10];     // 缓冲区指针
    s32 datsum;     // 数据和
    u16 cnt;        // 当前缓冲区数据个数
    u16 size;       // 缓冲区大小
    u16 ptr;        // 循环指针
}FilterDef;         // 平移滤波类型定义
调用如下,输出正确。
       FilterDef volt[32];

       for(i=0;i<32;i++)
        {
                data_adc[i]=SlideFilter(&volt[i],adc[i]);
        }

出0入76汤圆

发表于 2017-3-10 18:21:26 | 显示全部楼层
哈哈, 终于有人识得此宝了

出0入4汤圆

发表于 2017-3-10 18:47:05 | 显示全部楼层
foxpro2005 发表于 2017-3-8 19:44
上面4楼与14楼的会比较费内存了, 不就为了实现滑动平均吗, 我来开源分享两个更省更精简的:

滤波器数据 ...

和两个函数写得好,尤其第一个很巧妙,不过话说回来,与四楼的函数实现的结果并不一致。
并不能完全的剔除历史数据。

出0入76汤圆

发表于 2017-3-10 19:15:36 | 显示全部楼层
本帖最后由 foxpro2005 于 2017-3-10 19:17 编辑
MAD_FISH 发表于 2017-3-10 18:47
和两个函数写得好,尤其第一个很巧妙,不过话说回来,与四楼的函数实现的结果并不一致。
并不能完全的剔 ...


呵呵,都完全看明白了吗?

第一个滤波代码特性:
1)  不耗费RAM(当n比较大时),且也不用预置初值, 当刚开始滤波数据长度未达到时,对滤波后的结果也不会出现缓慢上升或为0的过程(当平均的长度n比较大时, 有些场合下不允许这样)。
2)  滤波结果有四舍五入的功能。

第二个滤波特性:去掉极大/小值后平均,同时也有四舍五入的功能, 这个也比较容易看懂了。

出0入4汤圆

发表于 2017-3-10 20:03:59 | 显示全部楼层
foxpro2005 发表于 2017-3-10 19:15
呵呵,都完全看明白了吗?

第一个滤波代码特性:

滤波结果四舍五入是怎样实现的呢?

出0入0汤圆

发表于 2017-3-10 22:54:35 | 显示全部楼层
这样的帖子很精彩,不错,极力支持!

出0入4汤圆

发表于 2017-3-11 08:56:17 来自手机 | 显示全部楼层
支持一下,马上用到

出0入0汤圆

发表于 2017-3-20 14:05:35 | 显示全部楼层
mark 软件滤波算法

出5入8汤圆

发表于 2017-3-22 13:27:04 | 显示全部楼层
foxpro2005 发表于 2017-3-8 19:44
上面4楼与14楼的会比较费内存了, 不就为了实现滑动平均吗, 我来开源分享两个更省更精简的:

滤波器数据 ...

大神,您这段代码的四舍五入是怎么实现的呢?

出0入76汤圆

发表于 2017-3-22 20:03:36 | 显示全部楼层
diyeyuye 发表于 2017-3-22 13:27
大神,您这段代码的四舍五入是怎么实现的呢?


兄弟, 去看看这个帖子, 你会有很多收获...

http://www.amobbs.com/forum.php? ... id=5649434#lastpost

出5入8汤圆

发表于 2017-3-22 20:06:08 | 显示全部楼层
foxpro2005 发表于 2017-3-22 20:03
兄弟, 去看看这个帖子, 你会有很多收获...

http://www.amobbs.com/forum.php?mod=viewthread&tid=5649 ...

感谢大神,马上拜读

出0入0汤圆

发表于 2017-3-22 22:44:41 来自手机 | 显示全部楼层
foxpro2005 发表于 2017-3-8 19:44
上面4楼与14楼的会比较费内存了, 不就为了实现滑动平均吗, 我来开源分享两个更省更精简的:

滤波器数据 ...

mark,字数补丁

出0入0汤圆

发表于 2017-3-22 23:03:03 来自手机 | 显示全部楼层
标记!!

出0入0汤圆

发表于 2017-3-22 23:33:31 | 显示全部楼层
面向对象+面向数据编程,骚年百度一下

出0入0汤圆

发表于 2017-3-23 10:31:26 来自手机 | 显示全部楼层
标记标记

出0入0汤圆

发表于 2017-3-23 15:28:05 | 显示全部楼层
4楼和37楼的程序都好,谢谢分享。努力学习

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-5-6 00:16

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

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