Eine Dokumentation über Visual Programming Language Im Rahmen des Microsoft Robotics Studio Praktikums
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Eine Dokumentation über Visual Programming Language Im Rahmen des Microsoft Robotics Studio Praktikums
Vorwort Dieses Dokument wurde im Rahmen des Softwarepraktikums MS Robotics Studio erstellt während des WS 07/08. Lieber Leser, liebe Leserin, unser Ziel ist es, einen ersten Einblick in die Funktionsweise von Microsoft Robotics zu geben, so dass auch jemand, der zum ersten Mal damit in Berührung kommt, in kurzer Zeit einen Roboter zu steuern vermag. Um dies zu erreichen, gibt es zunächst einen kleinen Rundgang durch die Standard- Steuerelemente wie Daten-I/O oder Rechenoperationen, danach wird der Umgang und die Kommunikation mit dem NXT-Roboter erläutert. Um das Ganze ein wenig aufzufrischen (und die Seitenzahl zu erhöhen) wird alles mit Hilfe von Beispielen motiviert, unter anderem ein einfaches „Hello World“, erste Fahrversuche des Roboters, vollendet mit dem Programm, welches NXT durch ein Labyrinth ohne fremde Hilfe geleitet. Einige dieser Beispiele werden sie auch auf der Homepage zum Download finden, wenn Sie mal nicht weiterkommen oder einfach ausprobieren wollen. Da es natürlich nicht möglich ist, im Rahmen eines solchen Praktikums alle Aspekte der Software zu erkunden, beschränken wir uns größtenteils darauf, die Visual Programming Language zu erläutern, einige Hinweise zu den anderen Bestandteilen werden Sie aber hier sicher auch finden. Dies begünstigt einen sanften Einstieg in die Materie, für wirklich umfangreiche Routinen ist jedoch die Auseinandersetzung mit C# im Rahmen von Microsoft Robotics ratsam. Viel Spaß bei der Lektüre wünschen Ihnen Buote Xu (Buote.xu'at'gmail.com) Robert Grandl (robert.grandl'at'googlemail.com) Mit freundlicher Unterstützung von Dr. Katja Mombaur und Benjamin Reh
Inhaltsverzeichnis Zu Microsoft Robotics Studio 1 VPL-Interface 2 Input/Output 3/4 Rechenoperationen 5 Timing-Probleme / Parallelisierung 6 Die verschiedenen Ausgänge eines Services 7 Und Action! 8ff. Robotik 13 Manifeste 14 Lego 15 FAQ 20
Ein kurzer Überblick Microsoft Robotics Studio (im Folgenden MRS) wurde im Jahre 2006 veröffentlicht und ist zum Download frei verfügbar. (Installation siehe Anhang) Mittlerweile in der Version 1.5 angekommen, bietet MRS eine Vielzahl an Möglichkeiten, mit dem Roboter Ihrer Wahl Verbindung aufzunehmen. So wird unter anderem das eigene VPL unterstützt, wer jedoch z.B. lieber in C#, Visual Basic oder .NET arbeitet wird sich auch hier heimisch fühlen. Das derzeit 87.5 MB große Softwarepaket umfasst derzeit die VPL Umgebung (die auf den nächsten Seiten erläutert wird), mit deren Hilfe man Programme schreiben und debuggen kann, web- und fensterbasierte Interfaces zur Kontaktaufnahme mit dem Roboter (wobei sich das Webinterface weigert, mit Opera zusammen zu arbeiten) sowie hardwarebeschleunigte 3D-Simulationen, in denen sich ein virtueller Roboter, der über den DSS Manifest Editor erstellt wurde, austoben kann. Intern steuert MRS über so genannte „Services“ den Roboter, diese können immer wieder aktualisiert werden um auch neuere Modelle zu unterstützen, sowie Fehler bei älteren auszumerzen, (der Lichtsensor funktioniert z.B. noch nicht 100% fehlerfrei mit den derzeitigen Services) auf diesem Wege können aber auch Beispielprogramme von Microsoft vertrieben werden wie eine Fußballsimulation oder ein Sumo-Wettstreit. Die Technik(Spiele)-Interessierten wird freuen, dass die 3D-Simulation über die PhysX- Engine von Ageia realisiert wird, die auch in vielen Spielen eingesetzt wird. Aber genug Gerede, jetzt geht es an die Praxis: 1
VPL: Visual Programming Language Wenn Sie das Programm zum ersten Mal öffnen, wird Ihnen dieses Fenster begegnen: 1 2 3 4 5 Unter 1 finden sie die Basic Activities, die größtenteils selbsterklärend sind, unter 2 haben sie schnellen Zugriff auf die verschiedenen Diagramme und Konfigurationen. Die große leere Fläche namens Diagram bei 3 wird unser Hauptarbeitsplatz sein und sich schneller füllen als Ihnen lieb ist, glücklicherweise gibt es rechts unten einen Schieberegler mit dem man sich etwas mehr Überblick verschaffen kann. Man füllt die Leere indem man Einträge aus 1 und 4 per Drag-and-Drop reinzieht. 4 beinhaltet die schon erwähnten Services, darunter so wichtige wie Text/Sprachausgabe, mathematische Funktionen und die NXT-Steuerung. Tutorials sind auch vorhanden, aber dazu haben wir ja den Guide hier. ;) 5 die Properties unterscheiden je nach ausgewähltem Objekt und bieten vor allem die Möglichkeit sich einen schnellen Überblick zu verschaffen über die gewählte Konfiguration des und die Daten, die über die Verbindung ankommen, Kommentare können hier auch festgehalten werden. Wir kommen zu unserem ersten Programm: 2
Good morning, Vietnam! Neu: Data, SimpleDialog Bei einem Klick auf int erhält man einen Überblick auf die unterstützten Datentypen, ansonsten ist Data unsere Methode Daten in unser Programm einzufügen. Wichtig: VPL ist case-sensitive. Dieser Service ist dafür zuständig, eine Dialogbox zu erzeugen, auf mehr als eine Weise! Um unser Programm zu schreiben, ziehen wir einfach die beiden Einträge auf das große Diagramm-Fenster und verbinden sie wieder mit Drag-and-Drop: So einfach wird der Bund fürs Leben geschlossen! Anmerkung: Der linke orange Kasten bezeichnet immer den Input, der rechte den Output eines Elements. Es sind mehrere Outputverbindungen möglich, jedoch immer nur ein Input. Wundern Sie sich nicht über die aufpoppende Box: Wie auch viele andere Services, bietet SimpleDialog mehrere Methoden an, wie es mit den Daten verfahren soll: AlertDialog erzeugt ein einfaches Popup, ConfirmDialog eine Ja/Nein-Wahl und PromptDialog wartet auf eine Eingabe des Nutzers mit möglicher Standardantwort, wir begnügen uns hier mit Alert. Die nächste Abfrage bestimmt darüber, welche Daten von links nach rechts übertragen werden, dies kann man am selben Output je nach Datenverbindung festlegen, also auch an 2 verschiedene SimpleDialogs unterschiedlich senden. Der Standardausgabewert vieler Objekte in MRS wie auch hier ist value. Ein Druck auf F5 sollte nach einem länglichen Kompiliervorgang eine Dialogbox mit dem geforderten Eintrag erzeugen. Glückwunsch zum ersten Programm! 3
Falls es nicht funktioniert hat: halten Sie den Mauszeiger über die Verbindung und warten Sie auf das Popup um nochmal zu überprüfen, dass alle Daten richtig übertragen werden oder klicken sie auf die Verbindung und betrachten die Properties. Machen Sie dies immer wenn Ihr Programm nicht läuft! MRS setzt gerne nach Änderung von Objekten Datenverbindungen auf null und der Nutzer tappt bei seiner Fehlersuche dann im Dunkeln. Wem es beliebt darf noch ein zweites SimpleDialog einfügen und sich gleichzeitig Length ausgeben lassen, das Ergebnis sollten 2 Dialogboxen sein. Hier sei noch zu erwähnen, dass Services mit dem selben Namen auch die gleiche Instanz bezeichnen, dies wird später noch nützlich sein, ist jedoch beim SimpleDialog nicht weiter wichtig. Weiter im Programm: Wie bereits erwähnt, kann PromptDialog eine vorgefertigte Standardantwort anbieten zusätzlich zur Beschriftung der Dialogbox, aber wie fügt man einen 2. Wert hinzu, es ist doch nur ein Input möglich? Die Lösung bietet folgende Basic Activity: neu: Join Dies ist das Mittel der Wahl um einen Verbund (auch bekannt als struct) weiter zu geben, dabei sind beliebig viele Unterpunkte möglich. Beispiel1: Dem Leser sei nun überlassen, die Verbindungen richtig anzulegen um die Frage „Was ist 5+5?“ zu stellen, die zweite Dialogbox soll unsere Eingabe ausgeben. Anmerkungen: Join gibt einen Verbund value aus mit Unterpunkten msg und msg0, man kann zwar diese direkt ansprechen, sollte sich aber im Hinterkopf behalten dass man sie eigentlich mit value.msg bzw. value.msg0 bezeichnen muss. Ausserdem wird auch nur ein Verbund ausgegeben, wenn alle Unterpunkte aktiv sind - ganz im Gegensatz zu merge, doch dazu später. PromptDialog gibt einen Verbund aus mit dem Unterpunkt TextData, man kann eine unterschiedliche Aktion im Falle eines Misserfolges definieren. 4
Du kannst mehr Mathe als du denkst. Im Jahr der Mathematik darf selbiges natürlich nicht zu kurz kommen: neu: Calculate Dieses Objekt stellt die vier Grundrechenarten sowie den modulo-Operator zur Verfügung, doch das ist nicht alles! Auch eingebaut ist eine implizite Typumwandlung, so z.B. von Zahlen in Strings. Weitere Anwendung ist die Extraktion von einzelnen Daten aus einem Verbund. Beispiel2: Wieder unser Beispiel von vorhin, doch diesmal mit einem zusätzlichen Kontrollmechanismus. Wie in vielen anderen Programmiersprachen findet hier die Umwandlung statt indem man einfach den Typ in Klammern vor den Ausdruck schreibt, so geschehen im ersten Calculate. Dass dies funktioniert hat, kann man an der Verbindung zum Join sehen, wenn man es mal selbst geschrieben hat: Das Popup berichtet über einen Datenfluss CalculatedResult -> Join vom Typ int. Strings werden wie so oft durch '' '' gekennzeichnet, unsere Typumwandlung lässt außerdem als Eingaben nur Integer zu, Werte wie 10.5 oder auch AB werden einen leeren String erzeugen. Beispiel3: Eine einfache Rechnung, zu beachten ist, dass man die Werte in Join sowohl als Verband als auch ohne vorangestelltes value anfordern kann. Da Dialogboxen auf Dauer langweilig sind, werden wir in den nächsten paar Programmen mit dem TexttoSpeech-Service arbeiten und einige Timing-Probleme besprechen. 5
Das leidige Problem mit der Zeit Die zeitliche Abfolge von Programmteilen ist mitunter etwas unklar, dies lässt sich gut mit dem erwähnten TexttoSpeech sowie zwei weiteren Basic Activities zeigen: neu: if und merge Verhält sich wie in jeder anderen Programmiersprache, über das Plus- Zeichen lassen sich weitere Abfragen einbauen, das Else-Kästchen darf dabei unverbunden bleiben. Zwar wurde zu Anfang behauptet, dass jedes Objekt nur einen Eingang besitzt, doch es gibt eine kleine Ausnahme: dieses kleine unscheinbare Objekt leitet alles weiter, was an seinen Eingang gelangt, dabei werden die Daten durchgelitten - es entsteht also ein Wert und kein Verbund. Um dies zu verdeutlichen, die nächsten Beispiele: Beispiel 4: Die If-Abfrage wird hier dreimal nacheinander ausgeführt, da 3 verschiedene Datenströme fließen, alternativ könnte man auch das ganze Konstrukt verdreifachen, die Reihenfolge der Ausgabe ist allerdings nicht klar definiert. Beispiel 5: Aufzählen der Zahlen von 10 bis 0 Zu diesem Programm ist zu sagen, dass das Endresultat nicht vorhersagbar ist! (Es ist übrigens auch eins der Microsoft-Beispielprogramme) 6
Dies ist dem Umstand zu verdanken, dass die Schleife in einer sehr kurzen Zeit durchlaufen wird, während die Textausgabe auf sich warten lässt. Die erhaltenen Ergebnisse werden also in einen Puffer geschrieben und mehr oder weniger zufällig ausgelesen, zumindest nicht 100% in der erwarteten Reihenfolge. Wie also lässt sich dies lösen? Weil TTS als Ausgabe nur Success bzw. Failure besitzt, ist es nicht möglich die Zahlen durch zu leiten und TTS in die Schleife einzubauen, bleibt also nur eine Lösung: Der Grund, wieso sie nicht schon zu Anfang eingeführt wurden ist folgender: Sie sind in der Handhabung etwas umständlich und für einfache Programme schlichtweg nicht notwendig, jedoch ist es an der Zeit sie einzusetzen: Variablendeklaration: Die Variablendeklaration erfolgt durch einen Klick auf ... des Variablensymbols oder durch Edit -> Variables, es muss ein Name und ein Datentyp angegeben werden. Der Zugriff auf die Variable erfolgt auch durch die beiden Methoden SetValue und GetValue: Die einfachste Methode einer Variable einen Wert zu zu weisen. Sie liefert auch einen Output mit dem Wert ihrer Variable, allerdings als Verbund. (also als value.counter) Den Wert der Variable liest man mit GetValue aus, welcher unverständlicherweise auch einen Verbund ausgibt. Dies kann zusammen mit join zu größeren Ketten/Variablennamen führen. Wichtige Notizen zu Variablen: – Der Zugriff auf eine Variable erfolgt ausschließlich über GetValue, es ist also nicht möglich in ein beliebiges Calculate „counter“ zu schreiben in der Hoffnung etwas zu erreichen. – Dies führt dazu dass der Eingang eines Variable-Blocks immer verbunden sein muss, damit entweder Get- oder SetValue ausgelöst werden kann. – Diese Variablen sind nicht global, dieses Problem wird uns im Zusammenhang mit selbst geschriebenen Activities begegnen. *Beispiel6: Die Lösung unseres Problems, man beachte ausserdem die Umstellung auf „SayTextSynchronous“; es wird erst dann „Success“ ausgegeben und die Schleife wieder in Gang gesetzt wenn der Text gesprochen ist, damit bleibt der Puffer leer. 7
Exkurs: Ergebnisse und Ereignisse Wer schon etwas eigenen Code gebastelt und dabei mit diversen Services herumexperimentiert hat wird festgestellt haben, dass einige Objekte zwei Anschlüsse besitzen, so zum Beispiel der Timer. Neu: Timer Mit dem Timer kann man zum Beispiel den gesamten Programmablauf für eine bestimmte Zeit pausieren (wait), es ist aber auch möglich nur eine bestimmte Aktion verzögert beginnen zu lassen. (SetTimer -> TimerComplete) Interessant: Wenn man an den runden Ausgang etwas anschließt, verschwindet der Eingang! Das liegt daran, dass der Timer sowohl einen Ergebnis- als auch einen Ereignis-Ausgang besitzt, wobei der runde Ereignisausgang hier eingangsunabhängig ist und diesen somit nicht benötigt. Alle unserer bisherigen Objekte geben nur einmal einen Wert aus, – von Schleifen natürlich abgesehen – genau dann, wenn ein Eingangswert anliegt. Ereignisse werden also verwendet, um mehrere Ausgaben zeitversetzt zu erzeugen. Beispiel7: Damit haben wir einen Wecker in VPL gebaut! Die Tick-Funktion führt dazu, dass einmal pro Sekunde der Datenfluss angeregt, danach die Uhrzeit ausgelesen und zum Schluss mit unserem gewünschten Wert verglichen wird. Beispiel8: Auch hier können wir viele Ausgaben kriegen, ohne VPL- Dateneingabe. Um die Seite voll zu machen: Das erste Objekt werden wir später noch einmal verwenden, um einen Roboter in der Simulation zu steuern. Aufgabe des Lesers ist es nun, sich damit selbst etwas zu befassen, bei Ermangelung eines echten Controllers muss der DirectionDialog herhalten. 8
Ordnung muss auch sein Neu: Activity, Switch Activities entsprechen Funktionen in C++ und werden eingesetzt, um Übersichtlichkeit zu gewährleisten und Code zu sparen bei Wiederholungen. Es gibt jedoch (leider) einige wichtige Regeln im Umgang mit ihnen zu beachten: – Activities besitzen verschiedene Funktionen (actions) mit jeweils eigenen Eingabe/Ausgabetypen. – Funktionen innerhalb einer Activity können nicht auf Variablen auf der Hauptebene zugreifen, man muss also ein separates Set deklarieren. – Ereignisse können nur in der Start-Action einer Activity verwendet werden – Alle Funktionen innerhalb derselben Activity können auf die selben, internen Variablen zugreifen. – Die Entwickler haben eine Sicherheitsmaßnahme implementiert, die im Zusammenhang mit Funktionen und Variablenmanipulation zu einem Programmstop führen kann: Eine Funktion, innerhalb derer eine Variable verändert wird, bezeichnet man als exklusiv - diese können nur ablaufen, wenn keine anderen Funktion (müssen nicht exklusiv sein) aktiv ist. Umgekehrt kann also letztere auch nicht ausgeführt wurden wenn bereits eine exklusive am arbeiten ist. Dies kann, wie noch in einem Beispiel demonstriert wird, zu erheblichen Problemen führen. (In der nächsten Revision wird man vor der Kompilation gewarnt, falls ein solcher Konflikt vorliegt.) Ein Switch dient dazu, einen ankommenden Wert mit einer vorgegebenen Auswahl zu vergleichen und gegebenenfalls die dazugehörige Anweisung auszuführen. Dabei wird, falls es mehrere matches gibt, nur der oberste Eintrag aktiviert. Hinweis: Else ist nicht unbedingt nötig, value als unterster Vergleichswert funktioniert auch. *Beispiel9: Rechner Ok, und weiter? So sieht zwar die oberste Ebene eines Taschenrechnerprogramms aus, doch wird man in dieser Ansicht nicht viel damit anfangen können, wir beginnen also erst einmal mit einem kleinen Crashkurs in Sachen Activity: Nachdem man eine neue Activity erstellt hat, kann man durch einen simplen Doppelklick Einfluss auf die Funktion unseres neuen Objektes ausüben. Bei 1 wird die gerade aktive Unterfunktion angezeigt, von denen man beliebig viele haben kann. Über 2 erhält man Zugriff auf ein neues 1 2 Menü, mit dem man einige nützliche Sachen machen kann. 9
Hier ist das angesprochene Menü, dabei hat jedes Activity sein eigenes. Unter 1 kann man weitere Aktionen erstellen, für die man jeweils Datentypen für die 2 Ein-bzw. 3 Ausgabe festlegen kann. 1 Wer gerne eine Ereignisausgabe haben möchte, muss dies im Reiter Notifications vornehmen, Beispiele dazu folgen. 2 3 Unser Programm soll nun in der Lage sein, 2 Werte und einen Befehl entgegen zu nehmen und daraufhin eine Lösung zu präsentieren. Es wird langsam eng! Nicht im Bild zu sehen: Das oberste SimpleDialog-Objekt ist mit dem Action-Eingang verbunden, diese Daten sind vom Typ string (oben als result zu sehen). Außerdem wird hier stark von der Möglichkeit Gebrauch gemacht, Werte direkt einzugeben, in unserem Fall hier sparen wir etwa 10 Data-Blöcke. Wichtig: DefaultValue des erstes Dialoges muss den Wert des Eingangs annehmen, da wir dies auch als Ergebnisausgabe nutzen werden. Die Calculate-Blöcke dienen dazu, die Eingaben der Prompts auszuwerten und weiterzugeben. Den Join-Block erreichen 3 Strings, da der PromptDialog nur diese erzeugt. Da die Beschriftung des Blocks eindeutig ist, sollte klar sein, an welcher Stelle welche Daten übergeben werden müssen, dies wird dann gesammelt an unsere Activity geschickt, jedoch an einen anderen Action-Block. Man beachte, dass unsere Action keinen Ausgang benötigt. 10
Hinweis: Man sieht in diesem Block einige Warnungen: Das liegt daran, dass eine Action nicht weiß, dass am Eingang ein Verbund von Daten anliegt und demnach nichts damit anfangen kann, wenn wir z.B. input.value1 anfordern. Der tatsächlichen Funktionsweise tut dies jedoch keinen Abbruch. 11
Einige Hinweise: – Da wir als Eingabewerte Strings erhalten haben, muss innerhalb der Calculate-Activity eine Typumwandlung vollzogen werden damit gerechnet werden kann. – Es wird auf die Unterpunkte des vorherigen Join zugegriffen, dies ist tatsächlich trotz der Warnungen, dass unsere angeforderten Werte nicht existieren, möglich. – Wir bedienen uns hier der Tatsache, dass ein Join erst dann etwas ausgibt, wenn alle Werte anliegen, switch steuert somit die Ergebnisauswahl, da nur ein Wert weitergegeben werden sollte. – Da Calculate immer value ausgibt, wird spätestens hier eine Übereinstimmung stattfinden und unser Programm auch bei einer fehlerhaften Eingabe weiterlaufen. (Selbst wenn es Humbug produziert) – Aus den verschiedenen Verbunden muss noch der Wert extrahiert werden, dies geschieht wie bisher über ein Calculate. – Unser Ergebnis wird zurück an unseren vorherigen Block geschickt, es sollte klar sein in welchem Rahmen das Ergebnis nun erscheinen wird. – Es ist theoretisch möglich beliebig lange weiter zu rechnen, beendet werden die rekursiven Aufrufe indem man bei einem der Dialogboxen auf x klickt. Neu: List, List Functions, Compile as Service Dieses Element wird verwendet um neue Listen zu erzeugen, dabei werden alle Standarddatentypen unterstützt. Elemente werden über listenname[i] ausgegeben, wobei der Zählindex i bei 0 anfängt. Es ist möglich, Listen zu vereinigen oder auch einfach nur Elemente anzuhängen, um spezifische Objekte zu verändern kann man sich der remove bzw. insert Option bedienen. *Beispiel10: – Dieses Beispiel berechnet mit Hilfe des Siebs von Erastothenes alle Primzahlen zwischen 2 und 10000 – Es wird zuerst eine Liste mit 10000 Einträgen geschaffen, wobei die hinteren 9998 auf den Wert 'true' gesetzt werden. – Beginnend bei 2 wird zunächst untersucht ob der Listeneintrag auf 'true' steht, wenn ja werden alle Vielfachen auf 'false' gesetzt, ansonsten wird einfach der nächste Eintrag überprüft. – Wenn man bei 100 angekommen ist, sind nur noch Primzahlen markiert – Dieses Verfahren ist in VPL durch die vielen Listenoperationen und Echtzeitkontrolle langsam, deshalb gibt es zum Vergleich noch *Beispiel11, dort wird einfach die Teilbarkeit durch Primzahlen überprüft, welches schneller funktioniert. – Als kompilierter Service beschleunigt sich die Durchführung um ein Vielfaches, das Ergebnis lässt sich im Service Directory betrachten. 12
Der Umgang mit einem Roboter Da Microsoft Robotics Studio vor allem der Kommunikation mit Robotern dienen soll, wird es Zeit, einige der Services vorzustellen die dies ermöglichen. Zunächst einige Hintergrundinformationen: – Wenn der gewählte Roboter kein eigenes MSRS-fähiges Betriebssystem besitzt ( wie zum Beispiel der Lego NXT ), wird ein Computer benötigt der die Steuerung des Roboters übernimmt, indem er mit ihm über eine geeignete Schnittstelle kommuniziert (seriell, Bluetooth, USB) – Bis jetzt ist MSRS auf Windows bzw. Windows CE lauffähig, da es sich um ein offenes Protokoll handelt (DSSP) ist jedoch auch eine Portierung auf andere Plattformen denkbar. – Die meisten Services besitzen bis jetzt nur Implementierungen für Zustandsänderungen, kompliziertere, z.B. kinetische Vorgänge müssen entsprechend selbst geschrieben werden, am besten in Visual C#. Wir beginnen nun mit einem kleinen Programm, welches eine simulierte Umgebung erstellt und uns eine kleine Rundfahrt ermöglicht. Neu: GenericDifferentialDrive, DirectionDialog Ein Service der 2 Motoren steuern kann, optionale Angaben für Reifenabstand/radius möglich für Drehungen um einen vorgegebenen Winkel. Benötigt als Anfangskonfiguration ein Manifest um die Hardwarezugehörigkeit zu ermitteln. Zur Steuerung, erzeugt ein Dashboard mit 5 verschiedenen Ausgaben. Beispiel11: 13
– Der DirectionDialog gibt einen Verband aus, muss daher über Calculate ausgewertet werden. – Über den Datenpfad SetDrivePower kann man den beiden Rädern jeweils einen der Werte weitergeben. – Beliebter Fehler: Beim Wenden in eine bestimmte Richtung muss jeweils das andere Rad eine positive DrivePower erhalten. – Über die Eigenschaften des GenericDifferentialDrive lässt sich ein Manifest laden, hier ist LEGO.NXT.Tribot.Simulation.Manifest.xml empfehlenswert, es wird eine simulierte 3D Umgebung geladen mit einem Tribot, den man über das Dashboard steuern kann. Exkurs: Manifeste Mit diesem Programm, welches zu Microsoft Robotics Studio gehört, lassen sich Manifeste erstellen. Diese sind *.xml-Dateien, die dazu dienen eine vorgegebene Liste von Services zu laden, dabei ist es möglich Konfigurationen vorzugeben. Darüber hinaus kann man schon in dieser Ebene Bauteile miteinander verbinden, wie zum Beispiel die Motoren mit der Lego Steuereinheit. Eine weitere Fähigkeit besteht darin, Deployment Packages zu erstellen, dies sind selbstextrahierende Archive die neben dem Manifest auch die einzelnen Services beinhalten. Zu guter Letzt kann man das Manifest starten, ähnlich wie in der VPL Umgebung wird dadurch der DSS-Host aufgerufen und das Webinterface steht bereit, eine kleine Abkürzung falls man kein eigenständiges Programm sondern nur die Services laden möchte. 14
Lego in der Praxis Hier sehen wir den Lego-Tribot, der während des Praktikums zum Einsatz gekommen ist. Dabei wurden, zusätzlich zur Steuereinheit und zweier Motoren, Lichtsensoren eingesetzt damit der Roboter auf eine Änderung seiner Umgebung korrekt reagieren kann. Auf den nächsten Seiten werden die Services vorgestellt, die es möglich machen den Lego-Tribot anzusprechen, diese sind mittlerweile in der Version 2 vorhanden und bei der Standardinstallation von Microsoft Robotics Studio mitgeliefert. Dazu werden noch einige Beispielprogramme gezeigt, die im Laufe dieses Projektes entstanden sind und die Fähigkeiten von VPL demonstrieren sollen, Kommentare werden nicht fehlen jedoch sollten einige trivialere Bausteine auch vom Leser schon verstanden werden. Den Abschluss bildet ein kleines FAQ, welches von VPL Distanz nimmt und einige generelle Fragen zu Microsoft Robotics Studio beantwortet, soweit es der Rahmen dieses Projekts ermöglicht. 15
The NXT Generation neu: LegoNXTBrickv2, LegoNXTLightSensorv2, LegoNXTDrivev2, LegoNXTSoundSensorv2, LegoNXTUltrasonicSensorv2 Dies ist die zentrale Steuereinheit die in jedem Projekt vorhanden sein muss, welches in irgendeiner Weise den Lego NXT Roboter verwendet. Es lässt sich sowohl die Kommunikationsmethode als auch der verwendete Port festlegen, welches man auch tunlichst machen sollte. Ein Service der vom GenericDifferentialDrive abgelitten wurde und somit die selben Methoden (und bugs) besitzt. Es lassen sich einige zusätzliche Einstellungen wie der Verbindungsport mit der Brick angeben, die Räder müssen einzeln zugeordnet werden. Wir kommen zur Gruppe der Sensoren, die größtenteils ähnlich aufgebaut und angesprochen werden können, da sie allesamt auf dem GenericAnalogSensor basieren. Der Lichtsensor ist leider etwas verbuggt, die LED muss manuell über den Eingang SpotlightUpdate eingeschaltet werden. Ansonsten funktioniert er wie erwartet und gibt einen Double-Wert zurück, der der Helligkeit entspricht und skaliert werden kann. Der Tastsensor besitzt sowohl einen analogen als auch einen digitalen Wiedergabewert, diese sind ziemlich klar. Ab einem Bereich von 70 cm gibt der Ultraschall-Sensor einigermaßen verlässliche cm-Werte aus. Der letzte unserer Sensoren, gibt die Intensität der Umgebungslautstärke an. Beispiel12: Spotlight anschalten Sieht einfach aus, ist es auch. Dem Leser sei überlassen das Ausschalten der LED über den TouchSensor zusätzlich zu implementieren. 16
Beispiel13: Hindernissen ausweichen Noch ein einfaches Programm bei dem unser Roboter lernt diversen Hindernissen auszuweichen indem er sich weg dreht, aber eigentlich ist das Programm relativ selbsterklärend. *Beispiel14: Labyrinth Hardware: Brick, 2 Motoren, 2 Lichtsensoren Wir verwenden hier eine Activity, die 3 Actions enthält: Initialize, Drivecontrol und Compare – Es wird eine Verzögerung eingebaut weil die LEDs eine gewisse Zeit benötigen um aktiviert zu werden, da die Initialize-Funktion einen Richtwert setzt mit dem im weiteren Programmablauf verglichen wird – geschieht dies zu früh ist natürlich die Messung nicht zu gebrauchen. Dies ist das Innere der Compare-Funktion, sie wird in der Drivecontrol verwendet und ist vom Lichtsensor unabhängig. 17
Dies ist nun der Kern des Programms, wie man sieht kann die Compare-Funktion mehrere Male genutzt werden, weil die DriveControl-Funktion keine Variablenmanipulation ausführt. Die Suche funktioniert nun folgendermaßen: Es wird möglichst versucht eine Rechtsdrehung zu vollführen sofern dies möglich ist, also der rechte Lichtsensor einen Weg „sieht“. Wenn der gerade Weg zur Verfügung steht, ist er die 1. Alternative, ansonsten wird auf gut Glück nach links gedreht – wenn nichts gefunden wird macht sich der Roboter auf den Rückweg. Dieser Algorithmus sorgt nun dafür, dass der Roboter zum Einen relativ stabil auf einer Geraden bleibt, zum Anderen wird sichergestellt, dass alle Wege einer Kreuzung zumindest einmal abgefahren werden, das Ziel wird also in einer endlichen Zeit gefunden. Der rekursive Aufruf macht hierbei das Timing wesentlich einfacher, da keine Variablen zur Zwischenspeicherung der Sensorwerte genutzt werden müssen. So eine Variante ist im Beispielordner vorhanden als Beispiel14b, so kann man sich selbst ein Bild machen welche zu bevorzugen ist. 18
*Beispiel15: Linie Hardware: Brick, Motoren, 1 Lichtsensor Diesmal ist die iterative Variante vorzuziehen, doch beide haben ihre diversen Probleme in VPL, bei der Bekämpfung selbiger lernt man aber schnell was dort einfach nicht möglich ist bzw. über Umwege gelöst werden muss. Bemerkenswertes zu Beispiel15/15b: (man möge die Datei in VPL laden und mitlesen) – für die verzögerte Ausführung eines einzelnen Programmteils ist es nicht sinnvoll, Wait zu verwenden, da dabei der gesamte Programmablauf unterbrochen wird. Stattdessen wird, wie in Drivecontrol geschehen, ein Timer gesetzt, der über den Ausgang TimerComplete abgefragt wird. – Problem: TimerComplete kann nur über den Ereignisausgang verwendet werden, also muss der Timer in die Start-Aktion gesetzt werden da nur dort Ereignisse auftreten können. – Um einen reibungslosen Ablauf zu garantieren, sollten Flags in Form von Variablen gesetzt werden, es gibt sonst keine Methode den Roboter einen bestimmten Winkel drehen zu lassen da diverse Sensorcheck Befehle immer wieder von neuem ausgeführt werden würden. – Bei der rekursiven Implementation ist es nicht möglich die Compare-Funktion innerhalb von Drivecontrol auszuführen, da beide auf Variablen zugreifen und somit gegen diverse Sicherheitsrichtlinien von MRS verstoßen – bleibt nur die Funktionen aufzuschlüsseln. – Das Programm funktioniert nun derart, dass über diverse Variablen sowohl der Suchbereich wie auch die Suchrichtung festgehalten werden und zwar folgendermaßen: (vorgeführt an der iterativen Variante ohne b) – search_direction ist ein double-Wert der zwischen 1 und -1 wechseln kann, dies ermöglicht effizient eine Richtungsänderung, da Drive_Power nur damit multipliziert werden muss. – Searching ist unsere Flag, die angibt ob gerade nach dem Weg gesucht wird. Solange dies für eine bestimmte Zeitspanne gemacht wird, kann der Vorgang nur dadurch gestoppt werden dass der Lichtsensor das richtige Signal findet. – Ist allerdings die Zeitspanne verronnen und es ist kein Pfad in Sicht, so wird durch step die Zeitdauer erhöht und die Suchrichtung gedreht, die Suche beginnt von neuem. – Diese Implementation führt dazu, dass bei einer geraden Strecke der Roboter gegensteuert, sodass er auch nach dem Wieder eintritt sehr schnell wieder geradeaus fährt und nicht „twitcht“. 19
Häufig gestellte Fragen: – Welche Betriebssysteme werden unterstützt? Bis jetzt ist MSRS lauffähig auf Windows2k/XP, CE sowie Vista ,wobei dort die Simulationsumgebung noch nicht funktioniert. Es soll aber laut Microsoft nur eine Frage der Zeit sein, bis auch die Unix-Systeme unterstützt werden. – Welche Anforderungen werden an meinen Roboter gestellt? Prinzipiell keine, so lange er programmierbar ist. Bis jetzt werden unter anderem der Lego NXT und die Fischer-Roboter nativ unterstützt, einige findige Hobbyisten sind dabei, viele Weitere zu implementieren – dies entspricht dem Aufwand den Roboter über C# anzusteuern. Da es nicht nötig ist, die Software auf dem Roboter selbst laufen zu lassen und somit genügend Rechenpower zur Verfügung steht, lassen sich auch andere Komponenten wie eine Webcam parallel ansteuern. – Wie schnell reagiert die Steuerung des Roboters? Es gibt verschiedene Faktoren die die Perfomanz negativ beeinflussen: Bluetooth ist sehr angenehm, verzögert aber jeden Signalrichtungswechsel um mehrere zig Millisekunden. Wenn man in VPL programmiert und eine niedrige Latenz benötigt wird, ist es empfehlenswert sein Programm zu kompilieren – sonst muss mit Leistungseinbußen gerechnet werden da VPL eine interpretierte Sprache ist. – Ich habe ein Problem, wo finde ich Hilfe? Deutschsprachige Anlaufstellen sind bis jetzt leider Mangelware, wer hingegen des Englischen mächtig ist findet schnell Hilfe im offiziellen Microsoft Developers Forum oder im MRS Wiki, auch einige der Programmierer von Robotics Studio stehen dort mit Rat und Tat zur Seite. – Wie ist die Einarbeitungszeit in Robotics Studio einzuschätzen? Dies kommt ganz darauf an, was man damit machen will. Solange man mit existenten Teilen arbeitet und nur sein erstes Programm sehen will, ist mit wenigen Stunden gut dabei, Erfahrung im Umgang mit visuellen Sprachen ist aber dabei hilfreich. Will man tiefer in die Materie einsteigen, also über die VPL und den Simulator hinaus neues entwickeln, dem sei ans Herz gelegt, dass es ein längerer Lernprozess sein kann der sich nur lohnt, wenn man sehr viel mit Robotern zu tun hat. 20
Sie können auch lesen