Question about basic smoothing example from arduino

My interpretation of the code of the smoothing example, is that each iteration of loop the latest sensor reading (t) is added to a running total, and the t - 1 reading is subtracted from the total. For a running average, shouldn't the oldest sensor reading be subtracted instead? I'm probably missing something really basic..

I was looking through these examples to try to find an efficient way to implement a weighted moving average for sensor data (both positive and negative from an IMU) in float.

I have this:

  float x_avg = 0;
  float y_avg = 0;
  x_avg *= 0.75;
  x_avg += 0.25 * x_reading;
  y_avg *= 0.75;
  y_avg += 0.25 * y_reading;

But apparently this is bad practice / really slow?

p.s. I found this class, but the author cautions it doesn't work for negative numbers (I think). You

Your interpretation is incorrect.
The oldest reading is subtracted, not the previous reading - it's a circular buffer

I'd maybe add 25% of the new reading to 75% of the running average, and not the other way round

Thanks for responding. Don't want to take too much of your time, but just in case you look at this. I still don't understand why. For example when the readindex is at 4, we add the latest sensor reading to the total (and save it at index 4) and subtract the reading which was saved at readindex 3, which is from the previous sensor read?

Thanks for the note on the 2nd example. Is there a way to get around multiplication/division to speed up the code. I'm still quite new at this, but I read that maths in floats is very slow.

You're not thinking in a circular fashion.
Start at index zero, and see what happens.

When the index is at four, you subtract the reading at four from the total, write the new reading at four, add the new reading to the total, and then increment the index for the next time around the loop.

(Also, the for loop in the setup function is superfluous)

1 Like

What kind of data do you want to smooth ?

As TheMemberFormerlyKnownAsAWOL already wrote, the smooth example is correct. It is a mathematical trick and it removes the oldest sample.
This is the actual tutorial: https://www.arduino.cc/en/Tutorial/BuiltInExamples/Smoothing

For high frequency electronic noise, it is enough to take a number of samples without the need of an array. See my example: averageRead.ino
For example when reading a voltage from a battery, then that is enough.

For a sensor with a acceleration, gyro, magnetometer, there are a number of mathematical filters, such as AHRS. It works very well because of Sensor Fusion (the disadvantages of each sensor is compensated by the other sensors).

A simple low-pass filter with a float variable is as you showed. Often 1% or less of the new data is used for the average.

// global variable for average
float average;

// in the loop()
  int rawADC = analogRead();
  float newValue = (float) rawADC;
  average = (0.99 * average) + (0.01 * newValue);

That works really well, and no array is needed. It can be used in combination with a millis-timer, to get a new sample every 10ms for example.

Reducing the mains 50Hz or 60Hz is not so easy. Trying to reduce that noise with shielding or a hardware filter might be needed.

What I wrote here is just a very small part of the smoothing possibilities. It depends on your project what the best smoothing is.

1 Like

Yes, and that form of low pass filter can easily be made using only integer arithmetic, or even bit shifting in some cases - making it tens or hundreds of times faster.

1 Like

I see now, couldn't get the logic on my own. Thank you!

Thank you, @Koepel for taking the time to reply.
Lots of useful info. It's basically angular velocity vectors, which are rotated by quaternions that I get from sensor fusion. These angular velocity vectors, are going to serve as inputs for the mouse library to move the mouse around. But I'm trying to smooth them out bit, so the device feels better to use. I think I should have been clearer, that what I'm trying to accomplish is to basically find a way to to the weighted average in some other way than float multiplication, since I read that's slow.

@anon57585045 Wow, that's a huge difference. Would you be able to share some link for this?

With integers, keep the actual number in bits somewhat to the left and use the lower bits as bits behind the decimal dot.

Which library do you use ? Please give a link to it.

:point_right: It starts with a good sensor :point_left:
I hope you are not using a fake, old, noisy MPU-6050 :scream:
Do you use a 5V (Pro) Micro board with a 3.3V sensor ? then you should fix the voltage mismatch of the I2C bus.

1 Like

@Koepel thank you again for replying. I'm using a BMX160 sensor and an arduino nano. This library. Sorry I'm a bit of a newbie, still not sure how I would implement your advice. I'm more than happy to look at some link/do research, just not sure where to start.

unsigned int smooth(unsigned int newVal) {
  static unsigned int oldVal = 0;
  unsigned long sum;
    //  optimize sum = (oldVal * 3 + newVal) / 4;
    sum = ( (oldVal << 1) + oldVal + newVal) >> 2;
  }
  oldVal = sum;
  return oldVal;
}
...
  // example use
  smoothedValue = smooth( analogRead(A0) );

...

For signed integer use, the bit shift part won't work. Make the variables signed and use:

 optimize sum = (oldVal * 3 + newVal) / 4;
1 Like

Those x_avg and y_avg variables need to be static so they persist globally.

You can code it as:

  x_avg += 0.25 * (x_reading - x_avg) ;
  y_avg += 0.25 * (y_reading - y_avg) ;

Makes it easier to change the filter pole as only one constant has to be changed and also only one multiply is needed per variable.

1 Like

@aarg Thank you for sharing. I'll test these in a few examples tomorrow.

@MarkT thanks Mark, I'll test this tomorrow and use going forward.

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