8x verschachteltes if...then...else

Hallo,

ich habe hier ein kleines Problem.

Folgendes möchte ich erreichen:

Wenn (Pos[0] - encoder.getPosition() == 0) dann tue nix
ansonsten
Wenn (Pos[0] - encoder.getPosition() > 0) {stepper.move(Pos[0] - encoder.getPosition());};
ansonsten
Wenn (Pos[1] - encoder.getPosition() == 0) dann tue nix
ansonsten
Wenn (Pos[1] - encoder.getPosition() > 0) {stepper.move(Pos[1] - encoder.getPosition());};
ansonsten
Wenn (Pos[2] - encoder.getPosition() == 0) dann tue nix
ansonsten
Wenn (Pos[2] - encoder.getPosition() > 0) {stepper.move(Pos[2] - encoder.getPosition());};
ansonsten
Wenn (Pos[3] - encoder.getPosition() == 0) dann tue nix
ansonsten
Wenn (Pos[3] - encoder.getPosition() > 0) {stepper.move(Pos[3] - encoder.getPosition());};
ansonsten stepper.move(4095 - encoder.getPosition());

  if (Pos[0] - encoder.getPosition() == 0) {}
   else
    if (Pos[0] - encoder.getPosition() > 0) {stepper.move(Pos[0] - encoder.getPosition());};
     else
      if (Pos[1] - encoder.getPosition() == 0) {}
       else
        if (Pos[1] - encoder.getPosition() > 0) {stepper.move(Pos[1] - encoder.getPosition());};
         else
          if (Pos[2] - encoder.getPosition() == 0) {}
           else
            if (Pos[2] - encoder.getPosition() > 0) {stepper.move(Pos[2] - encoder.getPosition());};
             else
              if (Pos[3] - encoder.getPosition() == 0) {}
               else
                if (Pos[3] - encoder.getPosition() > 0) {stepper.move(Pos[3] - encoder.getPosition());};
                 else stepper.move(4095 - encoder.getPosition());

leider kompiliert das so nicht.

Gibt es vielleicht eine elegantere Lösung?

Es darf nur ein Bewegung geben, und nicht zwei.
Eine der Bedingungen wird immer zutreffen.

Lieben Gruß,
Chris

'kompiliert nicht' ist echt viel sagend ... soll heißen?

Ok, anders formuliert: Fehlermeldung?

Irgendwie habe ich nämlich das Gefühl, daß der Kram bei mir ebenfalls NICHT kompiliert.

MfG

Moin,

lösche mal die Semikolon ; hinter den geschweiften Klammern und setze die letzte else-Abfrage in geschweiften Klammern (hier weiß ich nicht ob es nötig ist, aber sieht einheitlicher aus).

Achte auch auf richtige Einrückung des Sketches, auch bei deinem Codefragment. Fehlermeldung zu posten wäre für Helfer sehr hilfreich.

….habs nun doch gefunden. Vor dem else muss das ";" weg ::slight_smile:

nu geht's:

  if (Pos[0] - encoder.getPosition() == 0) {}
   else
    if (Pos[0] - encoder.getPosition() > 0) {stepper.move(Pos[0] - encoder.getPosition());}
     else
      if (Pos[1] - encoder.getPosition() == 0) {}
       else
        if (Pos[1] - encoder.getPosition() > 0) {stepper.move(Pos[1] - encoder.getPosition());}
         else
          if (Pos[2] - encoder.getPosition() == 0) {}
           else
            if (Pos[2] - encoder.getPosition() > 0) {stepper.move(Pos[2] - encoder.getPosition());}
             else
              if (Pos[3] - encoder.getPosition() == 0) {}
               else
                if (Pos[3] - encoder.getPosition() > 0) {stepper.move(Pos[3] - encoder.getPosition());}
                 else stepper.move(4095 - encoder.getPosition());

Auch das else braucht geschweifte Klammern.
Auch glaube ich dass es eine elegantere Lösung gibt. Abfragen ob er auf position ist, wenn nicht eins weiter fahren, eine Variable hochzählen und beim nächsten loop durchlauf dann ggf. die hochgezähle position anfahren. Hoffe es ist verständlich. Du hast da ja 0,1,2,3 stehen und machst sonst das gleiche. Dann mach doch:

move pos(i)
i++

mal gucken, ob es mir gelingt zu erklären was ich möchte....

Also wenn der Strom eingeschaltet wird, bootet mein nodeMCU ESP8266 und fragt von einem Magnetencoder eine Position ab.
Durch die verschachtelte if...then...else Schleifen versuche ich im Setup() herauszufinden, ob die zufällige Startposition mit einer der vier absoluten Positionen übereinstimmt. Wenn ja, soll nichts passieren.
Wenn nein, dann soll die Distanz zur nächsten Position berechnet werden und der Stepper dort hin fahren. Dann ist die Startposition erreicht, und die Loop() kann beginnen.

#define Microstepping 4                    // Stepperdriver is set to 1/n microsteps (here 1/4)
#define StepperStepsPerRevolution 200           //one revolution of the stepper is 200 steps
int StepsPerRevolution = (StepperStepsPerRevolution * Microstepping);       // results in 800 steps per one revolution of the stepper

#include <AccelStepper.h>
#include <AS5600.h>
int n;
//int Pos[4] = {176, 1202, 2222, 3250}; // Der Winkelsensor hat Werte für 0-360° Sensorradposition von 0-4095. Diese vier Werte in dem Array markieren im Wertebereich 0-4095 die Positionen der vier Mulden im Sensorrad
int Pos[4] = {0, 1024, 2048, 3072}; // Testdaten Absolutpositionen
int RelativeStep[4];
#define STEPpin  14
#define DIRpin   12
unsigned long previousMillis = 0;
unsigned long stepperMillis = 0;
const long interval = 10;
AS5600 encoder;
int output;
AccelStepper stepper(AccelStepper::DRIVER, STEPpin, DIRpin);
void setup()
{
  Wire.setClock(400000L); //setzt die I²C Taktfrequenz auf 400kHz
  Serial.begin(9600);
  stepper.setMaxSpeed(10000); // 10000 ist wunderbar
  stepper.setAcceleration(100000); // 100000 ist schön schnell, 200 ist schön langsam

  RelativeStep[0] = (map(Pos[1], 0, 4095, 0, StepsPerRevolution)) - (map(Pos[0], 0, 4095, 0, StepsPerRevolution));
  RelativeStep[1] = (map(Pos[2], 0, 4095, 0, StepsPerRevolution)) - (map(Pos[1], 0, 4095, 0, StepsPerRevolution));
  RelativeStep[2] = (map(Pos[3], 0, 4095, 0, StepsPerRevolution)) - (map(Pos[2], 0, 4095, 0, StepsPerRevolution));
  RelativeStep[3] = StepsPerRevolution - (map(Pos[3], 0, 4095, 0, StepsPerRevolution));


  if (Pos[0] - encoder.getPosition() == 0) {}
  else if (Pos[0] - encoder.getPosition() > 0) {
    stepper.runToNewPosition(Pos[0] - encoder.getPosition());
  }
  else if (Pos[1] - encoder.getPosition() == 0) {}
  else if (Pos[1] - encoder.getPosition() > 0) {
    stepper.runToNewPosition(Pos[1] - encoder.getPosition());
  }
  else if (Pos[2] - encoder.getPosition() == 0) {}
  else if (Pos[2] - encoder.getPosition() > 0) {
    stepper.runToNewPosition(Pos[2] - encoder.getPosition());
  }
  else if (Pos[3] - encoder.getPosition() == 0) {}
  else if (Pos[3] - encoder.getPosition() > 0) {
    stepper.runToNewPosition(Pos[3] - encoder.getPosition());
  }
  else stepper.runToNewPosition(4095 - encoder.getPosition());
}

void loop() {
  // put your main code here, to run repeatedly:

}

Die Berechnung funktioniert soweit, es fehlt mir nur noch der Befehl, um den Stepper blockierend in die Startposition zu fahren.
Welcher ist das?
runToNewPosition scheint nicht zu funktionieren?!

Mit der Stepper-Funktion kann ich dir nicht helfen, aber wenn ich das richtig sehe kannst du die Hälfte der if-Abfragen einsparen.

if (Pos[0] - encoder.getPosition() == 0) {}

usw., wenn Position == 0 dann mache nichts, verstehe ich das richtig, oder soll da noch was passieren? Sonst lass die gleich weg und frag nur > 0 ab.

Hi

.runspeedto() oder so müsste Die heißen - kA, ob Da Rampen drin sind - sonst: StateMaschine ginge auch in setup() in einer While ...

Oder: Du machst EINEN Step, prüfst die Entfernung und bei NICHT NULL macht Du EINEN Step, .... wieder While in setup();

MfG

Zuerst mal ist ein == bei solchen Sachen sehr gefährlich da da immer eine gewisse Ungenauigkeit drin ist wegen dem ungeraden Verhältnis zu Schritten und Winkelauflösung bzw der geringeren Schrittauflösung gegenüber der Positionsauflösung. Du mußt eine gewisse "Unschärfe" zulassen.
sagst daß ein Winkelfehler von 2-3 Grad annehmbar ist.
Dann weißt Du doch an wechen Zielwert der Motor sich nähert. wenn er von der Position 1 zu 2 bewegt dann brauchst Du Position 0,1 und 3 nicht kontrollieren.

Am Anfang kannst Du das auch einfach in einer Forschleife machen:
for (byte i = 0; i<4; i++)
{
Wenn Istwert ca Pos ist, dann ist die istposition = i.
}
Das ca kann man programmieren indem man kontrolliert ob der Istwert > sollwert - Abweichung && Istwert < sollwert + Abweichung kontrolliert.
Ein Winkel von 2-3 entspricht einem Wert von 23 bis 34 bei einem Encoder von 12 Bit pro Umdrehung.
Grüße Uwe

usw., wenn Position == 0 dann mache nichts, verstehe ich das richtig, oder soll da noch was passieren? Sonst lass die gleich weg und frag nur > 0 ab.

Ist das nicht nur halb durchdacht?
Wenn ich die Situation == 0 nicht abfrage und bei TRUE nicht aus der Abfrage aussteige, so wird dann auch aus == 0 zur nächsten Position gefahren, was ja eigentlich unnötig ist.

Zuerst mal ist ein == bei solchen Sachen sehr gefährlich da da immer eine gewisse Ungenauigkeit drin ist wegen dem ungeraden Verhältnis zu Schritten und Winkelauflösung bzw der geringeren Schrittauflösung gegenüber der Positionsauflösung. Du mußt eine gewisse "Unschärfe" zulassen.

Warum ist das == sehr gefährlich?

In dem extrem unwahrscheinlichen Fall, dass der Sollwert und der Istwert genau == sind, soll nix passieren. Das ist doch nicht falsch oder gefährlich?! Und genauso funktioniert es.

Bei allen anderen Werten wird die Differenz zur nächsten möglichen Position berechnet und dann dort hingefahren. Und genauso funktioniert es.

Ich hatte noch zwei kleine Rechenfehler drin, aber nun geht's mit der etwas umständlichen if..then..else Verschachtelung.

Hier der funktionierende Sketch, der noch mit einer Menge Debug Ausgaben versehen ist, aber das macht es leichter zu verstehen:

#define Microstepping 4                    // Stepperdriver is set to 1/n microsteps (here 1/4)
#define StepperStepsPerRevolution 200           //one revolution of the stepper is 200 steps
int StepsPerRevolution = (StepperStepsPerRevolution * Microstepping);       // results in 800 steps per one revolution of the stepper

#include <AccelStepper.h>
#include <AS5600.h>
int n;
int Pos[4] = {115, 1110, 2172, 3236}; // Der Winkelsensor hat Werte für 0-360° Sensorradposition von 0-4095. Diese vier Werte in dem Array markieren im Wertebereich 0-4095 die Positionen der vier Mulden im Sensorrad
//int Pos[4] = {0, 1024, 2048, 3072}; // Testdaten Absolutpositionen
int RelativeStep[4];
#define STEPpin  14
#define DIRpin   12
unsigned long previousMillis = 0;
unsigned long stepperMillis = 0;
const long interval = 10;
AS5600 encoder;
int output;
AccelStepper stepper(AccelStepper::DRIVER, STEPpin, DIRpin);
void setup()
{
  Wire.setClock(400000L); //setzt die I²C Taktfrequenz auf 400kHz
  Serial.begin(9600);
  stepper.setMaxSpeed(500); // 10000 ist wunderbar
  stepper.setAcceleration(100000); // 100000 ist schön schnell, 200 ist schön langsam

  RelativeStep[0] = (map(Pos[1], 0, 4095, 0, StepsPerRevolution)) - (map(Pos[0], 0, 4095, 0, StepsPerRevolution));
  RelativeStep[1] = (map(Pos[2], 0, 4095, 0, StepsPerRevolution)) - (map(Pos[1], 0, 4095, 0, StepsPerRevolution));
  RelativeStep[2] = (map(Pos[3], 0, 4095, 0, StepsPerRevolution)) - (map(Pos[2], 0, 4095, 0, StepsPerRevolution));
  RelativeStep[3] = StepsPerRevolution - (map(Pos[3], 0, 4095, 0, StepsPerRevolution));

  delay(100);
  Serial.println("");
  Serial.println("Start");
  Serial.print("Ist-Position: ");
  Serial.println(encoder.getPosition());
  if (Pos[0] - encoder.getPosition() == 0) {
    Serial.println("Steht bereits auf Startposition");
  }
  else if (Pos[0] - encoder.getPosition() > 0) {
    Serial.print("Pos0 ist am naechsten: ");
    Serial.println(Pos[0]);
    Serial.print("fahre weiter steps: ");
    Serial.println(Pos[0] - encoder.getPosition());
    stepper.runToNewPosition(map(Pos[0] - encoder.getPosition(), 0, 4095, 0, StepsPerRevolution));
  }
  else if (Pos[1] - encoder.getPosition() == 0) {
    Serial.println("Steht bereits auf Startposition");
  }
  else if (Pos[1] - encoder.getPosition() > 0) {
    Serial.print("Pos1 ist am naechsten: ");
    Serial.println(Pos[1]);
    Serial.print("fahre weiter steps: ");
    Serial.println(Pos[1] - encoder.getPosition());
    stepper.runToNewPosition(map(Pos[1] - encoder.getPosition(), 0, 4095, 0, StepsPerRevolution));
  }
  else if (Pos[2] - encoder.getPosition() == 0) {
    Serial.println("Steht bereits auf Startposition");
  }
  else if (Pos[2] - encoder.getPosition() > 0) {
    Serial.print("Pos2 ist am naechsten: ");
    Serial.println(Pos[2]);
    Serial.print("fahre weiter steps: ");
    Serial.println(Pos[2] - encoder.getPosition());
    stepper.runToNewPosition(map(Pos[2] - encoder.getPosition(), 0, 4095, 0, StepsPerRevolution));
  }
  else if (Pos[3] - encoder.getPosition() == 0) {
    Serial.println("Steht bereits auf Startposition");
  }
  else if (Pos[3] - encoder.getPosition() > 0) {
    Serial.print("Pos3 ist am naechsten: ");
    Serial.println(Pos[3]);
    Serial.print("fahre weiter steps: ");
    Serial.println(Pos[3] - encoder.getPosition());
    stepper.runToNewPosition(map(Pos[3] - encoder.getPosition(), 0, 4095, 0, StepsPerRevolution));
  }
  else {
    Serial.print("Pos0 ist am naechsten: ");
    Serial.println(Pos[0]);
    Serial.print("fahre weiter steps: ");
    Serial.println(4095 - encoder.getPosition() + Pos[0]);
    stepper.runToNewPosition(map(4095 - encoder.getPosition() + Pos[0], 0, 4095, 0, StepsPerRevolution));
  }
  Serial.print("Ist-Position: ");
  Serial.println(encoder.getPosition());
}

void loop() {
}

In dem extrem unwahrscheinlichen Fall, dass der Sollwert und der Istwert genau == sind, soll nix passieren. Das ist doch nicht falsch oder gefährlich?! Und genauso funktioniert es.

Daß Istwert gleich Sollwert ist kann auch nie passieren.

Aus der anderen Diskussion entnehme ich:
Motor Winkelauflösung: 200 Schritte pro umdrehung und 1/4 Microschritte das sind 800 Schritte pro Umdrehung.
Das entspricht einem Winkel von 0,45° pro Schritt.

Die Auflösung des Encoders ist 4096 Schritte pro Umdrehung.

Das heißt daß pro Motorschritt der Encoder 5,12 Schritte hat. Da ist ein zusammentreffen der gelesenen Werte mit dem Sollwerten für die Position ein Zufall.

Grüße Uwe

Da ist ein zusammentreffen der gelesenen Werte mit dem Sollwerten für die Position ein Zufall.

Ja das stimmt.
Aber wenn es mal so sein sollte (was natürlich extrem unwahrscheinlich ist) dann soll eben nix passieren.

Also ist das doch nicht falsch?!
Nur eben extrem unwahrscheinlich.

Wenn ich die Zeile weglasse und zufällig Istwert = Sollwert sein sollte, dann würde der Stepper die nächste Position anfahren, obwohl dies nicht nötig wäre.

Oder hab ich da irgendwo noch einen Denkfehler drin?!

Aber wenn es mal so sein sollte (was natürlich extrem unwahrscheinlich ist) dann soll eben nix passieren.

Aber wenn zB der Wert 1 drüber oder drunter ist dann hat es keinen Sinn daß der Motor sich dreht weil dadurch wieder viel drunter oder drüber (in diesem Fall 4 Encoderschritte) posizioniert. Das weil jeder Motorschritt 5 Encoderschritte entspricht.

Grüße Uwe

Da hast du natürlich auch Recht.
Wie baut man das denn geschickt auf?

siehe #8

#8 hab ich leoder nicht so ganz verstanden, bzw. kann das spontan nicht umsetzen

du sollst Code-Duplikate vermeiden und das was du über Iterationen schaffen kannst z.B. in eine For-Schleife packen:

das ersetzt 4 mal

  for (byte echtSoSchwer = 0; echtSoSchwer < 4; echtSoSchwer++)
  {
    if (Pos[echtSoSchwer] - encoder.getPosition() == 0) {
      Serial.println(F("Steht bereits auf Startposition"));
    }
    else if (Pos[echtSoSchwer] - encoder.getPosition() > 0) {
      Serial.print(F("Pos"));
      Serial.print(echtSoSchwer);
      Serial.print(F(" ist am naechsten: "));
      Serial.println(Pos[echtSoSchwer]);
      Serial.print(F("fahre weiter steps: "));
      Serial.println(Pos[echtSoSchwer] - encoder.getPosition());
      stepper.runToNewPosition(map(Pos[echtSoSchwer] - encoder.getPosition(), 0, 4095, 0, StepsPerRevolution));
    }
  }

themanfrommoon:
Gibt es vielleicht eine elegantere Lösung?

Ich denke ja, es ist eine Frage der Formulierung.

themanfrommoon:
... ob die zufällige Startposition mit einer der vier absoluten Positionen übereinstimmt. Wenn ja, soll nichts passieren.
Wenn nein, dann soll die Distanz zur nächsten Position berechnet werden und der Stepper dort hin fahren.

Der Motor soll sich bewegen, wenn die Ist-Position nicht mit einer der vier absoluten Positionen übereinstimmt. Die boolsche Algebra hilft.

Übrigens ist encoder.getPosition() immer ein zeitaufwändiger Zugriff mittels I2C, das muß nicht sein. Mal ein Versuch:

int aktPos = encoder.getPosition();
if( (Pos[0] != aktPos) && (Pos[1] != aktPos) && (Pos[2] != aktPos) && (Pos[3] != aktPos) )
{
  stepper.move(...);
}

Sieht eleganter aus, wenn es dann noch funktioniert ...

Hi

Du berechnest zu jeder der Positionen die Entfernung.
Von den ganzen Entfernungen suchst Du Dir die Kleinste aus (und merkst Dir dieses Ziel).
Wenn diese Entfernung kleiner als Deine halbe tolerierbare Abweichung ist: fertig.
(Halbe Abweichung, da Du ja links wie rechts davon stehen kannst und das Ziel in der Mitte liegen soll)
Sonst: Fahrt zum eben gemerkten Ziel, dem Endpunkt, Der der aktuellen Position am Nahesten liegt - nur in die richtige Richtung starten.

MfG

@noiasca:
tja, so hab ich am Anfang auch gedacht. So funktioniert es aber leider nicht.
In meinem verschachtelten if..then..else Konstrukt kommt immer nur EINE Aktion zum tragen. Nur ein Ausgang der Schleifen wird TRUE und somit wird nur eine Bewegung gemacht.
In deiner Variante werden je nach aktueller Position bis zu 3 (oder4?!) Positionen neu angefahren.
Die anderen werden nicht verriegelt.

Es soll aber bei jedem Start nur einmal bis zur nächsten der jeweils 4 möglichen Startpositionen gedreht werden.

Man könnte das als switch case bauen, aber dann ist der Aufwand doch noch größer als mit dem if..then..else?!
Oder in deiner Schleife ne flag setzen. Aber dann sind es ja noch mehr if's die durchlaufen werden müssen?!

@agmue:
Das stimmt, daran hatte ich auch schon gedacht, es war etwas quick&dirty die Position mehrfach abzufragen. Da es nur im Setup war hatte ich mal darüber hinweggesehen. Falls das Signal verrauscht sein sollte (war es bis jetzt nicht), könnten da möglicherweise komische Effekte zustande kommen.
Ich baue das also schnell um und frage den Wert wie von dir vorgeschlagen zum Start nur noch 1x ab.

-> muss ich noch machen

@postmaster-ino:
Die Richtung muss prinzipbedingt immer die gleiche sein, ich darf nicht rückwärts drehen, sonst verklemmt etwas oder ist nicht da wo es sein soll.
Bis auf die tolerierbare Abweichung mache ich das ja momentan schon (funktioniert ja auch).
Ein reboot vom ESP scheint leider einen Schritt auszulösen (Messwert steigt um 5 an), daher dreht er bei jedem reboot etwas weiter, ansonsten müsste er ja beim nächsten reboot stehenbleiben, wenn er einmal eine der 4 möglichen Starpositionen erreicht hat und danach nicht weitergedreht hat.

Tatsächlich habe ich momentan den Fall, dass bei Position 0 der Messwert und der Sollwert übereinstimmt und die Meldung "Steh bereits auf Startposition" kommt. Und zwar reproduzierbar bei jeder Umdrehung. ....warte ich mal ein wenig bis der Motor warm wird und der Messwert möglicherweise um 1/4096stel Umdrehung (0,0879°) driftet....