Projektvorstellung

selbststeuernder Roboter "Roberta"

Die Idee:

Nachdem ich beim Lesen des Buches "Die elektronische Welt mit Arduino entdecken" von Erik Bartmann einges über Servos und Motoren und deren Ansteuerung mittels Arduino erfahren habe, kam mir die Idee, einen Roboter bzw. ein Gefährt zu bauen, das sich möglichst selbstständig im Raum bewegen kann. Im oben erwähnten Buch wird auch ein Roboter gebaut, allerdings auf Basis von zwei LEGO-Motoren, welche ich nicht besitze und mit LEGO-Teilen, die ich nicht mehr rauskramen will.
Vom Uhr-Projekt waren aber noch einige Stücke Kunststoffplatte übrig; also beschloss ich, daraus was zu basteln.

 

 
Roberta von oben


Der Antrieb:

Nachdem ich früher schon mal ein fertiges (billiges) Roboter-Chassis mit zwei einfachen Elekromotoren als Antrieb getestet und enttäuscht nach einem Tag wieder zurückgeschickt hatte, machte ich mich auf die Suche nach einem beseren Antriebskonzept, das präzise zu steuern und trotzdem nicht zu teuer in der Anschaffung ist.
Die Wahl fiel auf zwei "gehackte" Servos. Dabei werden Servos zu modifiziert, dass sie sich nicht nur um 180° drehen, sondern wie kleine Getriebemotoren durchlaufen. Nähere Infos gibts hier. Später habe ich die in der Anleitung erwähnten festen Widerstände durch herausgeführte Spindeltrimmer ersetzt, um die Servos kalibrieren zu können.
Die Antriebsservos
sind mit Kunststoffplatten-Stücken an der Grundplatte montiert und mittels einer Metallstange vorne stabiliert (auf dem Foto ganz links)
Die Reifen sind mit Schrauben direkt auf die Servos geschraubt. Hier hatte ich einfach das Glück, passende Schrauben von der Schlittenaufhängung aus einem zerlegten DVD-Laufwerk zu haben.
Damit Robertas Hinterteil nicht auf dem Boden daherkommt, hat sie als drittes Rad eine einfache Möbelrolle aus dem Baumarkt.


Roberta von unten


Die Spindeltrimmer (blau) zum Kablibrieren der Antriebsservos

Was Wie guckst du?

Jetzt war noch die Frage zu klären, wie sich Roberta orientieren soll. Die erste Idee ware Infrarotsensoren, die mir allerdings zu teuer waren. Bei der Recherche im Netz fand ich einen Ultraschall-Sensor von Seedstudio
, der für meine Zwecke ideal schien - und auch immer noch ist. Davon bestellte ich zwei Stück. Der ürspüngliche Plan war nämlich, je einen Sensor links und rechts vorne am Roboter zu befestigen. Das habe ich aber in der endgültigen Version verworfen, da die beiden Sensoren nicht die gesamte Umgebung sauber erfassen konnten. Inzwischen hat Roberta nur noch einen Ultraschallsensor, der aber auf einem Servo sitzt und ständig die Umgebung vor ihr erfasst. Die LED unter dem Sensor hat keine wirkliche Funktion, sondern soll die Funktionsweise verdeutlichen und zeigt die "Blickrichtung" schön an.


Ich schau dir in die Augen...
Die Steuerung 

Die Versorgung der beiden Servos erfolgt über einen L293D. Nachdem erste Prototypen immer mit einem Breadboard rumgefahren sind, was entsprechend wackelig war, fand ich es an der Zeit, mein erstes Arduino-Shield selbst zu entwerfen und zu ätzen - was im zweiten Anlauf ganz passabel geworden ist. Neben dem L293D auf einem IC-Sockel beherbergt es Steckontakte für die Antriebs-Servos, das Display und die vordere Platine.
Die Stromversorung übernimmt ein LiPo-Akku mit 7,4V und 2200mAh - das hat bisher immer gereicht :-) Falls es mal eng werden sollte, schützt ihn ein LiPo-Saver vor gefährlicher Tiefentladung.

Das Display 

Das Display, das ursprüglich nur zum Überwachen der Werte und Debugging diente, ist inzwischen fester (und neuerdings auch fest montierter) Bestandteil von Roberta geworden. In der oberen Zeile werden die Entfernungen der nächsten Hindernisse in cm angezeigt (oder 99 bei > 1 Meter). Unten wird die Geschwindigkeit der beiden Antriebsmotoren mittels Sternchen angezeigt. Das Display wird über I2C vom Arduino angesteuert.



Der Sketch (das "Programm"):

 
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,16,2); 
Servo s1;
Servo s2;
Servo s3;
int s1o=8;
int s2o=7;
int pingpin=2;
int cm=0;
int abst[6];
int winkel[]={130,110,90,70,50};
int pos=0;
int aufab=1;
int count=0;

void setup(){
  s1.attach(9);
  s2.attach(10);
  s3.attach(4);
  pinMode(s1o, OUTPUT);
  pinMode(s2o, OUTPUT);
  lcd.init();
  lcd.backlight();
  schaurum(200);
  schaurum(100);
}

void loop(){
  pos=pos+aufab;
  if (pos==5){
    pos=3;
    aufab=-aufab;
  }
  if (pos==-1){
    pos=1;
    aufab=-aufab;
  }

  abst[pos]=mess(winkel[pos]);

  if ((abst[1]<20 && abst[3]<20) || abst[0]<10 || abst[4]<10){
    fahr(-40,-40,500);
    if (abst[0]< abst[4]){
      do{
        fahr(-60,60,300);
        schaurum(40);
        count++;
      } 
      while (abst[0]<5 || abst[1]<10 || abst[2]<20 || abst[3]<10 || abst[4]<5);
    }
    else{
      do{
        fahr(60,-60,300);
        schaurum(40);
        count++;
      } 
      while (abst[0]<5 || abst[1]<10 || abst[2]<20 || abst[3]<10 || abst[4]<5);
    }
    count=0;
  }  
  else
  {
    if (abst[0]<20 && abst[1]>40 && abst[2]>40 && abst[3]>40 && abst[4]>40){
      fahr(50,0,0);
    }

    if (abst[0]>40 && abst[1]>40 && abst[2]>40 && abst[3]>40 && abst[4]<20){
      fahr(0,50,0);
    }

    if (abst[0]>40 && abst[1]>40 && abst[2]>40 && abst[3]>40 && abst[4]>40){
      fahr(50,50,0);
    }

    if (abst[0]>70 && abst[1]>70 && abst[2]>70 && abst[3]>70 && abst[4]>70){
      fahr(60,60,0);
    }    

    if (abst[1]<40 || abst[2]<40 || abst[3]<40){
      if(abst[0]+abst[1]<abst[3]+abst[4]){
        fahr(50,30,0);
      }
      else{
        fahr(30,50,0);
      }
    }
  }
}

int mess(int a){
  s3.write(a);
  delay(80);
  schreib();
  long del;
  pinMode(pingpin, OUTPUT);
  digitalWrite(pingpin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingpin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingpin, LOW);
  pinMode(pingpin, INPUT);
  del = pulseIn(pingpin, HIGH);
  cm=del/78;
  return cm;
}

void schaurum(int srzeit){
  fahr(0,0,0);
  for (int j=0;j<5;j++){
    abst[j]=mess(winkel[j]);
    delay(srzeit);
  }
}

void fahr(int li, int re, int zeit){

  sterne(li,re);
  if (abs(li)>5) digitalWrite(s1o,1);
  else digitalWrite(s1o,0);
  if (abs(re)>5) digitalWrite(s2o,1);
  else digitalWrite(s2o,0);
  if (li>100) li=100;
  if (re>100) re=100;
  li *=0.35;
  re *=0.35;
  s1.write(90+li);
  s2.write(90-re);
  delay(zeit);  
}

void schreib(){
  lcd.setCursor(0,0);  
  for(int i=0;i<6;i++){
    lcd.print(" ");
    if (abst[i]>99){ 
      lcd.print("99");
    }
    if (abst[i]<10){ 
      lcd.print(" ");
    }
    if (abst[i]<100){ 
      lcd.print(abst[i]);
    }
  } 
}

void sterne(int sli, int sre){
  lcd.setCursor(0,1);
  for (int k=0;k<7;k++){
   if (sli>=10*(k+1)) lcd.print ("*");
   else if (sli<0 && abs(sli)>=10*(k+1)) lcd.print ("-");
   else lcd.print(" ");
   }
   lcd.print (" ");
   for (int k=7;k>=0;k--){
   if (sre>=10*(k+1)) lcd.print ("*");
   else if (sre<0 && abs(sre)>=10*(k+1)) lcd.print ("-");
   else lcd.print(" ");
   }
}



Ein paar Erklärungen zum Sketch:

Servo s1 ist für links, s2 rechts, s3 fürs "Radar"
Die Funktion fahr(int li, int re, int zeit) steuert die Antriebsservos an, wobei li und re die Geschwindigkeiten links und rechts bestimmen und zeit die Dauer. Die meisten Fahrbefehle kommen mit Zeit 0, um schnell auf Änderungen der Umgebung reagieren zu können. Nur die Routine zum Schwenken bei nahen Hindernissen bringt eine Zeit mit.

Die Funktion schaurum(int srzeit) wird zum Start zweimal und ansonsten beim Ausweichen von Hindernissen ausgeführt. Hier werden alle Entferungen erfasst, bevor es weiter geht.

schreib() und sterne() dienen der Ausgabe aufs Display

mess(int a) führt eine Messung mit Winkel a durch und gibt die Entfernung in cm zurück.

Im loop() - Bereich findet die eigentliche Steuerung statt, wobei je nach Entfernungen entsprechende Geschwindigkeits-Änderungen durchgeführt werden.


Material-Liste:


Video von Roberta in Aktion:


Ausblick, ToDo:

Falls jemand Vorschläge hat oder Fehler findet, darf er/sie sich gerne melden!


Alle in erwähnten Marken- und Warenzeichen oder Produktnamen sind Eigentum ihrer jeweiligen Inhaber.
Für die Inhalte externer Links sind die jeweiligen Seitenbetreiber verantwortlich.

Keine Haftung für eventuelle Schäden.