Drift des Steppers

Hallo zusammen. Bisher konnte ich die meisten Probleme, die in der Vergangenheit auftauchten mit Hilfe dieses Forums gut lösen. Wer lesen kann ist zumeist klar im Vorteil :grinning:

Jetzt jedoch bin ich mit einem letzten Problem meines Projektes am Ende allen Wissens und Möglichkeiten angekommen. Mein Projekt ist eine Drehscheiben-Steuerung für eine Spur1 Anlage. Die Hardware besteht aus einem Mega 2560, einem Nextion 7" Intelligent und einem Leadshine iEM 1708 Integrated Stepper Motor.

Auf Grund besserer Anpassungsmöglichkeiten habe ich zur FastAccelstepper Library gewechselt. Soweit scheint auch erst einmal alles ganz gut miteinander zu funktionieren. Allerding musste ich feststellen, dass sich bei Vor- und Rückwärtsbewegung nicht nur der Nullpunkt, sondern auch das Ziel im gleichen Maß in Richtung Plus verschieben. Der Schrittmotor macht 6400 Microsteps / Rev.

Wenn ich das Ziel 17700 eingebe, läuft er laut getCurrentPosition sauber nach vorne. mit dem Befehl moveTo(0); kommt er aber nicht wieder ganz dorthin zurück. Wenn ich jetzt moveTo(17700); läuft er wieder ordnungsgemäß nach vorne, wobei sich der Zielpunkt allerdings ebenfalls um den gleichen Betrag nach vorne verschoben hat, wie zuvor der Nullpunkt.

Beide Punkte driften also im gleichen Maß.
Hat dazu jemand eine Idee oder einen guten Vorschlag, wie ich das Problem in den Griff bekommen kann?
Stepper_Drift

#include <Arduino.h>
#include <Nextion.h>
#include <AVRStepperPins.h>  // Only required for AVR controllers
#include <FastAccelStepper.h>
#include <PWMServo.h>

uint32_t Schrittziel = 0;
short Position;

PWMServo sv1;
PWMServo sv2;
PWMServo sv3;
PWMServo sv4;
PWMServo sv5;
PWMServo sv6;
PWMServo sv7;

#define Relais 26  // Schuppenbeleuchtung

//Nextion Objekte Deklarieren
//(Seiten ID, Komponenten-ID, Komponentenname)
NexButton b1 = NexButton(0, 9, "b1");         //Horn
NexButton b2 = NexButton(0, 83, "b2");        //Reset
NexButton b3 = NexButton(0, 74, "b3");        //Stepper 50 Schritte zurück CCW
NexButton b4 = NexButton(0, 75, "b4");        //Stepper 10 Schritt zurück CCW
NexButton b5 = NexButton(0, 76, "b5");        //Stepper Stop
NexButton b6 = NexButton(0, 77, "b6");        //Stepper Run ohne Begrenzung
NexButton b7 = NexButton(0, 78, "b7");        //Stepper 10 Schritt vor CW
NexButton b8 = NexButton(0, 79, "b8");        //Stepper 50 Schritte vor CW
NexDSButton bt0 = NexDSButton(0, 59, "bt0");  //Schuppen Beleuchtung
NexDSButton bt1 = NexDSButton(0, 10, "bt1");  //Schuppen Servo Tor 1
NexDSButton bt2 = NexDSButton(0, 11, "bt2");  //Schuppen Servo Tor 2
NexDSButton bt3 = NexDSButton(0, 12, "bt3");  //Schuppen Servo Tor 3
NexDSButton bt4 = NexDSButton(0, 13, "bt4");  //Schuppen Servo Tor 4
NexDSButton bt5 = NexDSButton(0, 14, "bt5");  //Schuppen Servo Tor 5
NexDSButton bt6 = NexDSButton(0, 15, "bt6");  //Schuppen Servo Tor 6
NexNumber n1 = NexNumber(0, 57, "n1");        // Ziel-Position

NexTouch *nex_listen_list[] = {  // deklarieren aller Funktions-Buttons auf dem Display
  &b1,
  &b2,
  &b3,
  &b4,
  &b5,
  &b6,
  &b7,
  &b8,
  &bt0,
  &bt1,
  &bt2,
  &bt3,
  &bt4,
  &bt5,
  &bt6,
  NULL
};

// Stepper einrichten Leadshine closed Loop
// mit TB6600 = 36,35,33
#define dirPinStepper 23     //  grün
#define enablePinStepper 25  // grau
#define stepPinStepper 6     //  gelb (Timer4 0C4A)

#define Hall_Sensor 22       // Signalleitung
#define Hall_Sensor_Plus 24  // 5V Spannungsversorgung

FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper = NULL;

void (*resetFunc)(void) = 0;  // deklarieren der Hardware ResetFunction an Adresse 0
void forceStop();

void b1PushCallback(void *ptr) {  //Horn
  tone(52, 150, 500);             //Pin, Frequency, Dauer
}
void b2PushCallback(void *ptr) {  //Reset
  resetFunc();
}
void b3PushCallback(void *ptr) {  //Stepper 500 Schritte zurück CCW
  stepper->move(-500);
}
void b4PushCallback(void *ptr) {  //Stepper 100 Schritte zurück CCW
  stepper->move(-100);
}
void b5PushCallback(void *ptr) {  //Stepper Stop
  stepper->forceStop();
}
void b6PushCallback(void *ptr) {  //Stepper Run ohne Begrenzung
  stepper->keepRunning();
}
void b7PushCallback(void *ptr) {  //Stepper 100 Schritte vor CW
  stepper->move(100);
}
void b8PushCallback(void *ptr) {  //Stepper 500 Schritte vor CW
  stepper->move(500);
}
void bt0PushCallback(void *ptr) {  //Schuppenbeleuchtung
  pinMode(Relais, OUTPUT);
  Serial2.print("get bt0.val");
  Serial2.write("\xff\xff\xff");
  if ("bt0.val==1") {
    digitalWrite(Relais, HIGH);
  } else {
    digitalWrite(Relais, LOW);
  }
}
void bt1PushCallback(void *ptr) {  //Servo Tor 1
  Serial2.print("get bt1.val");
  Serial2.write("\xff\xff\xff");
  if ("bt1.val==1") {
    sv1.write(90);  // Schuppentor 1 AUF 90°
  } else {
    sv1.write(0);  // Schuppentor 1 ZU 0°
  }
}
void bt2PushCallback(void *ptr) {  //Servo Tor 2
  Serial2.print("get bt2.val");
  Serial2.write("\xff\xff\xff");
  if ("bt2.val==1") {
    sv2.write(90);  // Schuppentor 2 AUF 90°
  } else {
    sv2.write(0);  // Schuppentor 2 ZU 0°
  }
}
void bt3PushCallback(void *ptr) {  //Servo Tor 3
  Serial2.print("get bt3.val");
  Serial2.write("\xff\xff\xff");
  if ("bt3.val==1") {
    sv3.write(90);  // Schuppentor 3 AUF 90°
  } else {
    sv3.write(0);  // Schuppentor 3 ZU 0°
  }
}
void bt4PushCallback(void *ptr) {  //Servo Tor 4
  Serial2.print("get bt4.val");
  Serial2.write("\xff\xff\xff");
  if ("bt4.val==1") {
    sv4.write(90);  // Schuppentor 4 AUF 90°
  } else {
    sv4.write(0);  // Schuppentor 4 ZU 0°
  }
}
void bt5PushCallback(void *ptr) {  //Servo Tor 5
  Serial2.print("get bt5.val");
  Serial2.write("\xff\xff\xff");
  if ("bt5.val==1") {
    sv5.write(90);  // Schuppentor 5 AUF 90°
  } else {
    sv5.write(0);  // Schuppentor 5 ZU 0°
  }
}
void bt6PushCallback(void *ptr) {  //Servo Tor 6
  Serial2.print("get bt6.val");
  Serial2.write("\xff\xff\xff");
  if ("bt6.val==1") {
    sv6.write(90);  // Schuppentor 6 AUF 90°
  } else {
    sv6.write(0);  // Schuppentor 6 ZU 0°
  }
}


void setup() {
  Serial.begin(115200);
  Serial2.begin(115200);  // nextion(17, 16); //Rx blau, TX gelb

  engine.init();  // Initialisierung des Stepperencoders
  stepper = engine.stepperConnectToPin(stepPinStepper);

  Serial2.print("n1.val=0");  // Ziel-Position auf 0 setzen
  Serial2.write("\xff\xff\xff");

  Serial2.print("n2.pco=65535");
  Serial2.write("\xff\xff\xff");

  Serial2.print("n2.val=1");  // Ist-Position auf 1 setzen
  Serial2.write("\xff\xff\xff");

  Serial2.print("n3.val=0");  // Offset auf 0 setzen
  Serial2.write("\xff\xff\xff");

  pinMode(Hall_Sensor, INPUT_PULLUP);
  pinMode(Hall_Sensor_Plus, OUTPUT);

  //Geschwindigkeit und Beschleunigung des Steppers
  if (stepper) {
    stepper->setDirectionPin(dirPinStepper);
    stepper->setEnablePin(enablePinStepper);
    stepper->setAutoEnable(true);
  }

  // Referenzfahrt
  stepper->setSpeedInHz(500);     // Geschwindigkeit steps/s
  stepper->setAcceleration(150);  // Beschleunigung / Abbremsung steps/s²
  Serial2.print("click m13,1");
  Serial2.write("\xff\xff\xff");

  digitalWrite(Hall_Sensor_Plus, HIGH);
  delay(10);
  digitalRead(Hall_Sensor == LOW);

  while (digitalRead(Hall_Sensor) == HIGH) {
    stepper->moveTo(-25600);
  }
  stepper->setCurrentPosition(0);

  Serial2.print("n2.pco=0");  // Ist-Position Schriftfarbe auf Schwarz setzen
  Serial2.write("\xff\xff\xff");

  Serial2.print("n2.val=0");  // Ist-Position auf 0 setzen
  Serial2.write("\xff\xff\xff");

  Serial2.print("n0.val=10");  // Standard Zielvorgabe nach Neustart (Einfahrgleis)
  Serial2.write("\xff\xff\xff");

  Serial2.print("get n1.val");  // Abfrage der Anzeige Ziel
  Serial2.write("\xff\xff\xff");

  Serial2.print("get n2.val");  // Abfrage der Anzeige Ist
  Serial2.write("\xff\xff\xff");

  if ("n1.val==n2.val") {
    digitalWrite(Hall_Sensor_Plus, LOW);
  }

  Serial2.print("click m13,0");  // Grundposition der Bühne nach Neustart
  Serial2.write("\xff\xff\xff");
  // Referenzfahrt Ende

  stepper->setSpeedInHz(1700);    // Geschwindigkeit steps/s
  stepper->setAcceleration(400);  // Beschleunigung / Abbremsung steps/s²

  // Überwachung der Schalter und Servos
  sv1.attach(41);  // Schuppentor 1
  sv2.attach(43);  // Schuppentor 2
  sv3.attach(45);  // Schuppentor 3
  sv4.attach(47);  // Schuppentor 4
  sv5.attach(49);  // Schuppentor 5
  sv6.attach(51);  // Schuppentor 6
  sv7.attach(53);  // Bühnenverriegelung

  b1.attachPush(b1PushCallback, &b1);     // Horn
  b2.attachPush(b2PushCallback, &b2);     // Reset
  b3.attachPush(b3PushCallback, &b3);     // Stepper 10 Schritte zurück CCW
  b4.attachPush(b4PushCallback, &b4);     // Stepper 1 Schritt zurück CCW
  b5.attachPush(b5PushCallback, &b5);     // Stepper Stop
  b6.attachPush(b6PushCallback, &b6);     // Stepper keepRunning
  b7.attachPush(b7PushCallback, &b7);     // Stepper 1 Schritt vor CW
  b8.attachPush(b8PushCallback, &b8);     // Stepper 10 Schritte vor CW
  bt0.attachPush(bt0PushCallback, &bt0);  // Schalter Schuppenbeleuchtung
  bt1.attachPush(bt1PushCallback, &bt1);  // Schalter Schuppentor 1
  bt2.attachPush(bt2PushCallback, &bt2);  // Schalter Schuppentor 2
  bt3.attachPush(bt3PushCallback, &bt3);  // Schalter Schuppentor 3
  bt4.attachPush(bt4PushCallback, &bt4);  // Schalter Schuppentor 4
  bt5.attachPush(bt5PushCallback, &bt5);  // Schalter Schuppentor 5
  bt6.attachPush(bt6PushCallback, &bt6);  // Schalter Schuppentor 6
}

void loop() {
  nexLoop(nex_listen_list);

  n1.getValue(&Schrittziel);  // Im Zusammenhang mit uint32_t Schrittziel = 0; (0 = Min_Val)

  Serial2.print("get n1.val");  // Wert aus n1 abrufen
  Serial2.write("\xff\xff\xff");

  stepper->moveTo(Schrittziel);  // Ziel anfahren
  Position = stepper->getCurrentPosition();

  if (stepper->isRunning()) {
    Serial2.print("n2.bco=63488");  // Hintergrund- und Schriftfarbe von Ist-Position auf rot setzen
    Serial2.write("\xff\xff\xff");
    Serial2.print("n2.pco=65535");
    Serial2.write("\xff\xff\xff");
  } else {
    Serial2.print("n2.bco=65535");  // Hintergrund- und Schriftfarbe von Ist-Position auf zurücksetzen
    Serial2.write("\xff\xff\xff");
    Serial2.print("n2.pco=0");
    Serial2.write("\xff\xff\xff");
  }

  Serial2.print("n2.val=");
  Serial2.print(Position);  // Übertragen der Ist-Position an Nextion
  Serial2.write("\xff\xff\xff");
}

Hast Du das auch für die umgekehrte Drehrichtung getestet?

Du hast viele Lib's eingebunden, die HW-Timer verwenden. Um auszuschließen das es da Konflikte gibt: Hast Du mal einen ganz einfachen Testsketch geschrieben, der nichts anderes macht als genau dieses Stepper hin- und her und auch sonst nichts einbindet? Dann kannst Du prüfen, ob es an der HW ( Stepper Stromversorgung / missing Steps ... ) oder am Sketch liegt.

Danke erst einmal für die Anregungen. Das mit der Drehrichtungsumkehr klingt für mich zunächst wenig logisch nachvollziehbar, werde es aber testen.

@MicroBahner: Meine Vermutung geht bislang in folgende Richtung...
Es sind nicht wirklich die 17700 Schritte in Vorwärtsrichtung die Probleme machen, sondern der Aufruf moveTo(0);. Es macht den Eindruck, als ob sich der Nullpunkt immer weiter nach vorne verschiebt. Wie ist soetwas möglich ?

Was jedoch den Gedankengang wieder trübt, ist die Tatsache, dass es zwischen zwei beliebigen anderen Punkten genau das gleiche Resultat erzeugt.

Grundsätzlich also etwas, was nur beim Rückwärtsfahren geschieht?

Das wäre jetzt tatsächlich etwas, was man mit der Fragestellung DrDiettrich verifizieren könnte.

Ich werde das gleich mal testen und berichten.

Is the stepper connected to a mechanism and are you measuring at the mechanism output? What you describe sounds a bit like backlash.

The stepper motor has an incremental encoder. However, this does not have its own output. Only an error message can be output via the output ALM+ and ALM-, such as "positioning error".

Ein Stepper kennt nicht wirklich einen festen Nullpunkt. Nachdem Du einmal einen Nullpunkt gesetzt hast, rechnet er nur noch in Schritten. Wenn Du nach '17700 Schritte vorwaärts' ein moveTo(0) machst, ist es dasselbe wie '17700 Schritte rückwärts' Im Normalfall kommt er dann natürlich wieder beim Nullpunkt an. Wenn er aber zwischendurch - wodurch auch immer, z.B. mechanisch - Schritte verloren hat, stimmt das eben nicht mehr. Deshalb der Vorschlag einen ganz einfachen Testsketch zu schreiben, der nur diese Vorwärts- und Rückwärtsbewegung macht. Und dann schauen, ob sich die Position der hin- und Herbewegung verschiebt - eben wenn es z.B. mechanisch irgendwo etwas hakt. Wenn dieses einfachst Programm keine Verschiebung bewirkt, dann muss es irgendwo in der (kompletten) Software begründet sein.

Prüfst Du diesen Alarm-Ausgang? Poste mal Links zu deinen verwendeten Komponenten.

Wertest du denn die Alarmausgänge aus?
Poste mal das Datenblatt des Schrittmotors.
Ist das nun ein closed-loop-Schrittmotor oder ein open-loop?

Wenn es open-loop ist teste das vorwärts rückwärts fahren mit sehr viel kleinerer Geschwindigkeit.

FastAccelstepper-library hört sich nach möglichst schnellem Beschleunigen an.
Wenn man einen Schrittmotor aus dem Stand heraus mit zu großer Geschwindigkeit startet verliert er Schritte.
Wenn während der Fahrt eine Resonanz auftritt oder der mechanische Widerstand an einer bestimmten Stelle größer wird kann das auch zum Schritte verlieren führen.

Du definierst einen enablepin

#define enablePinStepper 25  // grau

Was macht fastAccelStepper mit dem enable-pin?
Wird enable jedesmal auf dis-abled gesetzt wenn eine Bewegung zu Ende ist? Dann kann sich der Schrittmotor ein kleines bisschen verdrehen.

Deshalb wäre auch wichtig das du mal schreibst um wie viel verschiebt sich denn der Nullpunkt?

  • so wenig dass man es erst nach der 10ten Fahrt bemerkt?
    oder
    nach einer Fahrt schon 2 Grad?

Wie lange sind die Step-pulse?
fastAccelStepper könnte die sehr sehr kurz machen damit eine hohe Maximalfrequenz möglich wird. Wenn die Impulslänge auf der Mindestlänge der Impulse des Step-eingangs ist könnte es sein, dass da einige Impulse nicht erkannt werden.

vgs

Sooooo, habe mal auf Grund eines guten Vorschlags mal wirklich auf das Notwendigste reduziert und einen 1 stündigen Testlauf gemacht. Das Ergebnis ist, dass keine Schrittverluste oder Verschiebungen aufgetreten sind.

Die Beschleunigungs- und Geschwindigkeitsparameter habe ich dabei so übernommen, wie in dem Originalsketch.

Auch die von mir weiter oben zitierten Schritte habe ich wieder übernommen. Also 17775 Schritte vorwärts und 17775 Schritte rückwärts.

So wie es jetzt ausschaut, kommen sich die von mir verwendete Nextion Library mit der FastAccelStepper Library ins Gehege.

Ich habe noch keinen Schimmer, wie ich das in den Griff bekomme, aber jetzt habe ich zumindest mal einen Anhaltspunkt.

Mit diesem Sketch lief alles ohne Probleme...

#include <AVRStepperPins.h> // Only required for AVR controllers
#include <FastAccelStepper.h>

#define dirPinStepper    23  // blau
#define enablePinStepper 25  // braun
#define stepPinStepper   6  // rot

uint32_t getMaxSpeedInHz();
uint32_t getCurrentPosition();
int8_t move(int32_t move, bool blocking = false);
int8_t moveTo(int32_t position, bool blocking = false);

FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper = NULL;

void setup() {
   engine.init();
   stepper = engine.stepperConnectToPin(stepPinStepper);

   if (stepper) {
      stepper->setDirectionPin(dirPinStepper);
      stepper->setEnablePin(enablePinStepper);
      stepper->setAutoEnable(true);
      stepper->setCurrentPosition(0);

      stepper->setSpeedInHz(1700);       // Geschwindigkeit steps/s
      stepper->setAcceleration(400);    // Beschleunigung / Abbremsung steps/s²
    }
}

void loop() {

        stepper->moveTo(17775);
delay(24000);
      stepper->moveTo(-17775);
delay(24000);
}

Deine Loop sollte noch ordentlich formatiert werden

void loop() {
  stepper->moveTo(17775);
  delay(24000);
  stepper->moveTo(-17775);
  delay(24000);
}

aus der Tatsache dass du da delay(24000) verwendest kann man schlussfolgern, dass die FastAccelStepper einen timer-interrupt zum Erzeugen der step-impulse benutzt.

Hier gibst du ständig was auf der seriellen Schnittstelle aus

  if (stepper->isRunning()) {
    Serial2.print("n2.bco=63488");  // Hintergrund- und Schriftfarbe von Ist-Position auf rot setzen
    Serial2.write("\xff\xff\xff");
    Serial2.print("n2.pco=65535");
    Serial2.write("\xff\xff\xff");
  } else {
    Serial2.print("n2.bco=65535");  // Hintergrund- und Schriftfarbe von Ist-Position auf zurücksetzen
    Serial2.write("\xff\xff\xff");
    Serial2.print("n2.pco=0");
    Serial2.write("\xff\xff\xff");
  }

was sich aber nur ab und zu ändert.
Da kannst du einbauen, dass es nur dann ausgegeben wird wenn es sich wirklich geändert hat

Und die Ist-Position 100 mal pro Sekunde ausgeben macht auch keinen Sinn

  Serial2.print("n2.val=");
  Serial2.print(Position);  // Übertragen der Ist-Position an Nextion
  Serial2.write("\xff\xff\xff");

zweimal pro Sekunde reicht auch. Das kann man mit nicht-blockierendem Timing machen.

Das sieht dann so aus

#include <Arduino.h>
#include <Nextion.h>
#include <AVRStepperPins.h>  // Only required for AVR controllers
#include <FastAccelStepper.h>
#include <PWMServo.h>

uint32_t Schrittziel = 0;
short Position;

boolean stepperIsRunning;
boolean lastStepperIsRunning;
unsigned long myUpdateTimer;

PWMServo sv1;
PWMServo sv2;
PWMServo sv3;
PWMServo sv4;
PWMServo sv5;
PWMServo sv6;
PWMServo sv7;

#define Relais 26  // Schuppenbeleuchtung

//Nextion Objekte Deklarieren
//(Seiten ID, Komponenten-ID, Komponentenname)
NexButton b1 = NexButton(0, 9, "b1");         //Horn
NexButton b2 = NexButton(0, 83, "b2");        //Reset
NexButton b3 = NexButton(0, 74, "b3");        //Stepper 50 Schritte zurück CCW
NexButton b4 = NexButton(0, 75, "b4");        //Stepper 10 Schritt zurück CCW
NexButton b5 = NexButton(0, 76, "b5");        //Stepper Stop
NexButton b6 = NexButton(0, 77, "b6");        //Stepper Run ohne Begrenzung
NexButton b7 = NexButton(0, 78, "b7");        //Stepper 10 Schritt vor CW
NexButton b8 = NexButton(0, 79, "b8");        //Stepper 50 Schritte vor CW
NexDSButton bt0 = NexDSButton(0, 59, "bt0");  //Schuppen Beleuchtung
NexDSButton bt1 = NexDSButton(0, 10, "bt1");  //Schuppen Servo Tor 1
NexDSButton bt2 = NexDSButton(0, 11, "bt2");  //Schuppen Servo Tor 2
NexDSButton bt3 = NexDSButton(0, 12, "bt3");  //Schuppen Servo Tor 3
NexDSButton bt4 = NexDSButton(0, 13, "bt4");  //Schuppen Servo Tor 4
NexDSButton bt5 = NexDSButton(0, 14, "bt5");  //Schuppen Servo Tor 5
NexDSButton bt6 = NexDSButton(0, 15, "bt6");  //Schuppen Servo Tor 6
NexNumber n1 = NexNumber(0, 57, "n1");        // Ziel-Position

NexTouch *nex_listen_list[] = {  // deklarieren aller Funktions-Buttons auf dem Display
  &b1,
  &b2,
  &b3,
  &b4,
  &b5,
  &b6,
  &b7,
  &b8,
  &bt0,
  &bt1,
  &bt2,
  &bt3,
  &bt4,
  &bt5,
  &bt6,
  NULL
};

// Stepper einrichten Leadshine closed Loop
// mit TB6600 = 36,35,33
#define dirPinStepper 23     //  grün
#define enablePinStepper 25  // grau
#define stepPinStepper 6     //  gelb (Timer4 0C4A)

#define Hall_Sensor 22       // Signalleitung
#define Hall_Sensor_Plus 24  // 5V Spannungsversorgung

FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper = NULL;

void (*resetFunc)(void) = 0;  // deklarieren der Hardware ResetFunction an Adresse 0
void forceStop();

void b1PushCallback(void *ptr) {  //Horn
  tone(52, 150, 500);             //Pin, Frequency, Dauer
}
void b2PushCallback(void *ptr) {  //Reset
  resetFunc();
}
void b3PushCallback(void *ptr) {  //Stepper 500 Schritte zurück CCW
  stepper->move(-500);
}
void b4PushCallback(void *ptr) {  //Stepper 100 Schritte zurück CCW
  stepper->move(-100);
}
void b5PushCallback(void *ptr) {  //Stepper Stop
  stepper->forceStop();
}
void b6PushCallback(void *ptr) {  //Stepper Run ohne Begrenzung
  stepper->keepRunning();
}
void b7PushCallback(void *ptr) {  //Stepper 100 Schritte vor CW
  stepper->move(100);
}
void b8PushCallback(void *ptr) {  //Stepper 500 Schritte vor CW
  stepper->move(500);
}
void bt0PushCallback(void *ptr) {  //Schuppenbeleuchtung
  pinMode(Relais, OUTPUT);
  Serial2.print("get bt0.val");
  Serial2.write("\xff\xff\xff");
  if ("bt0.val==1") {
    digitalWrite(Relais, HIGH);
  } else {
    digitalWrite(Relais, LOW);
  }
}
void bt1PushCallback(void *ptr) {  //Servo Tor 1
  Serial2.print("get bt1.val");
  Serial2.write("\xff\xff\xff");
  if ("bt1.val==1") {
    sv1.write(90);  // Schuppentor 1 AUF 90°
  } else {
    sv1.write(0);  // Schuppentor 1 ZU 0°
  }
}
void bt2PushCallback(void *ptr) {  //Servo Tor 2
  Serial2.print("get bt2.val");
  Serial2.write("\xff\xff\xff");
  if ("bt2.val==1") {
    sv2.write(90);  // Schuppentor 2 AUF 90°
  } else {
    sv2.write(0);  // Schuppentor 2 ZU 0°
  }
}
void bt3PushCallback(void *ptr) {  //Servo Tor 3
  Serial2.print("get bt3.val");
  Serial2.write("\xff\xff\xff");
  if ("bt3.val==1") {
    sv3.write(90);  // Schuppentor 3 AUF 90°
  } else {
    sv3.write(0);  // Schuppentor 3 ZU 0°
  }
}
void bt4PushCallback(void *ptr) {  //Servo Tor 4
  Serial2.print("get bt4.val");
  Serial2.write("\xff\xff\xff");
  if ("bt4.val==1") {
    sv4.write(90);  // Schuppentor 4 AUF 90°
  } else {
    sv4.write(0);  // Schuppentor 4 ZU 0°
  }
}
void bt5PushCallback(void *ptr) {  //Servo Tor 5
  Serial2.print("get bt5.val");
  Serial2.write("\xff\xff\xff");
  if ("bt5.val==1") {
    sv5.write(90);  // Schuppentor 5 AUF 90°
  } else {
    sv5.write(0);  // Schuppentor 5 ZU 0°
  }
}
void bt6PushCallback(void *ptr) {  //Servo Tor 6
  Serial2.print("get bt6.val");
  Serial2.write("\xff\xff\xff");
  if ("bt6.val==1") {
    sv6.write(90);  // Schuppentor 6 AUF 90°
  } else {
    sv6.write(0);  // Schuppentor 6 ZU 0°
  }
}


void setup() {
  Serial.begin(115200);
  Serial2.begin(115200);  // nextion(17, 16); //Rx blau, TX gelb

  engine.init();  // Initialisierung des Stepperencoders
  stepper = engine.stepperConnectToPin(stepPinStepper);

  Serial2.print("n1.val=0");  // Ziel-Position auf 0 setzen
  Serial2.write("\xff\xff\xff");

  Serial2.print("n2.pco=65535");
  Serial2.write("\xff\xff\xff");

  Serial2.print("n2.val=1");  // Ist-Position auf 1 setzen
  Serial2.write("\xff\xff\xff");

  Serial2.print("n3.val=0");  // Offset auf 0 setzen
  Serial2.write("\xff\xff\xff");

  pinMode(Hall_Sensor, INPUT_PULLUP);
  pinMode(Hall_Sensor_Plus, OUTPUT);

  //Geschwindigkeit und Beschleunigung des Steppers
  if (stepper) {
    stepper->setDirectionPin(dirPinStepper);
    stepper->setEnablePin(enablePinStepper);
    stepper->setAutoEnable(true);
  }

  // Referenzfahrt
  stepper->setSpeedInHz(500);     // Geschwindigkeit steps/s
  stepper->setAcceleration(150);  // Beschleunigung / Abbremsung steps/s²
  Serial2.print("click m13,1");
  Serial2.write("\xff\xff\xff");

  digitalWrite(Hall_Sensor_Plus, HIGH);
  delay(10);
  digitalRead(Hall_Sensor == LOW);

  while (digitalRead(Hall_Sensor) == HIGH) {
    stepper->moveTo(-25600);
  }
  stepper->setCurrentPosition(0);

  Serial2.print("n2.pco=0");  // Ist-Position Schriftfarbe auf Schwarz setzen
  Serial2.write("\xff\xff\xff");

  Serial2.print("n2.val=0");  // Ist-Position auf 0 setzen
  Serial2.write("\xff\xff\xff");

  Serial2.print("n0.val=10");  // Standard Zielvorgabe nach Neustart (Einfahrgleis)
  Serial2.write("\xff\xff\xff");

  Serial2.print("get n1.val");  // Abfrage der Anzeige Ziel
  Serial2.write("\xff\xff\xff");

  Serial2.print("get n2.val");  // Abfrage der Anzeige Ist
  Serial2.write("\xff\xff\xff");

  if ("n1.val==n2.val") {
    digitalWrite(Hall_Sensor_Plus, LOW);
  }

  Serial2.print("click m13,0");  // Grundposition der Bühne nach Neustart
  Serial2.write("\xff\xff\xff");
  // Referenzfahrt Ende

  stepper->setSpeedInHz(1700);    // Geschwindigkeit steps/s
  stepper->setAcceleration(400);  // Beschleunigung / Abbremsung steps/s²

  // Überwachung der Schalter und Servos
  sv1.attach(41);  // Schuppentor 1
  sv2.attach(43);  // Schuppentor 2
  sv3.attach(45);  // Schuppentor 3
  sv4.attach(47);  // Schuppentor 4
  sv5.attach(49);  // Schuppentor 5
  sv6.attach(51);  // Schuppentor 6
  sv7.attach(53);  // Bühnenverriegelung

  b1.attachPush(b1PushCallback, &b1);     // Horn
  b2.attachPush(b2PushCallback, &b2);     // Reset
  b3.attachPush(b3PushCallback, &b3);     // Stepper 10 Schritte zurück CCW
  b4.attachPush(b4PushCallback, &b4);     // Stepper 1 Schritt zurück CCW
  b5.attachPush(b5PushCallback, &b5);     // Stepper Stop
  b6.attachPush(b6PushCallback, &b6);     // Stepper keepRunning
  b7.attachPush(b7PushCallback, &b7);     // Stepper 1 Schritt vor CW
  b8.attachPush(b8PushCallback, &b8);     // Stepper 10 Schritte vor CW
  bt0.attachPush(bt0PushCallback, &bt0);  // Schalter Schuppenbeleuchtung
  bt1.attachPush(bt1PushCallback, &bt1);  // Schalter Schuppentor 1
  bt2.attachPush(bt2PushCallback, &bt2);  // Schalter Schuppentor 2
  bt3.attachPush(bt3PushCallback, &bt3);  // Schalter Schuppentor 3
  bt4.attachPush(bt4PushCallback, &bt4);  // Schalter Schuppentor 4
  bt5.attachPush(bt5PushCallback, &bt5);  // Schalter Schuppentor 5
  bt6.attachPush(bt6PushCallback, &bt6);  // Schalter Schuppentor 6
}

void loop() {
  nexLoop(nex_listen_list);

  n1.getValue(&Schrittziel);  // Im Zusammenhang mit uint32_t Schrittziel = 0; (0 = Min_Val)

  Serial2.print("get n1.val");  // Wert aus n1 abrufen
  Serial2.write("\xff\xff\xff");

  stepper->moveTo(Schrittziel);  // Ziel anfahren

  stepperIsRunning = stepper->isRunning();

  // nur wenn sich der Status geändert hat EINMAL ausgeben
  if (lastStepperIsRunning != stepperIsRunning) {
    if (stepperIsRunning) {
      Serial2.print("n2.bco=63488");  // Hintergrund- und Schriftfarbe von Ist-Position auf rot setzen
      Serial2.write("\xff\xff\xff");
      Serial2.print("n2.pco=65535");
      Serial2.write("\xff\xff\xff");
    }
    else {
      Serial2.print("n2.bco=65535");  // Hintergrund- und Schriftfarbe von Ist-Position auf zurücksetzen
      Serial2.write("\xff\xff\xff");
      Serial2.print("n2.pco=0");
      Serial2.write("\xff\xff\xff");
    }
    lastStepperIsRunning = stepperIsRunning;
  }

  // nur alle 500 Millisekunden EINMAL abfragen und ausgeben
  if ( TimePeriodIsOver(myUpdateTimer, 500) ) {
    Position = stepper->getCurrentPosition();
    Serial2.print("n2.val=");
    Serial2.print(Position);  // Übertragen der Ist-Position an Nextion
    Serial2.write("\xff\xff\xff");
  }
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

vgs

Guten Abend zusammen.

Ganz herzlichen Dank an alle, die mir mit ihren Fragen und Anregungen sehr viele Denkanstöße gegeben haben, die mir auf die eine oder andere Art zwar geholfen haben, aber das Problem nicht wirklich lösen konnten.

@Stefan: Auch dir ganz herzlichen Dank für deinen Korrekturvorschlag, den ich gerne umgesetzt habe. Es hat zwar auch nicht wirklich das Problem gelöst, hat aber sehr viel Ruhe auf der Seriellen gebracht. Das hat sich zumindest in der Verarbeitungsgeschwindigkeit deutlich bemerkbar gemacht.
Das mit dem Non Blocking Code muss ich mir wirklich mal deutlich hinter die Ohren schreiben :+1: :smiley:

Zurück zum Problem...
Es wird immer kurioser. Ich habe logischer Weise unterschiedliche Schrittziele programmiert. Alle laufen ohne Schrittverluste oder Verschiebungen bis zu der von mir vorgegebenen Größe von 17064 Schritten.
Darüber hinaus, also das letzte von mir vorgegebene Schrittziel 17775 hat in der Vorwärtsbewegung immer 50 Schritte mehr als vorgegeben. in der Rückwärtsbewegung 50 Schritte weniger als vorgegeben.

Ich bin mir also inzwischen sicher, dass der Fehler wie so häufig, vor dem Rechner sitzt. Ich sehe es einfach nur nicht. Deklarationsfehler??? oder einfach nur Murphy...

Die fastAccelStepper-library definiert als Rückgabewert für

getCurrentPosition() 

ein int32_t (das gleiche wie long

int32_t FastAccelStepper::getCurrentPosition() {
  return fas_queue[_queue_num].getCurrentPosition();
}

du hast für die Variable "position" deklariert

short Position;

Das ist vielleicht auch nicht die Ursache aber unsauber programmiert.
Ändere das mal auf

int32_t myPosition;

"Die Buchstabenfolge "position" kommt so oft in den Kommentaren vor das man viel mehr Fundstellen hat als die an denen es eine Variable ist.
Deshalb ändern auf myPosition
Das ist dann spezifisch für die Variable oder wenn dir besser gefällt
"aktPosition" oder "actualPosition" oder oder oder
Jedenfalls nicht den Allerweltsbegriff "position"

Heißt das
bis 17063 kein Schrittverlust
und
ab 17064 50 Schritte Schrittverluste?

Darüber hinaus, also das letzte von mir vorgegebene Schrittziel 17775 hat in der Vorwärtsbewegung immer 50 Schritte mehr als vorgegeben.
In der Rückwärtsbewegung 50 Schritte weniger als vorgegeben.

Wie hast du das genau ermittelt?

Hast du einen weiteren Mikrocontroller den man mal als Impulszähler verwenden könnte?

Wenn du ein Testprogramm schreibst, das Referenzfahrt macht.
Dann 10000 Schritte vom Referenzpunkt wegfährt und dann wieder auf den Referenzschalter zufährt und dabei die Schritte zählt bis der Referenzschalter umschaltet. Was kommt dabei heraus?

vgs

Nun, ob nun 50, oder vielleicht 56 Schritte sind kann ich nicht 100%ig sagen. Ich habe optische Markierungen auf einem Zahnrad gemacht. 6400 Steps/Rev. : 30 Zähne = 213,33 Steps.
Es war ein halber Zahn als Versatz zu erkennen also 107 Schritte Versatz.

17775 + 50 Steps in Vorwärtsrichtung = 17825 Steps
17825 - 50 Steps in der Rückwärtsbewegung = Differenz von 100 Steps zum Nullpunkt. Vielleicht nicht ganz professionell, aber brauchbar, denke ich.

Im Übrigen deckt sich das mit dem, was kurzzeitig im Display beim Reset angezeigt wurde.

Wie schon oben geschrieben laufen alle anderen Ziele selbst nach wiederholten Vor- und Zurückfahren einwandfrei nach 0 zurück. Festgestellt nach der eben beschriebenen Methode.

Also wir können jetzt hier ein bisschen plauschen mit Wischi-Waschi Geschreibsel oder professionell mit Präzision Fehleranalyse machen.

Was von beidem willst du?

Nochmal die Frage:

Hast du eine Art Referenz-Schalter der genau bei einer Drehstellung,
der bei genau einem Drehwinkel umschaltet?

Damit könnte man eine automatisierte Messung machen.
Fahre auf den Referenzschalter bis der Referenzschalter umschaltet.

Drehe 1000 Schritte vor.
Drehe einzelne Schritte zurück bis der Referenzschalter wieder schaltet.
Einzelne Schritte deshalb damit man nach jedem Schritt prüfen kann
hat der Referenzschalter umgeschaltet.

Drehe 2000 Schritte vor.
Drehe einzelne Schritte zurück bis der Referenzschalter wieder schaltet.
Einzelne Schritte deshalb damit man nach jedem Schritt prüfen kann
hat der Referenzschalter umgeschaltet.

usw. usw. usf.

Zweite Frage:

Hast du einen zweiten Mikrocontroller ?
Ja / nein

Wenn ja was ist das für ein microcontroller-typ.

Den zweiten µC könnte man benutzen um die elektrischen Impulse zu zählen die vom Steuerungs-µC ausgegeben werden.

Dein Steuerungs-µC erzeugt einen Impuls der auf den Step-Eingang des Schrittmotors geht. Dieser Impuls wird vom zweiten µC erkannt und gezählt.

Wenn die Schrittanzahl im Steuerungs-µC immer mit der gezählten Schrittanzahl übereinstimmt weiß man an der Impuls-Erzeugung liegt der Fehler nicht.

Also wat is:
bisschen Lotto spielen "probiere ich mal dies probiere ich mal jenes "
oder
präzsise Untersuchungen?

vgs

Es wurde nach dem Datenblatt für den Stepper gefragt..

ESS17-07_Full_Datasheet.pdf (399,8 KB)

Als Endschalter habe ich einen Hallgeber im Einsatz. Als zweites Board habe ich noch einen UNO zur Verfügung. Die Umsetzung des Versuchaufbaus muss jedoch bis zum Wochenende warten, da ich erst da wieder die nötige Zeit habe.

Um deine Frage zu beantworten…
Ja, eine eine fundierte Lösung soll es schon sein. Ich hoffe ja auch noch was dazuzulernen.

Problem erkannt und bereits einer Beseitigung zugeführt.

@Stefan: Deine Idee mit dem zweiten Controller war goldrichtig.
Ich habe mit dem UNO einen ISR-Zähler realisiert und dabei festgestellt, dass die vom MEGA ausgegebenen Taktsignale für die Vor- und Rückwärtsbewegung korrekt sind.

Habe dann vor jeder weiteren Messung am Step-Pin des MEGA einen Reset ausgelöst und damit den Nullpunkt immer wieder neu festgelegt. Damit waren für alle Messungen die Voraussetzungen gleich. Bis auf zwei Adressen, die auch immer wieder korrekt auf den Nullpunkt zurückgeführt wurden (mittels Hallgeber ermittelt), war bei allen anderen auffällig, dass sich der Nullpunkt immer wieder um 128 Mikroschritte in Richtung Plus verschob.

Ich habe nach einer Gemeinsamkeit der beiden korrekt ablaufenden Zieleingaben gesucht und dabei herausgefunden, dass ein unmittelbarer Zusammenhang mit der Auflösung der Schritteingaben einhergeht.

In der Dokumentation des Steppers steht, dass beim Enablen der Treiberstufe der Stepper immer zum Punkt des Vollschritts gezogen wird. Genau da ist der Grund für das merkwürdige Verhalten zu finden gewesen. Eine Korrektur der Schrittzahlen für die Ziele von teilweise nur wenigen Mikroschritten brachte hier den gesuchten Erfolg.

Durch dem Einsatz einer Untersetzung von 4:1 im späteren Betrieb, macht sich das nur in ganz wenigen Winkelsekunden bis knapp einer Winkelminute bemerkbar. damit kann ich gut leben.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.