Freitag, 1. Februar 2008
Die Arbeit wurde prämiert!
Um diesen Blog noch zu einem würdigen Ende zu führen: Meine Maturitätsarbeit mit dem Titel "Simulierte Evolution neuronaler Robotersteuerungen" wurde von der KZO prämiert! An dieser Stelle möchte ich all denen Danken, die mich bei diesem Prozess unterstützt haben und mir wichtige Tipps gaben!
Montag, 27. August 2007
Die Arbeit neigt sich dem Ende zu
Da nun der Abgabetermin nur noch ca. 2.5 Monate vor mir liegt, muss ich langsam daran denken, den praktischen Teil abzuschliessen und mich voll und ganz dem schriftlichen zu widmen. Auf Anregung von Lukas Neukom möchte ich aber noch eine Sache in dieser Arbeit ausprobieren: Wie verhalten sich all meine Roboter-Kreaturen auf zufälligem Untergrund (d.h. nicht einfach eine Ebene, sondern hügeliges Terrain) und ist es möglich, die Roboter dort anzupassen? Des Weiteren möchte ich untersuchen, ob meine Roboter auch in der Lage sein werden, kleine, vorerst äussert schwierig zu überwindende Hindernisse (wie z.b. ein im Terrain festgemachter Quader, der hervorragt) immer schneller zu passieren?
Fragen über Fragen, die ich innerhalb meiner Arbeit noch beantworten möchte.
Fragen über Fragen, die ich innerhalb meiner Arbeit noch beantworten möchte.
Samstag, 25. August 2007
Videos nun auf YouTube
Da es bei verschiedenen Personen Probleme gab, die Videos anzugucken, habe ich mich entschieden, die Videos auf YouTube hochzuladen. Im Moment sind nur die hochgeladen, die nicht direkt verlinkt werden konnten (Videos auf gratis File-Sharing-Seite). Das wären diese beiden:
Video grosse Spinne
Video kleine Spinne
Auch wenn die Qualität ein bisschen zu wünschen übrig lässt, kann man trotzdem gut erkennen, wie sich die Spinne fortbewegt. Bei YouTube kann ich auch sicher gehen, dass sie noch in einem Jahr noch nicht gelöscht wurden (wahrscheinlich), bei allen anderen gratis Webspace hingegen können unter Umständen die Videos sofort verschwinden.
Video grosse Spinne
Video kleine Spinne
Auch wenn die Qualität ein bisschen zu wünschen übrig lässt, kann man trotzdem gut erkennen, wie sich die Spinne fortbewegt. Bei YouTube kann ich auch sicher gehen, dass sie noch in einem Jahr noch nicht gelöscht wurden (wahrscheinlich), bei allen anderen gratis Webspace hingegen können unter Umständen die Videos sofort verschwinden.
Donnerstag, 2. August 2007
Die Spinne bekommt mehr Beine
Nachdem ich sie eine Nacht lang entwickeln liess, kam ein erstaunlicher Gang zu Stande: Er ist sehr gleich wie der einer richtigen Spinne, mit dem Unterschied, dass sich die vordersten und hintersten Beine parallel bewegen, die mittleren hingegen genau immer umgekehrt. Ein Videovergleich soll dies illustrieren:
Video simuliert
Video real
Das Diagramm links zeigt den Entwicklungsprozess über 700 Generationen (obwohl ich den PC insgesamt 7 Stunden laufen liess, konnten in der Zeit nicht mehr soviele Generationen wie das letzte Mal (1700) Generationen simuliert werden. Die höhere Komplexität der Spinnen hatte einen erheblichen Einfluss in die Performance der Physik-Engine. Das Diagramm zeigt, dass wahrscheinlich mehr oder weniger das Maximum erreicht worden ist.
Mittwoch, 1. August 2007
Neue Kreatur: Spinne
Die Hauptprobleme bei der Implementation waren, die aktuellen Gelenk-Informationen dem neuronalen Netz bereitzustellen. Leider bietet die Physik-Engine (AGEIA PhysX) keine direkten Funktionen an, um bei einem Gelenk die Winkel auszulesen. Deshalb musste ich komplizierte Vektorgeometrie anwenden, um die richtigen Winkel zu berechnen.
Nach einigen Stunden war dann die Kreatur bereit, trainiert zu werden. Es ist sehr wichtig, dass man alle Eingaben und Ausgaben bei einem neuronalen Netz von Hand überprüft, denn allfällige Fehler sind nicht direkt sichtbar, man merkt es erst, wenn sich die Kreatur nicht wirklich verbessert.
Im Video sieht man, wie die Spinne praktisch am Optimum des Möglichen ist. Die Bewegung ist sehr rund und extrem synchron (alle Gelenke werden jedoch einzeln angesteuert!). Da es sich bei der Spinne um eine relativ einfache Aufgabe handelt, eignet sie sich auch, um nochmal das Thema Steuerkommandos für Roboter wieder zu beleben.
Video nach ca. 1700 Generationen
Dienstag, 31. Juli 2007
Arbeiten schreiten voran
In den letzten Tagen habe ich mich intensiv mit dem schriftlichen Teil befasst. Ich habe mich entschieden, die ganze Arbeit in LaTeX zu schreiben, da es gegenüber Word erhebliche Vorteil bei der Stabilität und Formatierung aufweist, zusätzlich macht es einen wissenschaftlicheren Eindruck. Ich habe bereits einen ersten Entwurf des theoretischen Teils geschrieben. Er umfasst bereits 15 Seiten (jedoch mit vielen von Hand gemachten Abbildung, die aber mindestens so zeitaufwändig waren wie reiner Text zu schreiben) und beinhaltet die Themen Genetische Algorithmen, Neuronale Netze und Physik Simulationen. Dieser Teil ist vollkommen von meiner praktischen Arbeit entkoppelt, das heisst ich beziehe mich sehr wenig auf eigene Erfahrungen und Beobachtungen, sondern bringe einfach die graue Theorie. Ich denke, dass der Kommentar zum praktischen Teil viel umfangreicher ausfallen wird. Dies so, weil ich mit vielen Problemen konfrontiert wurde, die ich ausführlich kommentieren.
Auch habe ich am praktischen Teil (dem Programm selber) weitergearbeitet. Ich habe den Code aufgeräumt, damit er wiederbrauchbarer wurde. Aufgrund dieser Änderungen konnte ich dann relativ rasch einen neuen Roboter implementieren: Genau der gleiche Wurm, einfach mit doppelter Länge (d.h. er besteht aus 8 Elementen mit 7 Verbindungen). Anfangs bereitete es mir recht viele Probleme, da das Verhalten zu chaotisch war (die gleichen Gene lieferten unterschiedliche Resultate bei mehreren Testläufen). Die Fehlerquelle lag bei dem neuronalen Netz: Es waren zu viele versteckte Neuronen vorhanden, die ein sehr chaotisches Verhalten generierten. Indem ich die Anzahl reduzierte, konnte ein stabilerer Bewegungsablauf, der mehr oder weniger immer gleich läuft, erzeugt werden.
Video nach 90 Generationen (Optimum ist noch nicht erreicht!)
Auch habe ich am praktischen Teil (dem Programm selber) weitergearbeitet. Ich habe den Code aufgeräumt, damit er wiederbrauchbarer wurde. Aufgrund dieser Änderungen konnte ich dann relativ rasch einen neuen Roboter implementieren: Genau der gleiche Wurm, einfach mit doppelter Länge (d.h. er besteht aus 8 Elementen mit 7 Verbindungen). Anfangs bereitete es mir recht viele Probleme, da das Verhalten zu chaotisch war (die gleichen Gene lieferten unterschiedliche Resultate bei mehreren Testläufen). Die Fehlerquelle lag bei dem neuronalen Netz: Es waren zu viele versteckte Neuronen vorhanden, die ein sehr chaotisches Verhalten generierten. Indem ich die Anzahl reduzierte, konnte ein stabilerer Bewegungsablauf, der mehr oder weniger immer gleich läuft, erzeugt werden.
Video nach 90 Generationen (Optimum ist noch nicht erreicht!)
Montag, 18. Juni 2007
Steuerkommandos funktionieren (einigermassen), Selektionsalgorithmus angepasst
Nachdem es hier einige Zeit lang etwas ruhiger wurde, kann ich einen weiteren Fortschritt melden: Die Würmer können nun Steuerkommandos, ob sie nach links, recht oder gar nicht drehen sollen, entgegen nehmen. Dies habe ich erreicht mitfhilfe der Taktik des Test-Parcours, die ich im letzten Post beschrieben habe. Auf dem Weg dahin gab es noch einige Probleme: Wieder einmal eine Division durch 0 verursachte einen Programmabsturz. Das Problem an diesen Fehlern ist, dass sie nicht direkt erkennbar sind, wenn sie ausgeführt werden, sondern erst ein bisschen später, irgendwo anders im Programm, sich bemerkbar machen, wo man sie überhaupt nicht erwartet. Verursacht wurde der Division durch 0 Fehler, als der Richtungsvektor des Roboters (also in der Richtung in die der Roboter gerade sich fortbewegt), der aber ein Nullvektor war, normalisiert werden musste. Bei der Normalisierung eines Vektors wird dieser selbst durch seine Länge geteilt. Die Länge des Nullvektors ist logischerweise auch 0, und dies verursachte den Fehler.
Des Weiteren habe ich den Selektionsalgorithmus angepasst: Vorher war er so, dass einfach ein gewisser Prozentsatz der Population selektioniert wurde. Jetzt habe ich es so abgeändert, dass (fast) alles zufällig ist: Das beste Individuum kommt mit einer Chance von 1 in die nächste Generation. Befindet sich das Rating des Individuums unter dem Durchschnitt, so wird zwischen dem Durchschnitt und dem Minimum linear interpoliert, beim Minimum ist es die Chance 0 (gar nie) und beim Durchschnitt ist es 0.1. Oberhalb des Durchschnitt wird quadratisch interpoliert, nicht linear. Die Graphik links zeigt diesen Sachverhalt: Angenommen, das beste Rating sei 1.0, das schlechteste 0.0 und der Durchschnitt 0.5, so ordnet diese Funktion dem Rating die Wahrscheinlichkeit zu, dass die Gene in die nächste Generation kommen. Auffällig ist, dass die Wahrscheinlichkeiten zuerst sehr klein sind und dann fast explosiv artig ansteigen. Dies musste ich so machen, damit der Selektionsdruck etwas stärker wird. Bei linearer Interpolation war es teilweise so, dass über 50% in die nächste Generation kam und somit zu wenig elitär war, um Fortschritte zu erzielen.
Als letztes wurde noch die Mutationsrate abhängig gemacht von der Diversität eines einzelnen Genes (wie bereits beschrieben). Ob dies etwas genutzt hat oder nicht, kann ich nicht beantworten, denn direkt habe ich es nicht verglichen mit der alten Mutationsrate.
Zum Schluss kommt noch das obligatorische Video bei einem sichtbaren Fortschritt:
Link zum Video
Man sieht, dass sich der Roboter zuerst nach rechts dreht, danach mit einer Linksdrehung wieder auf seine ursprüngliche Richtung kommt. Ich habe dies so implementiert, dass man den Roboter mit der Tastatur steuern kann. Man muss dazu aber sagen, dass dieser sehr träge reagiert (ca. 5 Sekunden bis er reagiert).
Als letztes wurde noch die Mutationsrate abhängig gemacht von der Diversität eines einzelnen Genes (wie bereits beschrieben). Ob dies etwas genutzt hat oder nicht, kann ich nicht beantworten, denn direkt habe ich es nicht verglichen mit der alten Mutationsrate.
Zum Schluss kommt noch das obligatorische Video bei einem sichtbaren Fortschritt:
Link zum Video
Man sieht, dass sich der Roboter zuerst nach rechts dreht, danach mit einer Linksdrehung wieder auf seine ursprüngliche Richtung kommt. Ich habe dies so implementiert, dass man den Roboter mit der Tastatur steuern kann. Man muss dazu aber sagen, dass dieser sehr träge reagiert (ca. 5 Sekunden bis er reagiert).
Montag, 4. Juni 2007
Neues Design
Ich habe das Design meines Blogs geändert, weil das alte zu fest die Augen strapazierte - heller Text und dunkler Hintergrund ist vor allem am PC nicht angenehm.
Des Weiteren möchte ich im Laufe dieser Woche mich an die Implementation meines Vorhabens, das dann erlaubt, Roboter zu steuern, heranwagen, bisher habe ich leider noch nicht die Zeit gefunden.
Des Weiteren möchte ich im Laufe dieser Woche mich an die Implementation meines Vorhabens, das dann erlaubt, Roboter zu steuern, heranwagen, bisher habe ich leider noch nicht die Zeit gefunden.
Sonntag, 27. Mai 2007
Steuerkommandos - doch nicht so einfach?
Um zu erreichen, dass Roboter Steuerkommandos verarbeiten können, habe ich einen weiteres Input-Neuron beim neuronale Netz hinzugefügt, das bestimmen soll, ob sich der Roboter nach Links (Input -1), Rechts (Input 1) oder gar nicht (Input 0) drehen soll. Nun wählte ich für jeden Roboter eine zufällige Richtung, die der Roboter erreichen soll. Anhand dieser Zielrichtung berechnete ich nun, ob sich der Roboter nach links oder rechts drehen muss: Sobald die Abweichung grösser als 15° ist, so wird dem neuronalen Netz den entsprechenden Input gegeben, der bestimmt, dass er sich drehen soll. Die Bewertung eines Roboters war gleich wie vorher, nur dass noch ein Betrag abgezogen wird, je nach dem wie fest der Roboter von der Ziel-Richtung abweicht. Nun liess ich die Simulation laufen. Es zeigte sich aber relativ schnell Ernüchterung, da keines Wegs sich zeigte, dass sich die Population so entwickelte, dass Roboter ihren bestimmten Richtungen folgten, sie waren also nicht fähig, Steuerkommandos, ob sie nach links oder rechts drehen sollen, entgegen zu nehmen, das neuronale Netz war trotz der Evolution nicht fähig, dies richtig zu interpretieren.Nun ist es natürlich schwierig herauszufinden, warum dies nicht funktioniert. Im Allgemeinen kann man sagen, dass evolutionäre Algorithmen nur gute Leistungen erbringen können, wenn die Bewertungsfunktion von Genen zuverlässig und genau ist, sie muss auch Teilerfolge honorieren. Und hier liegt denke ich das Problem, warum Steuerkommandos nicht funktionieren. Ich denke, dass vor allem Roboter, die zufällig am Schluss die richtige Zielrichtung hatten, gut bewertet wurden. Somit war das Auswählen der guten Gene in einer Generation nicht systematisch, sondern rein zufällig. Ich bin aber fest davon überzeugt, dass neuronale Steuerungsnetze Kommandos entgegen nehmen können, nur muss man dafür eine effiziente Bewerungsfunktion (quasi Test-Parcour für die Roboter) wählen. Meine Idee hierfür ist, dass ich in einem ersten Abschnitt dem Roboter per Netzinput sage, er solle sich nach rechts drehen. Nach einer gewissen Zeit schaue ich, ob er das getan hat. Nun kommt die andere Richtung, links. Auch hier wird bewertet, ob er diesem Kommando gefolgt ist oder ob er sich weiterhin nach rechts gedreht hat (was dann zufällig wäre, was ich bestrafen werde). Mit dieser Methode kann ich auch schon nur kleinste Erfolge messen. Der Roboter muss sich ja nicht sehr schnell nach links bzw. rechts drehen, sondern auch wenn er sich langsam dreht, kann dies positiv bewertet werden. Wichtig dabei ist jedoch, dass nicht Individuen in die nächste Generation kommen, die nur stark in eine Richtung drehen und damit eine sehr gute Bewertung erzielen (weil dann die andere Richtung einen relativ kleinen Einfluss in die Bewertung hat), sondern dass ich beide Richtungen gleich gewichte, egal wie schnell sich der Roboter in die eine dreht. Wie ich das implementiere, bin ich mir noch nicht so sicher. Aber irgendeinen Weg dazu wird mir schon einfallen.
Mittwoch, 23. Mai 2007
Nächster Schritt
Seit langer Zeit gab es nicht mehr so viel neues auf meinem Blog, das liegt nicht zuletzt daran, dass ich in meinem anderen Hobby (Klavier) noch das Final eines Wettbewerbs absolvieren musste. Nun geht es aber daran, wie ich meine Maturarbeit fortsetzen kann, denn im Prinzip hätte ich schon genug Stoff im Praktischen gesammelt, um den schriftlichen Teil zu schreiben - damit gebe ich mich aber nicht zufrieden.
Zum einen möchte ich herausfinden, ob die Roboter auch fähig sind, Steuerkommandos entgegen zu nehmen, wie z. B. "bewege dich in dieser bestimmten Richtung fort". So konkret habe ich dieses Thema in noch keiner anderen Arbeit über evolutionäre Roboter gefunden, in den meisten ging es nur darum, dass sich der Roboter möglichst schnell fortbewegt. Dieses Thema bietet also etwas Neues, das noch nicht so intensiv erforscht wurde.
Zum anderen möchte ich aber auch neue Morphologien ausprobieren, ich peile vor allem eine ähnliche Struktur des Menschen an um zu schauen, wie effizient mein evolutionärer Algorithmus ist.
Damit bin ich bei einem weitern Thema angelangt, das Optimierungspotenzial bietet. Zur Zeit arbeitet dieser Algorithmus so, dass nur die besten Gene ausgewählt werden. Dies bewirkt, dass nach wenigen Generationen eine akzeptabler Roboter vorhanden ist, der sich schnell fortbewegen kann. Nun ist es aber so, dass durch einen Selektionsalgorithmus, der mit Zufall arbeitet (die besseren Gene aber die grössere Chance bekommen, in die nächste Generation zu gelangen), langfristig die besseren Ergebnisse erzielt werden können.
Man kann auch die Leistung des evolutionären Algorithmus erhöhen, indem die Anzahl Gene, die weiterkommen, davon abhängig macht, wie stark sich die Population momentan verbessert. Falls sie sich nur gering verbessert, verringert man den Selektionsdruck. Dadurch können sich auch andere (im Vergleich zur Mehrheit der aktuellen Generation) Bewegungsformen entwickeln, die anfänglich nicht effizient sind, jedoch im Laufe der Generation die ursprüngliche in der Leistung überholt. Verbessert sich hingegen eine Population laufend, so ist es sinnvoll, den Selektionsdruck zu erhöhen, damit quasi ein "Feintuning" der Gene vorgenommen werden kann und nicht effiziente Gene verworfen werden, da sich die Population in einer starken Entwicklungsphase befindet.
Ein dritter Punkt, der die Leistung des EA (evolutionären Algorithmus) positiv beeinträchtigt ist das stärkere Mutieren aufgrund des Abschätzens des Einfluss der einzelnen Gene auf die Leistung eines Individuums. Damit meine ich, dass Gene, die bei einer grossen Veränderung positiv die Leistung beitragen, stärker mutiert werden als Gene, die bei einer ebenfalls grossen Veränderung zu schlechten Ergebnissen führen. Dies kann man berechnen, indem angeschaut wird, wie fest ein einzelnes Gen über die ganze Population gestreut ist. Ist die Streuung gross (d.h. sehr verschiedene Werte), so werden sie stark mutiert. Bei Genen, die sehr ähnliche Werte haben, wird hingegen eine schwächere Mutation bevorzugt.
Link zu diesen Themen: http://de.wikiversity.org/wiki/Kurs:Genetische_Algorithmen/Kapitel_8
Zum einen möchte ich herausfinden, ob die Roboter auch fähig sind, Steuerkommandos entgegen zu nehmen, wie z. B. "bewege dich in dieser bestimmten Richtung fort". So konkret habe ich dieses Thema in noch keiner anderen Arbeit über evolutionäre Roboter gefunden, in den meisten ging es nur darum, dass sich der Roboter möglichst schnell fortbewegt. Dieses Thema bietet also etwas Neues, das noch nicht so intensiv erforscht wurde.
Zum anderen möchte ich aber auch neue Morphologien ausprobieren, ich peile vor allem eine ähnliche Struktur des Menschen an um zu schauen, wie effizient mein evolutionärer Algorithmus ist.
Damit bin ich bei einem weitern Thema angelangt, das Optimierungspotenzial bietet. Zur Zeit arbeitet dieser Algorithmus so, dass nur die besten Gene ausgewählt werden. Dies bewirkt, dass nach wenigen Generationen eine akzeptabler Roboter vorhanden ist, der sich schnell fortbewegen kann. Nun ist es aber so, dass durch einen Selektionsalgorithmus, der mit Zufall arbeitet (die besseren Gene aber die grössere Chance bekommen, in die nächste Generation zu gelangen), langfristig die besseren Ergebnisse erzielt werden können.
Man kann auch die Leistung des evolutionären Algorithmus erhöhen, indem die Anzahl Gene, die weiterkommen, davon abhängig macht, wie stark sich die Population momentan verbessert. Falls sie sich nur gering verbessert, verringert man den Selektionsdruck. Dadurch können sich auch andere (im Vergleich zur Mehrheit der aktuellen Generation) Bewegungsformen entwickeln, die anfänglich nicht effizient sind, jedoch im Laufe der Generation die ursprüngliche in der Leistung überholt. Verbessert sich hingegen eine Population laufend, so ist es sinnvoll, den Selektionsdruck zu erhöhen, damit quasi ein "Feintuning" der Gene vorgenommen werden kann und nicht effiziente Gene verworfen werden, da sich die Population in einer starken Entwicklungsphase befindet.
Ein dritter Punkt, der die Leistung des EA (evolutionären Algorithmus) positiv beeinträchtigt ist das stärkere Mutieren aufgrund des Abschätzens des Einfluss der einzelnen Gene auf die Leistung eines Individuums. Damit meine ich, dass Gene, die bei einer grossen Veränderung positiv die Leistung beitragen, stärker mutiert werden als Gene, die bei einer ebenfalls grossen Veränderung zu schlechten Ergebnissen führen. Dies kann man berechnen, indem angeschaut wird, wie fest ein einzelnes Gen über die ganze Population gestreut ist. Ist die Streuung gross (d.h. sehr verschiedene Werte), so werden sie stark mutiert. Bei Genen, die sehr ähnliche Werte haben, wird hingegen eine schwächere Mutation bevorzugt.
Link zu diesen Themen: http://de.wikiversity.org/wiki/Kurs:Genetische_Algorithmen/Kapitel_8
Sonntag, 29. April 2007
Problem Fixed, Performance Optimierungen
Problem
Mithilfe einer Analyse vom Remote Debugger der PhysX-Engine kam ich dem Fehler auf die Schliche:
Man sieht, dass der Roboter ins Nirvana transformiert wurde, d.h. die Position wurde auf eine ungültige Zahl gesetzt. Daraus folgte ein Absturz in der Physik-Engine.
Warum aber verhielt sich dies so? Nach wenigen Tests stellte sich heraus, dass setDriveOrientation dies verursacht, wenn man hier ein ungültiges Argument (NaN, Not a Number) übergibt. Dieses Argument ist direkt der Output vom neuronalen Netz, daraus folgt, dass wohl der Input des neuronalen Netzes NaN's enthielt. Nachdem ich diesen untersuchte, zeigt sich, dass ein Arkussinus als Input verwendet wird - dies ist immer gefährlich, denn dieser kann NaN's produzieren (wenn das Argument des Arkussinus im Betrag grösser als 1 ist). Nun habe ich also eine Prüfung eingebaut, dass das Argument beim Arkussinus nicht grösser als 1 ist. Der Fehler entstand wahrscheinlich durch Rechenungenauigkeiten bei Float-Variabeln, denn eigentlich sollte das Argument beim asin gar nicht grösser als 1 sein.
Performance
Beim neuronalem Netz werden jetzt C-Arrays anstatt stl-Container verwendet. Dies optimiert das Programm vor allem im Debug-Mode. Zusätzlich habe ich an der Anzahl Roboter geschraubt, die gleichzeitig simuliert werden. Es zeigte sich, dass je weniger Roboter vorhanden waren, die Physik proportional zur Anzahl Roboter schneller rechnen kann. Eine einfache Erklärung für dieses Problem finde ich nicht, denn die Roboter agieren vollständig unabhängig voneinander. Indem ich nun meine Generation von 200 Robotern in Häppchen von immer 10 unterteile (anstatt 100), wurde die Simulation ca. 30% schneller.
Als Abschluss des heutigen Arbeitstages habe ich noch ein Video hochgeladen, dass den fittesten Roboter zeigt: Er verhält sich verräterisch natürlich.
Video vom fittesten Roboter
Das Diagramm, welches die Entwicklung der Fitness zeigt, darf natürlich auch nicht fehlen.
Bemerkenswert dabei ist, dass der Roboter noch nicht klar das Maximum erreicht hat, die Kurve beginnt nur leicht zu stagnieren.
Mithilfe einer Analyse vom Remote Debugger der PhysX-Engine kam ich dem Fehler auf die Schliche:
Warum aber verhielt sich dies so? Nach wenigen Tests stellte sich heraus, dass setDriveOrientation dies verursacht, wenn man hier ein ungültiges Argument (NaN, Not a Number) übergibt. Dieses Argument ist direkt der Output vom neuronalen Netz, daraus folgt, dass wohl der Input des neuronalen Netzes NaN's enthielt. Nachdem ich diesen untersuchte, zeigt sich, dass ein Arkussinus als Input verwendet wird - dies ist immer gefährlich, denn dieser kann NaN's produzieren (wenn das Argument des Arkussinus im Betrag grösser als 1 ist). Nun habe ich also eine Prüfung eingebaut, dass das Argument beim Arkussinus nicht grösser als 1 ist. Der Fehler entstand wahrscheinlich durch Rechenungenauigkeiten bei Float-Variabeln, denn eigentlich sollte das Argument beim asin gar nicht grösser als 1 sein.
Performance
Beim neuronalem Netz werden jetzt C-Arrays anstatt stl-Container verwendet. Dies optimiert das Programm vor allem im Debug-Mode. Zusätzlich habe ich an der Anzahl Roboter geschraubt, die gleichzeitig simuliert werden. Es zeigte sich, dass je weniger Roboter vorhanden waren, die Physik proportional zur Anzahl Roboter schneller rechnen kann. Eine einfache Erklärung für dieses Problem finde ich nicht, denn die Roboter agieren vollständig unabhängig voneinander. Indem ich nun meine Generation von 200 Robotern in Häppchen von immer 10 unterteile (anstatt 100), wurde die Simulation ca. 30% schneller.
Als Abschluss des heutigen Arbeitstages habe ich noch ein Video hochgeladen, dass den fittesten Roboter zeigt: Er verhält sich verräterisch natürlich.
Video vom fittesten Roboter
Bemerkenswert dabei ist, dass der Roboter noch nicht klar das Maximum erreicht hat, die Kurve beginnt nur leicht zu stagnieren.
Samstag, 28. April 2007
First Post
Einführung
Ich habe mich entschieden, einen Blog einzurichten, um meine Gedanken festhalten zu können. Der Vorteil davon ist, dass ich so im Internet - von überall aus verfügbar - die Fortschritte zu Papier bringen kann, damit auch andere Beteiligte, wie z.B. mein Betreuer, sehen kann, wie sich meine Simulation weiterentwickelt.
Fortschritt bis Einführung zum Blog
Als erstes ging es darum, eine Umgebung für meine Roboter zu schaffen. Ich entschied mich für die irrlicht Engine, da sie sehr einfach zu gebrauchen ist und für meine Zwecke völlig ausreicht. Um die Physik zu simulieren nutze ich die PhysX-Engine, die auch in vielen professionellen Spielen Verwendung findet. Das Einrichten der Umgebung ging relativ schnell voran, da ich mit beiden Engines früher schon Erfahrung gesammelt hatte. Nun kam mein erstes Experiment: Der Hopper. Bei diesem Roboter ging es darum zu schauen, ob sich meine Idee bewährte und ob ich die Maturarbeit, wie ich sie mir vorstellte, durchführen konnte. Es handelte sich um einen Roboter, der ein automatisches Sprunggelenk (algorithmisch gesteuert, nicht neuronal) besitzt und somit kontinuierlich springt. Als Steuerung setzte ich im Prinzip ein neuronales Netz ohne Hidden-Layers ein (also in der Form: output = weight1*input1 + weight2*input2 + ... + weightn*inputn). Der Roboter war in der Lage, auf den Körper direkt ein Drehmoment auszuwirken, was physikalisch eigentlich nicht korrekt ist (normalerweise können Roboter nur ihre Gelenke steuern, nicht aber direkt Kräfte auf sich einwirken lassen). Dieser Drehmoment wurde mit dem simplen neuronalen Netz berechnet, also Inputs dienten verschiedene Informationen über die aktuelle Lage des Roboters wie Höhe über Boden, Winkel, Touch-Sensor und noch ein paar andere. Die Weights des neuronalen Netzes wurden dann evolutionär optimiert (dazu komme ich zu einem anderen Zeitpunkt noch). Es zeigte sich, dass relativ rasch (innerhalb weniger Generationen) Gewichte gefunden werden konnten, die den Roboter sehr effizient fortbewegen liessen, das Entwicklungspotenzial (d.h. wie effizient der Roboter noch werden kann) war aber auf Grund der relativ einfachen Aufgabestellung (sich möglichst schnell fortbewegen) und wegen der physikalischen Inkorrektheit sehr rasch erschöpft:

Diese Abbildung zeigt, wie sich die Fitness der Roboter (d.h. wie schnell sie sich fortbewegten) entwickelte. Die blaue Linie repräsentiert jeweils das beste Individuum in der Population, die Pinke den Durchschnitt.
Ich habe einen Film hochgeladen, der zeigt, wie sich der Roboter fortbewegt - bemerkenswert ist dabei, dass er sich physikalisch inkorrekt fortbewegt.
http://www.4shared.com/file/14912865/f15a14ba/firstexperiment.html
Mit diesem Experiment zeigte sich aber, dass es bei sich bei meinem Konzept um ein realistisches und realisierbares handelt, da ich mit relativ wenigem Zeitaufwand erste Resultate erzielen konnte.
Als nächstes implementierte ich dann ein richtiges neuronales Netz mit hidden Layers, da mir die bisherige Steuerung zu einfach und zu beschränkt war. Danach suchte ich einen neuen Phänotypen für den Roboter, da der Hopper wohl noch zu komplex für mich war (wenn er physikalisch korrekt simuliert werden sollte) und ich zuerst Erfahrungen bei einfacheren Robotern sammeln wollte.
Das Resultat war dann ein 2-dimensionaler Wurm mit 5 Teilen:

Hier ist mit 2-dimensional gemeint, dass er sich entlang einer Geraden und in die Höhe bewegen kann, nicht aber auf einer Ebene.
Der Wurm hat die Möglichkeit, jedes seiner 4 Gelenke über ein neuronales Netz zu steuern. Ich wählte 2 Hidden Layers mit je 12 Neuronen. Als Inputs dienten die 4 Winkel zwischen den jeweiligen Wurm-Teilen und die des letzten Frames, dies damit das neuronale Netz ein sich repetitiver Bewegungsablauf generieren konnte. 4 Output-Neuronen steuern dann die gewünschten Winkel zwischen den Wurm-Teilen. Die Physik Engine versucht dann mit entsprechenden Drehmomenten, diese zu erreichen.
Auch dieses Resultat lässt sich sehen: Im folgenden Video sieht man die Entwicklungen mit den Generationen: Jeweils der beste Roboter jeder Generation wird simuliert, zu hinterst die 1., zu vorderst die letze Generation.
http://www.4shared.com/file/14913483/371c0200/2dworm.html
Was ich heute gemacht habe
Mein Ziel war es, den Wurm so weiterzuentwickeln, dass er bei jedem Gelenk 2 Freiheitsgrade steuern kann. Der Vorteil ist, dass ich so von der 2D-Umgebung wegkomme. Die Folgen davon sind, dass die Bewegungsabläufe komplexer werden. Mein Wurm hat inzwischen ca. 400 Gene - die zu optimieren, ist keine einfache Aufgabe für den Computer. Ich könnte mir vorstellen, dass es viele Generationen braucht, damit sich der Wurm immer schneller fortbewegen kann.
Bei diesem Task war vor allem schwierig, das Rotations Target für das Gelenk zu berechnen. In der PhysX-Dokumentation wurde ich nicht schlüssig, wie ich die Rotation in 2 Teilrotationen aufsplitten kann, damit das neuronale Netz es einfacher hat, den Roboter zu steuern (eigentlich wäre ein Quaternion gefragt, welcher 4 Attributen besitzt, allerdings handelt es sich um ein Universal-Gelenk, das 2 Winkel als Target verlangt (siehe Grafik links)).
Die Lösung des Problems waren dann Euler-Winkel. Diese sind eigentlich genau die richtige Möglichkeit für mich, Rotationen zu beschreiben. Das Objekt wird zuerst entlang der einen Achse rotiert, danach wird es um die nächste Achse rotiert, wobei die Achse, um die rotiert wird, von der 1. Rotation bereits rotiert wurde.
Das Gleiche gilt für die dritte Achse. Man kann es auch mit einem Flugzeug vergleichen:
Dieses Prinzip kann man auch für Target-Rotationseinstellungen (wie die Rotation von den beiden Bodies, die am Gelenk angeschlossen sind, im Verhältnis sein soll) von Gelenken anwenden, dort wird ein Quaternion verlangt, denn man aber aufgrund von Euler-Winkel berechnet.
http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
Nun hatte ich dieses Problem also gelöst. Als Folge davon war das neuronale Netz mit der Zeit in der Lage, unseren 5-teiligen Wurm so zu steuern, dass er nach wenigen Generationen sich mehr oder weniger Effizienz fortbewegte. Im Release-Modus allerdings kam es immer wieder zu einem Crash. Das Problem ist, dass dieser Fehler nicht debug-bar ist, da er nur im Release-Modus und zudem in der DLL der Physikengine auftritt. Ich werde sehen, wie ich diesen Fehler wegkriege.
Ich habe mich entschieden, einen Blog einzurichten, um meine Gedanken festhalten zu können. Der Vorteil davon ist, dass ich so im Internet - von überall aus verfügbar - die Fortschritte zu Papier bringen kann, damit auch andere Beteiligte, wie z.B. mein Betreuer, sehen kann, wie sich meine Simulation weiterentwickelt.
Fortschritt bis Einführung zum Blog
Als erstes ging es darum, eine Umgebung für meine Roboter zu schaffen. Ich entschied mich für die irrlicht Engine, da sie sehr einfach zu gebrauchen ist und für meine Zwecke völlig ausreicht. Um die Physik zu simulieren nutze ich die PhysX-Engine, die auch in vielen professionellen Spielen Verwendung findet. Das Einrichten der Umgebung ging relativ schnell voran, da ich mit beiden Engines früher schon Erfahrung gesammelt hatte. Nun kam mein erstes Experiment: Der Hopper. Bei diesem Roboter ging es darum zu schauen, ob sich meine Idee bewährte und ob ich die Maturarbeit, wie ich sie mir vorstellte, durchführen konnte. Es handelte sich um einen Roboter, der ein automatisches Sprunggelenk (algorithmisch gesteuert, nicht neuronal) besitzt und somit kontinuierlich springt. Als Steuerung setzte ich im Prinzip ein neuronales Netz ohne Hidden-Layers ein (also in der Form: output = weight1*input1 + weight2*input2 + ... + weightn*inputn). Der Roboter war in der Lage, auf den Körper direkt ein Drehmoment auszuwirken, was physikalisch eigentlich nicht korrekt ist (normalerweise können Roboter nur ihre Gelenke steuern, nicht aber direkt Kräfte auf sich einwirken lassen). Dieser Drehmoment wurde mit dem simplen neuronalen Netz berechnet, also Inputs dienten verschiedene Informationen über die aktuelle Lage des Roboters wie Höhe über Boden, Winkel, Touch-Sensor und noch ein paar andere. Die Weights des neuronalen Netzes wurden dann evolutionär optimiert (dazu komme ich zu einem anderen Zeitpunkt noch). Es zeigte sich, dass relativ rasch (innerhalb weniger Generationen) Gewichte gefunden werden konnten, die den Roboter sehr effizient fortbewegen liessen, das Entwicklungspotenzial (d.h. wie effizient der Roboter noch werden kann) war aber auf Grund der relativ einfachen Aufgabestellung (sich möglichst schnell fortbewegen) und wegen der physikalischen Inkorrektheit sehr rasch erschöpft:
Diese Abbildung zeigt, wie sich die Fitness der Roboter (d.h. wie schnell sie sich fortbewegten) entwickelte. Die blaue Linie repräsentiert jeweils das beste Individuum in der Population, die Pinke den Durchschnitt.
Ich habe einen Film hochgeladen, der zeigt, wie sich der Roboter fortbewegt - bemerkenswert ist dabei, dass er sich physikalisch inkorrekt fortbewegt.
http://www.4shared.com/file/14912865/f15a14ba/firstexperiment.html
Mit diesem Experiment zeigte sich aber, dass es bei sich bei meinem Konzept um ein realistisches und realisierbares handelt, da ich mit relativ wenigem Zeitaufwand erste Resultate erzielen konnte.
Als nächstes implementierte ich dann ein richtiges neuronales Netz mit hidden Layers, da mir die bisherige Steuerung zu einfach und zu beschränkt war. Danach suchte ich einen neuen Phänotypen für den Roboter, da der Hopper wohl noch zu komplex für mich war (wenn er physikalisch korrekt simuliert werden sollte) und ich zuerst Erfahrungen bei einfacheren Robotern sammeln wollte.
Das Resultat war dann ein 2-dimensionaler Wurm mit 5 Teilen:
Hier ist mit 2-dimensional gemeint, dass er sich entlang einer Geraden und in die Höhe bewegen kann, nicht aber auf einer Ebene.
Der Wurm hat die Möglichkeit, jedes seiner 4 Gelenke über ein neuronales Netz zu steuern. Ich wählte 2 Hidden Layers mit je 12 Neuronen. Als Inputs dienten die 4 Winkel zwischen den jeweiligen Wurm-Teilen und die des letzten Frames, dies damit das neuronale Netz ein sich repetitiver Bewegungsablauf generieren konnte. 4 Output-Neuronen steuern dann die gewünschten Winkel zwischen den Wurm-Teilen. Die Physik Engine versucht dann mit entsprechenden Drehmomenten, diese zu erreichen.
Auch dieses Resultat lässt sich sehen: Im folgenden Video sieht man die Entwicklungen mit den Generationen: Jeweils der beste Roboter jeder Generation wird simuliert, zu hinterst die 1., zu vorderst die letze Generation.
http://www.4shared.com/file/14913483/371c0200/2dworm.html
Was ich heute gemacht habe
Mein Ziel war es, den Wurm so weiterzuentwickeln, dass er bei jedem Gelenk 2 Freiheitsgrade steuern kann. Der Vorteil ist, dass ich so von der 2D-Umgebung wegkomme. Die Folgen davon sind, dass die Bewegungsabläufe komplexer werden. Mein Wurm hat inzwischen ca. 400 Gene - die zu optimieren, ist keine einfache Aufgabe für den Computer. Ich könnte mir vorstellen, dass es viele Generationen braucht, damit sich der Wurm immer schneller fortbewegen kann.
Bei diesem Task war vor allem schwierig, das Rotations Target für das Gelenk zu berechnen. In der PhysX-Dokumentation wurde ich nicht schlüssig, wie ich die Rotation in 2 Teilrotationen aufsplitten kann, damit das neuronale Netz es einfacher hat, den Roboter zu steuern (eigentlich wäre ein Quaternion gefragt, welcher 4 Attributen besitzt, allerdings handelt es sich um ein Universal-Gelenk, das 2 Winkel als Target verlangt (siehe Grafik links)).Die Lösung des Problems waren dann Euler-Winkel. Diese sind eigentlich genau die richtige Möglichkeit für mich, Rotationen zu beschreiben. Das Objekt wird zuerst entlang der einen Achse rotiert, danach wird es um die nächste Achse rotiert, wobei die Achse, um die rotiert wird, von der 1. Rotation bereits rotiert wurde.
Das Gleiche gilt für die dritte Achse. Man kann es auch mit einem Flugzeug vergleichen:Dieses Prinzip kann man auch für Target-Rotationseinstellungen (wie die Rotation von den beiden Bodies, die am Gelenk angeschlossen sind, im Verhältnis sein soll) von Gelenken anwenden, dort wird ein Quaternion verlangt, denn man aber aufgrund von Euler-Winkel berechnet.
http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
Nun hatte ich dieses Problem also gelöst. Als Folge davon war das neuronale Netz mit der Zeit in der Lage, unseren 5-teiligen Wurm so zu steuern, dass er nach wenigen Generationen sich mehr oder weniger Effizienz fortbewegte. Im Release-Modus allerdings kam es immer wieder zu einem Crash. Das Problem ist, dass dieser Fehler nicht debug-bar ist, da er nur im Release-Modus und zudem in der DLL der Physikengine auftritt. Ich werde sehen, wie ich diesen Fehler wegkriege.
Abonnieren
Posts (Atom)