Navigation

Programmierung

Im Abschnitt der „Programmierung“ finden Sie Beispiele für die Programmierung der einzelnen Bausteine.

Hierbei werden der NXT-Baustein, die Motoren und Sensoren und deren programmierbare Möglichkeiten unterschieden.

Beim NXT-Baustein selbst, können sowohl das LCD-Display als auch die Buttons per Java angesteuert werden. Dies ermöglicht zum Beispiel eine Fehleranzeige direkt am NXT-Display oder einen Abbruch mittels Button.

Bild des schmatischen, schachbrettartigen Aufbaus eines NXT-Displays mit x- und y-Achse beginnend in der linken oberen Ecke

Schematischer Aufbau des NXT-Displays

Das Display des NXT ist 16 (0-15) Zeichen lang und 8 (0-7) Zeichen hoch. Über diese x- bzw. y-Koordinaten kann ein Text auf dem Display positioniert werden.

Um Texte auf dem LCD anzuzeigen stehen beispielsweise folgende Funktionen zur Verfügung (alle weiteren siehe Lejos API):

 

 

  • void drawString(String str, int x, int y): Zeigt einen String an Position x/y auf dem LCD an
  • void drawInt(int i, int x, int y): Zeigt einen Integerwert an Position x/y auf dem LCD an

Beispiel-Code:

 import lejos.nxt.LCD;
 public class LCDTest {

   public static void main(String[] args) throws Exception {
     LCD.drawString(„Total RAM:“,0,1);
     LCD.drawString((int) System.getRuntime().totalMemory()+“ bytes“, 0, 4);
     Thread.sleep(2000);
   }
}

Dieser Beispiel-Code zeigt am LCD den verfügbaren Speicherplatz an und wartet 2000 Millisekunden bis das Programm beendet wird.

Der NXT hat 4 Buttons (ENTER, ESCAPE, LEFT, RIGHT), die einzeln über Java angesprochen werden können.

Jeder dieser Buttons kann über folgende Funktionen (alle weiteren siehe Lejos API) angesprochen werden:

  • boolean isDown(): Prüft ob der Button gedrückt ist
  • boolean isPressed(): Prüft ob der Button gedrückt wurde (reagiert beim loslassen)
  • boolean isUp(): Prüft ob der Button oben (nicht gedrückt) ist.
  • void waitForPress(): Wartet bis der Button gedrückt wurde (reagiert beim loslassen).

 

Beispiel-Code 1:

 
 import lejos.nxt.LCD;
 import lejos.nxt.Button;
 public class ButtonTest {
   public static void main(String[] args) throws Exception {
     LCD.clear();
     Button.ENTER.waitForPressAndRelease();
     LCD.drawString(„Finished“, 1, 2);
     Thread.sleep(2000);
   }
 }

In diesem Beispiel wartet das Programm, bis die Enter-Taste gedrückt wurde und zeigt dann „Finish“ am LCD an.

Beispiel-Code 2:

 
 import lejos.nxt.LCD;
 import lejos.nxt.Button;
 public class ButtonPressed {

   public static void main(String[] args) throws Exception {
     while(true) {
       LCD.clear();
       if(Button.ENTER.isDown()) LCD.drawString(„ENTER“, 0, 1);
       if(Button.ESCAPE.isDown()) LCD.drawString(„ESCAPE“, 0, 1);
       if(Button.LEFT.isDown()) LCD.drawString(„LEFT“, 0, 1);
       if(Button.RIGHT.isDown()) LCD.drawString(„RIGHT“, 0, 1);
     }
   }
 }

In diesem Beispiel wird in einer Dauerschleife geprüft ob einer der Buttons gedrückt ist und falls einer gedrückt ist, die Beschreibung des jeweiligen Button auf dem LCD angezeigt.

Mit der Klasse Motor können die verschiedenen Motoren angesprochen werden. Der NXT-Baustein bietet die Möglichkeit 3 Motoren gleichzeitig anzuschließen (A, B und C). Diese können mittels Motor.A, Motor.B bzw. Motor.C angesprochen werden. Jeder dieser Motoren bietet eine Reihe an Funktionen (alle weiteren siehe Lejos API).

Die Motoren können auf verschiedene Weise fortbewegt werden. Forward() bzw. Backward() bewegt die Motoren so lange, bis diese gestoppt werden. Die verschiedenen Rotate()-Funktionen bieten einen genauere Ansteuerungsmöglichkeit durch Angabe eines Winkels.

Die Motoren können mittels forward() bzw. backward() einfach vor- bzw. rückwärts bewegt werden, bis die Motoren mit stop() gestoppt werden.

  • void forward(): Bewegt den Motor vorwärts bis stop() aufgerufen wird.
  • void backward(): Bewegt den Motor rückwärts bis stop() aufgerufen wird.
  • void stop(): Stoppt den Motor

Beispiel-Code:

 
 import lejos.nxt.Motor;
 public class Bewegung_einfach {
   public static void main(String[] args) throws Exception {
     Motor.A.forward();
     Motor.B.forward();
     Thread.sleep(5000);
     Motor.A.stop();
     Motor.B.stop();
   }
 }

In diesem Beispiel werden die Motoren A und B vorwärts bewegt und nach 5000 Millisekunden wieder gestoppt.

Mit den Rotate-Funktionen können die Motoren, durch Angabe eines Winkels, genauer bewegt werden.

  • void rotate (int angle): Dreht den Motor um den Winkel „angle“ weiter.
  • void rotateTo (int limitAngle): Dreht den Motor bis Winkel „limitAngle“.

 

Beispiel-Code:

 
 import lejos.nxt.Motor;
 public class Bewegung_Rotate {
   public static void main(String[] args) throws Exception {
     Motor.A.rotate(360);
     Motor.B.rotate(-360);
   }
 }

In diesem Beispiel werden die Motoren A und B um 360° nach vorne bzw. hinten bewegt. Dies dreht einen Roboter mit 2 Rädern (z.B. Wall Bumper) um 180°.

Die Geschwindigkeit eines Motors kann geregelt und abgefragt werden.

  • void setSpeed (int speed): Setzt die Geschwindigkeit des Motors auf „speed“.
  • int getSpeed(): Liefert die aktuelle Geschwindigkeit des Motors.

 

Beispiel-Code:

 
 import lejos.nxt.Motor;
 public class Geschwindigkeit {

   public static void main(String[] args) throws Exception {
     Motor.A.setSpeed(200);
     Motor.B.setSpeed(200);
     LCD.drawString(Motor.A.getSpeed()+““,0,1);
     LCD.drawString(Motor.B.getSpeed()+““,0,4);
     Thread.sleep(5000);
   }
 }

In diesem Beispiel wird die Geschwindigkeit der Motoren A und B auf 200 gesetzt und danach auf dem LCD angezeigt.

Im Basispaket enthält der NXT 4 verschiedene Sensoren (Geräusch-, Tast-, Ultraschall– und Lichtsensor), welche über getrennte Klassen angesprochen werden können. Für diese Sensoren stehen 4 Anschlussmöglichkeiten (1, 2, 3, 4) am NXT selbst zur Verfügung. Diese Zuordnung von Port und Sensor muss vom Programmierer vorgenommen werden.

Beispiel-Code:

 
 import lejos.nxt.SensorPort;
 import lejos.nxt.TouchSensor;
 public class TouchTest {
   public static void main(String[] args) throws Exception {
     TouchSensor touch = new TouchSensor(SensorPort.S1);
   }
 }

Der Geräuschsensor nimmt Geräusche auf und diese Lautstärke kann mittels int readValue() abgerufen werden.

Beispiel-Code:

 
 import lejos.nxt.*;
 public class SoundTest {
   public static void main(String[] args) throws Exception {
   SoundSensor sound = new SoundSensor(SensorPort.S4);
   while(!Button.ESCAPE.isDown()) {
     LCD.clear();
       for(int i=0; i<6; i++) {
         LCD.drawString(„“+sound.readValue(), 0, i);
         Thread.sleep(20);
       }
     }
   }
 }

In diesem Beispiel wird der Geräuschsensor am Port 4 betrieben. Dieser liefert, so lange bis der Escape-Button gedrückt wird, den jeweiligen Lautstärkewert und zeigt diesen am LCD an.

Der Tastsensor reagiert, so bald dieser gedrückt wurde. Hierfür steht die Funktion isPressed() zur Verfügung.

Beispiel-Code:

 
 import lejos.nxt.*;
 public class TouchTest {
   public static void main(String[] args) throws Exception {
     TouchSensor touch = new TouchSensor(SensorPort.S1);
     hile(!touch.isPressed()) {
       Motor.A.backward();
       Motor.B.backward();
     }
   }
 }

In diesem Beispiel wird der Tastsensor an Port 1 betrieben. Der Roboter (mit 2 Rädern, somit auch 2 Motoren) fährt so lange rückwärts, bis der TouchSensor gedrückt wurde.

Der Ultraschallsensor liefert, mit der Funktion int getDistance(), Abstände zu Objekten.

Beispiel-Code:

 
 import lejos.nxt.*;
 public class UltraschallTest {
   public static void main(String[] args) throws Exception {
     UltrasonicSensor sonic = new UltrasonicSensor(SensorPort.S3);
     while(!Button.ESCAPE.isDown()) {
       LCD.clear();
       LCD.drawInt(sonic.getDistance(), 0, 1);
       Thread.sleep(2000);
     }
   }
 }

In diesem Beispiel wird der Ultraschallsensor an Port 3 betrieben. Dieser liefert, so lange bis der Escape-Button gedrückt wird, die Distanz zu einem Objekt.

Der Lichtsensor liefert mit der Methode getNormalizedLightValue() den aktuellen Lichtwert.

Beispiel-Code:

 import lejos.nxt.*;
 public class LightTest {
   public static void main(String[] args) throws Exception {
     LightSensor light = new LightSensor(SensorPort.S2);
     while(!Button.ESCAPE.isDown()) {
       LCD.clear();
       LCD.drawInt(light.getNormalizedLightValue(), 0, 1);
     }
   }
 }

In diesem Beispiel wird der Lichtsensor am Port 2 betrieben. Dieser liefert, so lange bis der Escape-Button gedruckt wird, den jeweiligen Lichtwert und zeigt diesen am LCD an.

Weitere Informationen zur Java- bzw. LeJOS-Programmierung:

In der Java-Programmierung gibt es grundsätzlich drei Arten zu kommentieren:

    1. // Kommentar
     
    2. /*
       * Kommentar
       */
    
    3. /**
       * Kommentar
       */

Man benuzt 1. und 2. für Kommentare innerhalb einer Methode. Der Vorteil von 2. liegt lediglich in der Möglichkeit, über mehrere Zeilen hinweg zu kommentieren.
Die 3. Kommentarfunktion, welche immer mit /** beginnt und mit */ endet, werden die Methoden und Klassen dokumentiert.

    /**
    * Dies ist eine Methode, in
    * der zwei Kommentare stehen
    */
    public void eineMethode(){
        // Kommentar
        /* ja hier kommt
        *   noch ein Kommentar
        */
    }

Bei den blauen Kommentaren, handelt es sich um sogenannte Javadoc-Kommentare. Javadoc ist ein Software-Dokumentationswerkzeug, das aus Java-Quelltexten automatisch HTML-Dokumentationsdateien erstellt. Diese Dokumentationsdateien können verwendet werden, um sich schnell und effizient durch viele Seiten Code zu lesen und um gezielt nach Informationen zu suchen. Ein bekannter Effekt, welcher auf dem Javadoc basiert sind beispielsweise die kleinen Tooltips, die gelegentlich in Eclipse erscheinen, wenn man mit der Maus über eine Methode oder ein Attribut einer anderen Klasse fährt.
Bildschirmfoto eines JavaDoc-Kommentares, der die Funktion Einparkassistent.startParking dokumentiert

JavaDoc-Kommentar, der die Funktion Einparkassistent.startParking dokumentiert

Die entsprechende dem Quellcode entnommene Dokumentation dieser Methode aus dem Bild sieht dann ungefähr wie folgt aus:

     /**
     * This method executes the parking algorithm. Specified by the parameters
     * {@code left_parking} and {@code right_parking} the algorithm will try to
     * find a free slot on the given side or sides.
     *
     * The routine follows two steps:
     *
     * 1. Moving forward until one space was found 2. Place the car inside of
     * the selected space
     *
     * @param left_parking
     *            {@code true/false}
     * @param right_parking
     *            {@code true/false}
     */
    protected static void startParking(boolean left_parking,
            boolean right_parking) {

Hier werden ganz spezielle Schlüsselwörter verwendet, welche in der späteren Ausgabe einen ganz speziellen Effekt erzielen. Vergleiche hierfür den Quellcode mit der Ausgabe des Tooltips und versuche zu verstehen was die mit @ eingeführten Schlüsselwörter für einen Effekt haben. Diese Schlüsselwörter werden in der Fachsprache auf als Javadoc-Tags bezeichnet und helfen Javadoc bei der Erstellung einer schöneren Dokumentationsdatei.

  • http://de.wikipedia.org/wiki/Javadoc
  • http://www.oracle.com/technetwork/java/javase/documentation/javadoc-137458.html

Grundlagen: Erstellen eines parallelen Programmablaufs

 public static void main(String[] args) {
   EineKlasse k = new EineKlasse();
   k.eineMethode();
   k.eineAndere();
   System.out.println("hallo!");
 }
Bild einer schematischen Darstellung eines Lejos-code-Graphen

Schematische Darstellung eines Lejos-code-Graphen

Oben dargestellt ist ein sogenanntes sequenzielles Programm. Das bedeutet, dass in den meisten Fällen die Reihenfolge der einzelnen Aktionen der Anordnung innerhalb der main-Methode entspricht und zu keinem Zeitpunkt mehrere Aktionen gleichzeitig ausgeführt werden.

Bild einer schematischen Darstellung eines Lejos-code-Graphen

Schematische Darstellung eines Lejos-code-Graphen

Die Idee eines parallelen Programms besteht darin, Programmabschnitte zum selben Zeitpunkt parallel ausführen zu lassen. Wie die obere Graphik zeigt, handelt es sich hierbei um den Ablauf eines Programms, in welchem zum selben Zeitpunkt die eineMethode und eineAndere gleichzeitig ausgeführt werden.

In der Programmiersprache Java setzt man parallele Programme mit sogenannten Thread-Objekten (zu Deutsch: Faden) um. Man spricht auch von Aktivitätsfäden, in welche sich das Programm aufteilen kann.

Zum Erstellen des Aktivitätsfadens oder Thread-Objekts benötigt man eine Aufgabe, also eine Vorgabe der Arbeit die der Faden verrichten soll wenn er gestartet wird. Dazu implementiert man das Interface Runnable, welches lediglich eine Methode besitzt und zwar die run-Methode, in welcher man die Aufgabe des Fadens definieren muss.

 public class Aufgabe implements Runnable {
   public void run() {
     // Hier steht was
     //der Faden tun soll.
     eineMethode();
   }
 }

Nun haben wir uns ein Objekt Aufgabe erstellt und können dieses dem Faden auch übergeben. Das machen wir bei der Initialisierung des Thread-Objekts über den Konstruktor.

 public static void main(String[] args) {
   Aufgabe aufgabe = new Aufgabe();
   //Wir erstellen jetzt einen Thread
   Thread faden = new Thread(aufgabe);
   //…
 }

Wir wollen, dass der Faden nun auch die Aufgabe im Hintergrund für uns erfüllt. Daher muss dieser noch gestartet werden. Das geschieht mit der start-Methode des Fadens. Um sicher zu sein, dass alle Fäden die erstellt wurden, mit der Arbeit fertig sind, so an einer Stelle auf diese gewartet werden bzw. kontrolliert werden, ob sie bereits fertig sind. Das macht man mit der join-Methode der Fäden. Das fertige parallele Programm könnte dann wie folgt aussehen:

 public static void main(String[] args) {
   Aufgabe aufgabe = new Aufgabe();
   Aufgabe andereAufgabe = new AndereAufgabe();
   Thread faden1 = new Thread(aufgabe);
   Thread faden2 = new Thread(aufgabe2);
   faden1.start();
   faden2.start();
   //Beide Faeden wurden gestartet und arbeiten
   //…
   faden1.join();
   faden2.join();
   //Beide Faeden haben ihre Aufgabe erledigt
 }

Grundlagen: Erstellen eines parallelen Programmablaufs

Mithilfe dieser Onlineplattform (http://appinventor.mit.edu/explore/) wird die Erstellung von Androidapplikationen spielend leicht. Durch die klar strukturierte Benutzeroberfläche eine schnelle Einarbeitung möglich. Die Unterstützung von vordefinierten LEGO-MINDSTORMS-Objekten bietet jedem Nutzer die Möglichkeit, sein eigenes Smartphone zu einer Kontrolleinheit des Roboters umzugestalten.

Zur Erstellung eines Projekts benötigt man lediglich ein Google-Account.

Bildschirmfoto der Benutzeroberfläche der Software AppInventor im Modus Designerebene

Bildschirmfoto der Hauptebene (Designerebene) der Software AppInventor zur Progammierung der NXT-Bausteine

Zu sehen ist ein Screenshot der Applikation. Die Benutzeroberfläche ist eingeteilt in zwei Editorebenen – hier zu sehen ist die „Designer“ Ebene. Mit einem Klick auf den Knopf „Blocks“, am oberen rechten Bildschirmrand, wechselt die Ansicht in die „Blocks“ Ebene, in welcher die Logik bzw. die Funktionsweisen der einzelnen Bestandteile der Applikation definiert werden können. Dies wird mit graphischen Programmierelementen durchgeführt und erleichtert dem Entwickler die Arbeit enorm.

Bildschirmfoto der Benutzeroberfläche der Software AppInventor mit der Blocks-Ebene

Bildschirmfoto der Blocks-Ebene der Software AppInventor zur Progammierung der NXT-Bausteine

Der in der Palette befindliche LEGO® MINDSTORMS® Abschnitt bietet eine Auswahl aus den einzelnen Nxt-Bestandteilen. Per Drag&Drop werden die Objekte in die Applikation mit einbezogen und sind in der Blocks-Ebene definierbar. Damit das Smartphone allerdings mit dem Roboter kommunizieren kann, benötigt es eine Verbindung über Bluetooth. Diese wird mit einem BluetoothClient-Objekt und der Kopplung sämtlicher benutzer Nxt-Bestandteile sowie der Implementierung des Verbindungsprozesses in der Blocks-Ebene erstellt.