Steuerung langsamer Servo und Ultrasonic

Hallo an alle, da ich so ziemlich neu in dem ganzen Thema bin habe ich mich dennoch mal an eine kleine Schaltung gewagt. Das problem was ich habe ist das ich aus der "Schleife" nicht mehr weiss wie rauskommen so das der servo bei < abstand auf 0 und bei > Abstand auf 190 stehen Bleibt.

Wenn jemand vielleicht noch eine idee das ganze mit 2 servos umzusetzen so das beide servos gleichzeitig entgegengesetzt laufen .

Hier mein kleines Projekt : so sollte es mal aussehen :slight_smile:

ps. Ein großes danke schön nochmal an [noiasca] :slight_smile:

Hallo Andre

Du könntest deinen Skech hier ins Forum stellen, und dabei codetags nutzen. Dazu gibts oben die in der Symbolleiste des Editors die "Preformatted Text" Funktion. Oder in der IDE "für Forum kopieren. Dann schreibst Du noch dazu welche Schleife du meinst, dann müssen wir nicht raten.
Kannst auch gerne mal lesen "Wie benutze ich das Forum"

Heinz

2 Likes

ich kann das nicht nachvollziehen.

Was war an den anderen Beispielen die ich EXTRA FÜR DICH gemacht habe unverständlich?
Alles umsonst?!?

Mehrere Servos synchron ansteuern - International / Deutsch - Arduino Forum

was wird in diesem Thread anders sein - außer dass du wieder mit einem blockierenden Delay arbeitest?

Und der Sketch aus der wokwi kompiliert nicht :wink:

Nein es war überhaupt nicht umsonst.. das Problem an der Sache war nur dass sie servo s unterschiedlich laufen... Ich konnte sehr gute Erfahrung durch deine Hilfe sammeln😊

Deine Hilfe wird in einem anderen Projekt von mir umgesetzt

Moin @andre_swiss,

im Wokwi-Sketch sind ein paar geschweifte Klammern fehlerhaft eingefügt, die das Compilieren verhindern ...

Hier der korrigierte Sketch:

#include <ESP32Servo.h>

#define TRIG_PIN  23  // ESP32 pin GIOP23 connected to Ultrasonic Sensor's TRIG pin
#define ECHO_PIN  22  // ESP32 pin GIOP22 connected to Ultrasonic Sensor's ECHO pin
#define SERVO_PIN 26  // ESP32 pin GIOP26 connected to Servo Motor's pin
#define DISTANCE_THRESHOLD  50 // centimeters

Servo servo; // create servo object to control a servo

int angle =0;
int angleStep = 1;

int angleMin =0;
int angleMax = 190; 

// variables will change:
float duration_us, distance_cm;

void setup() {
  Serial.begin (9600);       // initialize serial port
  pinMode(TRIG_PIN, OUTPUT); // set ESP32 pin to output mode
  pinMode(ECHO_PIN, INPUT);  // set ESP32 pin to input mode
  servo.attach(SERVO_PIN);   // attaches the servo on pin 9 to the servo object

}

void loop() {
  // generate 10-microsecond pulse to TRIG pin
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // measure duration of pulse from ECHO pin
  duration_us = pulseIn(ECHO_PIN, HIGH);
  // calculate the distance
  distance_cm = 0.017 * duration_us;

  if (distance_cm < DISTANCE_THRESHOLD)
    for(int angle = 0; angle <= angleMax; angle +=angleStep) {
        servo.write(angle);
        Serial.println(angle);
        delay(30);   
    }
    
  for(int angle = 190; angle >= angleMin; angle -=angleStep) {
        servo.write(angle);
        Serial.println(angle);
        delay(50);
        
    }
// Hier war eine Klammer zuviel ->       }
  // print the value to Serial Monitor
  Serial.print("distance: ");
  Serial.print(distance_cm);
  Serial.println(" cm");

  delay(500);
// Hier war eine zweite Klammer zuviel ->       }
}

Auch wenn ich persönlich Wokwi für eine sehr gute Ergänzung halte, poste ich Sketche grundsätzlich hier im Forum. Auf diese Weise ist die Grundlage der Diskussion für alle sichtbar und sie bleibt vor allem gesichert für (fast) alle Zeiten nachlesbar.

Ist zwar nur eine kleine Sache, aber hier ein Link auf die korrigierte Version:

https://wokwi.com/projects/357383198631630849

Allerdings- wie @noiasca bereits geschrieben hat - die Verwendung von delay() behindert (wenn nicht verhindert) von vornherein, aus einem Sketch heraus mehrere Servos quasiparallel anzusteuern.

Und mit der von @noiasca im Post #50 bei
https://forum.arduino.cc/t/mehrere-servos-synchron-ansteuern/1030870/50

bereitgestellten Lösung sind m.E. bereits alle Funktionen gegeben, die es braucht, zwei (oder mehr) Servos unabhängig voneinander - und gerne auch entgegengesetzt - anzusteuern ... ?!?

Vielleicht kannst Du Dein Problem etwas konkretisieren? Oder benötigst Du nur weitere Erläuterungen zur Anwendung der von @noiasca erstellten Klasse SmoothServo ?

Gruß

ec2021

2 Likes

Vielen vielen lieben dank für die Berichtigung.

"Vielleicht kannst Du Dein Problem etwas konkretisieren? Oder benötigst Du nur weitere Erläuterungen zur Anwendung der von @noiasca erstellten Klasse SmoothServo ? "

Zur Erklärung ist zu sagen : wenn ich 2 servos habe und sie laufen nicht wirklich synchron gehen mir die Servoarme kaputt .

Das Bild dient nur zur Veranschaulichung:

Wenn der Staubsauger in die "Garage " fährt sollen die servos Langsam zufahren
und wenn er losfährt sollen die servos schnell auf fahren.

Ich würde das mit einer Feder oder gegengewichten realisieren.

Ein Motor bringt das Tor langsam runter und stopt in einer bestimmten Stelle. Dreht er ein wenig weiter verliert er den Kontakt zum tor und dieses öffnet sich durch sein Gegengewicht /Feder. Wenn der Motor weiter dreht, rastet er dann wieder ein um es langsam zu schießen.
Grüße Uwe

das ist nicht das was ich mir so vorgestellt habe .....

Aha, das erscheint mir (wie auch @uwefed) eher eine Aufgabe der Mechanik als der Software zu sein.

Wenn man die starre Koppelung der Servo-Arme mit dem Tor aufhebt und dort auf beiden Seiten eine Art radial verlaufendes "Langloch" verwenden würde, so dass zwischen den beiden Hubarm-Höhen ein gewisser Schlupf möglich ist, könnte das zu Verkantungen führen ...

Wenn das Tor jedoch links und rechts in zwei Schienen laufen würde und die Antriebsarme "lose" gekoppelt wären, könnte es funktionieren.

@uwefed 's Vorschlag hat natürlich dadurch besonderen Charme, dass die mechanische Belastung der Servos bei geeigneter Auslegung deutlich reduziert wird. Im besten Fall wären nur Haft- und Reibungswiderstände zu überwinden.

1 Like

Möglicherweise hast Du nur Probleme, das (schon weitgehend vollständige!) Beispielprogramm von @noiasca in Details anzupassen:

Für Endwinkel und Geschwindigkeit beim Öffnen und Schließen des Tores sind die folgenden Zeilen zuständig:

void doorOpen() {
  Serial.println(F("move open"));
  smoothServoA.set(90, 1000);
  smoothServoB.set(90, 1000);
  //smoothServoA.servo.write(90); // hardcoded write
}

void doorClose() {
  Serial.println(F("move close in a different speed"));
  smoothServoA.set(180, 500);    // angle, milliseconds for movement
  smoothServoB.set(115, 500);
}

Der erste Parameter der set-Funktion gibt den zu erreichenden Endwinkel an, der zweite beeinflusst die Zeit für das Erreichen des Endwinkels. Das geht natürlich nur in gewissen Grenzen, da ein Servo eine endliche Zeit benötigt, um einen bestimmten Winkel zu überstreichen, die nicht beliebig klein werden kann.

Am einfachsten ist es, wenn Du mit Deinem Aufbau und dem Sketch ein wenig experimentierst; ggf. am Anfang ohne das Tor montiert zu haben, um "Unfälle" zu vermeiden :wink:

P.S.: Im Beispiel passt die Aussage "move close in a different speed" nicht ganz zu den Werten; hier wäre "move close to different angles in the same time" korrekt. Aber vermutlich wurden die Parameter irgendwann verändert, ohne die Print-Ausgabe anzupassen. Ist eben als Beispiel zum Nachvollziehen und Mitdenken gedacht und ändert nichts an der guten Lösung der Aufgabe...

P.S.: Die Aussage "move close in different speed" wird im Beispiel dadurch demonstriert, dass die beiden Servos gleichzeitig unterschiedliche Winkelvorgaben erreichen, damit also ServoB im gleichen Zeitraum einen kleineren Winkel durchläuft. Alternativ kann man u.a. für beide Servos den gleichen ZielwInkel, jedoch unterschiedliche Zeiten vorgeben. (Danke @noiasca für die Korrektur!). Und an der guten Lösung ändert sich nichts :wink:

Gruß
ec2021

soll heißen, bei Unterschiedlich zu bewegenden Graden (alter Wert zu neuer Wert) und einer Vorgegeben gleichen Zeit, bewegen sie die Servos unterschiedlich schnell.

Das stimmt natürlich; werde das oben korrigieren!

Nee, was rechtens ist, soll auch rechtens bleiben :wink:

1 Like

Mich hat gestört, dass du dafür zwei Servos brauchst. Habe dann mal nach einem gesucht, der das ganze mit einem Servo baut, und siehe einer an, habe einen gefunden. Also das hier wäre wohl meine Vorgehensweise. Spricht was dagegen, nur einen Servo zu benutzen?

Franz

1 Like

Wow danke dir das Video kannte ich noch nicht ...... Sieht schon mal sehr gut aus .
Danke dir Franz :slight_smile:

OK, gerne. Das Thema hat mich ein bischen angefixt. Da wir auch so ein Ding unterm Schrank haben. Kann gut sein, dass ich mich damit in näherer Zeit auch beschäftige. Viel Spass weiter.

Franz

Lach @Franz54 ja da sind wir schon zwei :sweat_smile:

Für das genaue Vorgehen beim Öffnen/Schließen wäre es wichtig zu wissen, ob Dein Saugroboter nur genau dann wieder in die Garage fährt, wenn er laden will, oder ob er ggf. auch mal "zufällig" dort wieder landen kann ... Viele der Saugroboter fahren die Flächen nach mehr oder weniger Zufallsprinzip ab und kommen dabei gelegentlich auch wieder an der Station vorbei, ohne schon laden zu wollen.

Gerade bei der Enge der Garage braucht es (wie man am von Dir bereitgestellten Video sehen kann) einige Zeit, bis sie wieder herausfahren. Es wäre unschön, dann bereits das Tor zu schließen, da der Roboter noch nicht ladebereit ist.

Man könnte die Tatsache nutzen, dass sich der Roboter für den Ladezyklus in der Station stehen bleibt. Dann würde man das Torschließen davon abhängig machen, dass

a) ein sehr kurzer Abstand zum Ultraschallsensor
b) für einen längeren Zeitraum (z.B. > 10 s)

stabil gehalten wird. Das wäre der Trigger zum Torschließen.

Weiterhin ist es interessant zu erfahren, wie Dein Roboter gestartet wird:

  • Per WLAN?
  • Per Zeitvorgabe?
  • Per Infrarot-Fernbedienung?

Die ersten beiden funktionieren natürlich bei geschlossener Garage; für die IR-Fernbedienung würde es einen optischen Durchbruch (oder einen externen Sensor) benötigen.

Ich habe hier mal eine Lösung mit einem Servo und einer State Machine erstellt, die momentan auf Taster und Leds verzichtet (lassen sich einfach "nachrüsten"), und die von mir oben genannten Aspekte berücksichtigen sollte ...

/*
Steuerung eines Servos mit einer State Machine
Programmiert von ec2021
Stand: 25.02.2023, 15:40 Uhr

INIT:
      Initialisierung des Servos, der die Winkelstellung OpenAngle einnimmt.
      Danach Wechsel zu WAIT

WAIT:
      Regelmässiges Messen der Entfernung zu einem Objekt

      Ist die Entfernung für "AcceptTime" Sekunden kürzer oder gleich der "CloseDistance" cm
      und ist der vorherige Zustand OPENING so wird der Zustand CLOSING gestartet.
      War der vorherige Zustand CLOSING oder INIT, bleibt es bei WAIT.

      Ist die Enfernung größer als "OpenDistance" cm und der vorherige Zustand INIT oder CLOSING,
      so wird der Zustand OPENING gestartet.

     In allen anderen Fällen, bleibt der Zustand auf WAIT

OPENING: 
      Fährt den Servo in Stellung "OpenAngle" mit Einzelschritt alle "stepTimeOpening" ms
      Beim Erreichen des "OpenAngle" wechselt der Zustand zu WAIT.

CLOSING: 
      Fährt den Servo in Stellung "CloseAngle" mit Einzelschritt alle "stepTimeClosing" ms
      Beim Erreichen des "CloseAngle" wechselt der Zustand zu WAIT.

*/


#include <ESP32Servo.h>

static const int servoPin = 14;                //Anschlusspin des Servos
static int trigPin                =  23;       // Triggerpin für den  Ultraschall-E-Messer
static int echoPin                =  22;       // Echopin für den  Ultraschall-E-Messer 

static unsigned long stepTimeClosing = 20;     // Einzelschrittzeit in ms beim CLOSING
static unsigned long stepTimeOpening = 20;     // Einzelschrittzeit in ms beim OPENING
static unsigned long AcceptTime      = 10000;  // 10 Sekunden
static int OpenAngle              = 90;        // Winkelstellung nach OPENING             
static int ClosedAngle            = 180;       // Winkelstellung nach CLOSING
static int CloseDistance          =  5;        // Maximalentfernung, die zu CLOSING führt,
                                               // wenn sie für AcceptTime ms besteht
static int OpenDistance           =  15;       // Minimalenfernung, die sofort zum Öffnen führt

Servo servo1;

int ActualAngle     = 90;                      // Aktuelle Winkelvorgabe des Servos
unsigned long CloseDistanceTime = 0;           // Systemzeit in ms, zu der zuletzt eine
                                               // Entfernung größer als CloseDistance gemessen wurde
int dist = 0;                                  // Letzter gemessener Entfernungswert

void setup() {
    Serial.begin(115200);
    Serial.println("Start");
    servo1.attach(servoPin);
    pinMode(trigPin, OUTPUT);
    pinMode(echoPin, INPUT); 

}

void StateMachine(){
 enum State {INIT,OPENING,CLOSING,WAIT};
 static State state = INIT;
 static State prevState  = INIT;
 static uint32_t previousMillis = 0;
 
 switch(state) {
   case INIT : {
       Serial.println("INIT");
       servo1.write(ActualAngle);
       state = WAIT;
       prevState = INIT;
       previousMillis = millis();
       Serial.println("WAIT");
       break;
   }
   case WAIT : {
       if (millis() - previousMillis > 100){
          dist = getDistance();
          if (prevState == INIT || prevState == OPENING) {
             if (dist > CloseDistance) CloseDistanceTime = millis();
             if (millis()-CloseDistanceTime > AcceptTime){
              state = CLOSING;
              Serial.println("CLOSING");
             }
          }

          if (prevState == CLOSING) {
              if (dist > OpenDistance) {
                state = OPENING;
                Serial.println("OPENING");
              }
            }
          previousMillis = millis();
       };
       break;
   }
   case OPENING  : {
       if (millis() - previousMillis > stepTimeOpening){
           if (ActualAngle > OpenAngle) ActualAngle--;
           previousMillis = millis();
       }
       if (ActualAngle == OpenAngle) {
           prevState = state; 
           state = WAIT;
           Serial.println("WAIT ");
           previousMillis = millis();
           }
       break;
   }
   case CLOSING: {
       if (millis() - previousMillis > stepTimeClosing){
           if (ActualAngle < ClosedAngle) ActualAngle++;
           previousMillis = millis();
       }
       if (ActualAngle == ClosedAngle) {
           prevState = state; 
           state = WAIT;
           Serial.println("WAIT");
           previousMillis = millis();
        }
        break;
   }
 }
 servo1.write(ActualAngle);
}

int getDistance() {
  float duration_us, distance_cm;
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration_us = pulseIn(echoPin, HIGH);
  distance_cm = 0.017 * duration_us;
return int(distance_cm);
}


void loop() {
    StateMachine();
}

Der Sketch läuft bei Wokwi unter

https://wokwi.com/projects/357561697858544641

In dieser Version wird der Servo beim Start ohne zeitliche Steuerung in den OpenAngle kommandiert, so dass eine definierte Ausgangslage besteht. Steht der Roboter in der Station (die gemessene Entfernung ist für AcceptTime kleiner/gleich der CloseDistance, so wird das Tor nach AcceptTime geschlossen. In diesem Fall dauert das nach dem Einschalten 10 Sekunden. AcceptTime kann gerne auch kürzer sein, sollte aber so gewählt werden, dass ein verfrühtes und unnötiges Schließen vermieden wird.

Wenn die Startlage "Geschlossen" sein soll, kann das natürlich auch angepasst werden. Vermutlich ist es aber beim Einschalten der Anlage günstiger, wenn das Tor auf ist und nur schließt, wenn der Roboter auch in der Station steht.

Zwischen CloseDistance und OpenDistance habe ich noch einen Abstand gelassen, der auch bei kurzer AcceptTime ein hin- und her vermeiden sollte.