Autor: Piotr Rzeszut (Piotrva)
Redakcja: Dondu
Artykuł jest częścią cyklu: Kurs Arduino
Chyba każdy choć raz natknął się na problem segregowania kart na talię czerwoną i niebieską, czy tym podobne zajęcia wymagające rozpoznawania koloru. Nawet jeśli nie, to wizja budowy urządzenia rozróżniającego kolory maluje się dosyć ciekawie - czy będzie to urządzenie do segregacji kart (a takowe kiedyś popełniłem), czy cukierków M&M's musimy jakoś rozpoznać kolor obiektu.
Za pewne na początku pomyślicie o zastosowaniu kolorowej kamery, ale pomyślmy ile trudu będzie wymagało jej podpięcie do układu i odpowiednio szybkie odbieranie danych (o analizie obrazu nie wspominając) to raczej nie zadanie dla zwykłego Arduino - do tego lepiej by zastosować już smartfon (sterujący np. przez bluetooth naszym Arduino) lub komputer w stylu Raspberry PI - z drugiej strony jednak pytanie - po co wytaczać armatę (urządzenie mogące już analizować obraz i rozróżniać figury karciane a nie tylko kolory) na małą muszkę?
Lepiej poszperać w internecie, gdzie możemy znaleźć m. in. różne moduły z czujnikiem koloru TCS3200 lub podobnymi.
Moduł z czujnikiem TCS3200 i oświetleniem |
TCS3200 - co to za kolega?
Układ TCS3200 (i podobne układy z tej rodziny) to czujnik koloru oparty na kilkudziesięciu fotodiodach, z których cześć wyposażona jest w filtry: czerwony, zielony lub niebieski, a część nie posiada żadnego filtra.Moduł z czujnikiem otrzymałem do testów ze sklepu:
Dokumentacja do pobrania: Czujnik-koloru-TCS3200.pdf (kopia)
Układ posiada 5 wejść sterujących i 1 wyjście. Może być zasilany napięciem z zakresu 2.7~5.5V.
Dane wyjściowe układu to przebieg cyfrowy o wypełnieniu ok. 50% i częstotliwości proporcjonalnej do uśrednionego natężenia światła padającego na aktualnie wybraną grupę diod.
Wejścia posiadają następujące funkcje:
- OE - w stanie niskim aktywuje wyjście, w stanie wysokim wyjście wyłącza.
- S0..1 - te piny umożliwiają wybór zakresu częstotliwości, jakie na wyjściu podawać będzie układ.
- S2..3 - te wejścia z kolei umożliwiają wybór poszczególnych grup diod z danym filtrem (lub bez niego - CLEAR).
Funkcje wejść S0..3 |
Częstotliwość odpowiadająca opcji 100% i pełnemu oświetleniu czujnika to wg. danych z noty katalogowej około 600kHz.
Mierzymy
A więc nasze rozpoznawanie koloru będzie polegało przede wszystkim na pomiarze częstotliwości - do tego wykorzystamy bibliotekę FreqCount, która z wykorzystaniem timera pierwszego zlicza impulsy na pinie D5 w zadanym przez nas czasie (w milisekundach).Cały proces polegać za tym będzie na wyborze rozsądnej częstotliwości podstawowej (ja wybrałem opcję 20%), włączeniu wyjścia układu (te opcje ustawiamy na stałe zwierając odpowiednio piny układu), następnie w programie będziemy ustawiać odpowiednią konfigurację wejść S2..3, w celu wyboru odpowiedniej grupy diod (tj. badanego koloru) i zliczać impulsy na wyjściu układu w zadanym czasie.
W ten sposób otrzymamy jakieś dane o kolorze obiektu, od którego odbija się światło z zamontowanych na module diod LED. Żeby te informacje były dla nas bardziej czytelne, możemy przeprowadzić kalibrację - przez jakiś rejestrujemy poszczególne "surowe" wartości, umieszczając przed czujnikiem obiekt czarny i biały.
Następnie zapisujemy wartość minimalną i maksymalną z tego okresu "kalibracji" dla każdego kanału - potem te wartości możemy przyjąć jako odniesienie dla umownych 100% natężenia danej barwy i 0% natężenia.
#include <FreqCount.h> // połączenia const int S3 = 13; const int S2 = 12; const int Fo = 5; // OE -> GND // S0 -> 5V // S1 -> GND enum {RED=0, GREEN, BLUE, CLEAR} color=RED; long results[4]={0,0,0,0}; long max_results[4]={0,0,0,0}; long min_results[4]={0xFFFFFFFF/2,0xFFFFFFFF/2,0xFFFFFFFF/2,0xFFFFFFFF/2}; // aby poprawnie ustalać wartość min i max musimy odpowiedno zainicjalizować zmienne byte calibration = 1; void setup() { Serial.begin(57600); pinMode(Fo, INPUT); pinMode(S2, OUTPUT); pinMode(S3, OUTPUT); digitalWrite(S2, LOW); digitalWrite(S3, LOW); FreqCount.begin(100); } void loop() { // jeśli odebraliśmy znak z UART to sprawdzamy, czy to nie znak zakończenia kalibracji if(Serial.available()){ char c = Serial.read(); if(c=='x'){//jeśli tak to wyświetlamy dane kalibracji calibration=0; Serial.println("Calibration disabled"); Serial.print("RL: "); Serial.print(min_results[RED]); Serial.print(" RH: "); Serial.println(max_results[RED]); Serial.print("GL: "); Serial.print(min_results[GREEN]); Serial.print(" GH: "); Serial.println(max_results[GREEN]); Serial.print("BL: "); Serial.print(min_results[BLUE]); Serial.print(" BH: "); Serial.println(max_results[BLUE]); Serial.print("CL: "); Serial.print(min_results[CLEAR]); Serial.print(" CH: "); Serial.println(max_results[CLEAR]); delay(5000); } } // jeśli zakończono pomiar częstotliwości (liczenie impulsów w zadanym czasie) if (FreqCount.available()) { unsigned long count = FreqCount.read(); results[color]=count;//zapisujemy ich ilośc (odpowiadającą częstotliwości) // jeśli kalibracja jest aktywna to zapisujemy minimalną lub maksymalną wartość odczytu z tego kanału if(calibration){ if(results[color]>max_results[color])max_results[color]=results[color]; if(results[color]<min_results[color])min_results[color]=results[color]; } // blokujemy pomiar, w czelu zresetowania systemu liczenia FreqCount.end(); // w zależności od aktualnego koloru ustawiamy kolejny kolor do sprawdzenia i odpowiednią kombinację S2..3 switch(color){ case RED: color=GREEN; digitalWrite(S2, HIGH); digitalWrite(S3, HIGH); break; case GREEN: color=BLUE; digitalWrite(S2, LOW); digitalWrite(S3, HIGH); break; case BLUE: color=CLEAR; digitalWrite(S2, HIGH); digitalWrite(S3, LOW); break; case CLEAR: color=RED; digitalWrite(S2, LOW); digitalWrite(S3, LOW); // jeden raz na cały cykl pomiarowy wyświetlamy zebrane dane - najpierw w formacie nieprzetworzonym... Serial.print("R: "); Serial.print(results[RED]); Serial.print(" G: "); Serial.print(results[GREEN]); Serial.print(" B: "); Serial.print(results[BLUE]); Serial.print(" C: "); Serial.print(results[CLEAR]); // a tu, korzystając z danych kalibracji, przeskalowujemy wnikiki do zakresu 0..100 results[RED ]=map(results[RED ],min_results[RED ],max_results[RED ],0,100); results[GREEN]=map(results[GREEN],min_results[GREEN],max_results[GREEN],0,100); results[BLUE ]=map(results[BLUE ],min_results[BLUE ],max_results[BLUE ],0,100); results[CLEAR]=map(results[CLEAR],min_results[CLEAR],max_results[CLEAR],0,100); // i ponownie wyświetlamy "znormalizowane" dane Serial.print(" R: "); Serial.print(results[RED]); Serial.print(" G: "); Serial.print(results[GREEN]); Serial.print(" B: "); Serial.print(results[BLUE]); Serial.print(" C: "); Serial.println(results[CLEAR]); break; } // na koniec uruchamiamy pomiar ponownie (po zmianie wyboru koloru) na 100ms (w tym czasie zostaną zliczone impulsy) FreqCount.begin(100); } }
Do pobrania:
- kod.c (kopia)
- FreqCount.zip (kopia)
W przykładowym programie operacja taka wykonywana jest po resecie mikrokontrolera i trwa aż do momentu przesłania przez UART litery "x".
W zasadzie, jeśli chodzi o samo działanie modułu, nie ma tu już chyba nic do dodania - reszta to Wasza inwencja twórcza.
Słowo o światełku
No w zasadzie jednak mam jeszcze coś do dodania - a mianowicie wspomnieć o wrażliwości czujnika na zmiany oświetlenia. Otóż czujnik ten bada kolor i natężenie światła odbitego, a zatem jeśli dodatkowo oświetlmy nasz obiekt z zewnątrz (lub oświetlimy sam nasz czujnik) to wtedy jego wskazanie się zmieni.Pomimo tego, że światło diod LED na module jest stosunkowo jasne to np. oświetlenie Waszej pracowni ma spory wpływ na odczyt (wystarczy bez dotykania obiektu, którego kolor badamy rzucić na niego ręką cień).
Takie niepożądane zachowanie moglibyśmy eliminować na dwa sposoby:
- odnosząc natężenia poszczególnych barw do natężenia całkowitego (CLEAR),
- budując wokół czujnika osłonę z nieprześwitującego materiału (np. czarny karton)
Osłona przed światłem zewnętrznym |
Szkoda, że nie ma filmu ilustrującego działanie czujnika.
OdpowiedzUsuńCzujnik ciekawy, ale dla prostego rozpoznawania kolorów można zbudować zdecydowanie tańszy czujnik w oparciu o fotorezystor i diodę LED RGB (koszt razem 2zł). Zapalając poszczególne diody i mierząc rezystancję można sprawdzać kolor cukierków :)
OdpowiedzUsuńTeoretycznie tak, w praktyce fotorezystory mają paskudną charakterystykę spektralną, zupełnie różną niż oko. W dodatku poza wąskim zakresem z maksimum zależnym od użytego materiału, czułość fotorezystora gwałtownie maleje.
OdpowiedzUsuńto mnie ciekawi odnosząc natężenia poszczególnych barw do natężenia całkowitego zrobiłem np. czerwony/(czerwony+zielony+niebieski) i tak mocno reaguje na zacienienie. ten czujnik makabrycznie reaguje na zmianę światła - przysłonięcie dłonią
OdpowiedzUsuńTaki czujnik będzie rozpoznawał kolor zmieniący się z zielonego na czerwony kolor ten był by wyświetlany na ekranie monitora komputerowego ?
OdpowiedzUsuń