Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.

środa, 16 marca 2011

Arduino: Rozpoznawanie koloru czujnikiem TCS3200





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:
  1. OE - w stanie niskim aktywuje wyjście, w stanie wysokim wyjście wyłącza.
  2. S0..1 - te piny umożliwiają wybór zakresu częstotliwości, jakie na wyjściu podawać będzie układ.
  3. 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:



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:
  1. odnosząc natężenia poszczególnych barw do natężenia całkowitego (CLEAR),
  2. budując wokół czujnika osłonę z nieprześwitującego materiału (np. czarny karton)
na przykład taką:

Osłona przed światłem zewnętrznym



Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

3 komentarze:

  1. Szkoda, że nie ma filmu ilustrującego działanie czujnika.

    OdpowiedzUsuń
  2. 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ń
  3. 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ń

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.