Schrittmotor mit Arduino

Hallo zusammen,

wir möchten einen Schrittmotor Nema34 mit einem Motortreiber HB860H über
einen Arduino ansteuern. Die Stromversorgung erfolgt über ein 400W
Netzteil (Spannung 70V, Strom 5.7A).

Ohne Last im Leerlauf funktioniert die Ansteuerung ohne weitere
Probleme. Sobald er allerdings in der Anlage verbaut ist, funktioniert
es entweder nur mit starkem Ruckeln, oder der Motortreiber geht sofort
in Störung nach dem Starten. Dabei kommt für 3 Sekunden allerdings erst
noch ein kurzes Geräusch vom Motor. Manchmal taucht die Störung auch
erst nach einigen Durchläufen auf.

Es handelt sich um einen Sägeanschlag. Die Referenzfahrt funktioniert
hierbei nur beim Starten im hinteren Drittel. Im vorderen Bereich
funktioniert es nur mit manueller Anschieb-Hilfe.

Wir sind etwas verwirrt, da es im Leerlauf funktioniert, und erst unter
leichtgängiger / meiner Meinung nach vernachlässigbarer Last zu diesen
Problemen kommt.

Was könnte eurer Meinung nach die Ursache dafür sein?

Nachfolgend das Arduino-Programm:

#include <Keypad.h>
#include <LiquidCrystal.h>
#include <AccelStepper.h>

//--- Pin-Belegungen

const int rs = 0, en = 1, d4 = 2, d5 = 3, d6 = 4, d7 = 5; //Pin-Belegung für LCD
const int puls = 9, direct = 8;                           //Pin-Belegung für Motor
const int bremse = 6;                                     //Pin-Belegung für Bremse
const int referenzSensor = 7;                             //Pin-Belegung für Referenzsensor
const int saegeSensor = 10;                               //Pin-Belegung für Sägemotor
const int schraubstockSensor = 11;                        //Pin-Belegung für Schraubstock
const byte ROWS = 4;
const byte COLS = 4;
byte rowPins[ROWS] = {A0, A1, A2, A3};                    //Pin-Belegung für Keypad (1-4, von links nach rechts)
byte colPins[COLS] = {A4, A5, 12, 13};                    //Pin-Belegung für Keypad (5-8, von links nach rechts)

//--- Konstanten

const float lengthPerStep = 0.11f;                        //Länge pro Schritt
const float referenz = 3093;                              //Referenzposition nach der Referenzfahrt
const float extraLaenge = 10;                             //Extra-Länge bei der Schleifenfahrt
const float motorSpeed = 800;

//--- ab hier Programm ---

int mode = 0;
char eingabe[16];
long stelle = 0;
float laengeEingabe = 0;
float difLaenge = 0;
float pos = 0;
char posAnzeige[16];
long stepsNeeded = 0;
boolean saegeOff;

AccelStepper stepper(AccelStepper::DRIVER, puls, direct);
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);


char keys[ROWS][COLS] = {   //Zu entsprechende Tasten vom Keypad (* wurde bereits mit . ausgetauscht)
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'.', '0', '#', 'D'}
};


Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {
  pinMode(bremse, OUTPUT);
  pinMode(referenzSensor, INPUT);
  pinMode(saegeSensor, INPUT);
  pinMode(schraubstockSensor, INPUT);

  
  lcd.begin(16, 2);

  lcd.print("Referenzfahrt");

  stepper.setSpeed(100);
  stepper.setAcceleration(750);

  referenzfahrt();

  stepper.setSpeed(motorSpeed);

  lcd.clear();
  lcd.print("Eingabe:");
  lcd.setCursor(0, 1);
  mode = 1;
}

void loop() {
  char key = keypad.getKey();

  if (digitalRead(saegeSensor) == LOW) {
    saegeOff = true;
  }

  if (digitalRead(saegeSensor) == HIGH && mode != 4 && saegeOff) {
    saegeOff = false;
    mode = 4;
    lcd.clear();
    lcd.print("Freifahrt");

    digitalWrite(bremse, HIGH);
    stepper.move(lengthToSteps(40));
    stepper.runToPosition();
    digitalWrite(bremse, LOW);
  }

  if (digitalRead(schraubstockSensor) == HIGH && mode == 4 && mode != 5) {
    mode = 5;
    lcd.clear();
    lcd.print("Zurückfahren");

    digitalWrite(bremse, HIGH);
    stepper.move(lengthToSteps(-40));
    stepper.runToPosition();
    digitalWrite(bremse, LOW);

    lcd.clear();
    lcd.print("Eingabe:");
    lcd.setCursor(0, 1);
    memset(eingabe, 0, sizeof eingabe);
    stelle = 0;
    mode = 1;
  }


  if (mode == 1 && key != '\0') {
    if (key != 'A' && key != 'B' && key != 'C' && key != 'D' && key != '#' && stelle < 15) {    
      lcd.print(key);
      eingabe[stelle] = key;
      stelle++;
    }

    if (key == 'B') {    
        dtostrf(pos, 9, 4, posAnzeige);
    
        lcd.clear();
        lcd.print("Position:");
        lcd.setCursor(0, 1);
        lcd.print(posAnzeige);
        delay (2000);
        lcd.clear();
        lcd.print("Eingabe:");
        lcd.setCursor(0, 1);
        memset(eingabe, 0, sizeof eingabe);
        stelle = 0;
        mode = 1;
    }

    if (key == '#' && stelle > 0) {
      eingabe[stelle] = NULL;
      stelle--;
      lcd.setCursor(stelle,1);
      lcd.print(' ');
      lcd.setCursor(stelle,1);
    }

    if (key == 'D' && stelle > 0) {     
      laengeEingabe = atof(eingabe);

      if (laengeEingabe < 400 || laengeEingabe > 3100) {
        lcd.clear();
        lcd.print("Nur 400 - 3100");
        delay (2000);
        lcd.clear();
        lcd.print("Eingabe:");
        lcd.setCursor(0, 1);
        memset(eingabe, 0, sizeof eingabe);
        stelle = 0;
        mode = 1;
      }
      else {
        difLaenge = pos - laengeEingabe;
        mode = 2;
      }

    }
  }

  if (mode == 2) {
    if (difLaenge > 0) {
      stepsNeeded = lengthToSteps(difLaenge);
      digitalWrite(bremse, HIGH);
      stepper.move(stepsNeeded);
      stepper.runToPosition();
      digitalWrite(bremse, LOW);
      //stepper.step(stepsNeeded);
      /*for (int i = 0; i < stepsNeeded; i++) {
        stepper.step(1);          //ANPASSEN
      }*/

      pos = pos - stepsToLength(stepsNeeded);
    }

    if (difLaenge < 0) {
      stepsNeeded = lengthToSteps(-difLaenge);
      long extraSteps = lengthToSteps(extraLaenge);
      digitalWrite(bremse, HIGH);
      stepper.move(-(stepsNeeded + extraSteps));
      stepper.runToPosition();
      stepper.move(extraSteps);
      stepper.runToPosition();
      digitalWrite(bremse, LOW);

      pos = pos + stepsToLength(stepsNeeded);
    }

    dtostrf(pos, 9, 4, posAnzeige);

    lcd.clear();
    lcd.print("Position:");
    lcd.setCursor(0, 1);
    lcd.print(posAnzeige);
    mode = 3;
  }

  if (mode == 3 && key == 'A') {
    lcd.clear();
    lcd.print("Eingabe:");
    lcd.setCursor(0, 1);
    memset(eingabe, 0, sizeof eingabe);
    stelle = 0;
    mode = 1;
  }
}

void referenzfahrt() {
  digitalWrite(bremse, HIGH);
  while (digitalRead(referenzSensor) == LOW) {
    stepper.move(-500);
    stepper.runSpeedToPosition();
  }
  /*stepper.move(200);        //Testweise rauskommentiert
  stepper.runToPosition();


  stepper.setSpeed(50);
  while (digitalRead(referenzSensor) == LOW) {
    stepper.move(-500);
    stepper.runSpeedToPosition();
  }*/
  stepper.setCurrentPosition(0);
  digitalWrite(bremse, LOW);
  
  pos = referenz;
}

long lengthToSteps(float laenge) {
  long steps = laenge / lengthPerStep;
  return steps;
}

float stepsToLength(long steps) {
  float laenge = steps * lengthPerStep;
  return laenge;
}

Noch mehr Foren?

Hallo
meine erster Tip:
Energieversorgung.

Danke für die Antwort!

Mit verringerter Rampe sind die gleichen Probleme aufgetreten, würde es eventuell etwas bringen, im Motortreiber den Strom zu begrenzen? Haltemoment hat er definitiv genug, dafür reicht die Spannungsversorgung.

Hallo
Hast Du kein Messgerät um alle notwendigen Messungen an der Energieversorgung vorzunehmen?

welcher Strom ist für den Motor angegeben? Welcher Strom ist am Treiber eingestellt. 5,7 Ampere finde ich ein wenig schwach für das Netzteil. Wie ist der Motor angeschlossen?

Sagt etwas über die mechanische Größe, aber nichts über die elektrischen Werte.

Keine gute Idee, denn lt. Doku ...

blocks until it is at position

Mir fehlt stepper.run(); und stepper.move(); oder stepper.move();

Bedeutet Blockade und damit Kontrollverlust. Daher möchte ich mit Eurer Maschine lieber nicht im selben Raum sein wegen Unfallgefahr!

Danke dir für die Hinweise!

Da wir außer bei der Referenzfahrt mit Beschleunigung arbeiten wollen, habe ich in der Tat immer die Kombi:

stepper.move(stepsNeeded);
stepper.runToPosition();

verwendet. Das stepper.runToPosition() verwendet dabei die Werte vom zuletzt ausgeführten stepper.move(relative Position in Schritten). Dadurch blockiert bzw. stoppt das Programm auch an der Stelle (meiner Meinung nach auch wie gewünscht).
Wie gesagt - ohne Last läuft das Programm auch wie gewünscht durch inklusive Eingaben und Ausgaben auf dem LCD. Ich mach das aber auch ehrlich gesagt zum ersten Mal, ich bin mir sicher, dass es bessere Alternativen dafür gibt. Kannst du mir evtl. ein passendes Beispiel geben?

Danke für die Info mit dem delay(). Ich habe es (unbedacht) genutzt, um einfach die Ausgabe auf dem LCD zu verzögern. Was wäre eine passende Alternative an dieser Stelle?

Ja, wir werden morgen mit Messgerät dran und schauen, woran es liegt. Zusätzlich wollen wir noch evtl. die Parameter auf dem Motortreiber anpassen. Ich wollte heute noch möglichst Probleme mit dem Programm ausschließen im Vorfeld.

Ja, das ist in den Beispielen auch so dargestellt. Da geht es um eine einfache Bewegung, Ihr wollt aber eine Maschine bauen, das ist eine andere Liga!

Blockade heißt, mit geschlossenen Augen Auto - oder was auch immer - fahren. Das willst Du nicht, auch nicht mit einer Maschine. Beispielsweise kann der µC derweil keine Tastendrücke erkennen, also auch nicht den Druck auf die Stoptaste, den Bereichssensor und dergleichen.

Das Beispiel Bounce.pde kommt einer Fahrt mit geöffneten Augen nahe, weil man da in loop() noch Tasten und Sensoren abfragen könnte.

Weiß ich auch nicht, da ich Eure Anwendung nicht kenne, aber eine Alternative wären die MobaTools, weil die Bewegungen da per Interrupt abgearbeitet werden. Da gibt es das Beispiel Stepper_Slider.ino, an dem ich mitgewirkt habe.

Die grundsätzlichen Anforderungen an die Programmierung sind aber vergleichbar.

Das Problem hatte tatsächlich mit der Verkabelung zu tun, mittlerweile läuft es auch nahezu wie gedacht.

Nichtsdestotrotz bin ich auf die MobaTools gegangen, vielen Dank für den Tipp! Es funktioniert sehr gut damit. Allerdings haben wir das Problem, dass es nach mehrmaligen Fahrten zu Abweichungen kommt, welche sich aufaddieren. Es ist fast so, als würde er Schritte "verschlucken". Wir haben es pfusch-mäßig leider erstmal so gelöst, dass wir zusätzliche Schritte ausführen lassen, um die Abweichungen auch erfolgreich zu minimieren. Es ist natürlich keine zufriedenstellende Lösung. Sind da Probleme bekannt?

Danke im Voraus! :slight_smile:

Von den MobaTools her sind da bisher keine Probleme bekannt ( zumindest mir noch nicht zu Ohren gekommen :wink: ). Schrittverluste sind meist eher ein mechanisches Problem.
Aber ohne den aktuellen Sketch zu kennen kann man auch nichts weiter sagen.

Danke für die schnelle Antwort!
Das Programm vor den Anpassungen der zusätzlichen Schritte ist das:

#include <Keypad.h>
#include <LiquidCrystal.h>
#include <MobaTools.h>

//--- Pin-Belegungen

const int rs = 0, en = 1, d4 = 2, d5 = 3, d6 = 4, d7 = 5; //Pin-Belegung für LCD
const int puls = 9, direct = 8;                           //Pin-Belegung für Motor
const int bremse = 6;                                     //Pin-Belegung für Bremse
const int referenzSensor = 7;                             //Pin-Belegung für Referenzsensor
const int saegeSensor = 10;                               //Pin-Belegung für Sägemotor
const int schraubstockSensor = 11;                        //Pin-Belegung für Schraubstock
const byte ROWS = 4;
const byte COLS = 4;
byte rowPins[ROWS] = {A0, A1, A2, A3};                    //Pin-Belegung für Keypad (1-4, von links nach rechts)
byte colPins[COLS] = {A4, A5, 12, 13};                    //Pin-Belegung für Keypad (5-8, von links nach rechts)

//--- Konstanten

const int stepsUmdrehung = 1600;                          //Anzahl Schritte pro Umdrehung
const float lengthPerStep = 0.11f;                        //Länge pro Schritt
const float referenz = 3105.53f;                          //Referenzposition nach der Referenzfahrt
const float extraLaenge = 5;                              //Extra-Länge bei der Schleifenfahrt
const float motorSpeed = 1200;

//--- ab hier Programm ---

int mode = 0;
char eingabe[16];
long stelle = 0;
float laengeEingabe = 0;
float difLaenge = 0;
float pos = 0;
char posAnzeige[16];
long stepsNeeded = 0;
float actualStepsNeeded = 0;
boolean saegeOff;

MoToStepper myStepper( stepsUmdrehung, STEPDIR );

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);


char keys[ROWS][COLS] = {   //Zu entsprechende Tasten vom Keypad (* wurde bereits mit . ausgetauscht)
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'.', '0', '#', 'D'}
};


Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {
  pinMode(bremse, OUTPUT);
  pinMode(referenzSensor, INPUT);
  pinMode(saegeSensor, INPUT);
  pinMode(schraubstockSensor, INPUT);

  myStepper.attach( puls, direct );
  
  lcd.begin(16, 2);

  lcd.print("Referenzfahrt");

  myStepper.setSpeed(motorSpeed);
  myStepper.setRampLen(0);

  referenzfahrt();

  myStepper.setSpeed(motorSpeed);
  myStepper.setRampLen(200);
  
  lcd.clear();
  lcd.print("Eingabe:");
  lcd.setCursor(0, 1);
  mode = 1;
}

void loop() {
  char key = keypad.getKey();

  if (digitalRead(saegeSensor) == LOW) {
    saegeOff = true;
  }

  if (digitalRead(saegeSensor) == HIGH && mode != 4 && saegeOff) {          //Freifahrt
    saegeOff = false;
    mode = 4;
    lcd.clear();
    lcd.print("Freifahrt");

    digitalWrite(bremse, HIGH);
    myStepper.doSteps(-100);
    while (myStepper.moving() );
    digitalWrite(bremse, LOW);
  }

  if (digitalRead(schraubstockSensor) == HIGH && mode == 4 && mode != 5) {   //Zurückfahren
    mode = 5;
    lcd.clear();
    lcd.print("Zurückfahren");

    digitalWrite(bremse, HIGH);
    myStepper.doSteps(100);
    while (myStepper.moving() );
    digitalWrite(bremse, LOW);

    dtostrf(pos, 6, 1, posAnzeige);

    lcd.clear();
    lcd.print("Position:");
    lcd.setCursor(0, 1);
    lcd.print(posAnzeige);
    mode = 3;
  }


  if (mode == 1 && key != '\0') {
    if (key != 'A' && key != 'B' && key != 'C' && key != 'D' && key != '#' && stelle < 15) {    
      lcd.print(key);
      eingabe[stelle] = key;
      stelle++;
    }

    if (key == 'B') {    
      dtostrf(pos, 6, 1, posAnzeige);
  
      lcd.clear();
      lcd.print("Position:");
      lcd.setCursor(0, 1);
      lcd.print(posAnzeige);
      delay (2000);
      lcd.clear();
      lcd.print("Eingabe:");
      lcd.setCursor(0, 1);
      memset(eingabe, 0, sizeof eingabe);
      stelle = 0;
      mode = 1;
    }

    if (key == '#' && stelle > 0) {
      eingabe[stelle] = NULL;
      stelle--;
      lcd.setCursor(stelle,1);
      lcd.print(' ');
      lcd.setCursor(stelle,1);
    }

    if (key == 'D' && stelle > 0) {     
      laengeEingabe = atof(eingabe);

      if (laengeEingabe < 400 || laengeEingabe > 3100) {
        lcd.clear();
        lcd.print("Nur 400 - 3100");
        delay (2000);
        lcd.clear();
        lcd.print("Eingabe:");
        lcd.setCursor(0, 1);
        memset(eingabe, 0, sizeof eingabe);
        stelle = 0;
        mode = 1;
      }
      else {
        difLaenge = pos - laengeEingabe;
        mode = 2;
      }

    }
  }

  if (mode == 2) {
    if (difLaenge > 0) {
      stepsNeeded = lengthToStepsLong(difLaenge);
      actualStepsNeeded = lengthToStepsFloat(difLaenge);
      
      digitalWrite(bremse, HIGH);
      myStepper.doSteps(stepsNeeded);
      while (myStepper.moving() );
      digitalWrite(bremse, LOW);

      pos = pos - stepsToLength(actualStepsNeeded);
    }

    if (difLaenge < 0) {
      stepsNeeded = lengthToStepsLong(-difLaenge);
      actualStepsNeeded = lengthToStepsFloat(-difLaenge);
      long extraSteps = lengthToStepsLong(extraLaenge);

      digitalWrite(bremse, HIGH);
      myStepper.doSteps(-(stepsNeeded + 50));
      while (myStepper.moving() );
      myStepper.doSteps(50);
      while (myStepper.moving() );      
      digitalWrite(bremse, LOW);

      pos = pos + stepsToLength(actualStepsNeeded);
    }

    dtostrf(pos, 6, 1, posAnzeige);

    lcd.clear();
    lcd.print("Position:");
    lcd.setCursor(0, 1);
    lcd.print(posAnzeige);
    mode = 3;
  }

  if (mode == 3 && key == 'A') {
    lcd.clear();
    lcd.print("Eingabe:");
    lcd.setCursor(0, 1);
    memset(eingabe, 0, sizeof eingabe);
    stelle = 0;
    mode = 1;
  }
}

void referenzfahrt() {

  myStepper.setSpeed(50);

  digitalWrite(bremse, HIGH);
  myStepper.doSteps(100);
  while ( myStepper.moving() );
  myStepper.setSpeed(motorSpeed);
  myStepper.rotate(-1);
  while (digitalRead( referenzSensor ) == LOW);
  myStepper.rotate(0);
  while ( myStepper.moving() );
  myStepper.doSteps(-200);
  while ( myStepper.moving() );
  myStepper.setSpeed(50);
  myStepper.rotate(1);
  while (digitalRead( referenzSensor ) == LOW);
  myStepper.rotate(0);    
  while ( myStepper.moving() );        
  digitalWrite(bremse, LOW);

  myStepper.setZero();
  pos = referenz;
}

long lengthToStepsLong(float laenge) {
  long steps = laenge / lengthPerStep;
  return steps;
}

long lengthToStepsFloat(float laenge) {
  float difLaenge = laenge / lengthPerStep;
  return difLaenge;
}

float stepsToLength(long steps) {
  float laenge = steps * lengthPerStep;
  return laenge;
}

Das Problem wird bei der Freifahrt und Zurückfahrt deutlich. Wenn er 100 Schritte vor- und dann wieder zurückfährt, ist er nicht an der gleichen Stelle.

Die Bremse steht zwar mit drinne (wo selbstverständlich dann noch ein delay hinzukommen müsste), ist aber im Moment noch nicht verbaut, hat also keine Auswirkungen.

Was mir zunächst mal aufgefallen ist, dass Du nur mit dpSteps() arbeitest. Das ist etwas fehlerträchtig - zumindest können dir da die MobaTools ncht helfen, immer exakt zu positionieren. Wenn Du deine Zielposition pos bestimmt hast, kannst Du doch den MobaTools einfach sagen, er soll dorthin fahren writeSteps( lengthToStepsLong(pos) )
Das hin-und herrechnen zwischen float und long macht es nicht genauer. Berechne deine Zielposition immer in float, und zum Positionieren einmal in steps umrechnen und an myStepper.writeSteps() übergeben. Die notwendigen Schritte um von der aktuellen Positon zur Zielposition zu kommen berechnen die MobaTools.

Warum beim Freifahren/Zurückfahren nicht die Ausgangsposition erreicht wird, kann ich so nicht erkennen. Wenn sich die MobaTools schon bei so kleinen Schrittzahlen verzählen würden, wäre das sicher schon aufgefallen.

P.S. Das hier:

const int stepsUmdrehung = 1600;                          //Anzahl Schritte pro Umdrehung
...
const float motorSpeed = 1200;
...
    myStepper.setSpeed(motorSpeed);        / /  = 120 U/Min

führt zu Stepraten, die die MobaTools auf einem AVR standardmäßig nicht erreichen können, und auf 2500 Steps/Sec begrenzt werden. Wenn Du wirklich die 3200Steps/sec benötigst, musst Du in MobaTools.h etwas 'tunen'.

P.P.S. Du liebst die blockierende Programmierung. Bei so einer Maschine halte ich das für keine gute Idee. Da sollte das System jederzeit auf Sensorsignale reagieren können.