Licht
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.javaund ausführen$ java -Dgnu.io.rxtx.SerialPorts=/dev/rfcomm0 LightTestliefert 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":
Geschwindigkeit "25":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 "50":Ergebnis: Der Beginn der schwarzen Fläche wird um circa 1cm verfehlt. Geschwindigkeit "75":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 "100":Ergebnis: Der Überfahrbereich liegt zwischen 3 und 4 cm. 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.aspxUltraschall
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 MethodegetDistance()
: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 FunktionsetMetric(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
undCONTINOUS_MEASUREMENT
. Aufgrund des verwendeten Bluetooth-Protokolls ist es jedoch ratsam, beim StandardSINGLE_SHOT
zu bleiben, zumal iCommand in der aktuellen Version 0.5 bei jedem Aufruf von getDistance|s den Modus aufSINGLE_SHOT
setzt.
Konfiguration
Der Ultraschallsensor ist als einziger Sensor über die Methodecalibrate(byte scaleFactor, byte scaleDivisor, byte zero)
konfigurierbar. Die drei Parameter hier im Überblick:
Parameter | Standard-Wert | Bedeutung |
scaleFactor | 1 | Rohwert wird mit diesem Wert multipliziert |
scaleDivisor | 14 | Durch diesen Wert wird der Rohwert dividiert |
zero | 0 | Nullpunkt |
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: 72Im ersten Moment sehr auffällig sind die Fehlermeldungen
NXTCommand.LSGetStatus() error: Pending communication transaction in progressdie 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 Versuchsaufbauzusammen 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 Entfernung | Gemessene Entfernung |
5 | 9 |
10 | 15 |
20 | 27 |
30 | 32 |
39 | 43 |
40 | 44 |
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
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
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