小言_互联网的博客

单片机如何高效使用平均值采样法提高数据准确性?

488人阅读  评论(0)

目录

1、传统方法:直接法

2、高效方法:递推法

3、实践


开发嵌入式产品时,通常会用到平均值采样法,降低采集数据的误差,提高准确性。

平均值采样法指在一定时间内对采样的数据进行累加,用累加和除以采样的次数,得到平均数,将计算出来的平均值作为有效数据使用。

简要介绍直接计算法和递推法,了解两者的差别。

1、传统方法:直接法

直接法,直接进行加和求平均,方法简单直接,数学公式如下所示:

该方法缺点是在内存比较小的单片机系统中,若需要计算大样本集的场合,就捉襟见肘。例如几万样本时,内存可能就不够了。实现代码如下所示:


  
  1. float mean(float *pSample,int size)
  2. {
  3. if(pSample== NULL || size<= 0)
  4. return NAN;
  5. float sum = 0;
  6. for( int i= 0;i<size;i++)
  7. {
  8. sum += *pSample;
  9. pSample++;
  10. }
  11. return (sum/size);
  12. }

2、高效方法:递推法

递推法是对直接法的简化。

因为n个样本均值计算公式如下所示:

则前n-1个样本的均值计算公式如下所示: 

简化得出:

进而简化: 

所以递推法实现代码如下所示:


  
  1. float recursive_mean(float xn,int size)
  2. {
  3. static int index = 0;
  4. static float last_mean = 0.0f;
  5. float mean = 0.0f;
  6. if(index<size -1)
  7. {
  8. index++;
  9. mean = last_mean+(xn-last_mean)/index;
  10. }
  11. else
  12. {
  13. mean = last_mean+(xn-last_mean)/size;
  14. index = 0;
  15. last_mean = 0.0f;
  16. }
  17. last_mean = mean;
  18. return mean;
  19. }

当在样本窗未满时,按实际传入样本大小递推,在样本窗满后,按实际传入样本大小递推,并复位索引。

在嵌入式应用中,如果所需要统计的样本非常大时,这种算法将非常有实用价值,只需要极小的内存开销。尤其在一些传感器测量应用中,该方法非常有价值。

3、实践

随机生成一组数据,使用这两种方法,实现平均值采样,代码如下所示:


  
  1. #include <stdio.h>
  2. #include <stdbool.h>
  3. #include <math.h>
  4. float mean(float *pSample,int size)
  5. {
  6. if(pSample== NULL || size<= 0)
  7. return NAN;
  8. float sum = 0;
  9. for( int i= 0;i<size;i++)
  10. {
  11. sum += *pSample;
  12. pSample++;
  13. }
  14. return (sum/size);
  15. }
  16. float recursive_mean(float xn,int size)
  17. {
  18. static int index = 0;
  19. static float last_mean = 0.0f;
  20. float mean = 0.0f;
  21. if(index<size -1)
  22. {
  23. index++;
  24. mean = last_mean+(xn-last_mean)/index;
  25. }
  26. else
  27. {
  28. mean = last_mean+(xn-last_mean)/size;
  29. index = 0;
  30. last_mean = 0.0f;
  31. }
  32. last_mean = mean;
  33. return mean;
  34. }
  35. #define N (1000)
  36. #define SAMPLE_SIZE (100)
  37. int main(int argc, char *argv[])
  38. {
  39. float sim[N];
  40. float out[N/SAMPLE_SIZE];
  41. for( int i= 0;i<N;i++)
  42. {
  43. sim[i]=i* 5+rand()% 10;
  44. }
  45. printf( "\n\n");
  46. int j= 0;
  47. for( int i= 0;i<N;i=i+SAMPLE_SIZE)
  48. {
  49. out[j] = mean(&sim[i],SAMPLE_SIZE);
  50. j++;
  51. }
  52. for(j= 0;j<N/SAMPLE_SIZE;j++)
  53. {
  54. printf( "%.2f,",out[j]);
  55. }
  56. printf( "\n");
  57. j = 0;
  58. for( int i= 0;i<N;i++)
  59. {
  60. out[j]=recursive_mean(sim[i],SAMPLE_SIZE);
  61. if((i+ 1)%SAMPLE_SIZE== 0)
  62. j++;
  63. }
  64. for(j= 0;j<N/SAMPLE_SIZE;j++)
  65. {
  66. printf( "%.2f,",out[j]);
  67. }
  68. printf( "\n");
  69. return 0;
  70. }

 结果如下所示:


  
  1. 252. 14, 752. 38, 1251. 85, 1751. 82, 2252. 57, 2752. 25, 3251. 78, 3751. 58, 4252. 06, 4752. 02,
  2. 252. 14, 752. 38, 1251. 85, 1751. 82, 2252. 57, 2752. 25, 3251. 78, 3751. 58, 4252. 06, 4752. 02,

两种计算方法效果一样,但是第二种方法消耗极小的内存。当然这增加了函数调用次数,在大样本计算时非常有利。

拓展:单片机如何实现内置ADC高分辨率采样?


整理来源于:数学之美:均值计算的两种算法(C实现)


转载:https://blog.csdn.net/m0_38106923/article/details/108962236
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场