3 Schrittmotoren gleichzeitig ansteuern und Splines abfahren

Hallo,

für mein Kamera-Motion-Control-System suche ich eine Möglichkeit 3 oder mehr Schrittmotoren gleichzeitig laufen zu lassen.

Es sind zwei verschiedene Modi geplant: Kontinuierliche Bewegung und intervallweise Bewegung.

Zum Programm:

Ich will zunächst Anfangsposition, 3 Zwischenpunkte und die Endposition manuell anfahren und die entsprechenden Schritte speichern. (Funktioniert bereits).

Dann sollen die Punkte mit Splines verbunden werden und die Strecke abgefahren werden, was soweit auch funktioniert.

Benutzt habe ich dafür Arduino-Splines:

und AH-Pololu für die Schrittmotorenansteuerung:

Hier mal der Ausschnitt aus dem Programmcode

             float x[7] = {
    P4x*0.2,P0x,-P1x,-P2x,-P3x,-P4x,-P4x*1.2      };
  float y[7] = { 
    0,P0y,P1y,P2y,P3y,P4y,0      };
  tempCurve.setPoints(x,y,7);
  tempCurve.setDegree( Catmull );

  for( long i = 0; i <= -P4x; i+= 1 ) {
    float temp = tempCurve.value(i);
    float tempPREV = tempCurve.value(i-1);
    long tempGerundet = long(temp+0.5);
    long tempPREVGerundet = long(tempPREV+0.5);
    // Serial.println(temp);
    // Serial.println(tempGerundet);
    // Serial.println((tempGerundet-tempPREVGerundet)*1);
    stepperY.move((tempGerundet-tempPREVGerundet)*1);
    stepperX.move(-1);
  }

Die AH-Pololu-Library kann jedoch nur einen Schrittmotor gleichzeitig ansteuern. Bei 3 Schrittmotoren ist es nicht mehr hinnehmbar, dass die Motoren die Schritte immer hintereinander ausführen. Des Weiteren glaub ich, dass die Arduino-Splines-Library ziemlich rechenintensiv ist und dadurch eine schnelle Bewegung nicht möglich ist.

Ich hatte auch die Accelstepper-Library ausprobiert:
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();
  }
}

Jedoch weiß ich dort überhaupt nicht, wie ich die zwei weiteren Motoren mit einbinden kann...

Ich suche also nun eine Möglichkeit mehrere Schrittmotoren gleichzeitig verfahren zu lassen und dabei bestimmte Positionen abzufahren.(siehe Bild im Anhang).

BlueGene:
Ich suche also nun eine Möglichkeit mehrere Schrittmotoren gleichzeitig verfahren zu lassen und dabei bestimmte Positionen abzufahren.(siehe Bild im Anhang).

Ich würde die für Deine Steuerungszwecke unpassenden Libraries vergessen und stattdessen alle drei Schrittmotoren mit Einzelschritten direkt ansteuern, ohne fremde Library.

Die Verfahrwege in Abhängigkeit von der Zeit hast Du doch in Form der Splines vorliegen

x= f1(t)
y= f2(t)
z= f3(t)

Die Zeit t läuft für alle Motoren gleichmäßig nach vorne genau wie der millis() Zähler auf dem Arduino.

Jetzt mußt Du die Höchstgeschwindigkeit der Steppermotoren wissen, "Steps pro Sekunde", und auf diese maximale Anzahl "Steps pro Sekunde", die die Motoren steppen können, mußt Du die loop synchronisieren.

Mal angenommen, die Motoren können maximal 500 Schritte pro Sekunde ausführen, dann muß die loop-Funktion alle 2 ms eine Funktion aufrufen, die möglicherweise (oder auch nicht) einen Stepschritt an den drei Motoren vornimmt.

So einen Funktionsaufruf aus der loop im Takt von 2 Millisekunden ist z.B. so möglich:

void loop() {
  static long oldtime;
  long thistime;
  thistime= millis()/2;
  if (thistime!=oldtime)
  {
    doMotorSteps();  // ==> Aufruf alle 2 Millisekunden
    oldtime=thistime;
  }
}

Und die Funktion "doMotorSteps();" ermittelt dann alle 2 Millisekunden in einer Schleife für jeden der drei Motoren:

  • Soll-Position Motor feststellen ("Splines" X Y Z)
  • Vergleich mit Ist-Position Motor
  • ggf. 1 Step für den Motor ausführen und Ist-Position um +1 oder -1 anpassen, je nachdem in welche Richtung gesteppt wurde

Das einzige Problem an der Sache, das ich sehe, ist die Geschwindigkeit: Wenn Du mit maximal 500 Schritten pro Motor pro Sekunde steppst, dann darf die Funktion "doMotorSteps();" niemals länger als 2 ms laufen. Am längsten dürfte die Feststellung der drei Sollpositionen aus den drei Splines dauern.

Die Frage ist also: Bekommst Du für jede Zeit t die drei entsprechenden Stepper-Positionen X, Y, Z innerhalb von weniger als 2 ms?

Wenn ja, dann sollte es einfach sein, die Stepper mit Einzelschritten alle 2 ms und einer Stepprate von maximal 500 pro Sekunde zu steuern.

Wenn nein, wird es komplizierter und/oder Du mußt Abstriche an der Genauigkeit machen. Statt "Splines mit 5 Stützpunkten" zu berechnen, könntest Du beispielsweise "4 Geradenabschnitte zwischen 5 Punkten" berechnen, was nur eine einfache Berechnung einer linearen Interpolation zwischen zwei Punkten erfordert. Da sind wahrscheinlich sogar 1000 oder noch mehr Stepschritte pro Sekunde drin. Aber dann hättest Du als Verfahrkurven eben nicht mehr die Splines, sondern direkt verbundene gerade Strecken zwischen den Stützpunkten.

Wobei es mit dem Arduino Due auch einen Arduino gibt, der um ein vielfaches schneller taktet als die Atmegas auf UNO und MEGA Boards.

Es darf dann eben nur innerhalb der loop nichts anderes ablaufen, was den Sketch "bremst", sonst kommen die Abläufe mit den Motoren ins Stottern oder Stocken.

Außerdem habe ich mal eine Rückfrage wegen des Diagramms, wie schnell das alles ablaufen soll, das kann ich aus dem Diagramm nicht erkennen, da an der Zeitachse keine Einheit dransteht.

Also im Diagramm die Werte für X, Y, Z sind "Step-Schritte" und was sind die Zahlen auf der Zeitachse? Doch wohl hoffentlich nicht Millisekunden, d.h. der Ablauf im Diagramm soll innerhalb von 1000 ms = 1s ablaufen? Sondern irgendwie [Millisekunden*10] oder [Zehntelsekunden] als Einheit für die Zeitachse nach rechts aufgetragen, oder?

Und was ist mit den Motoren: Wird eine "Beschleunigungsphase" tatsächlich benötigt, oder können die Motoren vielleicht auch mit konstanter Geschwindigkeit laufen? Und wie viele Steps pro Sekunde müssen maximal berechnet und angesteuert werden?

Danke für die Antwort.

Also zunächst einmal:

Das Diagramm ist jetzt für den Intervallmodus, d.h. ich mache 1000 Bilder(X-Achse). Die komplette Bewegung wird also auf 1000 Zwischenbewegungen aufgeteilt. Also Bild machen-->Motoren bewegen sich ein Stückchen-->Pause-->Bild machen-->...

Die Schrittmotoren laufen mit 1/16 Schritten, zusammen mit dem 1:50 Getriebe brauche ich also 160.000 Schritte für eine komplette 360°-Drehung um die horizontale Achse. Mit meinem aktuellen Programme brauche ich für die komplette Umdrehung etwa 15 Min., was deutlich zu lange ist. Auf 1/8 oder 1/4-Schritte möchte ich nicht gehen, da es sonst zu sehr vibriert.

Beim Videomodus siehst du evtl., dass ich die X-Zeit nicht über die Zeit fahren lassen, sondern über die Bewegung der Drehachse(X). P1, P2,P3 und P4 sind also die absoluten Positionen (in Schritten) der horizontalen Drehachse.

Der Worst-Case wäre eine komplette 360°-Drehung, also 160.000 Schritte, dafür peile ich etwa 30 Sekunden an, was also ca. 5300 Schrittberechnungen pro Sekunde pro Achse entspricht, und da ist der Arduino deutlich überfordert. Daher habe ich auch an die Möglichkeit gedacht, die ganzen Werte nicht in Echtzeit zu berechnen, sondern im Voraus und dann später einfach abzufahren. Ein Array kommt nicht in Frage, da die Speicherkapazität des Arduino viel zu klein ist. Des Weiteren hatte ich überlegt die Werte evtl. in eine txt-Datei auf einer MicroSD-Karte zu speichern, jedoch weiß ich nicht, wie schnell der Arduino bei Auslesen ist.

BlueGene:
also 160.000 Schritte, dafür peile ich etwa 30 Sekunden an, was also ca. 5300 Schrittberechnungen pro Sekunde pro Achse entspricht

Das ist ja eine Menge Holz!

Irgendwie hast Du Dir da ein extrem anspruchsvolles Projekt ausgedacht.

Bei der hohen Schrittzahl pro Sekunde gehen mir die Ideen aus, es alles komplett mit einem Arduino zu berechnen und anzusteuern.

Hab jetzt mal ein wenig rumgespielt und Accelstepper zusammen mit den Splines und allen 3 Motoren hinbekommen:

#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();
  }

}

Wenn ich die Auflösung (n) erhöhe, wird es spürbar langsam. Ich habe es auch mit dem chipKIT u32(80MHz) probiert, jedoch gibt es da Komplikationen mit der Accelstepper und der Splines-Library, sodass ich es nicht zum Laufen bekommen habe. Ich werde mir demnächst einen Due anschaffen und es damit probieren.

BlueGene:
Hab jetzt mal ein wenig rumgespielt und Accelstepper zusammen mit den Splines und allen 3 Motoren hinbekommen:

Das dürfte aber nur mäßig synchron laufen. Zwar wird an jedem Zwischenpunkt synchron gestartet, aber wenn in einem Zwischenschritt ein Motor nur sehr wenige Schritte machen muß und ein anderer sehr viele, dann ist der Motor mit wenigen Schritten viel früher mit seiner Stellbewegung fertig und wartet auf die langsameren zwei

    if (stepperX.distanceToGo() == 0 && stepperY.distanceToGo() == 0 && stepperZ.distanceToGo() == 0){

um erst danach wieder zum nächsten Zwischenpunkt zu starten.

Wenn es sehr viele Zwischenpunkte gibt, dann verschleift sich die Nichtsynchronität zwischen den Einzelpunkten zwar, aber das System wird extrem langsam, weil es

  • von jedem Zwischenpunkt aus mit Geschwindigkeit 0 startet
  • zu jedem Zwischenpunkt hin auf Geschwindigkeit 0 runterbremst
    Und je mehr Zwischenpunkte es gibt, um so mehr Punkte mit Nullgeschwindigkeit gibt es.

Das mit der Accelstepper Library kann also nicht der Weisheit letzter Schluss sein:
Wenn es wenig Zwischenpunkte gibt, werden diese zügig angefahren, aber es herrscht hohe Nichtsynchronität
Wenn es viele Zwischenpunkte gibt, verbringt das System viel Zeit bei Verfahrgeschwindigkeiten nahe Null.

Hallo Jurs!

jurs:
Ich würde die für Deine Steuerungszwecke unpassenden Libraries vergessen und stattdessen alle drei Schrittmotoren mit Einzelschritten direkt ansteuern, ohne fremde Library.

Die Verfahrwege in Abhängigkeit von der Zeit hast Du doch in Form der Splines vorliegen

x= f1(t)
y= f2(t)
z= f3(t)

Die Zeit t läuft für alle Motoren gleichmäßig nach vorne genau wie der millis() Zähler auf dem Arduino.

Jetzt mußt Du die Höchstgeschwindigkeit der Steppermotoren wissen, "Steps pro Sekunde", und auf diese maximale Anzahl "Steps pro Sekunde", die die Motoren steppen können, mußt Du die loop synchronisieren.

Mal angenommen, die Motoren können maximal 500 Schritte pro Sekunde ausführen, dann muß die loop-Funktion alle 2 ms eine Funktion aufrufen, die möglicherweise (oder auch nicht) einen Stepschritt an den drei Motoren vornimmt.

Das klappt aber nur, wenn die Motoren mit konstanter Geschwindigkeit laufen. Und dann bräuchte man keine Splines zu berechnen... 500 Schritte/s wäre auch arg wenig.

Die geforderten 5kHz sind auf einem AVR garkein Problem. Ich habe seit Jahren auf einem Mega128 eine Lösung mit Beschleunigungsrampen implementiert, die 2 Achsen mit bis zu 50kHz ansteuern kann. Allerdings läuft da jeder Stepper mit einem eigenen Timer, so dass man das nicht ohne weiteres auf 4 Achsen aufbohren kann.

Eine etwas einfachere Variante wäre es, den Bresenham-Algoritmus auf 4 Achsen zu erweitern und mit einem gemeinsamen Timer für alle Achsen zu arbeiten. Der müsste aber schon mit 50-100kHz laufen, damit die einzelnen Achsen nicht durch zu großen Jitter aus dem Tritt kommen können. Bei ~5kHz Schrittrate und 50kHz Timer kann es ja im worst case passieren, dass alle 200µs ein Schritt gemacht wird und dann mal einer ausgelassen werden muss, d.h. der nächste Schritt kommt dann erst nach 220µs. Das wären immerhin 10% Jitter, bei 100kHz Timertakt schon nur noch 5%.

Mit freundlichen Grüßen
Thorsten Ostermann