Long long arithmetic optimisation

Long long arithmetic optimisation.

I’m controlling the speed and position of a motor with great accuracy across a wide range of values. Although I have a solution that works it feels a little klunky.

The trouble of course is maintaining the precision of the calculation.

The important calculation is the Required Encoder Count at a point in time.

Therefore we have a known start time, and a known point in time, i.e. millis() , both these are unsigned longs of course, and the motor could run for several hours, so the full range of the unsigned long could be used, but overrun is not an issue. We run for hours, not days.

The rate the motor must move is also well defined. This is 2701 microseconds per tick.

Therefore , given the start time, the current millis() and the Rate (2701 us/tick) calculate the Required Ticks at a point in time.

Let us say that Duration = millis() – StartTime

Required ticks is simply given by

const float rate = 2.701
unsigned long duration;
unsigned long requiredTicks;

duration = millis() – StartTime;

requiredTicks = duration / Rate;

Which of course fails miserably on the last line: The 32 bit duration cannot be cast to a float, and the rate cannot be cast accurately as a long.

So we move onto

const unsigned long rate = 2701
unsigned long duration;
unsigned long requiredTicks;

duration = millis() – StartTime;

duration *= 1000;

requiredTicks = duration / Rate;

Which work fine for about an hour and then the duration will roll over.

What I ended up with that works fine for many hours

const unsigned long long  rate = 2701
unsigned long long duration;
unsigned long requiredTicks;

duration = millis() – StartTime;

duration *= 1000;

requiredTicks = (long) (duration/rate);

However, using a long long sounds outrageously inefficient to me. I can’t help but think there is a better way, I just can’t see it...

Note that you are using millis() which are far to course for 2701 microseconds- millis would be either 2 or 3 for that amount of elapsed microseconds. And millis takes more than 1000 hours to overflow an unsigned long. In essence you are trying to adjust a value (duration) with only one significant digit of precision (in the area your testing) with a divisor of four significant digits... The basic approach is not very accurate. So you should switch to micros() instead if you really need that level of accuracy.

Further I would suspect that the Arduino would have a bit of trouble differentiating between 2701 uS per tick and 2700 uS per tick. Two or three significant digits instead of four.

In that case

(duration * 100) / 27 may well serve

The rate the motor must move is also well defined. This is 2701 microseconds per tick.
Do you control the speed of the motor with arduino? Kind of feedback?
Otherwise you do not need to mess with time, just count the ticks (when you know exactly the motor does 2701us/tick).. For example: the motor is 3374627364 ticks away :slight_smile:

wanderson:
(duration * 100) / 27 may well serve

This is a good idea.

At a point 5 hours into the run, 6664198 ticks will have elapsed. If you use 2700 instead of 2701 you come up some 2500 ticks short - i.e. out by 6 or 7 seconds... which is probably not intolerable.

Dividing duration by 100 won’t overflow until around 12 hours, which is probably tolerable as well, although only just.

pito:

The rate the motor must move is also well defined. This is 2701 microseconds per tick.
Do you control the speed of the motor with arduino? Kind of feedback?
Otherwise you do not need to mess with time, just count the ticks (when you know exactly the motor does 2701us/tick)..

Yes, a simple PID algorithm is used to control the motor speed.

I'm afraid I do need to mess with time, as I need to maintain the correct rate and distance from the start time. I tried just measuring the rate, but the inaccuracies caused too much drift and I couldn't get it to work.

The input to the PID algorithm is simply the difference between the required ticks and the actual ticks - which seem to work quite nicely.

You might consider putting a real-time clock on your Arduino instead of relying in micros/millis to always be accurate.

That kind of dead reckoning needs some good time base, RTC is not the best one as it is usualy with 1sec period. For example a 4-10MHz oscillator fed into an internal timer and a kind of phase loop locked on that timer, or something like that..

MichaelMeissner:
You might consider putting a real-time clock on your Arduino instead of relying in micros/millis to always be accurate.

I do have an RTC on the project for other reasons

The DS1307 only gives hours, mins and seconds. I have to run my PID cycle every 10ms or so.

There is an external feedback on the whole system that corrects for slight drift, millis seems to work well enough.

tomhow:

wanderson:
(duration * 100) / 27 may well serve

This is a good idea.

At a point 5 hours into the run, 6664198 ticks will have elapsed. If you use 2700 instead of 2701 you come up some 2500 ticks short - i.e. out by 6 or 7 seconds... which is probably not intolerable.

Dividing duration by 100 won’t overflow until around 12 hours, which is probably tolerable as well, although only just.

My point is that you are attempting to divide an integer (duration) by a fraction of that integer (2.701) which is inherrently inaccurate. Attempting that by artificially factoring the original duration, without adding additional precision to that duration does not alter the fundamental inaccuracy. -You will not be able to achieve that level of precision (+/- 1 microsecond). Further, I have my doubts that you could achieve +/- 1 microsecond even if the duration was in microseconds (with 1 microsecond precision) since the execution of the code would undoubtably affect the actual duration.

In the latter case you would have to accurate time and therefore adjust your code to account for the additional time caused by the execution steps.

So in short, you really do need long long, and a modification of your code to compute duration using microseconds() instead of millis() if you are to have any chance at achieving that level of precision. Accuracy will depend upon how well you calibrate the execution of your code.

Mind the arduino's oscillator is 100ppm one. In order to measure something with 1usec precison you need a calibrated oscillator. Also micros and millis are not for time precise measurements, rather simple delays. I am a little in doubt whether you can control the motor such you do not miss single tick, though :slight_smile: Such PID does not exist, considering the motor works with some load..

wanderson:
My point is that you are attempting to divide an integer (duration) by a fraction of that integer (2.701) which is inherrently inaccurate. Attempting that by artificially factoring the original duration, without adding additional precision to that duration does not alter the fundamental inaccuracy. -You will not be able to achieve that level of precision (+/- 1 microsecond). Further, I have my doubts that you could achieve +/- 1 microsecond even if the duration was in microseconds (with 1 microsecond precision) since the execution of the code would undoubtably affect the actual duration.

In the latter case you would have to accurate time and therefore adjust your code to account for the additional time caused by the execution steps.

So in short, you really do need long long, and a modification of your code to compute duration using microseconds() instead of millis() if you are to have any chance at achieving that level of precision. Accuracy will depend upon how well you calibrate the execution of your code.

No, I think you've really made be revise my criteria. Running 2700 or 2701 won't make a difference.

The precision I am actually getting with the current code is fine for the application, and I've just changed it to 2700 and there is no noticeable difference.

The rate at any moment is not critical to the microsecond either. The emphasis is more on getting smooth motion and the correct accumulative encoder target rather than the instantaneous rate.

Therefore the 100 factoring will be acceptable. I will do some experiments

Apologies for misleading a bit, but like all such things, it is difficult to get the question across without endless explanation, but you've made me see a bit of sense! :slight_smile:

pito:
Mind the arduino's oscillator is 100ppm one. In order to measure something with 1usec precison you need a calibrated oscillator. Also micros and millis are not for time precise measurements, rather simple delays.

Millis is enough for this application - see post i just made. I do need need to measure the rate to 1usec, i just need to keep the accumulated target correct.

You've all made me see a bit of sense?

pito:
I am a little in doubt whether you can control the motor such you do not miss single tick, though :slight_smile: Such PID does not exist, considering the motor works with some load..

I'm sorry, I don't quite understand? I don't have a problem with missing ticks?

Glad it helped; however, what I am saying is that there is even a problem using 2700 (or 27)...

When using millisecond precision, you would be better off using either a 2 or more probably a 3, rather than 2.7...

In order to make use of the additional precision 2.7 gives you over 3 you would need duration that is precise to at least tenths of a millisecond, which is not something you can achieve by multiplying a millisecond value by ten, which only shifts the existing precision, it doesn't increase the precision.

wanderson:
Glad it helped; however, what I am saying is that there is even a problem using 2700 (or 27)...

When using millisecond precision, you would be better off using either a 2 or more probably a 3, rather than 2.7...

In order to make use of the additional precision 2.7 gives you over 3 you would need duration that is precise to at least tenths of a millisecond, which is not something you can achieve by multiplying a millisecond value by ten, which only shifts the existing precision, it doesn't increase the precision.

I do follow your point - but I think I am massively overstating the precision.

Anyhow, empirically the existing code seems to work... here is a three hour exposure :slight_smile:


I will experiment. I am a big fan of "if it aint broke...." but at the same time I like to see things done as elegantly as possible.

I'm sorry, I don't quite understand? I don't have a problem with missing ticks?
To be more precise: I am a little in doubt whether you can control the motor such you do not miss single 2701us long tick, though.. So your ticks may come and you may count them all, but not all will be 2701us as your PID will not allow that..

pito:

I'm sorry, I don't quite understand? I don't have a problem with missing ticks?
To be more precise: I am a little in doubt whether you can control the motor such you do not miss single 2701us long tick, though.. So your ticks may come and you may count them all, but not all will be 2701us as your PID will not allow that..

The PID uses the tick error as input to adjust the motor speed.

Tick error is difference between counted ticks and required ticks (both since StartTime).

If we are ahead of ourselves, the motor reduces the PWM and so on. In the PID the integral term(I) is the sum of the tick errors. If the motor is not moving (PWM=0) the I term rapidly builds to around 300 or so and the P term looks after the small required changes after that. This does create an offset but it is not a problem.

In my system the DC motor needs a PWM of around 300 to move the motor, but it must be constantly adjusted as the DC motor moves extremely slowly. The DC motor spindle itself turns at 10rpm, which is fed into a 30:1 gearbox followed by a 360:1 gear to get the required reduction

The solution keeps the actual ticks count to within 3 or 4 ticks of where it needs to be, which is fine.

The PID doesn't care (and doesn't know) the time between the individual ticks.

..you are moving your telescope following the stars then ? :slight_smile:

pito:
..you are moving your telescope following the stars then ? :slight_smile:

This is correct. I have a remote controlled homemade telescope mount.

The motors need to move at a wide range of speeds. 10rpm to track the stars, or 4-5000rpm if we wish to slew from one part of the sky to another by computer control. The motors have 512cpr encoders (2048 in quadrature) so tracking the stars gives a tick frequency of about
400Hz, but slewing make the encoders tick at around 200kHz.

All this adds up to a lot of problems to solve: It has been an interesting project and 99% works now... just some untidy code to neaten up which precipitated this post.

Congratulation! So you may attach a 3 axis accelerometer to the telescope and you'll get a feedback where it points actually (as a backup in case your ticks are lost) :slight_smile:

Trying to do arithmetic with very large values is going to be problematic on an 8-bit machine.

Reading between the lines, what you're actually trying to achieve is calculate the target tick count for 'now'.

In that case, rather than determine when 'now' is to microsecond resolution and then multiply and divide to calculate the tick count, couldn't you simply increment the target tick count each time the required number of microseconds has passed?

I mean something like:

unsigned long lastTickMicros = 0;

if (micros() - lastTickMicros > tickIntervalMicros)
{
    tickCount++;
    lastTickMicros += tickIntervalMicros;
}