C语言 使用循环缓冲数组实现运行平均值



我正在尝试实现一个循环缓冲区,以便平均由嵌入式控制器上运行的C语言压力传感器生成的数据流。 这个想法是将最后N个压力读数存储在缓冲液中,同时保持缓冲液的运行总和。 平均值 = 总和/N。 应该是微不足道的。

但是,我看到的平均值是从压力读数附近开始的值(我用典型值预加载缓冲寄存器),但随后趋于零。 如果我也显示总和,它也逐渐下降到零。 如果压力发生变化,则平均值在压力变化的方向上远离零,但一旦压力稳定下来,就会恢复到零趋势。

如果有人能发现我所犯的错误,那将非常有帮助。

    #define ARRAYSIZE 100
    double Sum;                       // variable for running sum
    double Average;                   // variable for average
    double PressureValue[ARRAYSIZE];  // declare value array
    int i;                            // data array index
    int main(void) {
      while (1) 
      {
        if (i == ARRAYSIZE) i = 0;       // test index, reset if it reaches the upper boundary
        Sum = Sum - PressureValue[i];    // subtract the old datapoint from running sum
        PressureValue[i] = PRESSURE;     // replace previous loop datapoint with new data
        Sum = Sum + PressureValue[i];    // add back the new current value to the running sum
        Average = Sum / ARRAYSIZE;       // calculate average value = SUM / ARRAYSIZE
        ++i;                             // increment index
      }                                  // end while loop
    }                                    // end main

平均代码发生在中断处理程序中;我通过 I2C 从压力传感器读取数据,在每个 I2C 通信阶段结束时触发中断。 在最后一个阶段,在检索到构成压力数据的四个字节后,将它们组合成一个完整的读数,然后转换为包含在PRESSURE变量中的PSI中的十进制读数。

显然,这不是从我的代码中直接剪切和粘贴,但我不希望任何人必须涉足整个事情,所以我将其限制为与计算平均值相关的东西,并更改变量名称以使其更具可读性。 尽管如此,我还是看不出我做错了什么。

感谢您的关注!

道格·

我认为您的代码没有任何明显的问题,但正如您所说,您没有提供所有代码,所以谁知道其余部分发生了什么(特别是,如何/是否正在初始化iSum),但以下内容对我来说很好用,这基本上与您拥有的算法相同:

#include <stdio.h>
#include <stddef.h>
double PressureValue[8];
double Pressures[800];
int main(void) {
    const size_t array_size = sizeof(PressureValue) / sizeof(PressureValue[0]);
    const size_t num_pressures = sizeof(Pressures) / sizeof(Pressures[0]);
    size_t count = 0, i = 0;
    double average = 0;

    /*  Initialize PressureValue to {0, 1, 2, 3, ...}  */
    for ( size_t n = 0; n < array_size; ++n ) {
        PressureValue[n] = n;
    }
    double sum = ((array_size - 1) / (double) 2) * array_size;
    /*  Initialize pressures to repeats of PressureValue  */
    for ( size_t n = 0; n < num_pressures; ++n ) {
        Pressures[n] = n % array_size;
    }
    while ( count < num_pressures ) {
        if ( i == array_size )
            i = 0;
        sum -= PressureValue[i];
        PressureValue[i] = Pressures[count++];
        sum += PressureValue[i++];
    }
    average = sum / array_size;
    printf("Sum is %fn", sum);
    printf("Counted %zu pressuresn", count);
    printf("Average is %fn", average);
    return 0;
}

输出:

paul@local:~/src/c/scratch$ ./pressure
Sum is 28.000000
Counted 800 pressures
Average is 3.500000
paul@local:~/src/c/scratch$

还有一种可能性,当您说它们"转换为PRESSURE变量中包含的 PSI 中的十进制读数"时,以及在其他地方,就此而言,请确保您不会因为整数除法而将事情截断为零。如果你在添加更多内容时有"趋向于零"的东西,我会立即怀疑这一点。例如,将华氏度转换为摄氏度的一个经典错误是写c = (f - 32) * (5 / 9),其中(5 / 9)每次都截断为零,并且总是给您留下c == 0

另外,作为一般规则,我知道你"不希望任何人不得不涉足整个事情",但你会惊讶于真正的问题有多少次不在你认为的代码部分。这就是为什么提供 SSCCE 以确保您可以缩小代码范围并实际隔离和重现问题很重要的原因。如果您尝试缩小代码范围并发现无法隔离和重现问题,那么几乎可以肯定您的问题不是由您认为导致它的原因引起的。

您的代码也可能完全按预期工作。如果在此循环之外使用典型值预加载数组,然后运行此代码,则会得到所描述的行为。如果要预加载阵列,请确保预加载总和和平均值,否则实质上是在测量表压,并将预加载值作为大气压。

最新更新