Simple Robot - [Teil 2]

Teil 2 

Hallo liebe Bastler,

willkommen zum zweiten Teil des Simple-Robot-Projektes. In Teil 1 haben wir die Elektronik in Betrieb genommen, getestet und mit einem Potentiometer die Drehwinkelmitten der Servos ermittelt. Im folgenden Beitrag bauen wir nun den Roboter zusammen und bringen ihm das einfache Laufen bei. Los geht's.

Benötigte Hardware 

Anzahl Bauteil Anmerkung
Werkzeug (Schraubenzieher, Zange oder Saitenschneider)
Leichtes Holz (z.B. Pappel oder Birke)
1 Arduino Nano V3
4 SG90 Mikroservo
1 PCA9685 16 Kanal 12 Bit PWM Servotreiber
1 Breadboard
1 Taster
1 Potentiometer (hier 10KOhm)
Spannungsversorgung 5V (Labornetzteil oder ähnliches)
Verbindungskabel
Mehrere Kabelbinder
PC mit Arduino IDE und Internetverbindung

Der Aufbau 

Wir bauen nun das Grundgerüst unseres Roboters. Dazu benötigen wir Holz bzw. Pappe oder auch gedruckte Teile aus dem 3D-Drucker. Daraus fertigen wir drei Teile. Eines für den Rücken und zwei für die Füße oder Beine (auch wenn das nicht wirklich wie Beine aussieht). Dann nutzen wir die Schrauben, die den Servos beigelegt waren. Damit befestigen wir unsere Teile an den Servoarmen.

In der folgenden Abbildung habe ich Ihnen eine Grafik erstellt, in der Sie auch die Maße sehen können. Meine Holzplatten haben eine Länge von 92 mm sowie eine Stärke von 3 mm. Die große Platte ist 52 mm breit und die beiden Beinplatten jeweils 25 mm.


Die nächste Abbildung zeigt, wie der Roboter am Ende ungefähr aussehen soll.


Das größere Teil stellt den Rücken dar. An dessen Unterseite habe ich zwei der Servos senkrecht und kopfüber mit Kabelbindern befestigt. Sie können natürlich auch Halterungen basteln. Die Löcher an den Seiten der Servogehäuse sind dafür vorgesehen. Ich hab es simpel gelöst. Quick and dirty. Die Aufsätze der anderen beiden Servos habe ich an die kleineren Teile geschraubt und dann auf die Servoachsen gesteckt.

Jetzt müssen nur noch diese beiden Servos an den Aufsätzen der zuvor am Rücken angebrachten Servos befestigt werden. Das habe ich ebenfalls mit Kabelbindern gelöst. Wenn alles zusammengesteckt, verschraubt und "verkabelbindert" ist, sollte es aussehen, wie in der vorherigen Abbildung. Das folgende Foto zeigt, wie ich es zusammengebastelt habe. Hier liegt der Roboter allerdings kopfüber.


Haben Sie alles zusammengebaut, sollten Sie nochmal den Quellcode aus Teil 1 nutzen, um die Mittelpositionen zu finden. Damit können Sie die Servos jetzt so kalibrieren, dass die Beinteile gerade ausgerichtet sind. Ich habe die Servoarme nicht auf die Achsen geschraubt, sondern nur gesteckt. Dadurch kann man noch einige Korrekturen vornehmen.

Laufen lernen

Als Nächstes wollen wir nun versuchen, eine Bewegung umzusetzen. In der Theorie soll vorn ein Bein und hinten dazu entgegengesetzt die andere Seite angehoben werden. Das bedeutet, wir drehen die beiden Servos mit der liegenden Achse.

Im nächsten Schritt drehen wir die anderen Servos, deren Achsen nach unten zeigen, auch jeweils entgegengesetzt, damit die Vorwärtsbewegung entsteht. Um diese fortzuführen, werden danach die liegenden Servos jeweils in die andere Richtung gedreht, um die anderen "Beine" anzuheben und die vorherigen abzusenken.

Dann werden wieder die hängenden Servos am Rücken in die jeweils andere Richtung gedreht. In den folgenden Abbildungen will ich das schematisch darstellen. Ich hoffe, Sie können mir folgen.



Sie sehen die Grundposition einmal von oben und dann von vorn. Ich habe hier versucht, die ersten beiden Schritte darzustellen. Wir drehen in Schritt 1 (hier von vorn gesehen) die beiden liegenden Servos. Dann in Schritt 2 (hier von oben zu sehen) die beiden hängenden Servos. Es folgen die Schritte 3 und 4:


Zuerst die liegenden Servos und anschließend werden die hängenden Servos in die jeweils andere Richtung gedreht.

Wir nutzen nun den elektrischen Aufbau aus Teil 1 mit Arduino Nano, Servo Treiber Board, Poti, Taster und Spannungsquelle. Die beiden liegenden Servos werden Nr. 0 und 1. Die anderen beiden sind Nr. 2 und 3. Wir bauen auf den Quellcode aus Teil 1 als Basis. Allerdings ignorieren wir vorerst das Potentiometer. Der Taster wird die Bewegung starten und beenden. Wir wollen testen, ob sich der Roboter überhaupt vorwärts bewegt.

Kommen wir zum Quellcode. Zuerst ändern wir die Variable int state = 0 in den binären Datentypen bool und den Namen in Run. Ich habe bewusst nicht run gewählt, da der Name reserviert ist. Die Deklaration und Definition sieht dann so aus:

bool Run = LOW;

Wir hatten ursprünglich mit der Variable state die Servonummern durchgeschaltet. Das machen wir jetzt nicht mehr, da sich die Servos gleichzeitig bewegen sollen. Zumindest jeweils zwei von ihnen. Jetzt soll nach Betätigen des Tasters der Roboter loslaufen oder aufhören zu laufen. Also englisch Run oder !Run (für NOT RUN).

Für die Servos brauchen wir eine Variable, die unser Tempo bestimmt. Damit wir die Servos "gleichzeitig" bewegen und auch die Geschwindkeit variieren können, setzen wir Sie nicht einfach auf eine Position. Stattdessen nutzen wir Schleifen, in denen wir Zähler bis zu den Positionswerten in einzelnen Schritten hochzählen. Dazwischen positionieren wir Pausen.

Wir definieren uns eine Variable für das Tempo, dass die Pause zwischen den Schritten darstellt. Außerdem hatten wir schon ein Array definiert, in das wir unsere Mittelpunkte eingetragen hatten.

Zusätzlich benötigen wir nun weitere Arrays. Zunächst wollen wir festlegen, wie weit sich jedes Servo bewegen soll. Eventuell reicht auch eine einfache Variable, wenn sich alle Servos um den gleichen Winkel drehen. Als Nächstes nutzen wir ein Array für die aktuellen Positionswerte der einzelnen Servos. Damit untersuchen wir, ob wir eine bestimmte Position erreicht haben.

unsigned long tempo = 15; 
int SERVO_MIDDLE[MAXSERVOS] = {280, 316, 356, 284};
int SERVO_MAX[MAXSERVOS] = {35, 35, 35, 35};
int SERVO_POS[MAXSERVOS] = {0};

Wir nutzen an dieser Stelle deswegen Arrays, da wir den Code später einfacher erweitern können. Es ist nur eine Variable für alle und nicht für jedes Servo eine eigene. Hinzukommt, dass wir mit Schleifen einfach durch die Servos durchzählen können. Das verkürzt den Quellcode ungemein. Die nächste Ergänzung ist im setup() eine Schleife, mit der wir die Servos auf die Grundpositionen einstellen:

for (i = 0; i < MAXSERVOS; i++) {
    SERVO_POS[i] = SERVO_MIDDLE[i];
    pwm.setPWM(i, 0, SERVO_POS[i]);
}

Wir kopieren jeden Wert für die jeweilige Achsmitte der Servos in das Positionsarray. Anschließend geben wir die Werte an die PWM. Damit stellen wir alle Servos auf die von uns ermittelten Achsmitten. Als Nächstes ändern wir im loop() die Stelle, an der wir den Taster entprellen und bisher die Variable state inkrementiert hatten:

 

if (input == LOW && input_alt == HIGH && millis() - alte_zeit > prell_delay) {
    Run = !Run;
    alte_zeit = millis();
}

Wie Sie sehen, schalten wir immer nur um, statt zu zählen. Nun entfernen wir noch die Bildschirmausgabe und die Zeile für die PWM. Dort fügen wir nun mehrere Schleifen ein. Das werden die vier Schritte für die Bewegung.

if (Run) {
    for (; SERVO_POS[0] < SERVO_MIDDLE[0] + SERVO_MAX[0]; SERVO_POS[0]++, SERVO_POS[1]++) {
        pwm.setPWM(0, 0, SERVO_POS[0]);
        pwm.setPWM(1, 0, SERVO_POS[1]);
        delay(tempo);   
    }
   
    for (; SERVO_POS[2] < SERVO_MIDDLE[2] + SERVO_MAX[2]; SERVO_POS[2]++, SERVO_POS[3]--) {
        pwm.setPWM(2, 0, SERVO_POS[2]);
        pwm.setPWM(3, 0, SERVO_POS[3]);
        delay(tempo);   
    }
   
    for (; SERVO_POS[0] > SERVO_MIDDLE[0] - SERVO_MAX[0]; SERVO_POS[0]--, SERVO_POS[1]--) {
        pwm.setPWM(0, 0, SERVO_POS[0]);
        pwm.setPWM(1, 0, SERVO_POS[1]);
        pwm.setPWM(4, 0, SERVO_POS[4]);
        delay(tempo);   
    }
   
    for (; SERVO_POS[2] > SERVO_MIDDLE[2] - SERVO_MAX[2]; SERVO_POS[2]--, SERVO_POS[3]++) {
        pwm.setPWM(2, 0, SERVO_POS[2]);
        pwm.setPWM(3, 0, SERVO_POS[3]);
        delay(tempo);   
    }
}

Wir fragen zuerst ab, ob die Variable Run auf High steht. Wenn ja, dann führen wir die Schleifen aus. In der ersten Schleife zählen wir die Position des ersten Servos solange, bis sein Bewegungsmaximum erreicht wurde.

Noch einmal kurz zur Info: es handelt sich dabei nicht um das mechanische Winkelmaximum des Servos, sondern um die von uns festgelegte Bewegungsgrenze. Damit können wir später noch herumspielen.

Wir nutzen nur Servo Nr. 0 als Bedingung, zählen aber Servo 0 und 1 hoch. Beide sollen zuerst einmal die gleiche Bewegung durchführen. An dieser Stelle wird es später möglich sein, die Richtung der Laufbewegung zu ändern. Die gleiche Funktion haben die anderen Schleifen auch.

Jede Schleife dreht die jeweiligen Servos immer nur um einen Schritt weiter. Danach folgt eine Pause, die wir hier noch mit delay() umgesetzt haben. Warum das eigentlich keine gute Idee ist, merken wir, wenn wir mit dem Tastendruck den Roboter wieder anhalten wollen. Es werden erst alle Schleifen durchlaufen, bis der Tastereingang wieder untersucht wird. Der Programmcode blockiert also.

Mit diesem Wissen im Hinterkopf laden wir nun unser neues Programm auf den Arduino. Den seriellen Monitor benötigen wir vorerst nicht mehr. Nach dem Upload stellen sich die Servos auf die Mittelpositionen. Drücken wir den Taster, sollte er loslaufen. 

Wir stellen fest, dass es alles etwas kippelig aussieht. Während der Bewegung berühren nur zwei Punkte den Boden. Daher kippt der Roboter vorn oder hinten so um, dass er entweder das vordere Teil schiebt, oder das hintere hinterher zieht. Man sieht, warum es sich auszahlt, mehr als nur vier einfache Beine für einen Roboter zu verwenden.

Wir geben uns allerdings noch nicht geschlagen. Auch wenn wir keine Muskeln für das Gleichgewicht einsetzen können, haben wir noch Spielraum für Verbesserungen an dieser Stelle. Ich habe mir Kabelbinder als Beine zurecht geschnitten und an die Unterseiten geschraubt. Damit kann ich das Abkippen etwas abfedern und es sieht mehr wie eine Bewegung aus.

Probieren Sie es aus und lassen den Roboter laufen. Die Geschwindigkeit ändert Sie über die Variable tempo. Den Drehradius der Beine ändern Sie über das Array int SERVO_MAX[MAXSERVOS] = 35, 35, 35, 35;, in dem aktuell noch alle Werte identisch sind.

Die Software 

/*  Simple Robot 
 *  von Andreas Wolter 
 *  fuer AZ-Delivery.de 
 *   
 *  Version: 1.0 
 *   
 *  Funktion: 
 *  Mit Servos einen einfachen Roboter zum Laufen bringen. 
 *  Mit Taster die Bewegung starten und stoppen. 
 *   
 *  Verwendete Hardware: 
 *    - Arduino Nano V3 
 *    - SG90 Mikroservos (4x) 
 *    - PCA9685 16 Kanal 12 Bit PWM Servotreiber 
 *    - Taster 
 *    - Potentiometer (hier 10KOhm) 
 *    - externe Spannungsversorgung 5V 
 *   
 *  Verwendete Bibliotheken: 
 *    - wire 
 *    - Adafruit PWM Servo Driver Library 
 *   
 *  Beispielquelle aus der Adafruit PWM Servo Driver Library: servo 
 *   
 ***************************************************  
  This is an example for our Adafruit 16-channel PWM & Servo driver 
  Servo test - this will drive 8 servos, one after the other on the 
  first 8 pins of the PCA9685 
 
  Pick one up today in the adafruit shop! 
  ------> http://www.adafruit.com/products/815 
   
  These drivers use I2C to communicate, 2 pins are required to   
  interface. 
 
  Adafruit invests time and resources providing this open source code,  
  please support Adafruit and open-source hardware by purchasing  
  products from Adafruit! 
 
  Written by Limor Fried/Ladyada for Adafruit Industries.   
  BSD license, all text above must be included in any redistribution 
 **************************************************** 
 *   
 *  Pinout: 
 *   
 *  Arduino Nano  |   Servo Treiber   |   Externe Spannungsquelle       
 *  ------------------------------------------------------------- 
 *      GND       |         GND       | 
 *      5V        |         VCC       | 
 *      A4        |         SDA       | 
 *      A5        |         SCL       | 
 *                |     Connector V+  |      +5V 
 *                |     Connector GND |      GND 
 *   
 *  Arduino Nano  |   Input 
 *  ------------------------------------------------------------- 
 *      D8        |   Taster Pin 1 
 *      A0        |   Potentiometer Mitte (Schleifer Pin) 
 *      5V        |   Potentiometer Außen 1 
 *      GND       |   Potentiometer Außen 2 
 *      GND       |   Taster Pin 2 
 */ 
 
#include <Wire.h> 
#include <Adafruit_PWMServoDriver.h> 
 
#define SERVOMIN   68   // 0 bis 4096 - try and error 
#define SERVOMAX   510  // 0 bis 4096 - try and error 
#define SERVO_FREQ 50   // Analog servos run at ~50 Hz updates 
#define TOLERANZ   15   // Prozent vom Endanschlag 
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); 
 
// States       
bool Run = LOW; 
 
// Input 
int taster_pin = 8; 
bool input = HIGH; 
bool input_alt = HIGH;  
int input_analog = 0; 
 
// Servos 
// Berechne Min und Max mit Servotoleranz am Aussenanschlag 
const int MAXSERVOS = 4;  
int range = SERVOMAX - SERVOMIN; 
double temp = (range / 100) * (double)TOLERANZ; 
int NEWMIN = SERVOMIN + (int)temp; 
int NEWMAX = SERVOMAX - (int)temp; 
int i = 0; 
unsigned long tempo = 15; 
 
int SERVO_MIDDLE[MAXSERVOS] = {280, 316, 356, 284}; 
int SERVO_MAX[MAXSERVOS] = {35, 35, 35, 35};          // Bewegungsradius eingrenzen 
int SERVO_POS[MAXSERVOS] = {0};                       // aktuelle Servopositionen 
 
// Timer 
unsigned long prell_delay = 100; 
unsigned long alte_zeit = 0; 
 
void setup() { 
  Serial.begin(115200); 
  Wire.begin(); 
  pinMode(taster_pin, INPUT_PULLUP); 
  pwm.begin(); 
  pwm.setOscillatorFrequency(27000000);  // The int.osc. is closer to 27MHz   
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates 
  delay(500); 
 
  // auf Grundposition einstellen 
  for (i = 0; i < MAXSERVOS; i++) { 
    SERVO_POS[i] = SERVO_MIDDLE[i]; 
    pwm.setPWM(i, 0, SERVO_POS[i]); 
  } 
} 
 
void loop() { 
  // Input lesen und anpassen 
  input = digitalRead(taster_pin); 
  input_analog = analogRead(A0); 
  input_analog = map(input_analog, 0, 1023, NEWMIN, NEWMAX); 
 
  // Entprellen 
  // wenn Taster jetzt AN und vorher AUS und aktuelle Zeit - alte Zeit groesser, als vorgegebenes Delay 
  if (input == LOW && input_alt == HIGH && millis() - alte_zeit > prell_delay) { 
    Run = !Run; 
    alte_zeit = millis(); 
  } 
  input_alt = input; 
 
  if (Run) { 
 
    // Schritt 1: Füße drehen und damit anheben 
    // Servo 0 als Bedingung, aber Servo 0 und 1 drehen 
    // beide Servos führen die gleiche Drehung aus 
    for (; SERVO_POS[0] < SERVO_MIDDLE[0] + SERVO_MAX[0]; SERVO_POS[0]++, SERVO_POS[1]++) { 
      pwm.setPWM(0, 0, SERVO_POS[0]); 
      pwm.setPWM(1, 0, SERVO_POS[1]); 
      delay(tempo);    
    } 
 
    // Schritt 2: Beine drehen und damit Roboter bewegen 
    // Servo 2 als Bedingung, aber Servo 2 und 3 drehen 
    // beide Servos führen die gleiche Drehung aus 
    for (; SERVO_POS[2] < SERVO_MIDDLE[2] + SERVO_MAX[2]; SERVO_POS[2]++, SERVO_POS[3]--) { 
      pwm.setPWM(2, 0, SERVO_POS[2]); 
      pwm.setPWM(3, 0, SERVO_POS[3]); 
      delay(tempo);     
    } 
 
    // Schritt 3: Füße jeweils anders herum drehen und damit anheben 
    // Servo 0 als Bedingung, aber Servo 0 und 1 drehen 
    // beide Servos führen die gleiche Drehung aus 
    for (; SERVO_POS[0] > SERVO_MIDDLE[0] - SERVO_MAX[0]; SERVO_POS[0]--, SERVO_POS[1]--) { 
      pwm.setPWM(0, 0, SERVO_POS[0]); 
      pwm.setPWM(1, 0, SERVO_POS[1]); 
      delay(tempo);     
    } 
 
    // Schritt 4: Beine jeweils in die andere Richtung drehen und damit Roboter bewegen 
    // Servo 2 als Bedingung, aber Servo 2 und 3 drehen 
    // beide Servos führen die gleiche Drehung aus 
    for (; SERVO_POS[2] > SERVO_MIDDLE[2] - SERVO_MAX[2]; SERVO_POS[2]--, SERVO_POS[3]++) { 
      pwm.setPWM(2, 0, SERVO_POS[2]); 
      pwm.setPWM(3, 0, SERVO_POS[3]); 
      delay(tempo);     
    } 
  } 
} 

Vorschau 


Momentan hängt der Roboter noch an der Nabelschnur, er bewegt sich einfach nur geradeaus (im besten Fall) und er kann die Bewegung nicht dynamisch verändern. Er reagiert nicht autonom auf Veränderungen seiner Umwelt und Ausschalten ist auch noch schwierig.
Wir haben also noch jede Menge Möglichkeiten, ihn zu verbessern.
Dazu kommen wir im nächsten Teil.

Bis dahin.

Andreas Wolter

für AZ-Delivery Blog
 

3 Kommentare

Andreas Wolter

Andreas Wolter

@Markus: es gibt noch kein Video. Ich wollte eins aufzeichnen, wenn ich auf Batteriebetrieb umgestellt habe. Denn dann kann er sich theoretisch freier bewegen. Im Moment läuft er wie eine frisch geschlüpfte Eidechse :-)

Markus

Markus

Sehr interesssant!
Danke für den Beitrag.
Ich überlege gerade, den Robby “nachzubauen”.
Frage … gibt es irgendwo ein Video, wo man den Robby sich bewegen sieht?
Gruß Markus

Dirk

Dirk

Wieder toll erklärt. Ich freue mich schon auf das Nachbauen und den nächsten Teil. Ich finde es ist eine gute Basis um weitere Ideen (die mir mit Sicherheit kommen werden) zu entwickeln und umzusetzen. Auch die Erklärungen im Code, einfach Perfekt.

Einen Kommentar hinterlassen

Alle Kommentare werden vor der Veröffentlichung moderiert