Go Down

Topic: Smooth a value in one instruction. (Read 2443 times) previous topic - next topic

RIN67630

Feb 12, 2020, 08:43 pm Last Edit: Feb 12, 2020, 08:47 pm by RIN67630
Here is the way to smooth a value in one line:
Code: [Select]
A0smooth = A0smooth + (A0 - A0smooth) / 2;
smoothes A0 to A0smooth.
You should run that with A0 and A0smooth being floats.
If you want a stronger smoothing replace /2 by /3 or more.
The higher the value the stronger the smoothing.

This code does a smoothing in the same way than a RC filter in electronics.

DrAzzy

#1
Feb 21, 2020, 01:44 am Last Edit: Feb 21, 2020, 01:47 am by DrAzzy
That's one line of code, not one instruction (by my count, it's like 10-12 instructions - load, sub, rightshift, add, store - times 2 because on an 8-bit avr, and 16-bit operands...)

As an aside, dividing by 3 is much worse, while dividing by 4 (or other powers of 2) is not - division on AVRs is implemented in software and slow.
ATTinyCore for x4/x5/x61/x7/x8/x41/1634/828/x313 megaTinyCore for the megaavr ATtinies - Board Manager:
http://drazzy.com/package_drazzy.com_index.json
ATtiny breakouts, mosfets, awesome prototyping board in my store http://tindie.com/stores/DrAzzy

PaulMurrayCbr

AKA a recursive lowpass filter. In general
Code: [Select]

weight = .95;
value = value * weight + newValue * (1-weight);


See http://www.dspguide.com/ for a free webbook on this topic.

TheMemberFormerlyKnownAsAWOL

Quote
Here is the way to smooth a value in one line:
Code: [Select]

A0smooth = A0smooth + (A0 - A0smooth) / 2;


smoothes A0 to A0smooth.
... but remember that on a UNO, A0 always has the value 14, so doesn't really need smoothing. :D
Please don't PM technical questions - post them on the forum, then everyone benefits/suffers equally

RIN67630

Code: [Select]

weight = .95;
value = value * weight + newValue * (1-weight);

That will not do: You must add the weighted increment*, not the newValue

PaulMurrayCbr

That will not do: You must add the weighted increment*, not the newValue
The weighted increment is (newValue-value)*(1-weight), and - looking at it - I agree that doing it that way is better, as it involves one fewer floating-point multiplications. BUt mathematically it comes out the same.

Of course, subtracting the weight from 1 is just a result of what you decide that "weight" means.

johnerrington

I'm hesitant to contribute in such distinguished company; however I'd suggest it would be better to use integer arithmentic.

Suppose you get a 12 bit integer value "newValue" from the ADC;

using a weight of .75 ..

to smooth it you take int smoothValue = (smoothValue*3 + newValue) /4

as smoothValue = (smoothValue + smoothValue<1 + newValue) >>2
I'm trying to help. If I find your question interesting I'll give you karma. If you find my input useful please give me karma (I need it)

TheMemberFormerlyKnownAsAWOL

I'm hesitant to contribute in such distinguished company; however I'd suggest it would be better to use integer arithmentic.

Suppose you get a 12 bit integer value "newValue" from the ADC;

using a weight of .75 ..

to smooth it you take int smoothValue = (smoothValue*3 + newValue) /4

as smoothValue = (smoothValue + smoothValue<1 + newValue) >>2
better still, "smoothValue<<1"

Or just leave it as "(smoothValue*3 + newValue) /4", and let the compiler figure it out.
Please don't PM technical questions - post them on the forum, then everyone benefits/suffers equally

johnerrington

smoothValue<<1  loses precision.

Quote
Or just leave it as "(smoothValue*3 + newValue) /4", and let the compiler figure it out.
my suggestion is based on several factors:

firstly that CHOOSING to use integer arithmetic forces the use of a sensible weighting - i.e. not something like 0.93;

and secondly that  it helps to see where precision could be lost.

also I get very fustrated when reading that someone has read the value of a voltage with the adc as 3.4326171875 volts,  just because the reading was 703 on a 5V=1024 scale.

Maintaining precision in a digital filter is important.
I'm trying to help. If I find your question interesting I'll give you karma. If you find my input useful please give me karma (I need it)

TheMemberFormerlyKnownAsAWOL

as smoothValue = (smoothValue + smoothValue<1 + newValue) >>2
Loses even more precision ;)
Please don't PM technical questions - post them on the forum, then everyone benefits/suffers equally

johnerrington

#10
Sep 16, 2020, 06:54 pm Last Edit: Sep 17, 2020, 09:12 am by johnerrington
Yes;  the precision is of course lost in the final divide (or shift)

the integer values need to be manipulated at hgher precision for the results to be sensible.  

My old brain was struggling so I used excel.

This filter is Vn+1 = (3*Vn + 256*Sn) >>2

the "voltage" is calculated at the END by (integer) dividing by 256.

You can see that by using higher order bits in the integer value precision is maintained in the filter calculation.

A single bit is lost in the conversion to "voltage" - as it would be in converting from a float.


I've attached the spreadsheet as a zip file.  Change the yellow value to change the "seed" sample value.  Its filtering a step function.

The graph shows the filter response: Its important to realise that at any instant the filtered value is unlikely to match the current value of your reading.




I'm trying to help. If I find your question interesting I'll give you karma. If you find my input useful please give me karma (I need it)

TheMemberFormerlyKnownAsAWOL

It wasn't a shift I was highlighting, it was a Boolean expression
Please don't PM technical questions - post them on the forum, then everyone benefits/suffers equally

johnerrington

#12
Sep 16, 2020, 07:06 pm Last Edit: Sep 17, 2020, 09:13 am by johnerrington
My bad - a typo - unacceptable in a program!  However it triggered an interesting excursion.  Thanks.
I've updated the spreadsheet and graph above.
I'm trying to help. If I find your question interesting I'll give you karma. If you find my input useful please give me karma (I need it)

Go Up