Moving 3 steppers simultaneously with splines - keyframing

Hi,

I am building a motion-control-system for videography.

I am using a slider and a pan/tilt-unit, which are working with steppers.

My goal is to set up 5 points (start, 3 intermediate points and the endposition) manually for each axis.

Later on, the program should run all 3 steppers simultaneously and connect the points via splines, to achieve a smooth motion.

What is the best way to realise something like this?

I have already tried a mix of Arduino-Splines and Accelstepper. But I´ve the problem, that the stepper is moving really slow. Furthermore I´ve not figured out, how to use multiply steppers simultaneously.

void setup()
{  
  //Serial.begin(115200);
  digitalWrite (panM0, LOW);
  digitalWrite (panM1, LOW);
  digitalWrite (panM2, HIGH);

  stepper.setMaxSpeed(4000);
  stepper.setAcceleration(20000);

}

void loop()
{
  float x[7] = {
    -1000,0,2500,5000,7500,10000,15000  };
  float y[7] = { 
    0,0,8000,2000,8000,1000,0  };
  tempCurve.setPoints(x,y,7);
  tempCurve.setDegree( Catmull );

  for( long i = 0; i <= 10000; i+= 1 ) {
    stepper.moveTo(tempCurve.value(i));
    stepper.run();
  }
}

Later on, the program should run all 3 steppers simultaneously and connect the points via splines, to achieve a smooth motion.

Which stepper library are you using? Stepper runs one stepper at a time. AccelStepper can move multiple steppers simultaneously.

Of course, running one stepper at a time may be sufficient, if the steps per iteration are small enough.

  float x[7] = {
    -1000,0,2500,5000,7500,10000,15000  };
  float y[7] = { 
    0,0,8000,2000,8000,1000,0  };

Why is it necessary to re-declare these on each pass through loop. Static or global would be better choices.

  for( long i = 0; i <= 10000; i+= 1 ) {
    stepper.moveTo(tempCurve.value(i));
    stepper.run();
  }

The run method is not a blocking function. It does not wait for the stepper to get to the commanded position before it returns. Telling the stepper to move again before it gets to the last position is wrong.

You’ve set a max speed and an acceleration. You are confusing the steppers computation of current speed, since the stepper never actually gets to where it is going.

It will go a lot faster if you let it actually get up to speed (and to a position) before changing the target.

Telling the stepper to move again before it gets to the last position is wrong.

It will go a lot faster if you let it actually get up to speed (and to a position) before changing the target.

I think I´ve fixed now the issue with the change of the target, have I done it right?

All 3 steppers are moving now as desired. But if I set the resolution(n) higher, they are moving slowly. I think the MEGA with 16MHz is to slow for realtime-calculation of the values.

Is there a possibility to calculate the values in advance, save them somehow and later just read them and run the accelstepper-code?

#include <AccelStepper.h>
#include <spline.h>

Spline tempCurveX;
Spline tempCurveY;
Spline tempCurveZ;


AccelStepper stepperX(1, 33, 35);
AccelStepper stepperY(1, 51, 53); 
AccelStepper stepperZ(1, 24, 22); 

const int panM0 = 23;
const int panM1 = 25;
const int panM2 = 27;

const int tiltM0 = 41;
const int tiltM1 = 43;
const int tiltM2 = 45;

const int sliderMS1 = 34;
const int sliderMS2 = 32;
const int sliderMS3 = 30;

const int stepper4M0 = 52;
const int stepper4M1 = 50;
const int stepper4M2 = 48;

long nPosition = 0;

float n[7] = {
  -800,0,1000,2000,3000,4000,4800      };
float x[7] = { 
  0,0,9000,11000,9000,0,0      };
float y[7] = { 
  0,0,5000,0,5000,0,0      };
float z[7] = { 
  0,0,90000,100000,90000,0,0      };


void setup()
{  
  //  Serial.begin(9600);
  digitalWrite (tiltM0, LOW);
  digitalWrite (tiltM1, LOW);
  digitalWrite (tiltM2, HIGH);

  digitalWrite (panM0, LOW);
  digitalWrite (panM1, LOW);
  digitalWrite (panM2, HIGH);

  digitalWrite (sliderMS1, HIGH);
  digitalWrite (sliderMS2, HIGH);
  digitalWrite (sliderMS3, LOW);

  stepperX.setMaxSpeed(4000);
  stepperY.setMaxSpeed(4000);
  stepperZ.setMaxSpeed(4000);

}

void loop()
{

  tempCurveX.setPoints(n,x,7);
  tempCurveX.setDegree(Catmull);

  tempCurveY.setPoints(n,y,7);
  tempCurveY.setDegree(Catmull);

  tempCurveZ.setPoints(n,z,7);
  tempCurveZ.setDegree(Catmull);

  if(nPosition <=4000){
    if (stepperX.distanceToGo() == 0 && stepperY.distanceToGo() == 0 && stepperZ.distanceToGo() == 0){
      float tempY = tempCurveY.value(nPosition);
      float tempX = tempCurveX.value(nPosition);
      float tempZ = tempCurveZ.value(nPosition);

      long tempGerundetX = long(tempX+0.5);
      long tempGerundetY = long(tempY+0.5);
      long tempGerundetZ = long(tempZ+0.5);

      stepperX.moveTo(tempGerundetX);
      stepperY.moveTo(tempGerundetY);
      stepperZ.moveTo(tempGerundetZ);

      //  Serial.println(tempGerundetX);
      nPosition++;
    }
    stepperX.setSpeed(4000);
    stepperX.runSpeedToPosition();

    stepperY.setSpeed(4000);
    stepperY.runSpeedToPosition();

    stepperZ.setSpeed(3000);
    stepperZ.runSpeedToPosition();
  }

}

All 3 steppers are moving now as desired. But if I set the resolution(n) higher, they are moving slowly. I think the MEGA with 16MHz is to slow for realtime-calculation of the values.

Calculation of what values? You can time how long it takes, you know, using micros() before and after.

What happened to setAcceleration()?

    float tempY = tempCurveY.value(nPosition);
      float tempX = tempCurveX.value(nPosition);
      float tempZ = tempCurveZ.value(nPosition);

It think those calculations might take some time...

What happened to setAcceleration()?

I have removed the acceleration and using constant speed instead.

It think those calculations might take some time...

      unsigned long start = micros();
      float tempY = tempCurveY.value(nPosition);
      float tempX = tempCurveX.value(nPosition);
      float tempZ = tempCurveZ.value(nPosition);
      unsigned long end = micros();
      unsigned long duration = end - start;
      Serial.print("Time to calculate one position: ");
      Serial.print(duration);
      Serial.println(" microseconds.");

And I KNOW whether, or not, the calculations take a long time, and, therefore, whether it matters.

If it takes 4 microseconds to compute a value and 40 milliseconds to use the value, the computation time is irrelevant.

BlueGene:
I have removed the acceleration and using constant speed instead.

If the only purpose of the splines is to give a smooth transition then you might find it's easier to achieve that by using a constant speed between waypoints and use the acceleration to give you the smooth transition between speeds as you pass the waypoint i.e. the plot of position versus time for each motor will be linear rather than curved, but the transitions between different linear segments will be smoothed. That would avoid any need to calculate the splines.

PaulS:

It think those calculations might take some time...

      unsigned long start = micros();

float tempY = tempCurveY.value(nPosition);
      float tempX = tempCurveX.value(nPosition);
      float tempZ = tempCurveZ.value(nPosition);
      unsigned long end = micros();
      unsigned long duration = end - start;
      Serial.print("Time to calculate one position: ");
      Serial.print(duration);
      Serial.println(" microseconds.");



And I KNOW whether, or not, the calculations take a long time, and, therefore, whether it matters.

If it takes 4 microseconds to compute a value and 40 milliseconds to use the value, the computation time is irrelevant.

Using your code, I´ve got about 5000 microseconds... Which would result in about 200 calculations per second (1/0,005)

And about 180 microseconds using the chipKIT u32. (5555 calculations per second(1/0,00018))

Is there any possibility to improve my code?

There are notes in the AccelStepper library about how fast it will go (quite slow IIRC).