Linienerkennung muss ein Roboter ständig durchführen. Oftmals ist es problematisch, dass die nötigen Lichtmessungen nicht genau genug und nicht schnell genug ablaufen. Der Roboter schießt trotz Interrupt-Programmierung über das Ziel hinaus.
Die fertig gekauften Bauteile zur Helligkeitsmessung funktionieren meist über Fototransistoren oder langsame LDRs, deren Messwerte am Spannungsteiler mit LM393-Comparatoren in „0“ oder „1“, also in Binärwerte gewandelt werden.
Besser wäre hier eine schnelle Helligkeitsmessung, möglichst mit linearem Messwertverlauf. Daher kommen Fotodioden und deren Fotostrom-Aufbereitung mit einem Transimpedanzverstärker in Frage.
Prinzip des Transimpedanzverstärkers
Ein Transimpedanzverstärker wandelt den Fotostrom der Fotodiode in eine proportionale Ausgangsspannung um. Die Wandlung von Strom zu Spannung erfolgt an einem sogenannten Operationsverstärker. Die Verstärkung der Spannung wird mit der Größe eines Widerstands eingestellt: Je größer der Widerstand, desto größer die Ausgangsspannung. Der Kondensator unterdrückt eventuelles Rauschen.
Abb. 1: Schema des Testaufbaus
Erklärung zur Fotodiode:
Eine Fotodiode ist eine kleine Solarzelle; die verwendete BPW34 besitzt eine Fläche von 7,5 Quadratmillimetern und liefert damit nur einen sehr schwachen Strom von maximal 50μA bei lediglich 0,2-0,3 Volt Spannung. Damit kann der Arduino nichts anfangen!
Kurze Erklärung zum Operationsverstärker:
Der Strom der Fotodiode wird durch einen Operationsverstärker in eine Spannung gewandelt. Ein Operationsverstärker ist ein integriertes elektronisches Bauteil, welches grundsätzlich so aufgebaut ist:
Abb. 2: Darstellungen des Operationsverstärkers
- Zwei Anschlüsse zur Spannungsversorgung: : Ein „VDD“ für die positive Spannungsversorgung und einen an Masse (GND, „Ground“).
- Einen Ausgang: An „Out“ erhält man das vom Operationsverstärker aufgearbeitete Spannungs-Signal.
- Zwei Eingänge: Einen nicht invertierenden Eingang „IN+“ und einen invertierenden Eingang „IN-„. Prinzipiell wird der Spannungsunterschied zwischen den beiden Eingängen gemessen und sehr hoch verstärkt: Typischerweise etwa um den Faktor 100.000. Da man solche Verstärkungen nicht benötigt, stellt man mittels Widerständen die gewünschte Verstärkung ein.Hier eine sehr gute Online-Simulation eines Operationsverstärkers:
http://www.falstad.com/circuit/e-amp-invert.html
Es handelt sich hierbei um die Grundschaltung dieses Bauteils.
Erklärung zum weiteren Signalweg:
Die Ausgangsspannung des Operationsverstärkers wird vom Analog-Digital-Wandler des Arduinos gelesen und in einen Wertebereich zwischen 0 (dunkel: kein Fotostrom) und 1024 (hell: voller Fotostrom) gewandelt. In dieser Schaltung wird der Eingang der Analoge Eingang „A0“ des Arduinos verwendet.
Ziel der Schaltung
Wichtig ist, dass der Aufbau einfach und schnell und mit günstigen Bauteilen umgesetzt werden kann.
Die folgende Schaltung wurde in Multisim Blue simuliert, dann am Arduino aufgebaut und getestet:
Abb. 3: Foto des Testaufbaus
Abb. 4: Fritzingzeichnung des Testaufbaus
Als Bauteile werden benötigt:
- TLC271ACP Operationsverstärker (SingleSupply, recht günstig)
- BPW 34 Fotodiode (liefert 50 μAmpere Kurzschlussstrom, günstig)
- Metallwiderstand 100 kΩ
- Keramik-Kondensator 10 pF
Dieser Operationsverstärker kann direkt am Arduino mit 5V betrieben werden; da er aber nicht „Rail-to-Rail-Out“-fähig ist, reicht die Ausgangsspannung nur bis ca. 3.8Volt. Diese 3.8 Volt erzeugen bei der AD-Wandlung immerhin einen Wertebereich von 0 bis ca. 850 bei der 10Bit-Auflösung des Arduino-ADCs. Ein passender Rail-to-Rail-OpAmp wäre z.B. der LMC6482 (wird getestet; die Simulation lief vielversprechend)
Erläuterung zu Rail-to-Rail-Out:
Ein Operationsverstärker benötigt eine Spannungsversorgung, ähnlich wie ein normales elektronisches Gerät. Üblicherweise reicht aber die Ausgangsspannung -also die Spannung, die der OpAmp am Ausgang maximal liefern kann – nicht an die Spannungsversorgung heran. Eine besondere Eigenschaft ist es daher, wenn ein OpAmp das schafft, dann wird diese Konstruktionsform als „Rail2Rail-Out“ bezeichnet. Der TLC271 kommt ca. 1.2 Volt bis an die Versorgungsspannung heran, schafft also bei 5Volt Spannungsversorgung lediglich 3.8 Volt maximal.
Der Simulationsaufbau in Multisim Blue:
Abb. 5: Aufbau in Multisim Blue
Abb. 6: Ersatzschaltung in Multisim Blue
In der Ersatzschaltung wird die Fotodiode als Dreiecksstromquelle angenähert; das angeschlossene Oszilloskop zeigt den simulierten Spannungsverlauf.
Erläuterung der Simulation
Die Schaltung simuliert den Strom der Fotodiode, indem eine ständig wechselnde Stromstärke (hier mit 1kHz, das sind 1000 Hertz, also 1000 Wechsel pro Sekunde) auf die Schaltung wirkt. Die Simulation der Wechselspannung ist nötig, weil Operationsverstärker ihre Eigenschaften mit steigender Frequenz ändern: Meistens wird die Verstärkung schwächer und es kommen Signalverzerrungen hinzu.
Programmierung am Arduino
Der Arduino-Code für den Test ist sehr einfach: Es wird eine Serielle Verbindung zum Arduino aufgebaut, die über USB-Kabel ausgelesen wird. Etwa alle 25 Millisekunden wird dabei der aktuelle Helligkeitswert am Analogport „A0“ ausgelesen und unmittelbar an PC übermittelt:
int test; void setup() { pinMode(A0, INPUT); Serial.begin(9600); } void loop() { test = analogRead(A0); Serial.println(test); delay(25); }
Programmierung in Processing
Um die Daten zu visualisieren, genügt eigentlich der Serielle Plotter der Arduino-Programmierumgebung. Allerdings ist eine portable, Arduino-unabhängige Lösung praktischer, wie z.B. mittels Processing. Hier der Screenshot des Processing-Applets:
Abb. 7: Screenshot des Processing-Applets
Der Vollständigkeit halber noch der Quelltext des Processing-Applets:
import processing.serial.*; Serial arduinoKommunikation; String messwert; int x, xalt; int y, yalt; void setup() { size(640, 600); background(102); String portName = Serial.list()[0]; arduinoKommunikation = new Serial(this, portName, 9600); x = y = xalt = yalt = 0; } void draw() { if ( arduinoKommunikation.available() > 0) { messwert = arduinoKommunikation.readStringUntil('\n'); if (messwert != null) { if (x == 0) { stroke(0); fill(102); rect(0, 0, 640, 600); line(0, 560, 640, 560); line(0, 48, 640, 48); stroke(0); fill(0); textSize(12); text("null-Linie", 570, 575); text("1024-Linie", 570, 44); xalt = x; yalt = y; } messwert = trim(messwert); y = 560 - Integer.parseInt(messwert) / 2; //println(messwert); stroke(102); fill(102); rect(xalt + 5, yalt - 25, 45, 25); stroke(51); for (int i = 1; i < 10; i++) { line(0, 48 + i * 51, 640, 48 + i * 51); } fill(102); stroke(255); rect(x + 5, y - 25, 45, 25); stroke(255); fill(255); line(x, y, xalt, yalt); textSize(18); if (y < 100) { text(messwert, x + 10, y - 5); } else { text(" " + messwert, x + 10, y - 5); } xalt = x; yalt = y; x = x + 1; x = x % 640; } } }