0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AtomS3RとカラーセンサーTCS34725を使った色認識

Posted at

使用したカラーセンサー

事前調査の結果、カラーセンサーは、常に同一の測定条件でセンシングしないと、良い結果が得られないとわかったので、写真のような、センサーユニットの両端にLEDがあり、測定時には全体を覆ってしまうような構造のものを選定した。
こちらにTCS34725というカラーセンサーが乗っていた。

image.png

使用したマイコン

小型に作りたかったので、AtomS3Rを選定した。
制作事例があまり出てこず苦戦した…

とりあえず作ってみたが…

とりあえず、愚直にセンサーの値を取り込み、ディスプレイにその色を写すように、ちゃちゃっと作ってみたのだが、どうにも発色がおかしい…
センサーのSDA,SCLピンは、それぞれAtomS3Rの38, 39番ピンに接続している。

tsc34725.ino
#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include "M5AtomS3.h"

Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_300MS, TCS34725_GAIN_16X); // 1, 4, 16, 60

static uint16_t color16(uint16_t r, uint16_t g, uint16_t b) {
  uint16_t _color;
  _color = (uint16_t)(r & 0xF8) << 8;
  _color |= (uint16_t)(g & 0xFC) << 3;
  _color |= (uint16_t)(b & 0xF8) >> 3;
  return _color;
}

void setup() {
  // auto cfg = M5.config();
  Wire.end();
  //I2Cピン設定 I2C0を使用する SDA, SCL
  Wire.begin(38, 39);

  // AtomS3.begin(cfg);
  AtomS3.begin();
  Serial.begin(115200);
  strip.begin();

  if (!status) {
    M5.Lcd.println(F("Could not find a valid BMP280 sensor, check wiring or "
                     "try a different address!"));
    while (1) delay(10);  // Stop code execution if the sensor is not found.
  }

  while(!tcs.begin()){
    M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
    M5.Lcd.setCursor(0, 60);
    M5.Lcd.print("Sensor not found");     
    delay(1000);
  }
  M5.Lcd.println("Found sensor");
}

void loop() {
  // Typing any character into Serial Monitor will kick off a read / output cycle
  AtomS3.update();
    if (AtomS3.BtnA.wasPressed()) {
      // raw values of r,g,b,c as read by TCS3472
      uint16_t r, g, b, c; 

      // Get raw data from the TCS3472
      tcs.getRawData(&r, &g, &b, &c);

      // Print out values
      Serial.print("R: "); Serial.print(r); Serial.print(" ");
      Serial.print("G: "); Serial.print(g); Serial.print(" ");
      Serial.print("B: "); Serial.print(b); Serial.print(" ");
      Serial.println(" ");

      uint16_t boxcolor = color16(r, g, b);
      M5.Lcd.clear(boxcolor);  // ディスプレイを指定した色にセット
  }
}

キャリブレーションしてみる

参考になりそうな記事を発見し、RGB各センサーの値を補正してみる。

以下のようなキャリブレーション用のloop()関数を書き込み、真っ黒、つまり、(0,0,0)となるはずのものと、真っ白、つまり、(255,255,255)となるはずのもの、(反射の少ない布などがおすすめ)を実際に測定し、実際の値を計測してみた。

calib.ino
// Calib
String incomingString;

void loop(void) {
  uint16_t r, g, b, c, colorTemp, lux;
  unsigned int red, green, blue;

  if (Serial.available() > 0){
    incomingString = Serial.readString();
    tcs.getRawData(&r, &g, &b, &c);
    colorTemp = tcs.calculateColorTemperature_dn40(r, g, b, c);
    lux = tcs.calculateLux(r, g, b);

    Serial.print("Color Temp: "); Serial.print(colorTemp, DEC); Serial.print(" K - ");
    Serial.print("Lux: "); Serial.print(lux, DEC); Serial.print(" - ");
    Serial.print("R: "); Serial.print(r, DEC); Serial.print(" ");
    Serial.print("G: "); Serial.print(g, DEC); Serial.print(" ");
    Serial.print("B: "); Serial.print(b, DEC); Serial.print(" ");
    Serial.print("C: "); Serial.print(c, DEC); Serial.print(" ");
    Serial.println(" ");
  }
 }

多少誤差があるが、おおよそ以下のような結果になった

 redCal.blackValue = 1500;
 redCal.whiteValue = 65535;
 greenCal.blackValue = 3000;
 greenCal.whiteValue = 65535;
 blueCal.blackValue = 3000;
 blueCal.whiteValue = 65535;

全体的に黒に対して値が出ており、かつ、赤色のみ、黒検出時の値がちいさい。
これらに値を利用し、値をマッピングしなおした。
また、全体的にそのまま色を使うと暗い色になる印象があったため、色空間をRGBからYUVに変換し、彩度を上げて表示するようにしている。

tsc34725.ino
#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include "M5AtomS3.h"

Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_300MS, TCS34725_GAIN_16X); // 1, 4, 16, 60

struct colorCalibration {
  unsigned int blackValue;
  unsigned int whiteValue;
};

colorCalibration redCal, greenCal, blueCal;

static uint16_t color16(uint16_t r, uint16_t g, uint16_t b) {
  uint16_t _color;
  _color = (uint16_t)(r & 0xF8) << 8;
  _color |= (uint16_t)(g & 0xFC) << 3;
  _color |= (uint16_t)(b & 0xF8) >> 3;
  return _color;
}

// array to represent the gamma function
int gammatable[256];

// Function to map TCS3472 values to 0 to 255
// Same as Arduino map() function - rewritten to make variables compatiable with inputs and outputs
int RGBmap(unsigned int x, unsigned int inlow, unsigned int inhigh, int outlow, int outhigh){
  float flx = float(x);
  float fla = float(outlow);
  float flb = float(outhigh);
  float flc = float(inlow);
  float fld = float(inhigh);

  float res = ((flx-flc)/(fld-flc))*(flb-fla) + fla;

  return int(res);
}

void setup() {
  // auto cfg = M5.config();
  Wire.end();
  //I2Cピン設定 I2C0を使用する SDA, SCL
  Wire.begin(38, 39);

  // AtomS3.begin(cfg);
  AtomS3.begin();
  Serial.begin(115200);
  strip.begin();

  if (!status) {
    M5.Lcd.println(F("Could not find a valid BMP280 sensor, check wiring or "
                     "try a different address!"));
    while (1) delay(10);  // Stop code execution if the sensor is not found.
  }

  while(!tcs.begin()){
    M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
    M5.Lcd.setCursor(0, 60);
    M5.Lcd.print("Sensor not found");     
    delay(1000);
  }
  M5.Lcd.println("Found sensor");

  redCal.blackValue = 1500;
  redCal.whiteValue = 65535;
  greenCal.blackValue = 3000;
  greenCal.whiteValue = 65535;
  blueCal.blackValue = 3000;
  blueCal.whiteValue = 65535;

  // Gamma function is Out = In^2.5
  // Required to correct for human vision
  // Store values in an array
  // normalized for 0 to 255
  for (int i=0; i<256; i++) {
    float x = i;
    x /= 255;
    x = pow(x, 2.5);
    x *= 255;

    gammatable[i] = int(x);
  }
}

void loop() {
  // Typing any character into Serial Monitor will kick off a read / output cycle
  AtomS3.update();
    if (AtomS3.BtnA.wasPressed()) {
      // raw values of r,g,b,c as read by TCS3472
      uint16_t r, g, b, c; 

      // Variables used to hold RGB values between 0 and 255
      int redValue;
      int greenValue;
      int blueValue;
      int clearValue;

      // Get raw data from the TCS3472
      tcs.getRawData(&r, &g, &b, &c);

      // Convert TCS3472 raw reading into value between 0 and 255 for analogwrite function
      redValue = RGBmap(r, redCal.blackValue, redCal.whiteValue, 0, 255);
      greenValue = RGBmap(g, greenCal.blackValue, greenCal.whiteValue, 0, 255);
      blueValue = RGBmap(b, blueCal.blackValue, blueCal.whiteValue, 0, 255);

      // Print out values
      Serial.print("R: "); Serial.print(redValue); Serial.print(" ");
      Serial.print("G: "); Serial.print(greenValue); Serial.print(" ");
      Serial.print("B: "); Serial.print(blueValue); Serial.print(" ");
      Serial.println(" ");

      // uint16_t boxcolor = color16((int)redValue, (int)greenValue, (int)blueValue);
      uint16_t boxcolor = color16(gammatable[redValue], gammatable[greenValue], gammatable[blueValue]);
      M5.Lcd.clear(boxcolor);  // ディスプレイを指定した色にセット
  }
}

結果

おおよそ納得のいく色がセンシングできた。
ただ、取得した色が全体的に多少暗いイメージがあるので、ガンマ補正に加えて、色空間の変換を行った後の彩度の強調などを行えば、もっときれいになると思われる。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?