Go Down

Topic: Equation giving different results (Read 3071 times)previous topic - next topic

Jmok

Jul 15, 2014, 03:13 pm
Hi there, I am using an Arduino Uno and a Motor Shield to control a DC motor with an encoder. To calculate the RPM's of the motor I use:

RPM_A = (60*(newpositionA - oldpositionA) * 1000) /((newtimeA-oldtimeA)*16*2);

This equation works perfectly fine when going in the positive direction, once I start going in the negative direction it says its rotating at ridiculously high positive speeds. So I broke the equation into:
part1 = 60*(newpositionA - oldpositionA) * 1000;
part2 = (newtimeA-oldtimeA)*16*2;
part3 = part1/part2;

Where part 3 should equal RPM_A, strangely I receive the proper result, I do not understand what is different between the two approaches, mathematically they are the same but through arduino it is different. newposition and oldposition are variable long, newtimeA & oldtimeA are unsigned long, and RPM_A, part1,part2,part3 are double.

econjack

#1
Jul 15, 2014, 03:24 pmLast Edit: Jul 15, 2014, 03:28 pm by econjack Reason: 1

Code: [Select]
`RPM_A = (60*(newpositionA - oldpositionA) * 1000) /((newtimeA-oldtimeA)*16*2);`

how is RMP_A defined? If newpositionA - oldpositionA is as small as 1, the numerator for the expression will overflow an int. I'm pretty sure the variable needs to be a long. I'm only guessing since you didn't post your code.

edit:

I see now that you have it as a long. If RPM_A is a double, which is not supported by the Arduino IDE, change it to a float. Also, by default, the compiler will treat numeric constants as int. Try:

Code: [Select]
`RPM_A =(float) (60.0 * (newpositionA - oldpositionA) * 1000.0) /((newtimeA-oldtimeA)*32.0);`

and see what happens.

Peter_n

#2
Jul 15, 2014, 03:58 pm
You can do all kind of in-between-calculations. The compiler will make the same code.

Code: [Select]
`// multiply by 60 because ... ?// multiply by 1000 because ... ?long t1 = 60L * 1000L *(newpositionA - oldpositionA);// multiply by 16 because ... ?// multiply by 2 because ... ?long t2 =16L * 2L * (newtimeA-oldtimeA);// Calculate the rpmRPM_A = (float) t1 / (float) t2;`

cmiyc

#3
Jul 15, 2014, 04:11 pm

I do not understand what is different between the two approaches, mathematically they are the same but through arduino it is different.

Not different in the Arduino, but different to the compiler.  When different variable types are mixed, the compiler sometimes make a bad guess on how to handle the overall calculation.  Keep in mind that numbers like "60" are constants and usually, the compiler will treat them as an integer (int).  So you're mixing ints, longs, and floats (double doesn't exist in AVR).

1.  Keep the intermediate steps.  They'll likely get optimized out.  Personally this is my method because it allows me to visually see the variable types and their expected results when I scan through the code.

2.  Convert your integers into floats, so they get treated as floats (or as long).  The simplest way is adding the decimal.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

el_supremo

#4
Jul 15, 2014, 05:42 pm
Quote
the compiler sometimes make a bad guess on how to handle the overall calculation.

The compiler doesn't guess.

Pete
Don't send me technical questions via Private Message.

Jmok

#5
Jul 15, 2014, 06:03 pm
Thanks for the feedback,

I didn't realize double wasn't part of the Arduino IDE, I changed it to float and added decimal places to the numbers and it works.

KeithRB

#6
Jul 15, 2014, 06:25 pm
It is not that double is not a part of the IDE, it is just that floats and doubles are the same size.

Peter_n

#7
Jul 15, 2014, 07:46 pm

Quote
the compiler sometimes make a bad guess on how to handle the overall calculation.

The compiler doesn't guess.

Pete

I guess the compiler doesn't have to guess, because it "feels" what to do

Robin2

#8
Jul 15, 2014, 10:02 pm

Quote
the compiler sometimes make a bad guess on how to handle the overall calculation.

The compiler doesn't guess.

Pete

Perhaps it was the guys who wrote it that did the guessing.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

el_supremo

#9
Jul 15, 2014, 11:06 pm
The language specification defines how a compiler must handle things like precedence of operators (e.g. a*b + c) and mixed mode operations (e.g. the result of an int * float) and all the other aspects of the language.
There would hardly be any point trying to write a program if the first time you compiled the statement "a = b/5;" the compiler guessed that the 5 was an integer and the next time it guessed that it was a float.

Pete
Don't send me technical questions via Private Message.

Robin2

#10
Jul 16, 2014, 10:19 am

guessed that the 5 was an integer and the next time it guessed that it was a float.

Pete, you are taking us too literally.

Of course the copiler doesn't guess. But if the people writing code (me for example) don't know the precise compiler rules we may "guess" wrong about what the compiler will do. The end result is pretty much the same as if the compiler was doing the guessing because we know what we wanted to happen (we would have got the correct answer with a calculator) - but it doesn't happen.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

el_supremo

#11
Jul 16, 2014, 05:10 pm
The problem is that a newbie might interpret "the compiler guessed wrong" literally. I would have had no problem if the statement had been "the programmer guessed wrong".

Pete
Don't send me technical questions via Private Message.

cmiyc

#12
Jul 16, 2014, 08:31 pm
I think we've visited this pedantic rat-hole for long enough.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

GoForSmoke

#13
Jul 16, 2014, 09:03 pm
You can use casting to do the integer calculation in 32 bit and safely put the result into 16 bit.
This avoids the occasional .999999 is 1 floating point funnies and run a magnitude or two faster than float on AVR.

Code: [Select]
`int RPM_A;..........RPM_A = (int) ( 60L * ((long) newpositionA - (long ) oldpositionA ) * 1000L ) / (((long) newtimeA - (long) oldtimeA ) * 16L * 2L );`
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

el_supremo

#14
Jul 17, 2014, 04:34 pm
@James C4S: Rather than acknowledge that your statement was wrong and move on, your first response on the issue is an ad hominem attack. Classy.

Pete
Don't send me technical questions via Private Message.

Go Up