Programming linear motor interpolation

AWOL:
Best test for some minimum difference.

Something linke this?

if(x.getCurrPos() < x.getNewPos() && y.getCurrPos() < y.getNewPos() && z.getCurrPos() < z.getNewPos())
{
  readyForNextCmd = false;
} else {
  readyForNextCmd = true;
}

But getCurrPos() also could be greater than getNewPos()

Something linke this?

More like:

if(abs(x.getCurrPos() - x.getNewPos()) < 0.1 &&
   abs(y.getCurrPos() - y.getNewPos()) < 0.1 &&
   abs(z.getCurrPos() - z.getNewPos()) < 0.1)
{
}

Change the 0.1 to whatever "close enough" means to you.

Thank you I will try that! Neat workaround! :slight_smile:

It works!
Thank you!

Just curious: float calculations are also a bit
slower than long or int? The motors seem to be a bit
slower and I get more vibration.

float calculations are also a bit slower than long or int?

With no dedicated FPU, this is to be expected.

The motors seem to be a bit slower and I get more vibration.

Not sure why the motors should be slower, unless it is simply a matter of the "step, dammit!" instructions taking longer to be generated.

There should be no affect on vibration, so I don't know why that is happening.

asuryan:

	//Init values ///////////////////////////////////
_maxAxisLength = maxAxisLength;
_limitSwitchPin = limitSwitchPin;
_motorPin = motorPin;
_dirPin = dirPin;
_leadPitch = leadPitch;
_stepPerRev = steps;
_normLenghtPerStep = _leadPitch / _stepPerRev;

For future reference you might want to stop using leading underscores for data names (they are reserved). From the C standard:

All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.

Also see:

I know it is handy to have similar names for class variables, compared to function parameters, but try to put the underscores at the other end, eg.

	maxAxisLength_ = maxAxisLength;

The sad thing is that I cant use unsigned longs because the linear interpolation has
to handle negative values as well (when going back)

You could get clever, and remember the direction, do all your arithmetic unsigned, and only then negate if necessary.
I'll have a little think.

Edit: Or you could do a sort of quantised fixed point (I made the term up), with large numbers broken down into a few large fixed-size steps, with the remainder representing the fraction.
Would probably use a bit more memory, but no much.

EDIT: I removed that post because my youtube link wasnt finished so I wanted to
wait. Thanks AWOL for your fast answer! :slight_smile:

First of all thanks all for your patients and for your help! Im really happy! :slight_smile:
Thanks Nick for the explanation. I do a lot javascript scripts so in advanced
c programming Im quite new. :slight_smile:

Not sure why the motors should be slower, unless it is simply a matter of the "step, dammit!" instructions taking longer to be generated.

I made a little video about the speed problem with float/long calculations:

Both times Im going to Point(50;10).

  1. Part: All calculations and positions storings are FLOAT
  2. Part: All calculations and positions storings are LONG (I choosen a value thats still ok for long calcs <2.147.000.000)

What I observed:

  1. With all float values the motors need to get to Point(50;10) approx 45 seconds, and
    the motors are very loud
  2. With all long values the motors need 22 seconds (thats twice as fast) and the motors dont get
    that loud.

My stepping method looks like this:

void AxisCtrl::motorStep()
{
	unsigned long currentMicros = micros();
	if(currentMicros - _previousCycle > _motorSpeed) {
		if(_stepDone == 0){
			setMotorDir();
			incrementCurrStep();
		}
		_previousCycle = currentMicros;
		if(_motorPinState == 0){
			_motorPinState = 1;
		} else {
			_motorPinState = 0;
		}
		digitalWrite(_motorPin,_motorPinState);
		++_stepDone;
		if(_stepDone == 2){
			_stepDone = 0;
			if(_currPos > 0){
				_axisHomed = false;
			}
		}
	}

}

void AxisCtrl::incrementCurrStep()
{
	if(_motorDir == true){
		_currPos += _normLenghtPerStep;
	} else {
		_currPos -= _normLenghtPerStep;
	}
}

void AxisCtrl::setMotorDir()
{
	if(_currPos<_newPos){
		digitalWrite(_dirPin, HIGH);
		_motorDir = true;
	}else{
		digitalWrite(_dirPin, LOW);
		_motorDir = false;
	}	
}

And the main code:

if(!readyForNextCmd){
      
      // XY INTERPOLATION
       x2 = x.getCurrPos();
       y2 = linearInterpolate(x.getOldPos(),y.getOldPos(),x.getNewPos(),y.getNewPos(),x2); 
       y.setNextPos(y2);
       
       // IF ALL DONE READY FOR NEXT COMMANDS
       if(abs(x.getCurrPos() - x.getNewPos()) < 0.0005 && 
          abs(y.getCurrPos() - y.getNewPos()) < 0.0005)
       {
         readyForNextCmd = true;
       }
       
        // DO THE STEPPING
       if(abs(x.getCurrPos() - x.getNewPos()) > 0.0005){
         x.motorStep();
       }
       if(abs(y.getCurrPos() - y.getNextPos()) > 0.0005){
         y.motorStep();
       }
}

float linearInterpolate(float x1, float y1, float x2, float y2, float X)
{
  float _Y = 0;
  if(x1 == x2){
    _Y = y2;
  } else {
    _Y = y1 + ((X-x1)*y2 - (X-x1)*y1)/(x2-x1);
  }
  return _Y;
}

Im using only 1 direction pin for all 3 motors. Im switching the pin everytime the
each motor has to move. So the motor looks at the direction and decides LOW or HIGH
everytime.

The sad thing is that I cant use unsigned longs because the linear interpolation has
to handle negative values as well (when going back) and longlongs are no option too
because that makes the system even slower than float :frowning:

Any ideas what that could cause?

PaulS:
With no dedicated FPU, this is to be expected.

Could you please explain what this means or some hint to implement and can I do this
optimisation for my project (considering my "noobyness")?

Because it seems I have no choice... :frowning:

  1. long is not "long" enough
  2. unsigned long is no option because I can go also only
    until 100mm and Im not able to go backwards
  3. float works but Its quite slow and very loud (see my video in the post before)
  4. long long makes movement very very slow
  5. int: too small

Thanks AWOL for your suggestion but Im not sure what you mean... :frowning:

Hm I think there has to be a workaround or some genuis rocket science... Ive seen so
many Arduino CNCs that are able to run fast and far more centimeters than 10cm.

FPU = floating point unit.

Grown up processors have hardware to perform floating point operations, but the AVR has to do them in software, which is slower.

Ive seen so many Arduino CNCs that are able to run fast and far more centimeters than 10cm.

What I'm having trouble understanding is how you're 130 cm by 130 cm surface results in values passed to the function that are in the 50000 range. Can you explain that?

PaulS:
What I'm having trouble understanding is how you're 130 cm by 130 cm surface results in values passed to the function that are in the 50000 range.

It´s 130mm -> 13cm!!

Why 50.000? Is the floats range only 50.000? I tried the position 130x130
and it worked well (but noisy and slow).

Thats my setup so thats why I get that high numbers and cant use longs:
The lead lenght of the leadscrew is 0.8mm/revolution.
The EasyStepper are in microstepping 1/8 mode so one revolution results in 1600 Steps/revolution
So 1mm = 2000 Steps or 0.0005mm/Step.
So the maximum value the UNO would have to handle using steps would be: 260.000x260.000 = 67.600.000.000 (only long
long could handle that but thats super slow)

@AWOL: seems its not possible with the ATMega328? :frowning:

@AWOL: seems its not possible with the ATMega328

Did I say that?

I don't see why you can't break down a move of, say 10.01 cm into maybe three moves of 2.5cm and one of 2.51cm.
Then, you'd only have to use long arithmetic.
Maybe I'm oversimplifying, but I don't have any hardware to play with.

It´s 130mm -> 13cm!!

See? That's exactly why we don't use that crazy system here.

All you have to do is scale longs. A long can go up to 2^32 (4294967296), right? So you have 4294967296 steps of resolution. Now if your table is 130 mm in each direction, a long can have a resolution of 130/4294967296 mm which is 3.0267e-008 mm. That's incredibly small. Almost as small as a boson, lol.

So you come up with a system that (say) 1000 units represents a mm. So the table is then 130000 units. Well within a long. And your resolution is 1/1000 mm.

Nice table BTW, wish I had one. :wink:

That's exactly why we don't use that crazy system here.

...and look what happens

Or an aircraft crashes.

haha go easy on me :wink:

I think I will change my setup to full step mode so I only have
200 steps/0.8mm: 1Step/0.004mm thats enough for my needs and the biggest
possible number would be 1.056.250.000 and thats in long range.

Only one problem: I dont get the EasyStepper into full stepping mode... but thats
another thread :wink:

thanks all for your patients and helpfullness! I really appreciate that!