Close

TCS3200 color sensor with k-Nearest Neighbor classification algorithm

A project log for Say hello to AR3

STEM-based humanoid robot

M. BindhammerM. Bindhammer 03/03/2019 at 14:140 Comments

The k-NN (k-nearest neighbors) algorithm is among the simplest of all machine learning algorithms. We will use this algorithm to classify the color of a new object. As our training set of learned colors will only contain one sample per color, k = 1, and the object is simply assigned to the class of that single nearest neighbor. We will work in a 3-dimensional Euclidean space ℝ3. The x-axis represents the ratio of the clear channel frequency to the red channel frequency, the y-axis the ratio of the clear channel frequency to the green channel frequency and the z-axis the ratio of the clear channel frequency to the blue channel frequency (see TCS3200 data sheet for more details).

The image below illustrates the 3-dimensional Euclidean space, the trained colors (white, orange, yellow, red, green and blue point) and a new sample (black point), which color needs to be classified. The black lines are the so called Euclidean distances from the new sample point to every trained color point. All we need to do is to find the smallest distance.

Fig. 1 Euclidean distances of RGB values

In general, for an n-dimensional Euclidean space ℝn, the Euclidean distance between two points

and

is given by

Trouble shooting:

Machine learning code:

#include <SPI.h>
#include <Wire.h>
#include <Servo.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "musical_notes.h"
#define SCREEN_WIDTH 128 /* OLED display width */
#define SCREEN_HEIGHT 64 /* OLED display height */

/* Declaration for an SSD1306 display connected to I2C (SDA, SCL, reset pin) */
#define OLED_RESET 9 /* Reset pin */
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

byte LED = 8, S0 = 7, S1 = 6, S2 = 5, S3 = 4, OUT = 3;
char* color[] = {"white", "orange", "yellow", "red", "green", "blue", "out of range"};
char point[] = "Point color sensor to";
char object[] = " object";
char submit[] = "Press the push buttonif it's done";
int learned_colors[3][6];
int PercentageRed, PercentageGreen, PercentageBlue;
float OutOfRange;
int switchPin = 23;
int debounce = 200;
/* Speaker connected to digital pin 26 */
int speakerPin = 26;

void setup() {
  Serial.begin(9600);
  /* Init OLED */
  display.begin(SSD1306_SWITCHCAPVCC, 0x3D);
  /* Clear the buffer */
  display.clearDisplay();
  TCS3200_Setup();
  pinMode(switchPin, INPUT);
  pinMode(speakerPin, OUTPUT); 
}

void loop() {
  learning_mode();
  while(1) {
    new_sample();
    classify();
  }
}

void TCS3200_Setup() {
  pinMode(S0,OUTPUT);
  pinMode(S1,OUTPUT);
  pinMode(S2,OUTPUT);
  pinMode(S3,OUTPUT);
  pinMode(LED,OUTPUT);
  pinMode(OUT,INPUT);
}

void TCS3200_On() {
  digitalWrite(LED,HIGH); // switch LED on
  digitalWrite(S0,HIGH); // output frequency scaling (100%)
  digitalWrite(S1,HIGH);
  delay(5);
}

void TCS3200_Off() {
  digitalWrite(LED,LOW); // switch LED off
  digitalWrite(S0,LOW); // power off sensor
  digitalWrite(S1,LOW);
}

void NoFilter() {
  digitalWrite(S2,HIGH); // select no filter
  digitalWrite(S3,LOW);
  delay(5);
}

void RedFilter() {
  digitalWrite(S2,LOW); // select red filter
  digitalWrite(S3,LOW);
  delay(5);
}

void GreenFilter() {
  digitalWrite(S2,HIGH); // select green filter
  digitalWrite(S3,HIGH);
  delay(5);
}

void BlueFilter() {
  digitalWrite(S2,LOW); // select blue filter
  digitalWrite(S3,HIGH);
  delay(5);
}

void input() {
  while (1) {
    if(digitalRead(switchPin) == 1) {
      squeak();
      break;
    }
  }
}

void GetColor() {
  float FrequencyClear, FrequencyRed, FrequencyGreen, FrequencyBlue;
  TCS3200_On();
  NoFilter();
  FrequencyClear = float(pulseIn(OUT,LOW,10000));
  RedFilter();
  FrequencyRed = float(pulseIn(OUT,LOW,10000));
  GreenFilter();
  FrequencyGreen = float(pulseIn(OUT,LOW,10000));
  BlueFilter();
  FrequencyBlue = float(pulseIn(OUT,LOW,10000));
  TCS3200_Off();
  PercentageRed = int((FrequencyClear / FrequencyRed) * 100.0);
  PercentageGreen = int((FrequencyClear / FrequencyGreen) * 100.0);
  PercentageBlue = int((FrequencyClear / FrequencyBlue) * 100.0);
  OutOfRange = 500.0 / FrequencyClear;
}

void learning_mode() {
  for(byte i = 0; i < 6; i ++) {
    display.clearDisplay();
    display.setTextSize(1);             
    display.setTextColor(1, 0); 
    display.setCursor(0, 0);
    display.print(point);
    if(i == 1) display.print("an ");
    else display.print("a ");
    display.print(color[i]);
    display.println(object);
    display.println("");
    display.println(submit);
    display.println("");
    display.display();
    input();
    GetColor();
    learned_colors[0][i] = PercentageRed;
    learned_colors[1][i] = PercentageGreen;
    learned_colors[2][i] = PercentageBlue;
  }
}

void new_sample() {
  GetColor();
  delay(500);
}

void classify() {
  display.clearDisplay();
  display.setTextSize(1);             
  display.setTextColor(1, 0); 
  display.setCursor(0, 0);
  int i_color;
  int ClosestColor;
  float MaxDiff;
  float MinDiff = 100.0;
  if(OutOfRange < 1.1) ClosestColor = 6; /* object out of range */
  else {
    /* Find nearest neighbor, k = 1 */
    for (i_color = 0; i_color < 6; i_color ++) {
      /* compute Euclidean distances */
      float ED = sqrt(pow((learned_colors[0][i_color] - PercentageRed),2.0) +
      pow((learned_colors[1][i_color] - PercentageGreen),2.0) + pow((learned_colors[2][i_color] - PercentageBlue),2.0));
      MaxDiff = ED;
      /* Find minimum distance */
      if (MaxDiff < MinDiff) {
        MinDiff = MaxDiff;
        ClosestColor = i_color;
      }
    }
  }
  if(ClosestColor != 6) display.print("Object is "); /* Print result */
  else display.print("Object ");
  display.println(color[ClosestColor]);
  display.display();
}

void beep (int speakerPin, float noteFrequency, long noteDuration) {    
  int x;
  // Convert the frequency to microseconds
  float microsecondsPerWave = 1000000/noteFrequency;
  // Calculate how many HIGH/LOW cycles there are per millisecond
  float millisecondsPerCycle = 1000/(microsecondsPerWave * 2);
  // Multiply noteDuration * number or cycles per millisecond
  float loopTime = noteDuration * millisecondsPerCycle;
  // Play the note for the calculated loopTime.
  for (x=0;x<loopTime;x++) {   
    digitalWrite(speakerPin,HIGH); 
    delayMicroseconds(microsecondsPerWave); 
    digitalWrite(speakerPin,LOW); 
    delayMicroseconds(microsecondsPerWave); 
  } 
}

void squeak() {
  for (int i=100; i<5000; i=i*1.45) {
    beep(speakerPin,i,60);
  }
  delay(10);
  for (int i=100; i<6000; i=i*1.5) {
    beep(speakerPin,i,20);
  }
}

The musical_notes.h file is available in the Files section of this project. It must be placed in the same folder as the machine learning file.

Discussions