Smoothing to a single average

Hello,

I am trying to edit the example Arduino code "smoothing" to just take 1 average for the first 10 values and no other averages. Then I want subtract the average from each integer of data from then on and multiple that by -1.

This is the original Arduino Smoothing code:

/*

Smoothing

Reads repeatedly from an analog input, calculating a running average
and printing it to the computer. Keeps ten readings in an array and
continually averages them.

The circuit:

  • Analog sensor (potentiometer will do) attached to analog input 0

Created 22 April 2007
By David A. Mellis dam@mellis.org
modified 9 Apr 2012
by Tom Igoe

This example code is in the public domain.

*/

// Define the number of samples to keep track of. The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input. Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
const int numReadings = 10;

int readings[numReadings]; // the readings from the analog input
int readIndex = 0; // the index of the current reading
int total = 0; // the running total
int average = 0; // the average

int inputPin = A0;

void setup() {
// initialize serial communication with computer:
Serial.begin(9600);
// initialize all the readings to 0:
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0;
}
}

void loop() {
// subtract the last reading:
total = total - readings[readIndex];
// read from the sensor:
readings[readIndex] = analogRead(inputPin);
// add the reading to the total:
total = total + readings[readIndex];
// advance to the next position in the array:
readIndex = readIndex + 1;

// if we're at the end of the array...
if (readIndex >= numReadings) {
// ...wrap around to the beginning:
readIndex = 0;
}

// calculate the average:
average = total / numReadings;
// send it to the computer as ASCII digits
Serial.println(average);
delay(1); // delay in between reads for stability
}

Here is what I have so far and it isn't terribly much:

/*

Smoothing

Reads repeatedly from an analog input, calculating a running average
and printing it to the computer. Keeps ten readings in an array and
continually averages them.

The circuit:

  • Analog sensor (potentiometer will do) attached to analog input 0

Created 22 April 2007
By David A. Mellis dam@mellis.org
modified 9 Apr 2012
by Tom Igoe

This example code is in the public domain.

*/

// Define the number of samples to keep track of. The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input. Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.

const int numReadings = 10;
int readings[numReadings]; // the readings from the analog input
int index = 0; // the index of the current reading
int total = 0; // the running total
int average = 0; // the average

int inputPin = A0;

void setup() {
// initialize serial communication with computer:
Serial.begin(9600);
pinMode(A0, INPUT_PULLUP);
// initialize all the readings to 0:
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0;
}
}

void loop() {
// subtract the last reading:
total = total - readings[index];
// read from the sensor:
readings[index] = analogRead(inputPin);
}

// calculate the average:
average = total / numReadings;
for (inputPin - average; (not sure what goes here; and here) )
{
statement(s);
}
}

I'd really appreciate any help or feedback! Thank you!

You really should have read How to use this forum before posting. (Especially item #7.)

ie Your code and any error messages should always be placed between [code]code tags[/code]. Posting it inline as you have done makes it much harder to read or copy and paste for diagnosis, and often also corrupts the code with italics or smilies.

It's still not too late to edit your post and do this. You'll make potential helpers much happier. :slight_smile:

Are you sure you want to do that? I suspect that what you really want to do is to gradually Change from a given Setting to a new Setting. For example if the old average was 128 and your new reading is 135 you want to gradually increase from 128 to 135.

However, if you subtract the old from the new and then multiply by -1 strange things could happen as a result.

Assume the old average was 128 and the new reading is 0. 0 - 128 = -128. -128 * -1 = 128. Process a new reading of 0 one thousand times and you still have 128. Is that what you wanted?

Assume the old average was 256 and the new reading is 128. 128 - 256 = -128. -128 * -1 = 128. The result of the calculation is not different from the new reading.

Are you sure you don't want to average the first ten and then average the average with each successive reading?

Average = 128. New = 0. Average of 128 and 0 = 64. On the next read of 0, the result is 32. Then 16, then 8, and slowly goes down to 0.

Hi,

Am i reading that original code right? If so, what is the purpose of the array?
If we store numbers that we never use, we dont really have to store them.

To average you dont need an array anyway. Just subtract the old average before adding the new value once you get the number of readings required for the averaging.
This boils down to avg=((N-1)avg+new)/N for an average over N samples.
So for 10 samples, it would be avg=(9
avg+new)/10.
Of course the data type has to be able to hold the max value.
So there the old avg is weighed by a factor of 9 while the new reading is weighted by only a factor of 1. That is similar to a low pass filter, which smooths the input.
Im not entirely sure that's what you want to do, but it looks like you do want to do that at some point anyway. You decide :slight_smile:

Mr Al, great algorithm, but it is a good idea to state that this method needs special code to "kick start" it. If avg starts at 0 it can take a while to get reasonable readings.

You generally have two options,
Simply use the first new as is, and make it avg the first time through. Or - and this is what I usually do - for the first N samples return the actual average of the data, so the first run is new0, the second (new0+new1)/2 and so on.

If you just want to average, it is OK to put values into the
array sequentially and always start at the beginning of the
array to do the calculation.
A simple way to do a running average is to keep track
of the values in an array. Then you pick up the old value
from the array and subtract it from the last total. Add the
new value to the total and save the new value to be
subtracted later.
If you want to weight the values such that the latest values
entered have more effect than the older values, you have
to do one of two methods.
If you have a lot of time between measurements, you can
shift the values such that the next value is always place
at the beginning.
Another but slightly more complicated way is to keep
a current pointer and start from that one for enter and
extract.
In this case the pointer rotates.
If doing weighted, one needs to keep track of LIFO
( last in first out ) or FIFO ( first in first out ). Either
way works but the coefficients need to be ordered to
match.
Dwight

Hi,

For this app i thought that only 10 samples would come in fast, and i also mentioned that "once you have the number required for averaging", but no big deal really. It is good to know that the averaging could use a kick start.

Also from filter theory, we have:
y[n]=(x[n]+19*y[n-1]+x[n-1])/21

That comes from a frequency domain filter, using a bilinear transform to transform into a Z transform, then converting that to a difference equation. So we go from the frequency domain to the discrete domain which we use in the code.

The above would translate to:
avg=(current_val+19*previous_avg+previous_val)/21

That also would do well with a starting method as Keith mentioned (thanks).

The above and the previous method are both first order, but this next one is second order using the same procedure:
y[n]=(x[n]+198y[n-1]+2x[n-1]-81*y[n-2]+x[n-2])/121

where the values are:
y[n]=new average
x[n]=current value
x[n-1]=previous value
x[n-2]=previous previous value
y[n-1]=previous average
y[n-2]=previous previous average

This one however is as of yet untested, and of course requires storage of the last two values read by the ADC and the last two averages that where previously calculated. I might try this last one out a little later today or tomorrow morning. The constants do look good though.
Again, perhaps a starting method would help it get going if needed.

The catch to any averaging method is that it will take time to start because of the (transformed) time domain view exponential part, and since that only goes away during steady state, it will still take time to react to a new sample even after many samples have been processed. We hope that the values do not change much though, but if there is a large change (such as 100 to 0) it will take time for that to average to the right value anyway. So we're kind of stuck with choosing the method time constant (which translates to number of samples in the average), or else we have to go to higher order or add some non linear terms.

But as to the array, we really dont need one, but if you choose to use one then it should be set up as a first in last out FILO register, which i think the original code did not adhere to that either :slight_smile:

Rather than averaging, you might be better off using digital low-pass filtering. This will ensure a smooth transition from one stable state to the next, with no spikes or dips, and the response time is easily change by simply modifying the coefficient:

const float filterCoeff = 0.1;
float filteredValue = 0;

filteredValue = filteredVaue + ((newValue - filteredValue) * filterCoeff);

Regards,
Ray L.

adbc22:
Hello,

I am trying to edit the example Arduino code "smoothing" to just take 1 average for the first 10 values and no other averages. Then I want subtract the average from each integer of data from then on and multiple that by -1.

I'd really appreciate any help or feedback! Thank you!

Something like this you mean:

float average ;

void setup ()
{
  long total = 0L ;
  for (byte i = 0 ; i < 10 ; i++)
    total += analogRead (pin) ;
  average = total * 1.0 / 10 ;
}

float read_pin ()
{
  return -1 * (analogRead (pin) - average) ;
}

Using float to gain more accuracy / less bias

Many of the replies above aren't reading your clearly set out requirements alas...

You'll have to admit, his statement isn't all that clear.
I suspect he wants to keep a running average by just
keeping the total and subtracting the oldest value while
adding the newest value. This requires saving the values
in an array.
At least I think that is what he is trying to do??
Dwight

The bug in the OP's posted code is that he is not advancing index.

Hi again,

Yes there are mistakes in the original code, but the reason i started posting the filtering methods was because they match the array method pretty well. There's almost no difference, so it doesnt make sense to have to store 10 values when we dont have to.
I've done averaging over many more values, as many as 64k, and it would be nuts to have to store that many values :slight_smile:
Of course you can use an array, but realize that it's pretty much a waste of storage space and code space as well.
Most notable is that one value out of 10 does not make a huge difference for just one sample, no matter how we do it. And that is true for the entire run time of the program because of the nature of 'filtering' of which averaging is just one type of filtering.

This turned out to be an interesting thread though in any case.

RayLivingston:
Rather than averaging, you might be better off using digital low-pass filtering. This will ensure a smooth transition from one stable state to the next, with no spikes or dips, and the response time is easily change by simply modifying the coefficient:

const float filterCoeff = 0.1;

float filteredValue = 0;

filteredValue = filteredVaue + ((newValue - filteredValue) * filterCoeff);




Regards,
Ray L.

Hi there,

There is no difference between averaging and that "low pass filter".

If you look back, you'll find the averaging method:
avg=((N-1)*avg+new)/N

and with your digital filter:
filteredValue = filteredVaue + ((newValue - filteredValue) * filterCoeff);

that is exactly the same formula just written out differently.
In fact, filterCoeff of the second formula is equal to 1/N or -1/N in the first formula. N is the number of samples to average over in both cases.