Nema 17 Schrittverlust

Hallo Leute,

Für eine Linearsteuerung verwende ich einen Nema 17 Schrittmotor und ein Tb6600 Controller zur Steuerung der Schritte. Da ich verschiedene Positionen gerne so schnell wie möglich ansteuern möchte und auch keine Schrittverluste bei der Anfahrt bekommen möchte, verwende ich die Accelstepper library.
Trotz verschiedener Einstellungen wie zb. Halbschritte, Viertelschritte und verschiedene Accelstepper Einstellungen habe ich bei jeder Fahrt minimale Abweichungen bei den Positionen d.h fahr ich eine Position 2 mal an ist die Abweichung jedes mal min 1-2mm.

Wie kann ich diese Schrittverluste verhindern ohne etwas an der Geschwindigkeit zu ändern?

Für eure Antworten bedanke ich mich schonmal

Gruß Mike

Was ich vergessen habe:

Die Abweichungen bei den Positionen addieren sich bei jeder Fahrt, sodass die Abweichungen nach ein paar Fahrten zu groß werden.

Hi

Wenn Es immer die gleiche Abweichung ist - fährst Du vll. eine andere Strecke zurück, als hin?

Nema 17 ist das Lochbild/die Größe des Motor.
Über die technischen Daten sagt Das NICHTS.

Wie sieht Dein Aufbau aus?
Was ist womit wie verbunden, was ist wo angeschlossen?
Womit versorgst Du die Stepper, womit den Arduino?
Ist der Treiber auf den Stepper eingestellt?
Hast Du zufällig auch einen Sketch, Den Du uns zeigen könntest?

... so Kleinigkeiten halt, Die wir nicht wissen können ...

MfG

Du hast uns keine rilevanten Daten gegeben; weder die elektrischen Daten des Motors, noch die Einstellungen /Versorgungsspannung des Treibers noch den Sketch.

Hast Du schon mal versucht ohne die Accelstepper library den Motor anzusteuern und einfach langsam x Schritte (Impulse) vor und zurück gemacht n mal wiederholt?

Grüße Uwe

Hallo,

Ich hab eine externe 12V 20A Spannungsversorgung, die die Tb6600 Treiber versorgen. Der Arduino wird über das USB Kabel versorgt. Die Schrittmotoren sind an die Treiber angeschlossen mit einer Strombegrenzung von 2A.
Zum Test fahre ich immer die Positionen 2000 und 8000 an, heißt er sollte 6000 Schritte vor und zurück machen, was er auch tut, jedoch mit der minimalen Abweichung jedes Mal. Zum Start definiere ich jedes Mal einen Nullpunkt mit Hilfe eines Endschalters, welcher jedoch nach einigen durchläufen gerade noch so berührt wird.

Zu den Treiber Daten:

Input Current:
0~5A
Output Current:
0.5~4.0A
Control Signal
3.3~24V
Power (MAX):
160W
Micro Step:
1, 2/A, 2/B, 4, 8, 16, 32
Temperature:
-10~45℃

Zum Sketch:

#include <AccelStepper.h>

#define home_switch 12
#define MaxSpeedX 1500
#define MaxAccelX 3000


// Define some steppers and the pins the will use
AccelStepper stepper1(1,9,8);   //Step,dir
AccelStepper stepper2(1,6,5);

long TravelX = -1;
long TravelY = -1;
float aktuellePosX;
float aktuellePosY;
char Zahl = 0;
double rate = 0.145;
float summe = 0;
int counter=0;
int Eins[6]={6000,1000,13500,20000,10500,500};
short groesseEins = 6;
int Zwei[]={8000,6500,500};
short groesseZwei = 3;
int Drei[]={10000,9000,5500,3000,7500,500};
short groesseDrei = 6;
long initial_homingX=-1;

void homingX();

void setup()
{  
   pinMode(home_switch,INPUT);

   stepper1.setMaxSpeed(MaxSpeedX);

   stepper1.setAcceleration(MaxAccelX);

   stepper2.setMaxSpeed(3000);

   stepper2.setAcceleration(3000);

   Serial.begin(9600);

   //homingX();
   delay(1000);

   Serial.println("Machine is ready to operate!");
   Serial.println("Press 1 for X");
   Serial.println("Press 2 for Y");
   Serial.println("Press 3 for Z");
   Serial.println("Press 4 to home the machine");
   float aktuellePosX = 1;
   float aktuellePosY = 1;
}

void loop()

{
 while (Serial.available()>0)  {
 //Zahl=Serial.read();
 Zahl = Serial.parseInt();
 
 switch (Zahl)
 {
   case 1: {
             Serial.println(sizeof(Eins));
             for(int i=0;i<groesseEins;i++)
             {
             //TravelY = 2500;
             TravelX = Eins[i];
             //rate= (abs(aktuellePosY-TravelY)/abs(aktuellePosX-TravelX));
             aktuellePosX = TravelX;
             Serial.print("Position ");
             Serial.print(i);
             Serial.print(":");
             Serial.println(Eins[i]);
             //aktuellePosY = TravelY;
             stepper1.moveTo(TravelX);
               while(stepper1.distanceToGo() != 0){
               stepper1.run();
               }
               delay(2000); 
             }
             /*while(stepper1.distanceToGo() != 0 && stepper2.distanceToGo() != 0)
             if(summe >= 10)
             {
               summe=summe-1;
               if ((stepper1.distanceToGo() != 0)) {
               stepper1.run();  // Move Stepper into position
               }
               if ((stepper2.distanceToGo() != 0)) { 
               stepper2.run();  // Move Stepper into position
               }
             }else
             {
               summe=summe+rate;
               if ((stepper1.distanceToGo() != 0)) {
               stepper1.run();  // Move Stepper into position
               }  
             }
             summe= 0 ;*/
             break;
           }
   case 2: {
             for(int i=0;i<groesseZwei;i++)
             {
             //TravelY = 2500;
             TravelX = Zwei[i];
             //rate= (abs(aktuellePosY-TravelY)/abs(aktuellePosX-TravelX));
             aktuellePosX = TravelX;
             //aktuellePosY = TravelY;
             stepper1.moveTo(TravelX);
             while(stepper1.distanceToGo() != 0){
               stepper1.run();
               }
               delay(2000); 
             }
             break;
           }
   case 3: {
             for(int i=0;i<groesseDrei;i++)
             {
             //TravelY = 2500;
             TravelX = Drei[i];
             //rate= (abs(aktuellePosY-TravelY)/abs(aktuellePosX-TravelX));
             aktuellePosX = TravelX;
             //aktuellePosY = TravelY;
             stepper1.moveTo(TravelX);
             while(stepper1.distanceToGo() != 0){
               stepper1.run();
               }
              delay(2000);  
             }
             break;
           }
   case 4: {
            homingX();
            Serial.println("homing completed");
            Serial.println("");
           }                     
 }
 Serial.println("Bereit");
 }
// Check if the Stepper has reached desired position
 if ((stepper1.distanceToGo() != 0)) {
   
   stepper1.run();  // Move Stepper into position
 }
 if ((stepper2.distanceToGo() != 0)) { 
   stepper2.run();  // Move Stepper into position
 }
}

void homingX()
{
 stepper1.setMaxSpeed(100.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
 stepper1.setAcceleration(100.0);  // Set Acceleration of Stepper


// Start Homing procedure of Stepper Motor at startup

 Serial.print("Stepper X is Homing . . . . . . . . . . . ");

 while (digitalRead(home_switch)) {  // Make the Stepper move CCW until the switch is activated   
   stepper1.moveTo(initial_homingX);  // Set the position to move to
   initial_homingX--;  // Decrease by 1 for next move if needed
   stepper1.run();  // Start moving the stepper
   delay(5);
}

 stepper1.setCurrentPosition(0);  // Set the current position as zero for now
 initial_homingX=1;

 while (!digitalRead(home_switch)) { // Make the Stepper move CW until the switch is deactivated
   stepper1.moveTo(initial_homingX);  
   stepper1.run();
   initial_homingX++;
   delay(5);
 }
 stepper1.moveTo(50);
 if ((stepper1.distanceToGo() != 0)) {
   
   stepper1.run();  // Move Stepper into position  
 }
 stepper1.setCurrentPosition(0);
 stepper1.setMaxSpeed(7000.0);      // Set Max Speed of Stepper (Faster for regular movements)
 stepper1.setAcceleration(4000.0);  // Set Acceleration of Stepper

}

hier ist der Fehler:
float aktuellePosX = 1;
float aktuellePosY = 1;

Und die Daten des Motors?

Grüße Uwe

uwefed:
hier ist der Fehler:
float aktuellePosX = 1;
float aktuellePosY = 1;

Und die Daten des Motors?

Grüße Uwe

Hallo Uwe,

warum ist da der Fehler? Die Variablen werden am Anfang der loop-Schleife immer neu definiert?

Und hier sind die Motordaten:

Schrittwinkel 1,8°
Schritte pro Umdrehung 200
Nennsstrom 1,7A/Phase
Nennspannung 3,7V
Haltemoment 4000gcm
Rastmoment 220g
cm
Rotorträgheit 54g*cm2
Spulenwiderstand 2,0 Ohm/Phase

Hi

Float ist nicht für seine absolute Genauigkeit bekannt - eher für 'so und so viele Stellen vor/nach dem Komma sind ok'.
Mit Float kannst Du mit sehr großen Zahlen, oder auch mit sehr kleinen Zahlen rechnen - Vergleich auf Gleichheit ist suboptimal.
Sofern Du keine Komma-Steps machst, nimm unsigned int oder int - bis zu 64bit sind möglich.
Serial.print 'kann' aber nur bis 32 Bit anzeigen (bzw. als DEZ ausgeben).
Mit uint32_t hättest Du immerhin 4294967295 Schritte - oder die Hälfte nach Vorne und nach Hinten.
Wenn Du auf die Ausgabe im Terminal verzichten kannst und eben mehr Schritte benötigst, int64_t wäre auch noch möglich, mit viel mehr Schritten in beiden Richtungen.

Wenn Du Komma-Steps ins Auge gefasst hast, erhöhe die Anzahl der Steps so hoch, daß Du auf Ganzzahlen kommst.
Also statt 1/4tel Schritt Step 0,25 Step 0,5 Step 0,75 Step 1 machst Du Step 1 ... 2 ... 3 ... 4.

Auch kann der µC mit INT-Werten wesentlich schneller rechnen, als mit Float.
Und Vergleiche mit INT sind 'klar' - die Werte sind als Bitmuster eindeutig vergleichbar.

MfG

PS: Deine oben genannten 2A Strombegrenzung waren eher eine Hausnummer, als definitiv so eingestellt? Bei 2A wirst Du bei diesem Motor nicht lange Freude haben (immerhin 17% Mehr)

Die Variablen werden am Anfang der loop-Schleife immer neu definiert?

Werden sie nicht!

Es gibt einen globalen Satz, welcher mit 0 initialisiert wird.
Und einen ungenutzten lokalen Satz in setup() welcher mit 1 initialisiertet wird.
Auch wenn sie den gleichen Bezeichner haben, sind es dennoch nicht die gleichen Variablen.

Also nochmal genau hinschauen!

Außerdem ist es unglücklich (bei Positionen von Schrittmotoren) mit float zu arbeiten.
Es drohen lustige Rundungsfehler, welche sich durchaus aufsummieren können.

postmaster-ino:
Hi

Float ist nicht für seine absolute Genauigkeit bekannt - eher für 'so und so viele Stellen vor/nach dem Komma sind ok'.
Mit Float kannst Du mit sehr großen Zahlen, oder auch mit sehr kleinen Zahlen rechnen - Vergleich auf Gleichheit ist suboptimal.
Sofern Du keine Komma-Steps machst, nimm unsigned int oder int - bis zu 64bit sind möglich.
Serial.print 'kann' aber nur bis 32 Bit anzeigen (bzw. als DEZ ausgeben).
Mit uint32_t hättest Du immerhin 4294967295 Schritte - oder die Hälfte nach Vorne und nach Hinten.
Wenn Du auf die Ausgabe im Terminal verzichten kannst und eben mehr Schritte benötigst, int64_t wäre auch noch möglich, mit viel mehr Schritten in beiden Richtungen.

Wenn Du Komma-Steps ins Auge gefasst hast, erhöhe die Anzahl der Steps so hoch, daß Du auf Ganzzahlen kommst.
Also statt 1/4tel Schritt Step 0,25 Step 0,5 Step 0,75 Step 1 machst Du Step 1 ... 2 ... 3 ... 4.

Auch kann der µC mit INT-Werten wesentlich schneller rechnen, als mit Float.
Und Vergleiche mit INT sind 'klar' - die Werte sind als Bitmuster eindeutig vergleichbar.

MfG

PS: Deine oben genannten 2A Strombegrenzung waren eher eine Hausnummer, als definitiv so eingestellt? Bei 2A wirst Du bei diesem Motor nicht lange Freude haben (immerhin 17% Mehr)

Hi,

Vielen Dank für die Antwort!
Ich habe hier float gewählt, weil ich (ist im Text ausgeklammert) die Schritte der X Achse berechnen möchte, die vergehen, bevor die Y-Achse ein Schritt machen soll. Das sollte eig dazu führen, dass die Achsen gleichzeitig losfahren und auch ankommen.

Die Schritte, die gemacht werden und auch ob 1/4, 1/8 oder 1/16 Schritte wird am Treiber eingestellt. Auf den Treiber wird nur die Anzahl der auszuführenden Schritte angegeben(Bei 1/2 wird die doppelte Strecke bei gleicher Schrittanzahl zurückgelegt als bei 1/4 usw.).

Mike07:
Und hier sind die Motordaten:

Schrittwinkel 1,8°
Schritte pro Umdrehung 200
Nennsstrom 1,7A/Phase
Nennspannung 3,7V
Haltemoment 4000gcm
Rastmoment 220g
cm
Rotorträgheit 54g*cm2
Spulenwiderstand 2,0 Ohm/Phase

Wenn Du diesem Motor 2A gibst brennt er früher oder später durch.
Grüße Uwe

postmaster-ino:
nimm unsigned int oder int - bis zu 64bit sind möglich.

Da weißt Du mal wieder mehr als ich. Welcher Variablentyp hat auf dem Arduino UNO 64 Bit? ich kenne nur 32.
Grüße Uwe

Mike07:
Hi,

Vielen Dank für die Antwort!
Ich habe hier float gewählt, weil ich (ist im Text ausgeklammert) die Schritte der X Achse berechnen möchte, die vergehen, bevor die Y-Achse ein Schritt machen soll. Das sollte eig dazu führen, dass die Achsen gleichzeitig losfahren und auch ankommen.

Die Schritte, die gemacht werden und auch ob 1/4, 1/8 oder 1/16 Schritte wird am Treiber eingestellt. Auf den Treiber wird nur die Anzahl der auszuführenden Schritte angegeben(Bei 1/2 wird die doppelte Strecke bei gleicher Schrittanzahl zurückgelegt als bei 1/4 usw.).

Du wirfst mit Variablentypen herum ohne Dir im Klaren zu sein welche Probleme auf Dich zukommen.
Int kann nur werte zwischen ca -32000 und ca +32000 annehmen.
Float kann große Zahlen darstellen aber hat nur 5-6 sigificante Stellen. Rechne zu float 10.000.000 tausend mal 1 dazu. Welchern Wert erwartest Du und welchen Wert bekommst du?

Grüße Uwe

Welcher Variablentyp hat auf dem Arduino UNO 64 Bit?

Zum Beispiel, alle diese:

unsigned long long testa = 1;
signed long long   testb = 1;
long long testc = 1;
uint64_t  testd = 1;
int64_t   teste = 1;

void setup() 
{
  Serial.begin(9600);
  Serial.println(sizeof(testa));
  Serial.println(sizeof(testb));
  Serial.println(sizeof(testc));
  Serial.println(sizeof(testd));
  Serial.println(sizeof(teste));
}

void loop() 
{

}

uwefed:
Du wirfst mit Variablentypen herum ohne Dir im Klaren zu sein welche Probleme auf Dich zukommen.
Int kann nur werte zwischen ca -32000 und ca +32000 annehmen.
Float kann große Zahlen darstellen aber hat nur 5-6 sigificante Stellen. Rechne zu float 10.000.000 tausend mal 1 dazu. Welchern Wert erwartest Du und welchen Wert bekommst du?

Grüße Uwe

Hi Uwe,

Also sollte ich für die aktuelle Position lieber int benutzen? Im Moment benutze ich die Variable nicht, da ich mich erstmal auf den Schrittverlust konzentrieren möchte, aber ich weiß immer noch nicht wie dieser Zustande kommt. Wie gesagt gebe ich eine zahl(z.b 10000) in die Software ein und je nach Schritteinstellung am Treiber macht dieser dann unterschiedlich weite Wege. Trotzdem ist bei jeder Einstellung, je nachdem ob halbe oder viertel Schritte die Abweichung bei jedem Durchlauf gleich groß und addiert sich wie schon gesagt.

Ich weiß aber leider nicht was ich noch verändern kann da ich jetzt schon mehrmals den Code durchlaufen habe und geguckt habe was evtl falsch sein könnte

(deleted)

Hi

uwefed:
Da weißt Du mal wieder mehr als ich. Welcher Variablentyp hat auf dem Arduino UNO 64 Bit? ich kenne nur 32.
Grüße Uwe

Habe Mal etwas rumgeschustert:

uint64_t byte64;
uint32_t byte32;
uint16_t byte16;
uint8_t byte8;

void setup() {
  Serial.begin(9600);
}

void loop() {
  byte64--;
  byte32--;
  byte16--;
  byte8--;
  uint32_t highdword = byte64 >> 32;
  uint32_t lowdword = byte64;
  ownprintserial(highdword, 8);
  ownprintserial(lowdword, 8);
  Serial.print(" ");
  ownprintserial(byte32, 8);
  Serial.print(" ");
  ownprintserial(byte16, 4);
  Serial.print(" ");
  ownprintserial(byte8, 2);
  Serial.println();
}


// Gibt die übergebene Zahl als HEX mit der angegebenen Zahl an Stellen mit führenden Nullen aus
// Kontroll-Ausgaben via Serial.print funktionieren nur bis 32bit, deshalb dieser Umweg
void ownprintserial(uint32_t ausgabezahl, byte stellen) {
  //  Serial.print("Auszugeben ");
  //  Serial.println(ausgabezahl,HEX);
  char buffer[33]; //32 Zeichen + '0'
  memset(buffer, '0', sizeof(buffer)); // buffer ausnullen
  buffer[32] = 0; //EndofString
  byte zeichennummer = 31;
  while (ausgabezahl) {
    //    Serial.print("Ausgabezahl ");
    //    Serial.print(ausgabezahl,HEX);
    byte bcd = ausgabezahl & 0x0F;
    //    Serial.print(" Zeichen ");
    //    Serial.println(bcd,HEX);
    if (bcd > 9) {
      bcd += 0x37;
    } else {
      bcd += 0x30;
    }
    buffer[zeichennummer] = bcd;

    //    Serial.print(" Zeichen ");
    //    Serial.write(buffer[zeichennummer]);
    //    Serial.println();
    zeichennummer--;
    ausgabezahl /= 16;
  }
  //  Serial.print("Komplett ");
  //  Serial.println(buffer);
  //  Serial.print("neu belegt ");
  //  Serial.println(&buffer[zeichennummer+1]);
  //  Serial.print("gewünschte Zeichenzahl ");
  Serial.print(&buffer[32 - stellen]);
  //  Serial.println();
}

Serial.print kann mit uint64_t Nichts anfangen, auch hatte ich kleinere Probleme, die führenden Nullen vor die Zahlen zu bekommen.
Bin jetzt den Umweg über ein char-Array gegangen, Welches ich mit '0'en initialisiere.
Dann wird die Zahl in 32bit gesplittet, daß ich Diese via Serial.print auch ausgeben kann - eigentlich nicht mehr nötig, das Zusammenkopieren der Einzelstellen sollte auch direkt mit der 64bit Zahl funktionieren.

MfG

PS: Hoffe, ich habe mich in diesem Konstrukt nicht zu sehr blamiert - das Serial.print(&buffer[startzeichennummer]); hatte ich nur vermutet - try&error mit Glück :slight_smile:
Wenn man in der Funktion die ganzen REM raus nimmt, sieht man etwas mehr vom Aufbau.

*21:26h noch sind wir erst beim 17.ten Bit beim Rückwärtszählen - da jedes weitere Bit die bereits vergangene Zeit zusätzlich haben will, wird Das wohl noch 'etwas' dauern - und wohl nicht bis zum neuen Anwählen des Nano beendet sein (aka das Programm wird dann neu starten).
Aber noch zählen alle Bitbreiten brav '-1', wohl bis der Arzt kommt ;).

Hallo Mike07,
ich hatte das selbe Problem bei meiner Eigenbau-CNC.

Zur Synchronisation der x-y-z Fahrten, also dass alle 3 Stepper bei unterschiedlichen Geschwindigkeiten zur selben
Zeit am Ziel ankommen, war es nötig die Dauer der Einzelschritte möglichst hoch aufzulösen.
Ich benutzte auch die AccelStepper-library.

Da wie hier schon erwähnt der Datentyp float wegen der Rundungsfehler keine zuverlässigen Ergebnisse brachte,
wechselte ich auf double.

Dabei stellte sich heraus, dass die Auflösung von double auf dem Uno und auch Mega nicht ausreichte um mein Ziel zu erreichen.

Erst mit dem Arduino Due klappte alles zur vollsten Zufriedenheit, denn der kann 32 Bit für double auflösen.
Außerdem hatte die höhere Taktung des Due den Vorteil, dass serielle Aus- und Eingaben während des Stepperbetriebs
diesen nicht aus dem Takt brachten.

Aber die beste Lösung fand ich mit der grbl-Software.
Da kannst du deine jetzige Hardware-Konfiguration beibehalten.

Die Ansteuerung des Arduino erfolgt dann direkt mittels gcode, den man dann natürlich etwas kennen muss, was aber im Vergleich zur Arduino-Programmierung sehr einfach ist.

Unabdingbare Voraussetzungen um Schrittverluste zu vermeiden sind nach wie vor:

  • möglichst wenig Spiel bei Vor/Rücklaufumkehr an den Achsen
  • die Frequenz und der Strom aus der Treiberendstufe müssen den technischen Daten des Steppermotors entsprechen

Den Uno kann man problemlos mittels der Arduino-IDE mit grbl flashen.
(Einfach mal nach "grbl gcode" googeln und dann erst lesen....)

Nachteil:
Es handelt sich um keinen üblichen Arduino-Sketch, lässt sich also nicht so ohne weiteres modifizieren.
Dazu muss man schon direkt in den C++-Dateien arbeiten. Dies ist aber im Normalfall nicht nötig.

Ich bin mit dieser Lösung jedenfalls vollstens zufrieden,
grbl erledigt alles was ich mir vorher mühevoll erarbeiten musste.

Gruß Welle

Dabei stellte sich heraus, dass die Auflösung von double auf dem Uno und auch Mega nicht ausreichte um mein Ziel zu erreichen.

Auf den 8Bit Arduinos ist Double und Float identisch.

Also sollte ich für die aktuelle Position lieber int benutzen?

Definitiv!
Oder long.
Auf jeden Fall ein Ganzzahlentype.