Code for a rolling average

Below is some code I wrote to find the rolling average of a temp sensor hooked up to an arduino and display it to an lcd display. My issue is that for the first minute it is running, the average is not correct. It starts at 0 and then progresses up to where it should be (~85F) in increments of approximately 3. After the first minute it is fine and works as it should.

What I want the code to do is to simply take the average of the sum of the values that have been recorded in the first minute.

My knowledge of C is fairly limited(I only had a semester long class on it) so that is why it is simple. I am a mechanical engineering student and this code is for project at my internship.

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

double array;
double rollingaverage=0;
int i=0, j=0;
double sum=0;
void setup() {

Serial.begin(9600);
lcd.begin(20,4);
mlx.begin();
Wire.begin();

}

void loop() {
if(j=0){
sum += array[i];
if(i>0){
rollingaverage = sum/(i+1);
}
j=1;
}
else{
if(i < 29){ //wraps i back around to 0 if it is at 29 to subtract the oldest value
sum = sum + array[i] - array[i+1];
rollingaverage = sum/30;
}
else{
sum = sum + array[i] - array;
rollingaverage = sum/30;
i=0; //reset index to 0
}
}
i++;

lcd.clear();
lcd.print("Belt: "); lcd.print(rollingaverage);
lcd.setCursor(1,2);

delay(2000);
}

Its not a huge deal because it works fine after the first minute, it is just annoying that I can't figure out why it doesn't work as intended in the first minute. Thanks

• Example, if you have 25 valid values and 5 zero, you need a counter for "valid values" and change rollingaverage = sum/30; and replace 30 with the counter value. Oct 1 '18 at 16:47
• if(j==0) instead of (j=0) would be nicer perhaps. Jan 19 '19 at 18:38

Your problem stems from you starting out with 30 values of 0 which you include in your average. You have to take that into account and ignore those values, dividing your total by only the number of actual values you have.

I have a library that does it all for you that would make things easier for you:

One way is to initialize the whole buffer to the first value. Your average won't be "true" in some sense, until the whole buffer has been replaced with read data, but as long as that first value isn't too different from the true temperature, your average during that period will be more sensible. I.e., it won't have to ramp up from zero.

Another way is to use an Exponential Average instead of the Rolling- or Boxcar-Average. The Exponential Average doesn't depend on storing old samples and can adjust well - i.e. within a few samples, depending on 'X' (see below) - to a sudden change (which temperatures don't normally do, anyway).

It calculates a new average by summing X percent of the current average + (100-X) percent of the next sample - you choose the value of 'X' that works best in your own application. I have good results averaging temperatures, even from analog sensors, using X=75. For fixed-point numbers such as the 4 binary places returned by the Max DS18b20 sensors, would be

// (3 * avg + 1 * sample) / 4
avg = (((avg<<2) - avg + sample) + 2) >> 2;

For floating point numbers, it would just be:

avg = (3. * avg + sample)/4.;

Update:

Initialization is as much an issue with an exponential average as it is with a boxcar average.

Yes, it is. I initialize my exponential averagers with the first sample and usually get along pretty well. It might not work so well if the averager was too heavily damped.

... a boxcar of width 30 ...

An exponential average, by its nature, can give recent samples more weight than older ones so giving so little weight as you suggest to the next sample will make it quite heavy-handed! X=75% has worked quite well for me to reduce noise without heavy-handed damping. What works well for one system may not work for another, but by running a saved sample-set through a few trials on your desktop will quickly get you to a reasonable X.

We don't know how that width (30) was arrived at; perhaps it is necessary? But averaging, by its nature, being both low-pass (usually desired) and delaying (generally not desired), spreads sudden features like spikes while it limits their amplitude. I've sometimes use a spike suppressor ahead of a lighter averaging filter, where the alternative average-only would have required enough averaging to damp significant signal features too heavily.

• Initialization is as much an issue with an exponential average as it is with a boxcar average. Specifically, a boxcar of width 30 has roughly the same “averaging strength” as an exponential average with X = 100×(1−1/30). And the latter will need more than 30 samples to completely settle. Oct 1 '18 at 18:42

The other answers address the problem with your code, however a better solution is to use a different algorithm.

I would use exponential smoothing (which is simpler to code, requires less data and produces a similar result). This is the technique I used to use when responsible for producing statistical performance reports. If readings are plotted this results in a smoother graph, which shows trends and discounts noise.

Briefly take the current sample, multiply by a discounting factor and add the a proportion of the current value.

s = x * α + (1-α) * s

To produce results from the start initialise the sample to the first reading then calculate.

I would probably start with a smoothing factor of 0.3 - choose a smaller value to give more weight to past values.

• You are essentially repeating JRobert's answer. Oct 2 '18 at 7:33