Problem with averaging function

I have quite a few variables in my code that i need to average the value of(around 12), from various sensors.
So i made this simple moving average function which works, the problems began when i added new things to the code which required calling this function more times and now it has gotten quite slow, results need around 5 seconds to stabilize which is too much.

In it's arguments i add the variable to be averaged, the array to use and number of samples.

uint16_t calculateAverage(uint16_t variable,
                          uint16_t *valueArray,
                          uint16_t num_samples)
{
    valueArray[index1] = variable; // Store the current sensor value in the array
    index1++;                      // Increment the index
    if (index1 >= num_samples)
    {
        index1 = 0; // Reset the index when it reaches the window size
    }

    uint64_t sum = 0; // Calculate the moving average for the current window
    for (int i = 0; i < num_samples; i++)
    {
        sum += valueArray[i];
    }
    double movingAverage = static_cast<double>(sum) / num_samples; 
                                                                  
    return static_cast<uint16_t>(movingAverage);
}

So i want to improve this function and i discovered that i could speed up the code by using a circular buffer.
I made this function which works, but now i need to modify it so i can call it for averaging multiple different variables
without the results getting mixed up.

uint16_t circularAvg(uint16_t variable,
                     uint16_t *valueArray,
                     uint16_t num_samples)
{
    static CircularBuffer<uint16_t, 128> buffer; // Create a circular buffer of size 'num_samples' to store the sensor values. The type int can be changed according to your requirement (uint16_t or any other).

    if (buffer.isFull())
    {
        buffer.shift(); // If the array is full, remove the oldest element before adding a new one. This ensures that we are always considering 'num_samples' elements for calculating average.
    }
    else
    {
        ++index1; // Increment index if buffer isn’t yet at its maximum size – so it wraps around to zero when full, just like your original code
    }

    buffer.push(variable); // Add the current sensor value into circular buffer using push() function instead of add(). This will automatically shift elements if necessary.

    uint64_t sum = 0; // Calculate moving average for the current window by adding all elements in array and dividing it with number of samples – just like your original code but using CircularBuffer methods instead.

    for (int i = 0; i < num_samples && num_samples != 0; ++i)
    {                     // Check if the window size is not zero to avoid division by zero error
        sum += buffer[i]; // Add the 'ith' element in circular buffer to sum variable. This will be equal to current moving average if num_samples is 1, or a weighted average otherwise.
    }

    uint16_t movingAverage = (num_samples != 0) ? round(sum / num_samples) : 0; // Calculate the rounded-off value of moving average from sum and divide it by number of samples, but only if num_samples is not zero to avoid division by zero error

    return movingAverage;
}

Probably if i would create separate instances of the CircularBuffer for each variable and then i would pass them as function arguments it could work?
Would really appreciate some help on how i could achieve this.

Using STM32 bluepill.

some weeks ago I have written a circular buffer with a moving average.

May be it is of help:

2 Likes

If you want to reduce noise from analog sensors, such as a battery voltage or a LDR or NTC, then you don't need a buffer. The average of 10 (5 to 100) samples will get rid of most of the noise.

const int nSamples = 10;
unsigned long total = 0;
for(int i=0; i<nSample; i++)
{
  total += analogRead(A0);
}

float average = float(total + nSamples/2) / float(nSamples);

The "half-a-bit" correction is explained here: https://gammon.com.au/adc

A low-pass filter in software does also not need a buffer.

// global variable
float filtered;

// in the loop()
float newValue = take_a_sample();
filtered = 0.01 * newValue + 0.99 * filtered;

The low-pass filter adds a certain percentage to the filtered value. In the above example it is 1%.

Other things can be even more important: grounding, ratiometric sensors, a power supply that is not extremely noisy, and so on.

have you considered leaky integration

avg += (samp - avg) * k; // k < 1

where k = 1 / N, the avg approached samp in 3 * N iterations.

avg can be intialized to samp

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.