Tweet | ![]() |
Roboterpraktikum an der TU Dresden
-
Seit 2009 gibt es an der Technischen Universität Dresden für die Erstsemestler der Studiengänge Informatik (sowie Physik und Informationsystemtechnik mit Informatik als Nebenfach) ein Einführungspraktikum, wo man in einem Team von 3-4 Leuten einen autonomen Roboter programmieren muss, welcher in der Lage sein sollte eine vorgegebene Linie in einem Parcour folgen zu können.
Zeit für das Projekt: 11 Tage inkl. einem Wochenende (Zeiteinteilung obliegt den einzelnen Teams)
Gegeben ist ein Bauset von Lego Mindstorm NXT 2.0 mit 3 Motoren, 2 Tastsensoren, einem Lichtsensor, einem Ultraschallsensor und einem Soundsensor.
Und schon hat man die erste Hürde, die man meistern muss, denn der Roboter ist nicht als ein solcher gegeben, sondern nur dessen Einzelteile. Man muss ihn also selber erstellen und zusammensetzen. Leider klingt es leichter als es wirklich ist, da im Verlaufe des Praktikums es vorkommen kann, dass man den bestehenden Roboter mehrmals umbaut um ihn besser an die Zielvorgaben anzupassen.
Den Roboter so zu programmieren, dass er einer geraden schwarzen Linie auf weißem Hintergrund folgen soll ist ziemlich simpel. Doch leider besteht der Parcour aus 5 Abschnitten:
- eine runden Links- und Rechtskurve, dann
- aus einer Links- und Rechtsspitzkehre (die bis zu 15° abgehen können), sowie
- einem großen rechteckigen Hindernis (welches auch “schräg” auf der Linie stehen kann),
- einer gestrichelten Linie (wo die Lücken bis zu 10 cm groß sein können und im 90° Winkel abgehen), und
- einer oder mehrere Kombinationen von den vorherigen Abschnitten (in meinem Fall war es eine gestrichelte Linie mit Hindernis an einer Spitzkehre)
Hardware
Damit der Roboter diese alle meistern kann, muss man dazu vieles beachten:
Zunächst, wie schon vorher erwähnt, ist der Bau des Roboters sehr wichtig. Wir haben unserem Roboter 3-Rad-mäßig aufgebaut, also vorne 2 Antriebsräder, welche jeweils an einem Motor anmontiert worden sind sowie einem drehbaren Hinterrad. Hier hat sich das Hinterrad als ein wenig problematisch erwiesen, da der Roboter beim mehrmaligen Rückwärtsfahren und drehen bei demselben Befehl (z.B. fahre kurz rückwärts und drehe dich für 2 Sekunden) nicht immer an derselben Stelle landete wo er eigentlich sollte. Sprich: Wenn er sich um 30° drehen soll, dann dreht er sich mal um 30°, aber manchmal auch nur um 27° oder 34°. Unter anderem ruf auch ein halb leerer Akku dieses Problem hervor. Man musste also hier Abweichungen mit einberechnen und den Roboter immer neu aufladen. Manche Gruppen haben dieses Problem gelöst, indem sie die Hinterachse einfach mit einer großen Kugel ersetzt haben und somit kein störendes drehendes Rad haben. Aber dennoch konnte unser Roboter alle Vorgaben trotz des lästigen Hinterrads bestehen ;D.
Neben dem Hinterrad gibt es aber auch noch ein paar mehr Teile, die an dem Roboter angebaut werden müssen, wobei sich auch hier die Frage stellt: muss man für die Zielstrecke wirklich alle Teile des Baukasten verwenden? Natürlich nicht! So wurden z.B. unter anderem zwar 2 Tastsensoren und ein Ultraschallsensor mit gegeben(s.o.), jedoch hatten wir an unserem finalem Roboter lediglich nur einen Lichtsensor und einen Tastsensor. Soundsensor war für dieses Projekt sowieso irrelevant und wenn man den Roboter geschickt gebaut hatte, dann reichte für das Erkennen eines Hindernisses ein Tastsensor völlig aus. Wieso also kompliziert aufbauen, wenn es auch einfach und simpel geht? Damit der Roboter an der Seite mit den Rädern an Ecken und Kanten nicht “hängen” bleibt, haben wir den Tastsensor mit eine Art “Geweih” erweitert.
Dann der Ultraschall-Sensor: ist meiner Meinung nach eine nette Erweiterung. Es kann ebenso Hindernisse erkennen. Man könnte meinen, der Ultraschall-Sensor würde dann demnach auch alleine reichen und man bräuchte kein Tastsensor. Dem kann ich leider nur widersprechen, da dieser viel zu ungenau arbeitet und man ihn an einem Motor anbauen müsste, sodass sich der Sensor “dreht”. Den Sensor statisch aufzubauen und den Roboter drehen lassen würde die Sache andernfalls nur verschlimmern. Demnach haben wir es bevorzugt lieber nur ein Tastsensor zu benutzen und den Roboter gegen Wände, Stühle oder anderes rammen zu lassen, damit er zu 100% weiß, dass er irgendwo gegen gefahren ist. Ich habe dazu ein Algorithmus geschrieben, welcher den Roboter es ermöglichte Hindernisse zu umfahren ohne einen Ultraschall-Sensor benutzen zu müssen. Ich werde später dazu näher darauf eingehen.
Nun zuerst zum Lichtsensor: damit er richtig arbeiten und die Linie erkennen kann, darf der Laser nicht zu nah am Boden sein, denn ansonsten würde er wohl alles als schwarz erkennen, weil er dann seinen eigenen Schatten sehen würde. Daher muss er einen gewissen Abstand zum Boden halten, aber je größer der Abstand, desto ungenauer arbeitet der Sensor. Unter anderem reicht z.B. die alltägliche Sonnenbestrahlung schon aus um schwarz dennoch als “weiß” zu erkennen. Um dieses Problem zu lösen haben wir eine Art Trichter um den Lichtsensor gebaut, damit er von anderen möglichen Lichtquellen nicht beeinflusst werden kann. Das Herz des Roboters besteht aus dem NXT-Rechner mit einem 32-Bit-Mikroprozessor und ist trotz seines Retro-looks sogar bluetoothfähig. An diesem Gerät sind alle Sensoren und Motoren verbunden. Damit diese Steuerzentrale auch etwas auf die Reihe bringen kann, muss man es zuvor programmieren. Mögliche Programmiersprachen hierfür sind z.B. Java, NXT-G, NBC, pbLua, NxC, etc.
Software
Zur Programmierung haben wir NxC (Not eXactly C) genutzt und wie der Name schon vermuten lässt, handelt es sich hierum um eine Pseudosprache von C. Die Syntax ist relativ simpel und selbst als Laie kann man sich recht schnell damit zurechtfinden, da man für dieses Projekt nicht so viele verschiedene Befehle kennen muss damit der Roboter einwandfrei läuft. Aufpassen muss man da lediglich nur auf solche “Verschachtelungen”, wenn man z.B. eine Schleife in eine Schleife einer Schleife mit einer if-Bedingung packen muss, da diese Programmiersprache keine Rekursion beherrscht. (Jeder Programmieramateur kennt es… die Laien hassen es…)
Detailiertere Informationen über die Sprache mit seinen ganzen Befehlen, etc. gibt es in diesem gut erklärten Tutorial:
http://www.roberta-home.de/de/was-bietet-roberta/roberta-reihe/nxc-tutorial
Nun stellt sich die Frage: wie kann man denn überhaupt in einem Team gemeinsam programmieren? Einer schreibt den Code und alle anderen schauen zu? Die Lösung ist
Die TU-Dresden hat auf seinen eigenen Servern über eine Subdomain GForge eingerichtet, damit mehrere Personen gleichzeitig am Projekt arbeiten können. Die Funktionsweise ist recht einfach: alle Nutzer richten ein SVN auf ihrem Rechner ein (z.B. mit dem Programm TortoiseSVN), indem man einen Ordner erstellt, wo die ganzen Dateien zu dem Praktikum gespeichert sein sollten und diesen per Rechtsklick mit “SVN Checkout” auswählt, sofern man ein SVN Programm vorher installiert hat. Zuvor muss man sich aber auf das GForge registrieren, wo es “installiert” wurde. Von GForge bekommen alle Mitglieder, die in deren jeweiligen Gruppe sind, dann eine Linkadresse, die man für das SVN-Checkout braucht. Nun können alle Teammitglieder Dateien, Codeschnippsel, etc. per Rechtsklick im Ordner mit “SVN-Update” bzw. “SVN-Commit” auf den externen Server abrufen oder hochladen.
Nachdem die Frage, ob ein Teamwork von Programmieren möglich ist, geklärt wurde, komme ich zu unserer Problemlösung zum Parcour. Da ich davon ausgehe, dass dieses Praktikum weiterhin jährlich angeboten wird, werde ich weder Codeteile noch irgendwelche Befehle veröffentlichen, sondern nur unseren Ablaufalgorithmus erklären, wie der Roboter in den jeweiligen Situationen handeln sollte und auf welche Probleme wir gestoßen sind.
Eins noch vorweg: die Motoren haben zwar einen eingebauten Drehwinkelsensor und können sich gradgenau drehen, jedoch können sie nicht den Roboter gradgenau drehen ohne physikalische-mathematische Formeln verwenden zu müssen um das Verhältnis zwischen Drehwinkel-Motor und Drehwinkel-Roboter-insgesamt zu ermitteln. Während viele Gruppen meiner Meinung nach zu viel Zeit damit verschwendet hatten, haben wir die Werte einfach nur grob und experimentell ermitteln und im Code somit statische Werte verwendet. Die Nutzung von dynamischen Werten ist natürlich auch sehr elegant, aber man sollte ja nicht sein Ziel aus dem Augen verlieren. Jedenfalls sind dementsprechend alle Werte nur ungefähre und keine exakten Angaben, die ich dann im Folgendem verwende.
Also der Roboter soll einer schwarzen Linie folgen. Wenn diese Linie in eine Kurve übergeht, dann hat er, wenn er gerade aus fährt, folglich keine schwarze Linie mehr unter sich. Also sollte er sich erstmal um 30° nach links drehen und dann 30° nach rechts (genau genommen um 60°, da er ja von links wieder 30° drehen muss um zum Ausgangspunkt zu kommen). Wenn er auf eine schwarze Linie trifft, dann folgt er diese weiter. Wenn nicht, dann macht er eine große Drehung um 165° nach links und dann von der Ausgangssituation nach rechts. D.h. zuerst sucht er nach kleinen Kurven ab und danach schaut er nach Spitzkehren, wenn er nichts findet.
Jedoch kann es ja auch vorkommen, dass er nun gestrichelte Linien vor sich hat. Dann würde er selbst mit den ganzen Drehungen nicht weit kommen. Daher fährt er bei erfolgloser Suche ein wenig vorwärts und beginnt mit dergleichen Prozedur noch einmal von vorne. Nun, leider können im schlimmsten Fall die gestrichelten Linien auch in eine Kurve oder gar in einem 90° Winkel abgehen. Damit der Roboter auch diese Linien findet, fährt er danach wieder zurück, dreht sich kurz nach links und fährt danach vorwärts und beginnt wieder mit seinem Suchalgorithmus (analog dann auch für die rechte Seite). Jedoch musste man hier aufpassen, dass man wieder die Werte anpasst. Weil wenn der Roboter sich nach links dreht und dann ein wenig vorwärts fährt um die gestrichelten Linien zu finden, dann würde er bei den gleichen Werten die vorherige Linie wiederfinden, welches zu einer 180° Drehung führen würde und dementsprechend würde der Roboter den Weg wieder zurückfahren, welches man unbedingt vermeiden sollte.
Was passiert jedoch, wenn ein Hindernis auf dem Weg liegt?
Jedes Mal wenn der Tastsensor betätigt wurde, fährt er (1.) kurz zurück und macht (2.) eine 90° Drehung nach rechts. Danach versucht der Roboter (3.) mit kleinen scharfen Bögen das Hindernis zu umfahren. Wenn der Roboter wieder auf das Hindernis stößt, fängt er wieder bei (1.) an. Diese Prozedur macht er solange bis er nochmals gegen das Hindernis fährt und von vorne anfängt oder der Lichtsensor wieder schwarze Linien sieht. Dann folgt er aber nicht der schwarzen Linie, weil es kann ja vorkommen, dass er in solchen “scharfen” Bögen gefahren ist, sodass er wieder auf das Hindernis treffen würde (siehe (4.) ) und danach immer nur im Kreis fahren würde. Daher fährt er wieder kurz zurück und macht eine etwas kleinere Drehung als 90° und fährt danach in einem flacheren Bogen, sodass er schlimmstenfalls nur noch in einem Winkel von 90° auf die schwarze Linie auftritt. Sollte er die Linie also nochmals treffen, dann dreht er ganz kurz nochmal nach rechts (5.), damit er dann beim Folgen der Linie nicht nach links und zum Rückweg abdriftet, denn unser Roboter war so programmiert, dass er beim nicht-Finden der Linie immer zuerst links absucht und dann nach rechts geht.
Klingt zwar kompliziert, aber die Befehle wiederholen sich und durch die Schleifen bleibt auch der Programmcode recht übersichtlich. Unser Roboter konnte alle Anforderungen erfüllen und den Parcour meistern. Am Ende jedoch musste das Herz ein wenig bluten, weil am Ende des Praktikums mussten wir den Roboter wieder in seine Einzelteile zerlegen und das Legobauset in seinem Ausgangszustand zurückgeben.
Ich hoffe ich konnte euch einen kleinen Einblick in das Roboterpraktikum der TU Dresden geben und euch eventuell für einen Lego-Roboter begeistern, denn damit kann man viel anfangen und ist nicht nur für Studenten 😉
Tweet
04.04.2011 um 21:59 Uhr
Hallo,
ein sehr interessantes Projekt 🙂 (Nur muss ich mich leider hier outen, dass ich selbst nach über einem halben jahr info unterricht, fast kein java kann – und auch so irgendwie nie den einstieg ins programmieren finde :/ (In der Schule hat leider eh kein Lehrer Ahnung …))
Grüße,
Jonas
04.04.2011 um 22:13 Uhr
Hey, danke 🙂
Der Einstieg in eine Programmiersprache (egal ob Java oder eine andere) ist immer der schwierigste Schritt. Ich kann nur empfehlen über Tutorials zu üben, denn niemand kann einem das Programmieren beibringen, denn das kann man nur selbst durch “Learning by doing”.
08.04.2011 um 11:04 Uhr
Ein wirklich tolles Projekt! Da sieht man, dass es doch noch kluge Menschen auf diesen Planeten gibt 😉
10.04.2011 um 23:51 Uhr
Sehr schönes Projekt! Wir hatten früher auch so Roboter die wir programmieren mussten, leider kamen wir nie soweit, dass wir an irgendwelchen Wettbewerben teilnehmen durften – Schade!
07.03.2012 um 12:42 Uhr
Ich glaube aber, dass da ein Fehler am Anfang steht, “Physik und Informationsystemtechnik mit Informatik als Nebenfach” klingt, als könne man IST mit Nebenfach Informatik belegen, dabei bezieht sich das mit dem Nebenfach nur auf die Physiker