Smoothing analog input values......

Hi,

I'm trying to average/smooth values from an analogue input pin. Heres the code...

  // Read in a value from an analogue input pin.
  int smoothEle = readElevatorPot();

  // Create a 'rolling average' of the last 8 values....
  _eleAvg = ((_eleAvg * 7) / 8) + (smoothEle / 8);

  Serial.print("smoothEle=<");Serial.print(smoothEle);Serial.print(">, _eleAvg=<");Serial.print(_eleAvg);Serial.println(">");

the rolling average snippet comes from.....
http://forum.arduino.cc/index.php?topic=198618.msg1466451#msg1466451

Here's the output (when stable)

smoothEle=<922>, _eleAvg=<913>
smoothEle=<922>, _eleAvg=<913>
smoothEle=<922>, _eleAvg=<913>
smoothEle=<922>, _eleAvg=<913>
smoothEle=<922>, _eleAvg=<913>
smoothEle=<922>, _eleAvg=<913>

The function readElevatorPot() just returns a number between 0 and 1023 from an analogue input pin. (this bit works ok)

But I was expecting the line _eleAvg = ((_eleAvg * 7) / 8) + (smoothEle / 8); to create a sort of rolling average of the last 8 values from the analogue input pin. As smoothEle varies, the value of _eleAvg also varies, but why is it consistently between 5 and 10 lower than smoothEle? Even after it's been given time to stabilize?

We need all the code.
What size is _eleAvg?

What does it start with? 0? if so, you need to have code to calculate a regular average for the first 7-8 values.

I am guessing it does not converge because of the truncation.

I did the math throwing away any fractional values after the division and got 919.

It is not an average of the last 8 inputs. It is an average of every input since the program started.
As KeithRB has said, there's a problem with integer truncation too. It works a bit better if you use this:

  _eleAvg = (_eleAvg * 7 + smoothEle)/8;

Pete

I did an excel spreadsheet using int() to simulate integer division.
If I start _eleAvg at 0 it takes 45 steps to converge to 913.

If I start it at 922, it takes 3 steps to converge to 920, which is pretty good, within 0.2%.

Not exactly el_supremo. It is pretty heavily weighted for the last 8 values. After all, the effect of the initial 0 is gone after 45 steps. A full average of all the steps is still 900.

After all, the effect of the initial 0 is gone after 45 steps.

That is only because of the finite precision of a 16-bit word and the integer division. If this were done in 64-bit floating point, the initial values would still be having an impact long after the code started.
Depending upon the application and the sampling rate, 45 steps might be unacceptable.

A full average of all the steps is still 900.

All which steps?

Pete

el_supremo:
It is not an average of the last 8 inputs. It is an average of every input since the program started.
As KeithRB has said, there's a problem with integer truncation too. It works a bit better if you use this:

  _eleAvg = (_eleAvg * 7 + smoothEle)/8;

Pete

There's a more robust way to implement a digital filter, you need to be more careful about
where the errors can accumulate. Its not an average its a IIR digital filter and you'd
normally do it thus:

float filtered ;

...
  float input = analogRead (pin) ;
  filtered += (input - filtered) * 0.125 ;

The reason to do it this way is so that the DC gain is exactly 1.0 whether or not
the coefficients are exactly representable (you might be using fixed point rather
than floating point in digital signal processing for greater speed). You also
only need one coefficient, rather than a pair of them that might get out of step.

In fixed point you might do something like

unsigned int filtered ;

...
  unsigned int input = analogRead (pin) << 6 ; // scale to 16 bits, 6 guard bits
  filtered += (input - filtered) >> 3 ;

But note that when the difference of (input - filtered) is in the range 1..7 the shifted
version won't work any more in steady state conditions - it can be improved by
doing more sensible rounding:

  filtered += (input - filtered + 4) >> 3 ;  // add 0.5 x 8

Now the difference can be in the range -4..3 before the value jams up.

The solution to this problem is to inject a noise value into the system, with
just enough amplitude to allow the value to converge. In all sampled systems
injecting the right amount of noise increases accuracy, odd though it sounds!
But here the 6 guard bits are probably enough to give good enough results.

Thanks for all your help guys. For my purposes, making sure the initial value of _eleAvg is correctly set at the beginning seems to give the desired results.

Thank you!

There's a more robust way to implement a digital filter, you need to be more careful about
where the errors can accumulate. Its not an average its a IIR digital filter and you'd
normally do it thus:

float filtered ;

...
  float input = analogRead (pin) ;
  filtered += (input - filtered) * 0.125 ;

[/quote]

Sorry to Jump in here, but MarkT, can you explain what hapens to 'Filtered' when you add the + in front of it, and WHY " *0.125"?
I used " filtered += (input - filtered) * 0.125 ;" to smooth out some analog inputs in a circuit thats in my car, and WOW does it work well!
I just need to understand the formula!!! :slight_smile:

Thank you!

  float input = analogRead (pin) ;
  filtered += (input - filtered) * 0.125 ;

Can be expanded to:

  float error_term = input - filtered ;
  filtered = filtered + error_term * 0.125 ;

Think of it like this - the value of filtered is sluggish to respond
to changes since only a fraction of the error term is allowed to
correct it on each cycle - it takes a dozen or more cycles to shift
significantly close to the input value when there's a step change.

As a consequence noise is mainly cancelled out when its rapidly
fluctuating. Long term changes in the input do get through.

More technically is a 1-pole digital IIR filter

OK! got you now.
Makes sense.
:slight_smile:

IS there a way to speed it up slightly?
And where does the 0.125 factor come from? Obviously this has been derived... :wink:
Can I sacrifice some accuracy but get a quicker responding output?

Can I sacrifice some accuracy but get a quicker responding output?

Sure, but that conflicts with the post title. You need to back up and explain what you are trying to accomplish.

PaulS:

Can I sacrifice some accuracy but get a quicker responding output?

Sure, but that conflicts with the post title. You need to back up and explain what you are trying to accomplish.

Hi Paul.

Essentially, this is a pretty basic Multi Sensor system. 4 Analog Inputs being displayed on a LCD inside a car.
I was battling with noise / just unstable measurements because of the harsh environment. However, this 1-pole digital IIR filter works like a charm!!!! :slight_smile:
But, i find that the readings displayed on the LCD change a little bit to slowly - not instant enough.
On startup, when certain reading are zero, within one second these readings should jump to around a value of 7. This takes sometime ti get to because of the system obviously trying to correct the error.

IF, i could manipulate this filter and make it half for twice the speed, that would probably suffice. :slight_smile:

Thanks for your time!
Jared

MarkT explained what all the terms mean. The 0.125 is the fraction of the new reading to add to the existing average. Increase that fraction to get the average to change faster. Decrease it to get the average to change slower.