Inhalt | 3. Steuerung der Motoren | 5. Mit Java Fortran-Code ausführen

Sensoren

  1. Licht
  2. Ultraschall
  3. Geräusch
  4. Berührung

Licht

Lichtsensor Mit Hilfe des Lichtsensors lassen sich Grautonwerte auslesen und die Helligkeit der Umgebung. Allerdings scheint der Sensor nicht sonderlich hochauflösend, so kann er feine Unterschiede nicht zuverlässig auslesen, da die Werte zu schwanken scheinen. So liefert ein testweise Auslesen der Farbenskala auf der Testunterlage nicht immer das gleiche Ergebnis. Zwar sind die Werte in einem bestimmten Bereich angesiedelt, allerdings muss immer mit einem gewissen Akzeptanzbereich gerechnet werden. Demnach sind die Werte der folgenden Tabelle als ungefähre Richtwerte anzusehen.

Beim Auslesen der Farben des Untergrunds kann eine Abhängigkeit von der Umgebungshelligkeit ausgeschlossen werden, da der Lichtsensor eine eigene Lichtquelle besitzt.

64 65 65 59 49 43 54 49 46
39 43 48 50 58 65

Auslesen der Lichtdaten mit iCommand

Zunächst benötigen wir zum Auslesen der Daten ein Objekt der Klasse Light. Dazu übergeben wir beim Aufruf des Konstruktors den Sensorport, an dem der Lichtsensor angeschlossen ist, Standard ist Port 3:
Light licht = new Light(Sensor.S3);
                Zum Auslesen der Farbwerte werden zwei Funktionen bereitgestellt:
                
int wert1 = licht.getLightValue();
int wert2 = licht.getLightPercent();

Genauigkeit des Lichtsensors

Wenn der Roboter in Bewegung ist und dabei der Lichtsensor ausgelesen werden soll, um auf Farbunterschiede zu reagieren, muss beachtet werden, dass der Aufruf der Funktionen eine gewisse Zeit beansprucht. Hierzu ein kleines Beispielprogramm LightTest.java:
import icommand.nxtcomm.*;
import icommand.robotics.*;

public class LightTest {

        public static void usage() {
                System.out.println("Usage:");
                System.out.println("  java LightTest <speed>");
                System.out.println("");
                System.out.println("     speed:     Integeger between 1 and 255");
        }

        public static void main(String[] args) {
                if(args.length < 1) {
                        usage();
                        return;
                }

                int speed = Integer.parseInt(args[0]);

                SyncMotors sm = new SyncMotors(Motor.C, Motor.B);
                sm.setSpeed(speed);
                sm.forward();

                Light l = new Light(Sensor.S3);

                int color = l.getLightPercent();
                long start = System.currentTimeMillis();
                for(int i=0; i<99; i++) {
                        System.currentTimeMillis();
                }

                System.out.println("Für den Aufruf von 100x System.currentTimeMillis() benötigte Zeit: " (System.currentTimeMillis() - start));

                System.out.println("Ab jetzt: Messung der Aufrufdauer von Licht.getLightPercent():");
                start = System.currentTimeMillis();
                while(color > 35) {
                        color = l.getLightPercent();
                        System.out.println("Benötigte Zeit: " (System.currentTimeMillis()-start"ms, zurückgegebener Wert: " + color);
                        start = System.currentTimeMillis();
                }

                sm.stop();

                NXTCommand.close();
        }
}
Compilieren
$ javac LightTest.java
und ausführen
$ java -Dgnu.io.rxtx.SerialPorts=/dev/rfcomm0 LightTest
liefert folgende Ausgabe:
[...]
Für den Aufruf von 100x System.currentTimeMillis() benötigte Zeit: 1
Ab jetzt: Messung der Aufrufdauer von Licht.getLightPercent():
Benötigte Zeit: 56ms, zurückgegebener Wert: 54
Benötigte Zeit: 57ms, zurückgegebener Wert: 54
Benötigte Zeit: 59ms, zurückgegebener Wert: 53
Benötigte Zeit: 59ms, zurückgegebener Wert: 54
Benötigte Zeit: 59ms, zurückgegebener Wert: 54
Benötigte Zeit: 48ms, zurückgegebener Wert: 54
Benötigte Zeit: 43ms, zurückgegebener Wert: 53
Benötigte Zeit: 44ms, zurückgegebener Wert: 53
Benötigte Zeit: 44ms, zurückgegebener Wert: 53
Benötigte Zeit: 43ms, zurückgegebener Wert: 53
Benötigte Zeit: 44ms, zurückgegebener Wert: 53
Benötigte Zeit: 44ms, zurückgegebener Wert: 53
Benötigte Zeit: 44ms, zurückgegebener Wert: 53
Benötigte Zeit: 58ms, zurückgegebener Wert: 53
Benötigte Zeit: 59ms, zurückgegebener Wert: 53
Benötigte Zeit: 59ms, zurückgegebener Wert: 53
[...]
Man sieht hier ziemlich deutlich, wieviel Zeit ein einziger Aufruf in Anspruch nimmt: Der erste Test liefert 56ms, ansonsten liegt der Wert in der Regel zwischen 40ms und 60ms. Dies war Anlass genug für einen kleinen Versuch, wie weit der NXT bei unterschiedlichen Geschwindigkeiten "übers Ziel hinausschießt". Der Mindstorm soll solange geradeaus fahren, bis er auf eine schwarze Fläche trifft, die mit dünnen weißen Linien im Abstand von 1cm versehen ist (siehe obiges Programm).

Geschwindigkeit "15":

Versuch 1 Versuch 2 Versuch 3
Ergebnis: Der Mindstorm fährt kaum über die schwarze Begrenzung hinaus und berührt noch nicht einmal die erste weiße Linie, stoppt also weniger als 1cm nachdem die schwarze Fläche begonnen hat.
Geschwindigkeit "25":
Versuch 1 Versuch 2 Versuch 3
Ergebnis: Der Beginn der schwarzen Fläche wird um circa 1cm verfehlt.
Geschwindigkeit "50":
Versuch 1 Versuch 2 Versuch 3
Ergebnis: Abgesehen vom ersten Test scheint der Mindstorm nach spätestens 2cm zu stehen. Das erste Bild jedoch macht klar, dass man beim Lichtsensor keineswegs davon ausgehen sollte, dass Farbübergänge exakt getroffen werden.
Geschwindigkeit "75":
Versuch 1 Versuch 2 Versuch 3
Ergebnis: Der Überfahrbereich liegt zwischen 3 und 4 cm.
Geschwindigkeit "100":
Versuch 1 Versuch 2 Versuch 3
Ergebnis: Hier vergrößert sich der Bereich schon auf 5-12 cm.

Messpunkt/-fläche

Auf den oberen Bildern gut erkennbar sind die beiden Lichtkreise, wobei der Innere intensiver leuchtet als der Äßere. Um eine Farbe gut zu erkennen muss der innere Kreis komplett diese Farbe erfassen. Unter gewissen Umständen genügt es, wenn die Hälfte dieser Kreisfläche diese Farbe umschließt, aber leider nicht immer.


1: http://lejos.sourceforge.net/p_technologies/nxt/icommand/api/icommand/platform/nxt/Light.html <Lokale Kopie>
2: http://mindstorms.lego.com/Overview/Light_Sensor.aspx

Ultraschall

Ultraschallsensor

Auslesen der Daten

Mit dem Ultraschallsensor lassen sich Entfernungen messen. Wie beim Lichtsensor auch muss man erst ein Objekt der Klasse Ultrasonic erzeugen:
Ultrasonic us = new Ultrasonic(Sensor.S4);
Port 4 wird standardmäßig für den Ultraschallsensor verwendet. Zum Auslesen verwendet man in der Regel die Methode getDistance():
int d = us.getDistances();
Diese liefert die Entfernung zum nächsten Objekt in Zentimetern zurück. Ein Umschalten auf Inch ist derzeit nicht möglich, da die entsprechende Funktion setMetric(boolean) in Version 0.5 nicht implementiert ist. Die API bietet noch eine andere Methode zum Auslesen von Entfernungen an:
byte [] pings = us.getDistances();
Diese Funktion fürt jedoch lediglich 8 "Pings" durch und gibt diese als Byte-Array zurück. Ein "Ping" (oder auch "Single Shot") bezeichnet das Aussenden eines Ultraschall-Signals und Empfangen des Zurückgeworfenen Signals. Es gibt noch andere Modi, in denen man den Ultraschallsensor betreiben kann, wie z.B. SINGLE_SHOT und CONTINOUS_MEASUREMENT. Aufgrund des verwendeten Bluetooth-Protokolls ist es jedoch ratsam, beim Standard SINGLE_SHOT zu bleiben, zumal iCommand in der aktuellen Version 0.5 bei jedem Aufruf von getDistance|s den Modus auf SINGLE_SHOT setzt.

Konfiguration

Der Ultraschallsensor ist als einziger Sensor über die Methode calibrate(byte scaleFactor, byte scaleDivisor, byte zero) konfigurierbar. Die drei Parameter hier im Überblick:
ParameterStandard-WertBedeutung
scaleFactor1Rohwert wird mit diesem Wert multipliziert
scaleDivisor14Durch diesen Wert wird der Rohwert dividiert
zero0Nullpunkt

Geschwindigkeit

Auch zum Ultraschallsensor ein kleines Programm zum Testen der Aufrufzeiten (UltrasonicTest.java):
import icommand.platform.nxt.*;
import icommand.nxtcomm.*;
import icommand.robotics.*;

public class UltrasonicTest {

  public static void usage() {
    System.out.println("Usage:");
    System.out.println("  java UltrasonicTest <speed> <distance>");
    System.out.println("");
    System.out.println("     speed:     Integeger between 1 and 255");
    System.out.println("     distance:  Integeger between 10 and 60");
  }

  public static void main(String[] args) {
    if(args.length < 2) {
      usage();
      return;
    }

    int speed = Integer.parseInt(args[0]);
    int distance = Integer.parseInt(args[1]);

        if(distance < 10)
            distance = 10;
        if(distance > 60)
            distance = 60;

    SyncMotors sm = new SyncMotors(Motor.C, Motor.B);
    sm.setSpeed(speed);
    sm.forward();

    Ultrasonic us = new Ultrasonic(Sensor.S4);

    int d = us.getDistance();
    long start = System.currentTimeMillis();
    for(int i=0; i<99; i++) {
      System.currentTimeMillis();
    }

    System.out.println("Für den Aufruf von 100x System.currentTimeMillis() benötigte Zeit: " (System.currentTimeMillis() - start));

    System.out.println("Ab jetzt: Messung der Aufrufdauer von us.getDistance():");
    start = System.currentTimeMillis();
    while(d > distance) {
      d = us.getDistance();
      System.out.println("Benötigte Zeit: " (System.currentTimeMillis()-start"ms, zurückgegebener Wert: " + d);
      start = System.currentTimeMillis();
    }

    sm.stop();

          NXTCommand.close();
  }
}

Und hier exemplarisch ein Ergebnis eines Aufrufs:
Für den Aufruf von 100x System.currentTimeMillis() benötigte Zeit: 0
Ab jetzt: Messung der Aufrufdauer von us.getDistance():
Benötigte Zeit: 133ms, zurückgegebener Wert: 117
NXTCommand.LSGetStatus() error: Pending communication transaction in progress
Benötigte Zeit: 174ms, zurückgegebener Wert: 114
Benötigte Zeit: 122ms, zurückgegebener Wert: 112
Benötigte Zeit: 123ms, zurückgegebener Wert: 110
Benötigte Zeit: 141ms, zurückgegebener Wert: 255
Benötigte Zeit: 130ms, zurückgegebener Wert: 255
Benötigte Zeit: 130ms, zurückgegebener Wert: 102
Benötigte Zeit: 123ms, zurückgegebener Wert: 99
Benötigte Zeit: 133ms, zurückgegebener Wert: 97
NXTCommand.LSGetStatus() error: Pending communication transaction in progress
Benötigte Zeit: 179ms, zurückgegebener Wert: 92
Benötigte Zeit: 130ms, zurückgegebener Wert: 92
Benötigte Zeit: 115ms, zurückgegebener Wert: 89
Benötigte Zeit: 123ms, zurückgegebener Wert: 85
Benötigte Zeit: 121ms, zurückgegebener Wert: 82
Benötigte Zeit: 114ms, zurückgegebener Wert: 82
Benötigte Zeit: 129ms, zurückgegebener Wert: 80
NXTCommand.LSGetStatus() error: Pending communication transaction in progress
Benötigte Zeit: 186ms, zurückgegebener Wert: 77
Benötigte Zeit: 121ms, zurückgegebener Wert: 72
Im ersten Moment sehr auffällig sind die Fehlermeldungen
NXTCommand.LSGetStatus() error: Pending communication transaction in progress
die immer wieder auftauchen. Auch interessant sind die, im Vergleich zum Lichtsensor relativ langen, Antwortzeiten. Nur mit genauem Hinsehen erkennt man noch etwas Merkwürdiges: Immer wieder sind die Entfernungen 255 eingestreut.

Genauigkeit

Zum Testen der Genauigkeit des Sensors folgender Versuchsaufbau
Skizze des Versuchaufbaus
zusammen mit dem Testprogramm DistanceTest.java
import icommand.platform.nxt.*;
import icommand.nxtcomm.*;
import icommand.robotics.*;

public class DistanceTest {

  public static void main(String[] args) {
    Ultrasonic us = new Ultrasonic(Sensor.S4);
      int d = us.getDistance();
    System.out.println("Entfernung: " + d + "cm");

        NXTCommand.close();
  }
}

liefert diese Tabelle
Tatsächliche EntfernungGemessene Entfernung
59
1015
2027
3032
3943
4044
Die Werte schwanken stark und es scheint keinen Zusammenhang zwischen Entfernung und Fehler zu geben. Daher löst auch ein internes Fehlerkorrigieren oder ein Setzen des Nullpunkts das Problem nicht. Zudem trifft bei diesen Versuchen auch das Ultraschallsignal annähernd senkrecht auf das Hindernis auf, bei gekrümmten Hindernissen dürfte es hier zu noch größeren Messfehlern kommen.


1: http://lejos.sourceforge.net/p_technologies/nxt/icommand/api/icommand/platform/nxt/Ultrasonic.html <Lokale Kopie>
2: http://mindstorms.lego.com/Overview/Ultrasonic_Sensor.aspx

Geräusch

Geräuschsensor Der Geräuschsensor misst die Lautstärke seiner direkten Umgebung. Zum Auslesen muss man auch hier erst ein Objekt der Klasse Sound erzeugen, Standard ist Port 2:

Sound s = new Sound(Sensor.S2);
Ausgelesen werden können die Daten in zwei Formen. Zum einen die reinen Dezibel-Werte
int wert1 = s.getdB();
und zum anderen als A-bewerteten Schalldruck:
int wert2 = s.getdBA();

Auch das Auslesen des Geräuschsensors benötigt zwischen 40 und 60 ms (Siehe auch SoundTest.java).


1: http://lejos.sourceforge.net/p_technologies/nxt/icommand/api/icommand/platform/nxt/Sound.html <Lokale Kopie>
2: http://mindstorms.lego.com/Overview/Sound_Sensor.aspx

Berührung

Berührungssensor Der Berührungssensor ist der einfachste der vier mitgelieferten Sensoren. Er wird aktiviert durch leichten Druck auf den orangen Teil. Wird dieser Teil eingedrückt, liefert der Sensor 1, ansonsten 0.

Entsprechend spartanisch ausgestattet ist die zugehörige iCommand Klasse:

Constructor Summary
Touch(Sensor sensor)
           
 
Method Summary
 boolean isPressed()
           

Wie auch bei den übrigen Sensoren muss man erst ein Objekt der Klasse Touch erzeugen, Standard ist Sensorport 1:

Touch t = new Touch(Sensor.S1);
Das Abfragen des Zustands ist sehr simpel:
boolean touched = t.isPressed();

Auch hier benötigt der Aufruf zwischen 40 und 60 ms. Hierzu wieder ein Beispielprogramm TouchTest.java:

import icommand.nxtcomm.*;
import icommand.robotics.*;

public class TouchTest {

        public static void usage() {
                System.out.println("Usage:");
                System.out.println("  java TouchTest <duration>");
                System.out.println("");
                System.out.println("     duration:     Integeger between 1 and 30");
        }

        public static void main(String[] args) {
                if(args.length < 1) {
                        usage();
                        return;
                }

                int duration = Integer.parseInt(args[0]);
                if(duration > 30)
                        duration = 30;
                duration = duration * 1000;

                Touch t = new Touch(Sensor.S1);

                boolean touched = t.isPressed();
                long start = System.currentTimeMillis();
                for(int i=0; i<99; i++) {
                        System.currentTimeMillis();
                }

                System.out.println("Für den Aufruf von 100x System.currentTimeMillis() benötigte Zeit: " (System.currentTimeMillis() - start));

                System.out.println("Ab jetzt: Messung der Aufrufdauer von Touch.isPressed():");
                start = System.currentTimeMillis();
                long last_time = System.currentTimeMillis();
                while(System.currentTimeMillis() - start < duration) {
                        touched = t.isPressed();
                        System.out.println("Benötigte Zeit: " (System.currentTimeMillis()-last_time"ms, zurückgegebener Wert: " + touched);
                        last_time = System.currentTimeMillis();
                }

                NXTCommand.close();
        }
}
Ausgabe:
[...]
Für den Aufruf von 100x System.currentTimeMillis() benötigte Zeit: 0
Ab jetzt: Messung der Aufrufdauer von Touch.isPressed():
Benötigte Zeit: 44ms, zurückgegebener Wert: false
Benötigte Zeit: 59ms, zurückgegebener Wert: false
Benötigte Zeit: 59ms, zurückgegebener Wert: false
Benötigte Zeit: 59ms, zurückgegebener Wert: true
Benötigte Zeit: 50ms, zurückgegebener Wert: true
Benötigte Zeit: 58ms, zurückgegebener Wert: true
Benötigte Zeit: 37ms, zurückgegebener Wert: true
Benötigte Zeit: 43ms, zurückgegebener Wert: true
Benötigte Zeit: 43ms, zurückgegebener Wert: true
Benötigte Zeit: 44ms, zurückgegebener Wert: true
Benötigte Zeit: 44ms, zurückgegebener Wert: true
[...]


1: http://lejos.sourceforge.net/p_technologies/nxt/icommand/api/icommand/platform/nxt/Touch.html <Lokale Kopie>
2: http://mindstorms.lego.com/Overview/Touch_Sensor.aspx

Nach oben | Inhalt | 3. Steuerung der Motoren | 5. Mit Java Fortran-Code ausführen