Handy -> Bluetooth -> RGB-LED [Teil 1: Protokoll]

Die Vernetzung von Haushaltsgegenständen ist heutzutage nicht mehr weg zu denken. Das so genannte Internet der Dinge wird in den nächsten Jahren immer mehr Geräte miteinander vernetzen und untereinander kommunizieren lassen. Ein kleiner Teil davon wird die Wohnraumbeleuchtung sein. RGB-LEDs sind mittlerweile günstig auf dem Markt erhältlich und werden konventionelle Lampen ablösen. Dieser Artikel beschreibt Überlegungen für die Steuerung einer RGB-LED über ein serielles Protokoll.

Für dieses Projekt wird eine RGB-LED und der Mikrocontroller ATtiny2313A verwendet. Das Bluetooth Modul HC-05 bildet die Schnittstelle zwischen Beleuchtungseinheit und Steuerungssoftware auf Andorid Basis.

Um die Übertragung von Helligkeitswerten mit möglichst wenig Aufwand zu realisieren, wird ein Protokoll erstellt, dass aus einem 4 Byte großem Block besteht. Der Block beginnt mit einem Null Byte (0x00), darauf folgen die Helligkeitswerte für Rot, Grün und Blau. Das Nullbyte dient zur Synchronisation. Die sonstigen Bytes dienen zum Übertragen von Kommandos. Somit ist das Protokoll am Ende bei Bedarf erweiterbar, da erst nach der Synchronisation wieder mit den Farbwerten gerechnet wird.

Telegramm zur Steuerung der Lampe

Die Sonstigen Bytes können für Steuerbefehle verwendet werden. Um erneut eine Farbe zu übertragen muss das Byte 0x00 übertragen werden und danach die Bytes für Rot, Grün und Blau. So lässt sich zum Beispiel der Status der Lampe mit dem Code 0x01 abfragen, der aktuelle Batteriezustand mit 0x02 und so weiter. Die Befehle hier sind erst einmal nur als Beispiel gedacht, um die Grundfunktionen zu implementieren.

Steuerbefehle

Als nächstes wird der Aufbau der Hardware folgen.

[WIP] Ein neuer Versuch mit ASURO

Ich bearbeite für das Studium eine Projektarbeit für die Roboterplattform ASURO. Den Verlauf der Entwicklung werde ich hier dokumentieren. Ziel ist es ein Betriebssystem zu erstellen, dass die Werte der sechs Sensoren kontinuierlich ermittelt und sie für den Programmierer aufbereitet. Dazu gehört neben der reinen Speicherung der Messwerte eine, abhängig vom Sensor, kontinuierliche Verarbeitung. Deshalb habe ich mich in letzter Zeit intensiv mit der Wandlung von analogen Spannungen zu digitalen Werten beschäftigt; konkret mit der Wandlung von Spannungen eines IR-Transistors an einem ATmega8.
Der ADC des ATmega8 hat 6 Eingänge in der PDIP Konfiguration und zwei Ausleseverfahren. Einmal die einmalige Wandlung des aktuellen Wertes, und dann die kontinuierliche Wandlung. Dabei wird nach jeder vollständigen Konvertierung direkt eine neue angestoßen. Wenn ich kontinuierlich alle verfügbaren ADC Eingänge auslesen möchte, muss dazu eine interne Logik entwickelt werden, die die Messwerte und die Sensorauswahl steuert. Der normale Verlauf ist, dass der ADC Multiplexer (MUX) eingestellt wird, die Spannung gemessen und das Ergebnis als 10 bit Wert in einem 16 bit Register abgelegt wird. Wenn man aber so schnell wie möglich den Wert eines beliebigen Eingangs verwenden will, muss die Wandlung schon im Voraus durchgeführt und die Ergebnisse vorgehalten werden. Für diesen Fall sind 6 Eingangsgrößen zu bestimmen. Jede der Größen wird nacheinander gewandelt und soll in einem Array zur Verfügung stehen. Nach jeder Konvertierung des ADC wird eine Interrupt Service Routine (ISR) aufgerufen. Diese sorgt für die Verarbeitung des aktuell ermittelten Messwertes. Dabei ist darauf zu achten, dass der Wert, der im Register zur Verfügung steht, der ist, der vor zwei ISR Aufrufen beantragt wurde. Das kommt daher, dass Der MUX beim Aufruf der ISR umgeschaltet wird. Dann wird die noch laufende Konvertierung beendet bevor der neu eingestellte Eingang des MUX an den ADC weitergegeben wird. Dann wandelt der ADC den Wert und ruft die ISR nach Beendigung dessen auf. Jetzt liegt als Ergebnis im Register der Wert für den vor zwei Aufrufen geschalteten MUX Eingang. Dies muss bei der Verwendung der Daten unbedingt beachtet werden, denn sonst kann nicht garantiert werden, dass die Werte an der richtigen Stelle im Array landen. Dazu habe ich eine einfache Funktion geschrieben, die als inline direkt in der ISR eingefügt werden kann. V wird immer um eins erhöht und bei erreichen von max wieder auf 0 gesetzt.

static inline uint8_t cycleValue(uint8_t v, uint8_t max) {
if(v == max) v = 0;
else v++;
return v;
}

Im ASURO sind zwei Refelktionslichtschranken verbaut. Diese zeigen auf die Schwarz-Weißen Encoderscheiben am Getriebe der Räder. Für die Ermittlung der Drehtakte für die Räder wird so von den IR-Transistoren ein sinusförmiges Signal erzeugt. Dieses Signal ist einerseits einfach zu analysieren, andererseits stören Faktoren wie Streulicht, oder axiale Bewegungen der Zahnräder im Getriebe. Dieser Umstand wird mit einem Median-Filter umgangen. Dieser filtert alle Ausreißer in den Messwerten heraus und erzeugt dadurch eine ganz anschauliche Sinusform. Abbildung 1 zeigt den Verlauf von je 300 Messwerten der beiden Radsensoren vor und nach der Filterung.

Zwei sinusförmige Schwingungen, die mit einem Medianfilter geglättet wurden
Abb. 1 Sensorwerte Kanal 0 und 1 ohne und mit Medianfilter

Die Taktung der Radgeschwindigkeit geschieht einmal beim Übergang von hohem Messwert auf niedrigen und andersherum. Mit einem unteren und oberen Schwellwert, wie in Abbildung 2, rote Linie erhält man vier Zustände, die das Auswertesystem annehmen kann: Wert kleiner als die Maximumschwelle steigende Flanke, Wert kleiner als die Maximumsschwelle flanke fallend und des gleiche für die Minimalschwelle. Dabei bietet es sich an die Übergänge  in einem Zustandsautomaten zu berechnen.

Abb. 2 Grenzwert (rote Linie) und Markierungen für Ticks

Der Automat benötigt lediglich die Werte des ADC und kann dann dementsprechend zwischen den Zuständen durchschalten. Das Zählen der Schritte wird ebenfalls in dem Automaten erledigt. Es bietet sich an die Schritte in den unten markierten Zuständen zu zählen, aber es können natürlich auch die beiden anderen Zustände zum Zählen der Schritte verwendet werden.

extern inline void detectEdgeFlip() {
switch (edge) {
case RISING:
if(adc_median_value < threshold[MIN]){
edge = RISING_WAIT;
}
break;
case RISING_WAIT: // Hier kann ein Schritt gezählt werden
if(adc_median_value > threshold[MIN]){
edge = FALLING;
}
break;
case FALLING:
if(adc_median_value > threshold[MAX]){
edge = FALLING_WAIT;
}
break;
case FALLING_WAIT: // Hier kann ein schritt gezählt werden
if(adc_median_value < threshold[MAX]){
edge = RISING;
}
break;
}

Da die Sensoren auf dem Board des ASURO dem Umgebungslicht ausgesetzt sind, ist es wichtg, dass der Minimal- und Maximalwert ständig betrachtet wird und die Schaltgrenzen gegebenenfalls angepasst werden. Dazu werden die letzten 64 Messwerte für jeden Kanal abgespeichert und bei vollem Puffer der Minimal und Maximalwert gesucht.

Sehr wichtig ist es, dass die ISR nicht länger dauern darf als die Wandlung des ADC, denn sonst überholt der ADC die CPU und es kommt zur Blockade der Software, da nur noch die ISR verarbeitet werden kann. Die aktuelle ISR des ADC sieht sehr voll aus und ich werde demnächst die Laufzeitdauer messen. Eventuell müssen einige Funktionen ausgelagert und so sporadisch wie möglich aufgerufen werden.

Hough Transformation für Geraden

Die von Paul V. C. Hough 1962 patentierte Methode zur Erkennung von komplexen Strukturen [1] verwendet ein Parameterraum in dem jeder Punkt im Bild der auf einer Kante liegt, jede mögliche zu findende Form durch diesen Punkt zugewiesen bekommt. Das sind bei unterschiedlichen Formen unterschiedliche Parameter, bei einer Gerade zum Beispiel Steigung und Y Achsenabschnitt oder bei einem Kreis Radius und Mittelpunkt. Wenn alle Kantenpunkte im Parameterraum abgebildet sind, werden durch eine Häufigkeitsanalyse die Parameter der gesuchten Figuren bestimmt. [2] In Software wird der Parameterraum häufig durch ein mehrdimensionales Array von Ganzzahlen ausgedrückt. Das Kantenbild liegt häufig als 2 dimensionales binäres Array vor. Dadurch sind die Raumtransformation und die Maximafindung einfach zu lösen. Um eine Gerade in einem Bild zu beschreiben gibt es mehrere Möglichkeiten.

Die Gerade $$ l_{m_0 b_0} : y = m_0 x + b_0 $$ im \(xy\) − Raum kann als Punkt \(( m_0 ,b_0 )\) im \(mb\)-Raum interpretiert werden. Ein Kantenpunkt lässt sich durch unendlich viele Geraden mit der Eigenschaft $$ y_i = mx_i + b$$ erzeugen, die allgemein als Gerade \( b = − mx_i + y_i\) im \(mb\)-Raum
dargestellt werden können. Jeder Punkt einer Geraden \(l_{m_0 b_0}\) im Bild führt zu einer Geraden im \(mb\)-Raum. Diese Geraden schneiden sich im Punkt \(( m_0 ,b_0 )\) .
Den \(mb\)-Raum bezeichnet man als Akkumulator für die Geradenfindung, da die Werte aller Punkte dort Zusammengezählt werden.

Abb. 1 Gerade im xy-Raum als Schnittpunkt im mb-Raum
(a) Punkte, (b) Schnittpunkt, (c) Gerade

Zu den in Abbildung 1a gezeigten Punkten soll die dazugehörige Gerade ermittelt werden. Die Punkte werden als Geraden im \(mb\)-Raum (Abbildung 1b) betrachtet und der Schnittpunkt bei ( 1,1 ) entspricht den Parametern der Ausgangsgeraden (Abbildung 2a).
$$y = mx + b → b = − xm + y $$
Um den Berechnungsaufwand zu reduzieren, können nun die einzelnen Kantenpunkte vorher auf eventuelle Nachbarpunkte untersucht werden und der Akkumulator darauf hin aufgebaut werden. Hierzu können die acht benachbarten Felder eines Kantenpunktes betrachtet werden. Wenn ein Nachbarfeld auch ein Kantenpunkt ist, besteht die Wahrscheinlichkeit, dass durch diese beiden Punkte eine Gerade läuft. Wenn das Nachbarfeld kein Kantenpunkt ist, ist die Wahrscheinlichkeit gering, dass durch diese Punkte eine Gerade verläuft. Hier muss ein Kompromiss getroffen werden, wie genau man die Geradende-
tektion durchführen möchte. Ein weiterer Nachteil der gezeigten Methode ist, dass senkrechte Geraden eine
unendliche Steigung haben. Dies kann durch die Abbildung in der Hess’schen Normalform mit \(0 ≤ ρ ≤ 2π;l ≥ 0\) umgangen werden.
$$ l = xcosρ + ysinρ $$
Jeder Punkt impliziert eine Reihe von Geraden. Transferiert in den \(ρl)\-Raum ergibt sich eine sinusförmige Kurve.

Abb. 2 Gerade im \(xy\)-Raum als Schnittpunkt im \(ρl)-Raum

Diese Werte werden in einem diskreten Akkumulator \(H [ ρ ][ l ]\) mit endlich vielen Werten \(ρ = 0,∆ρ,2∆ρ,…\) und \(l = 0,∆l,2∆l,…\) abgebildet. Die binären Kantenpunkte des Bildes sind bekannt: \(K : {( x_1 ,y_1 ) ; ( x_2 ,y_2 ) ; … ; ( x_n ,y_n )}\) . Man setzt alle Werte des Akkumulators \(H\) auf \(0\). Für alle Kantenpunkte in \( K \) berechnet man zuerst \( ρ \) und \( l \), dann erhöht man den Wert in \(H [ ρ ][ l ]\) um \( 1 \). Nachdem alle Kantenpunkte berücksichtigt wurden sucht man auffällig hohe Werte in \(H\). Man kann davon ausgehen, dass jeder lokale Spitzenwert in \(H\) eine Gerade im Bild beschreibt. Ein Beispiel zu dieser Methode zeigt Abbildung 2. Hier wurden die Punkte im Bild 2a als Sinuskurven transformiert. Schnittpunkte im \(ρl\)-Raum führen zu lokalen Maxima im Akkumulator. Das Parameterpaar eines Maximums im Akkumulator (Schnittpunkt in 2b) kann als eine Gerade \(l_{ρ_0 l_0} : l_0 = x /cosρ_0 + y /sinρ_0\) im \(xy\)-Raum betrachtet werden.
Um die Genauigkeit der Formdetektion zu erhöhen, kann anstelle einer Binär Matrix zur Kantenerkennung ein Graustufenbild verwendet werden. Hier können die Grauwerte eine Aussage über die Kantenstärke geben. Je stärker die Kante ist, desto mehr wird der Akkumulator für diese Stelle erhöht. So ergibt sich ein gewichtetes Bild, in dem kontrastreichere Kanten stärker herausstechen.

Literatur

[1] Hough, P. V. C.: US3069654: Method and Means for Recognizing Complex Pat-
terns, 1962.
[2] Prof. Dr. Xiaoyi Jiang, Michael Schmeing und Sönke Schmid: Computer
Vision und Mustererkennung: Einführung – Kapitel 7: Bildsegmentierung: Hough,
Okt. 2012.

Artikel herunterladen