Pages: 1 [2]   Go Down
Author Topic: Help with signal conditioning  (Read 4559 times)
0 Members and 1 Guest are viewing this topic.
Maryland, USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 79
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

A fast moving average would be something like this:

mav = (1-alpha)*mav + alpha * new_data;

where 0<alpha<1 as a weight for new observations. The smaller alpha is, the longer memory mav has of historical data.

If you pick alpha to be 1/2^n, the math gets considerably easier as you can simply shift. For example, for alpha = 1/16:

mav = mav + (new_data - mav) / 16.

essentially, 1 subtraction, 1 shift, 1 increment.


That's very elegant.
It eliminates the need for an array to keep track of the last "n" numbers (and the ++ and &= operations on the counter) and eliminates the initializing function altogether.

Thanks!

P.S. Question for the C++ wiz. Would:

mav += ((new_data - mav) / 16) ;

work? faster? are the external () necessary?


Logged

There are three kind of people in the world: Those who can count, and those who can't

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 220
Posts: 13836
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
P.S. Question for the C++ wiz. Would:

mav += ((new_data - mav) / 16) ;

work? faster?

You can easily test if this works (depending on the datatypes you can have certain side effects !)
faster? think yes you need to measure 10000 times or so to see diffs.
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Left Coast, CA (USA)
Offline Offline
Brattain Member
*****
Karma: 361
Posts: 17301
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
P.S. Question for the C++ wiz. Would:

mav += ((new_data - mav) / 16) ;

work? faster?

You can easily test if this works (depending on the datatypes you can have certain side effects !)
faster? think yes you need to measure 10000 times or so to see diffs.


So would this in effect be acting as a low pass filter on the signal? If so how is the 16 related to the 'corner frequency of the effective filtering action, or is that more a function of how often the statement is executed per time unit?

Lefty
Logged

Maryland, USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 79
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


So would this in effect be acting as a low pass filter on the signal? If so how is the 16 related to the 'corner frequency of the effective filtering action, or is that more a function of how often the statement is executed per time unit?


Ah... I yield to dhenry and robtillaart (not that I would not know the answer....)  smiley-red

 
Logged

There are three kind of people in the world: Those who can count, and those who can't

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
mav += ((new_data - mav) / 16) ;

Better yet:

Code:
mav += ((signed short) (new_data - mav)) >> 4;

Before you get too excited about this approach, it has limitations:

It requires that the new_data to be greater than 1/16th. Otherwise, the moving average will degenerate to 0. One way to avoid this is to use fixed point math. For example, use the last two four (binary) digits as "decimal points":

Code:
  new_data=analogRead(MY_CHANNEL) << 4; //read the data
  mav += ((signed short) (new_data - mav)) >> 4; //compute moving average

You just need to recognize that mav is 16 times bigger.

You can also multiply the reading by 100 (for example) to obtain two decimal points.

The key is to avoid degeneration with small numbers.
Logged

Maryland, USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 79
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@ dhenry.

Thanks for the clarification!

In my particular case, new_data is often = 0 as it is the difference between two sensors values taken at very short interval, so it won't work for me.

your original suggestion:

Code:
mav = mav + (new_data - mav) / 16 ;

is not affected by this problem, or is it?

Thanks
Logged

There are three kind of people in the world: Those who can count, and those who can't

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There are two issues: signed vs. unsigned, and integer math.

Which one are you referring to?
Logged

Maryland, USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 79
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

There are two issues: signed vs. unsigned, and integer math.

Which one are you referring to?

The value I am averaging is a signed integer which varies roughly between -5 and +5 (but in theory could be more) and the 16 periods moving average (in my tests) is very often 0 with some peaks between -2 and +2.

Thanks
 
Logged

There are three kind of people in the world: Those who can count, and those who can't

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, it will have issues with long memory: the smoothed signal would accurate reproduce the peaks / valleys.

Two solutions:

1) use shorter memory: rather than 1/16th, use 1/8th, or 1/4th. The data generated this way will be more volatile.
2) scale up observations: rather than having data series in -5 - +5 range, scale them up by 10 (or 16, using left shifts) for example. The resulting moving average will be correspondingly scaled up.
Logged

Maryland, USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 79
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks!
Logged

There are three kind of people in the world: Those who can count, and those who can't

Maryland, USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 79
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Just wanted to report on my results, for whomever may be interested.

I applied a simple "decision" to the 16 period moving average:

IF moving average > 0  I am blowing on the sensor
IF moving average < 0  I am NOT blowing on the sensor

with the corollary that:

IF moving average == 0 I am doing whatever I was doing before  // No need to actually state

This is the result in graphic form:

The blue line is the sensor data, the red line is the moving average (multiplied by 100 for ease of plotting) and the green line is the result of the decision: 100 I am blowing, 0 I am not blowing.

It's getting accurate, though there are some false positives and false negatives now and then and, in this very example, there are 86 ms of delay from the beginning of the decay (when I stop blowing) to the response of the moving average (when the software realized that I stopped blowing).

I'll continue tweaking the software.

If there is interest I'll keep posting updates.

 


* Moving Average with blowing decision.png (30.1 KB, 1008x630 - viewed 39 times.)
Logged

There are three kind of people in the world: Those who can count, and those who can't

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
there are 86 ms of delay

Unfortunately, there isn't a whole lot one can do about it: the process we implemented here is an integration process. The longer the memory, the better it is at eliminating noises, and the longer the delay is.

You will just have to be careful in selecting the memory.
Logged

Maryland, USA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 79
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, I'm coming more or less to the same conclusion.

Since the signal is quite clean to begin with, the next thing will be to see if an 8 period average gives a shorter delay with an acceptable low number of false positives/negatives.

Interesting (and very frustrating) the portion of sketch that sends the MIDI signal to the serial would not work unless i put a delay (10) ; In the main loop.
But it will work without the delay, if I Serial.print() ; the data on the serial monitor for debugging.

Go figure.....






Logged

There are three kind of people in the world: Those who can count, and those who can't

Pages: 1 [2]   Go Up
Jump to: