Greifautomat mit ATmega2560, Schrittmotoren und Servos - AZ-Delivery

Wie oft haben Sie schon diese Automaten gesehen, mit dem man vermeintlich sehr leicht an eins von vielen Kuscheltieren kommen kann. Sie werfen eine Münze ein, bewegen mit dem Joystick die Kralle, richten sie über dem Kuscheltier aus und drücken den Knopf. Der Greifer fährt runter, scheinbar perfekt, die Kralle schließt sich, der Krahn fährt hoch und plötzlich fällt das Kuscheltier wieder herunter. Eventuell versuchen Sie es erneut und am Ende gehen Sie, oder ihre Kinder, ohne Kuscheltier nach Hause.

Wie wäre es, eine eigene Maschine zum Fangen von Plüschtieren zu basteln? Nun, in diesem Tutorial zeige ich eine einfache Möglichkeit, wie Sie Ihren eigenen Greifautomaten bauen können. Damit können Sie Kuscheltiere, oder alles andere einfangen, was Sie hineinstecken. Also legen wir los.

Benötigte Hardware

Material für die Konstruktion

Benötigte Software, Libraries und Sketches

Schaltplan und Beschreibung der Komponenten

Schaltplan

Ich verwende drei 28BYJ48-Schrittmotoren und ihre entsprechenden ULN2003-Treiber. Ein Motor ist für die Vorwärts- oder Rückwärtsbewegung, ein anderer für die Rechts- oder Linksbewegung des Klauenwagens und der dritte Motor wird verwendet, um die Klaue anzuheben oder abzusenken. Um die Bewegung des Wagens zu steuern, verwende ich das KY-023-Joystick-Modul, für das Öffnen und Schließen der Klaue den MG90s-Servomotor. Ich nutze außerdem vier KY-10-Module mit Optokoppler-Sensoren, um die Endpositionen der Motoren zu erkennen. Der Greifer wird dann am Ende des Weges einfach in die entgegengesetzte Richtung weiterfahren. Der Sensorwird auch verwendet, damit sich der Greifer öffnet, wenn er die "Home"-Position erreicht. All dies wird von einem AZ-MEGA 2560 Mikrocontroller gesteuert.

 

Beschreibung der Funktionalität und des Sketches

Die Motoren werden in zwei Geschwindigkeiten drehen. Die niedrige Geschwindigkeit dient zur genaueren Kontrolle der Bewegung des Greifers mit Hilfe des Joysticks. Sobald er sich über dem Objekt befindet, wird der Taster des Joysticks betätigt und er fährt nach unten. Hat er dort sein Ziel erreicht, schließt der Servomotor die Kralle. Es folgt eine Bewegung nach oben, dann zur Seite und anschließend nach vorn. Immer dann, wenn der Greifer seine Ausgansposition der jeweiligen Strecke erreicht hat. Dann wird die Kralle geöffnet und das Objekt fällt heraus.

Ich erläutere nun den Ablauf des Sketches.

Zu Beginn wird die Servo-Bibliothek eingebunden. Damit können wir ein Servo-Objekt erzeugen.

#include <Servo.h>
Servo servo_claw;

Für den Joystick werden drei Variablen deklariert und definiert. Sie enthalten die Pin-Nummern des Tasters und der Achsen. X- und Y-Position sind analoge Werte zweier Potentiometer. Daher werden diese Anschlüsse mit den anlogen Pins des Mikrocontrollers verbunden.

const int SW_pin = 6;
const int X_pin = 0;
const int Y_pin = 1;

Als Nächstes werden Variablen für die Verwendung der Schrittmotoren deklariert. speed_motor definiert die Geschwindigkeit. Die Variable stepCounter beschreibt die Position im Array stepsLookup. Die Motoren müssen 4076 Schritte durchführen, um eine Komplette Drehen zu vollenden. Diesen Wert enthält die Variable stepsPerRev. numSteps ist die Anzahl der Elemente des Arrays. stepsLookup[] enthält die Schritte der Motoren in Halbphasenfolge.

int speed_motor = 1200;
int stepCounter = 0;
int stepsPerRev = 4076;
const int = numSteps;
const int stepsLookup[numSteps] = { B1000, B1100, B0100, B0110, B0010, B0011, B0001, B1001 };

Es werden vier Optokoppler-Sensoren für die Begrenzung des Schlittenweges verwendet. Die maximale Grenze (limit_sensor) und die minimale Grenze (home_sensor), sind die äußeren Grenzen. An dieser Position öffnet sich der Greifer. Die folgenden Variablen definieren die Pin-Nummern der Sensoren am Mikrocontroller

int home_sensor_X = 53;
int limit_sensor_X = 52;
int home_sensor_Y = 51;
int limit_sensor_Y = 50;

und die Variablen zur Speicherung des Zustands der einzelnen Sensoren

int home_sensor_X_value;
int limit_sensor_X_value;
int home_sensor_Y_value;
int limit_sensor_Y_value;

Es folgt nun die setup()-Funktion.

Als erstes wird der Serial Monitor initialisiert, damit wir den Signalstatus unserer Komponenten sehen können.

Serial.begin(9600);

Mit dem nächsten Befehl wird der Pin des Mikrocontrollers für den Taster des Joysticks als Eingang konfiguriert. Der Pin verwendet den internen Pull-up-Wiederstand. Dadurch ist der Pin „active low“.

pinMode(SW_pin, INPUT_PULLUP);

Der Grund für die Aktivierung des internen Widerstands ist die Stabilität des Messwerts und die Vermeidung eines zusätzlichen Widerstands in unserer Schaltung. Mit der Pull-up-Modus-Konfiguration wird der Mikrocontroller-Pin mit +5 Vdc versorgt, während der Taster nicht gedrückt wird. Er hat dadurch einen HIGH-Pegel. Wird der Taster gedrückt, leiten wird die Spannung auf Masse umgeleitet und der Pegel des Pins geht auf LOW. Damit wird der Tastendruck erkannt.

Um den Servomotor zu verwenden, muss mit dem attach()-Befehl die Pin-Nummer übergeben werden. Mit dem write()-Befehl wird die Ausgangsposition (Greifer offen) des Servomotors in Grad übertragen.

servo_claw.attach(5);
servo_claw.write(50);

Im setup() werden auch die Pin-Nummern des Mikrocontrollers festgelegt, an die die drei Schrittmotoren angeschlossen werden. Sie müssen als Ausgang konfiguriert werden, da wir die entsprechenden Signale an die Treiber für den Vorwärts- oder Rückwärtslauf der Motoren senden werden.

Die Anschlüsse des X-Achsen-Motortreibers:

pinMode (29, OUTPUT);
pinMode (28, OUTPUT);
pinMode (27, OUTPUT);
pinMode (26, OUTPUT);

Die Anschlüsse des Y-Achsen-Motortreibers:

pinMode (25, OUTPUT);
pinMode (24, OUTPUT);
pinMode (23, OUTPUT);
pinMode (22, OUTPUT);

Die Anschlüsse für den Greifer-Motortreiber:

pinMode (33, OUTPUT);
pinMode (32, OUTPUT);
pinMode (31, OUTPUT);
pinMode (30, OUTPUT);

Nun müssen die Pins, an die die vier Optokoppler-Sensoren angeschlossen wurden, als Eingänge konfiguriert werden.

pinMode(home_sensor_X, INPUT);
pinMode(limit_sensor_X, INPUT);
pinMode(home_sensor_Y, INPUT);
pinMode(limit_sensor_Y, INPUT);

Am Ende des setup()-Blocks werden zwei Schleifen implementiert, um den Klauenschlitten nach dem Einschalten in der "Home"-Position der X- und Y-Achse zu positionieren. Es ist eine Zählschleife, in der mit jedem Schritt ein Aufruf der Funktion return_name_axis_home() durchgeführt wird. Ich erkläre hier die Schleife in Verbindung mit der X-Achse. Die andere Achse wird identisch angesteuert.

In return_X_axis_home() wird der stepCounter hochgezählt und zurückgesetzt, wenn der Wert der Variablen numSteps entspricht. Deren Wert ist 8. Danach wird X_axis_positioning_home() aufgerufen und der stepCounter übergeben. Die Variable stepCpunter ist der Index des Arrays stepsLookup[]. Sie wird an X_axis_positioning_home() übergeben. Dort wird jedes Bit einzeln ausgelesen und dessen Wert an den jeweiligen Pin ausgegeben. Wenn der Optokoppler-Sensor HIGH ist, wurde die „Home“-Position erreicht und der Motor wird ausgeschaltet. Dafür wird motor_X_off() aufgerufen. In der Funktion werden die Pins des Motors LOW, also aus, gesetzt. In der Übergeordneten for-Schleife wird mit der Variablen speed_motor als Pause die Geschwindigkeit des Motors bestimmt.

Wenn sich die X-Achse in der Ausgangsposition befindet, wird der gleiche Prozess für die andere Achse ausgeführt, um sie in die Ausgangsposition zu bringen. Es ändert sich nur der Name der Funktionen.

Sobald sich die beiden Achsen in der Ausgangsposition befinden, loop() ausgeführt. Als erstes werden der Zustand des Schalters und die Werte der Joystick-Achsen, sowie die vier Optokoppler-Sensoren, ausgelesen. Deren Daten und Zustände werden über den seriellen Monitor ausgegeben.

Serial.print("Switch:  ");
Serial.print(digitalRead(SW_pin));
Serial.print("\n");
Serial.print("X-axis: ");
Serial.print(analogRead(X_pin));  
Serial.print("\n");
Serial.print("Y-axis: ");
Serial.println(analogRead(Y_pin));
Serial.print("\n\n");
delay(20);

home_sensor_X_value = digitalRead(home_sensor_X);
Serial.print("Home sensor X: ");
Serial.println(home_sensor_X_value);
limit_sensor_X_value = digitalRead(limit_sensor_X);
Serial.println(limit_sensor_X_value);      
home_sensor_Y_value = digitalRead(home_sensor_Y);
Serial.print("Home sensor Y: ");
Serial.println(home_sensor_Y_value);      
limit_sensor_Y_value = digitalRead(limit_sensor_Y);
Serial.print("Limit sensor Y: ");
Serial.println(limit_sensor_Y_value);

Die folgenden if-Anweisungen reagieren auf Änderungen der X- und Y-Achsen des Joysticks.

Es sind vier Richtungen und der Druck auf den Taster möglich. Die Bewegungen sind durch Grenzwerte eingeschränkt. Außerhalb dieser Werte bewegen sich die Motoren nicht. Es ist aber möglich, die Richtung zu wechseln, wenn das Ende der Strecke erreicht ist.

if (analogRead(X_pin)>450 && analogRead(X_pin)<550) {
  motor_X_off();
}
  
if (analogRead(X_pin)>=551) {
  left_hand_movement();
}
 
if (analogRead(X_pin)<=449) {
  right_hand_movement();
}

Ist der ausgelesene Wert größer oder gleich 551, erfolgt der Aufruf der Methode left_hand_movement(), um den Greifer auf der X-Achse nach links zu bewegen. Der Zustand der Mikrocontroller-Pins wird von HIGH auf LOW und umgekehrt geändert, um die Motorspulen in der Halbphasen-Sequenz zu aktivieren und die entsprechende Bewegung durchzuführen. Wenn eine vollständige Sequenz ausgeführt wird, misst der Optokoppler-Sensor für die "Grundstellung" der Achse den Wert und es wird eine Prüfung mit einer einfachen Bedingung durchgeführt. Solange der Wert LOW ist, kann der Motor seine Bewegung nach links fortsetzen. Wenn bei der Prüfung der Bedingung der Wert HIGH ist, wird die Methode right_hand_movement() aufgerufen. Der Greifer bewegt sich weiter in die entgegengesetzte Richtung.

void left_hand_movement() {
  digitalWrite(29, LOW); 
  digitalWrite(28, LOW);  
  digitalWrite(27, HIGH);  
  digitalWrite(26, LOW);  
    delayMicroseconds(speed_motor);
  digitalWrite(29, LOW); 
  digitalWrite(28, LOW);  
  digitalWrite(27, HIGH);  
  digitalWrite(26, HIGH);  
    delayMicroseconds(speed_motor);
  digitalWrite(29, LOW); 
  digitalWrite(28, LOW);  
  digitalWrite(27, LOW);  
  digitalWrite(26, HIGH);  
    delayMicroseconds(speed_motor);
  digitalWrite(29, HIGH); 
  digitalWrite(28, LOW);  
  digitalWrite(27, LOW);  
  digitalWrite(26, HIGH);  
    delayMicroseconds(speed_motor);
  digitalWrite(29, HIGH); 
  digitalWrite(28, LOW);  
  digitalWrite(27, LOW);  
  digitalWrite(26, LOW);  
    delayMicroseconds(speed_motor);
  digitalWrite(29, HIGH); 
  digitalWrite(28, HIGH);  
  digitalWrite(27, LOW);  
  digitalWrite(26, LOW);  
    delayMicroseconds(speed_motor);
  digitalWrite(29, LOW); 
  digitalWrite(28, HIGH);  
  digitalWrite(27, LOW);  
  digitalWrite(26, LOW);  
    delayMicroseconds(speed_motor);.
  digitalWrite(29, LOW); 
  digitalWrite(28, HIGH);  
  digitalWrite(27, HIGH);  
  digitalWrite(26, LOW);  
    delayMicroseconds(speed_motor);
  home_sensor_X_value = digitalRead(home_sensor_X);
  if (home_sensor_X_value == HIGH) {
    right_hand_movement();
  } 
}

Wäre dagegen der Messwert der X-Achse des Joysticks kleiner oder gleich 449, würde die Methode right_hand_movement() aufgerufen, um den Greifer auf der X-Achse nach rechts zu bewegen. Die Reihenfolge der Zustandsänderung der mit dem X-Motor-Treiber verbundenen Mikrocontroller-Pins wird wie oben erläutert umgekehrt, so dass die Bewegung des Motors nach rechts erfolgt. Wie in der vorherigen Methode wird bei der Durchführung jeder vollständigen Sequenz das Auslesen des Optokoppler-Sensors vorgenommen. In diesem Fall ist es jedoch der Sensor auf der anderen Seite. Wenn der Sensor reagiert, wird die Methode left_hand_movement() aufgerufen, so dass der Greifer wieder in die entgegengesetzte Richtung bewegt wird.

void right_hand_movement(){
        digitalWrite(29, LOW); 
        digitalWrite(28, LOW);  
        digitalWrite(27, LOW);  
        digitalWrite(26, HIGH);  
          delayMicroseconds(speed_motor);
        digitalWrite(29, LOW); 
        digitalWrite(28, LOW);  
        digitalWrite(27, HIGH);  
        digitalWrite(26, HIGH);  
          delayMicroseconds(speed_motor); 
        digitalWrite(29, LOW); 
        digitalWrite(28, LOW);  
        digitalWrite(27, HIGH);  
        digitalWrite(26, LOW);  
          delayMicroseconds(speed_motor);
        digitalWrite(29, LOW); 
        digitalWrite(28, HIGH);  
        digitalWrite(27, HIGH);  
        digitalWrite(26, LOW);  
          delayMicroseconds(speed_motor);
        digitalWrite(29, LOW); 
        digitalWrite(28, HIGH);  
        digitalWrite(27, LOW);  
        digitalWrite(26, LOW);  
          delayMicroseconds(speed_motor);
        digitalWrite(29, HIGH); 
        digitalWrite(28, HIGH);  
        digitalWrite(27, LOW);  
        digitalWrite(26, LOW);  
          delayMicroseconds(speed_motor);
        digitalWrite(29, HIGH); 
        digitalWrite(28, LOW);  
        digitalWrite(27, LOW);  
        digitalWrite(26, LOW);  
          delayMicroseconds(speed_motor);
        digitalWrite(29, HIGH); 
        digitalWrite(28, LOW);  
        digitalWrite(27, LOW);  
        digitalWrite(26, HIGH);  
          delayMicroseconds(speed_motor);
        limit_sensor_X_value = digitalRead(limit_sensor_X);
        if (limit_sensor_X_value == HIGH) {
              left_hand_movement();
        }         
}

Für die Bewegungen des Joysticks in Y-Richtung wurden für die X-Achse drei Bedingungen programmiert, die je nach dem Wert zu einem bestimmten Zeitpunkt ausgeführt werden. Die Erklärungen der Funktionen für diese Achse entsprechen denen der vorhergehenden. Es ändern sich nur die Namen der Funktionen, der Optokoppler-Sensoren, der Variablen für das Lesen der Werte des Sensors für den maximalen Grenzwert und "Home"-Position.

Nachdem nun die Bewegung der Kralle entlang der Achsen implementiert wurde, fehlt noch die Bewegung der Kralle selbst. Sie muss auf Knopfdruck nacheinander abgesenkt, geschlossen, angehoben, zur Ausgangsposition der X- und Y-Achse gebracht und dort geöffnet werden, um den aufgenommenen Gegenstand fallen zu lassen.

Der Pin für den Taster ist, wie oben beschrieben, low-active. Sein Zustand ist HIGH, wenn er nicht betätigt wird. Der Motor für die Auf- und Abwärtsbewegung der Kralle wird nicht bewegt.

if (digitalRead(SW_pin) == HIGH) {
  motor_claw_off();                                     
}

In der Funktion motor_claw_off() werden alle Pins für den Motortreiber auf LOW gesetzt. Diese Funktion wird aufgerufen, wenn die genannte Bedingung erfüllt ist.

void motor_claw_off() {
  digitalWrite(33, LOW); 
  digitalWrite(32, LOW);  
  digitalWrite(31, LOW);  
  digitalWrite(30, LOW);
}

Drückt man auf den Joystick, wird der Taster betätigt. Dadurch geht der Pin auf LOW. Die vorherige Bedingung wird nicht erfüllt. Es werden dann die folgenden Codezeilen ausgeführt. Es wird hier auf den Zustand LOW getestet. Ist diese Bedingung erfüllt, werden Schleifen für die Bewegungen durchlaufen.

if (digitalRead(SW_pin) == LOW) {

  for (int i = 0; i < stepsPerRev * 4.5; i++) {
    clockwise();
    delayMicroseconds(speed_motor);
  }
  delay(1000);                                        
  for (int pos=50; pos<120; pos +=1) {
    servo_claw.write(pos);                           
  }
  delay(1000);
  
  for (int i = 0; i < stepsPerRev * 4.5; i++) {
    anticlockwise();
    delayMicroseconds(speed_motor);
  }
  delay(1000);           

  for (int i = 0; i < stepsPerRev * 2; i++) {
    return_X_axis_home();
    delayMicroseconds(speed_motor);
  }

  for (int i = 0; i < stepsPerRev * 2; i++) {
    return_Y_axis_home();
    delayMicroseconds(speed_motor);
  }

  for (int pos=120; pos>50; pos -=1) {
    servo_claw.write(pos);        
  }
              
}

Zuerst wird der Greifer abgesenkt. Der Motor macht viereinhalb Umdrehungen. Dieser Wert ist abhängig von der Höhe der Konstruktion. Es wurde zu Beginn die Variable stepsPerRev deklariert. Deren Wert ist für eine komplette Umdrehung zuständig. Für die Bewegung nach ganz unten muss dieser Wert mit 4.5 multipliziert werden. Innerhalb der Schleife wird die Funktion clockwise() aufgerufen. Damit wird der Motor im Uhrzeigersinn gedreht und der Greifer abgesenkt. Nach den viereinhalb Umdrehungen wird eine Pause von 1 s eingelegt.

for (int i = 0; i < stepsPerRev * 4.5; i++) {
  clockwise();
  delayMicroseconds(speed_motor);
}
delay(1000);

Der Motor arbeitet in Halbphasenfolge. Es gibt acht Kombinationen, um die Spulen anzuregen und dadurch die Achse des Motors zu drehen. In der clockwise()-Funktion gibt es den Zähler stepCounter, der Werte von 0 bis 7 annehmen kann. Dieser Wert ist der Index für das Array stepsLookup[]. Wie bereits erwähnt besteht jedes Element aus vier Bits. Der Funktion setOutput() wird der Index übergeben. Darin werden die Bits an den Stellen im Array ausgelesen und an die Motortreiber-Pins ausgegeben.

void clockwise() {
  stepCounter++;
  if (stepCounter >= numSteps) {
    stepCounter = 0;
  }
  setOutput(stepCounter);
}
void setOutput(int step) {
        digitalWrite(30, bitRead(stepsLookup[step], 0));
        digitalWrite(31, bitRead(stepsLookup[step], 1));
        digitalWrite(32, bitRead(stepsLookup[step], 2));
        digitalWrite(33, bitRead(stepsLookup[step], 3));
}

Im Ablauf der verschiedenen Schleifen bewegt eine den Servomotor mit dem Befehl servo_claw.write(pos) von der 50-Grad-Position (Greifer offen) in die 120-Grad-Position (Greifer geschlossen). Danach wird wieder eine Sekunde pausiert.

for (int pos=50; pos<120; pos +=1) {
  servo_claw.write(pos);                           
}
delay(1000);

Die Funktion anticlockwise() ist ähnlich der Funktion clockwise(). Der Unterschied ist, dass der Zähler nicht inkrementiert, sondern dekrementiert wird. Auch hier wird er von 7 bis 0 begrenzt.

void anticlockwise() {
        stepCounter--;
        if (stepCounter < 0) {
    stepCounter = numSteps - 1;
        }
        setOutput(stepCounter);
}

Sobald sich der Greifer in seiner ursprünglichen Position befindet, wird er in die Ausgangsposition der X- und Y-Achsen bewegt. Zu Beginn habe ich bereits gezeigt, mit welchen Funktionen der Greifer „nach Hause“ gebracht wird.  Die Methoden werden hier in den Schleifen nacheinander aufgerufen.

/******  Positioning of X-axis motor in home position  ******/
for (int i = 0; i < stepsPerRev * 2; i++) {
  return_X_axis_home(); 
  delayMicroseconds(speed_motor);
}

/******  Positioning of Y-axis motor in home position  ******/
for (int i = 0; i < stepsPerRev * 2; i++) {
  return_Y_axis_home();
  delayMicroseconds(speed_motor);
}

Wenn sich die beiden Achsen in der Ausgangsposition befinden, muss nur noch der Greifer geöffnet werden. Dafür muss die Gradzahl des Servomotors von 120 Grad (geschlossen) auf 50 Grad geändert werden.

for (int pos=120; pos>50; pos -=1) {
  servo_claw.write(pos);        
}

Sketch download

Ich hoffe, dass Sie dieses Projekt interessant fanden und wie immer freue ich mich auf Anregungen in den Kommentaren.

 

Sollte das Video nicht angezeigt werden, überprüfen Sie bitte die Cookie-Einstellungen Ihres Browsers.

Für arduinoProjekte für anfängerSensoren

Kommentar hinterlassen

Alle Kommentare werden von einem Moderator vor der Veröffentlichung überprüft

Empfohlene Blogbeiträge

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery