身の周りのものの色を自動で再現する装置を作ってみた

# 0.はじめに
はじめまして．私は情報系の大学院生をしている者です．
ここでは私が以前製作した"身の周りのものの色を読み取りそれを絵の具で再現する装置"を紹介します．これを作ったのはもう2年近くも前のことですが，ありがたいことに製作時に大きな反響をいただき，いつかはその内容をアウトプットしたいと思っていたので，今更ですがこうして記事を書かせていただきます．

なお製作時に書いたコードはgithubに公開していますのでそちらも参照してください．
https://github.com/shutakahama/Color_maker

# 1.概要
### 何を作ったか
私が製作したのは環境中のものの色を全自動で再現できる装置です．具体的には，

- センサーでものの色を読み取る
- ディスプレイ上にその色を出力して線が引ける
- 絵の具を適切に混ぜ合わせてその色を再現できる

というようなことができます．写真が作った装置です．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470270/c473607e-3f53-903d-43e5-cf2194c22037.jpeg" width="480">

色の再現度はかなり高く，写真のようにオレンジや緑が綺麗に再現できていることがわかります．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470270/28587609-3b09-f60e-a216-a5d2890fdf39.jpeg" width="480">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470270/3d240f79-ef94-4ce3-909f-3ac953a5c317.jpeg" width="480">

### 反響は？
この製作は大学の講義の一環だったのですが，成果発表後に学科の中での最優秀賞をいただき，学科長から表彰されました．

さらに製作後これをtwitterに投稿したところ，およそ2万いいね，1万リツイートとかなりの反響がありました．twitterには装置が動いている動画も載せていますので見てみてください！
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">反響が大きくて驚いてます！<br>一応動いている動画もあるので上げておきます <a href="https://t.co/Adv9B7PyIa">pic.twitter.com/Adv9B7PyIa</a></p>&mdash; たかはま (@grouse324st) <a href="https://twitter.com/grouse324st/status/952926703923347457?ref_src=twsrc%5Etfw">January 15, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

twitterでのいわゆる"バズり"を受けて記事も書いていただきました．
[Gigazine](https://gigazine.net/news/20180118-reproduce-color-paint/)
[ねとらぼ](https://nlab.itmedia.co.jp/nl/articles/1801/18/news115.html)

さらに学祭で展示発表した場面をテレビでも紹介していただきました．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470270/771f8b5e-41e8-cc92-6ce2-7167fe7066a4.png" width="480">

それ以外にも例えばtwitter経由でこの装置に関する開発の相談がきたこともあり，私とって非常に意味のあるプロダクトになりました．

# 2.なぜこんな装置を作ることになったか
### きっかけ
そもそもこれを製作したのは大学の学部3年時の演習でした．私は機械工学と情報工学を扱う学科に所属していたのですが，そこでの講義の１つに，ロボットアームを動かしたり機械学習による画像分類をしたりするような実践的な演習の時間がありました．そしてその演習の最後に「何でもいいから好きなものを作る」という課題が与えられ，1ヶ月ほどの期間の中で各自が好きなものを製作することになりました．

### アイデア
何を作ろうか考えたとき，比較的簡単なプログラミングで電気回路や出力装置などのハードウェアを動作させることのできるArduinoを用いることを考えました．そしてたまたま書店にあったこの本を買いました．
https://www.amazon.co.jp/実践Arduino-電子工作でアイデアを形にしよう-平原-真/dp/4274220818

Arduinoを扱う初歩から始まり，面白いプロダクトを作るまでの手順が詳細に書かれている良書だと思います．また著者の方のサポートページには実際に制作する上での追加の参考情報が載っています．
http://makotohirahara.com/jissenarduino

この本の中に，「周りの物体の色を読み取ってディスプレイ上に出力する装置」が載っており，とても面白いプロダクトに興味を惹かれました．その制作自体は手順に従えば難しくなさそうでしたので，私はさらに「ディスプレイ上に色を表示するだけでなく実際に絵の具を混ぜて再現できたら面白いんじゃないか」と考えました．これが今回の装置の制作の始まりです．

本で紹介された「カラーピッカー」の動画
https://youtu.be/4TuFcdJptco

# 3. 使ったもの

- ノートPC
- Arduino Uno R3
- サーボモーター(マイクロサーボ SG92R) 4個
- カラーセンサー(Adafruit TCS34725)
- フルカラーLED(OSTA5131A)
- 抵抗(100Ω 2個，150Ω 1個，10kΩ 3個)
- タクトスイッチ 3個
- ユニバーサル基板(AE-ARDUINO_UNI-G)
- 外部電源(単3乾電池 2個)
- その他ワイヤー類、ピンソケットなどの電子工作部品
- ゴムチューブ(熱収縮チューブ Φ1.5×0.2×1m) 2本
- プラスチック容器(4分割 box型)
- ジュラルミン製外枠(大学の工房で自分で切削)
- カラーセンサーカバー(3Dプリンターで作成)
- 絵の具(中善画廊 3原色カラー，ぺんてる 黒)
- 絵の具用筆

電気部品については基本的に秋月電子(秋葉原)で購入しました．それ以外の部品も主に100均やネットで安く購入できるものです．
[秋月電子通商](http://akizukidenshi.com/catalog/)

また動作環境としてはArduino-1.8.5(Arduino IDE)，Processing-3.3.6を用いました．

以下ではこの装置の各部分について解説していきます．

# 4.色の読み取り
まず物体の色を読み取りその情報をArduinoへ送る部分です．

ここではカラーセンサーで物体の色を読み取ります．カラーセンサーは市販の[Adafruit TCS34725](http://akizukidenshi.com/catalog/g/gM-08220/)（880円）を使いました．これは対象物のRGB3つの値それぞれを0〜255の段階で読み取ることができます．

なおこのセンサーは白色光を当てて対象物からの反射光から色を読み取る仕組みなので、センサーと対象物が近すぎると色をうまく認識できず、また周囲の光も大きく影響します．それらの問題を解決するため，カラーセンサーの周りに覆いを作りました．この部分は[実践！Atduino](http://makotohirahara.com/jissenarduino)に書いてあるものと同じ構造で，著者の方が[Webページ](http://makotohirahara.com/jissenarduino)に適切な覆いの3Dデータを提供してくれています．ですので私はそのデータをもとに大学の3Dプリンターで覆いを作成しました．

写真が完成した覆いで，中にセンサーを取り付けてあります．周囲を覆いつつ対象物とセンサーが一定距離離れるような構造をしています．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470270/233a85d9-9fd2-ef68-11e4-0cdc76431800.jpeg" width="480">

これよって、256段階のRGB値をArduinoに送ることができます．なおカラーセンサーを使う際には、ArduinoIDE上でライブラリ「Adafruit TCS34725 by Adafruit」をインストールする必要があります．

# 5. 色をディスプレイ上に表示する
次にカラーセンサーで読み取った値をArduinoからPCに転送し、PC上でマウスを使ってその色の絵を書けるようにする部分です．こちらも[実践！Atduino](http://makotohirahara.com/jissenarduino)の内容をベースとして使っています．

Arduinoで取得したデータをPC側で処理する際にはProcessingを使いました．Processingはグラフィクスやサウンドを扱うために開発されたプログラミング言語で，Arduinoのピンの状態を取得することもできるためディスプレイとの連携によく使われます．Processingから直接Arduinoを操作することもできますが、今回はArduino内部でカラーセンサーの情報の処理を行なっていたため，ArduinoとProcessingをシリアル通信させるようにしました．

シリアル通信は2つの機器を同期させるため、

- まずProcessingから仮データを送信し、
- それを受け取ったArduinoがセンサーの色の値を送信し、
- それをProcessingが受け取って解釈した後
- 再びProcessingが仮データを送信する

というような形式で行われています．

Processing内の操作はソースコードの[colordraw.pde](https://github.com/shutakahama/Color_maker/blob/master/colordraw.pde)に書いています．
Arduino側からは各時間でのセンサーのRGB値3つが送信されるので，それを配列として読み込みます。
その後格納された値をまずRGBからHSBに変換します．HSBとは色相、彩度、明度で色を表現する方法であり、ディスプレイ上での色表現に適しています．この変換はcolorMode()関数で行われています．なおディスプレイで表現する際には彩度と明度を1.5倍にすることで現実に見える色に近づけています．

```C++:colordraw.pde
void serialEvent(Serial myPort){
  String received = myPort.readStringUntil('\n');
  println(received);
  
  if(received != null){
    //改行コードなどを除く
    received = trim(received); 
    //カンマの区切りごとに配列に格納
    int sensorColor[] = int(split(received, ',')); 

    if(sensorColor.length == 3){
      //各値に格納
      int red = sensorColor[0];
      int green = sensorColor[1];
      int blue = sensorColor[2];
      
      //カラーモードRGBでmycolorに格納
      colorMode(RGB, 255, 255, 255, 255);
      myColor = color(red, green, blue);
      
      //カラーモードHSBで変換
      colorMode(HSB, 1.0, 1.0, 1.0, 1.0); 
      myColor = color(hue(myColor), saturation(myColor)*1.5, brightness(myColor)*1.5);
    }
    //仮データ送信
    myPort.write('A');
  }
}
```

色を格納したらそれをmycolor変数に入れます.表示を担当している部分はdraw()関数です．ここでは 色情報を変数myColorとして登録し、

- マウス左ボタンが押されたらmycolorの色の丸を表示する
- マウス右ボタンが押されたら白色の丸を表示する
- マウス中央ボタンが押されたら画面全て白で塗りつぶす

という操作を定義しています。これによって、左ボタンのドラッグでmyColorの色の線が引け、右ボタンで消しゴム、中央ボタンで画面クリアをすることができます.このあたりの操作は参考書より多少増やしています．

```C++:colordraw.pde
void draw(){
    if(mousePressed && mouseButton == LEFT){
        //左ボタンで指定した色の円
        fill(myColor);
        ellipse(mouseX, mouseY, 50, 50);
    }else if(mousePressed && mouseButton == RIGHT){
        //右ボタンで白色円（消しゴム）
        fill(0,0,0);
        ellipse(mouseX, mouseY, 50, 50);
    }else if(mousePressed && mouseButton == CENTER){
        //中央ボタンで画面クリア
        background(0,0,0);
    }
}
```

# 6. 絵の具を混ぜて色を再現する
ここまでの内容はあくまで参考書の内容をほぼそのまま使ったものでしたが，ここからが私の作ったプロダクトのメインの部分になります．やりたいことはセンサーから読み取ったRGB値を元に絵の具の配分を決め，実際に決めた量の絵の具を注ぐハードウェアを作ることです．

Arduinoによる色の読み取りから絵の具の混ぜ合わせまでの処理は[color_sensor.ino](https://github.com/shutakahama/Color_maker/blob/master/color_sensor.ino)で行われています．

### RGB-CMYK変換
RGB(Red, Green, Blue)は光の3原色で，ディスプレイの表示の際などに用いられます。一方絵の具で絵を描く際などの色の3原色はCMY(Cyan, Magenta, Yellow)です．RGBを全て混ぜると白になるのに対し，CMYを混ぜると黒になるという大きな違いがあります．なお実際は完全な黒にはならないことがあるため，これにK(Key plate, 黒のこと)を加えてCMYKを原色とすることが多いです．

RGBとCMYの間にはおおよそ R+G=Y, G+B=C, B+R=M というような関係があるので、理論的な変換式は以下のようになります．

```math
\begin{align}
K &= min (1-R, 1-G, 1-B)\\
C &= \frac{(1-R-K)}{(1-K)}\\
M &= \frac{(1-G-K)}{(1-K)}\\
Y &= \frac{(1-B-K)}{(1-K)}
\end{align}
```

なおこれは簡易的な変換式であり RGB→CMYKの変換は線形的ではないので、実際にはこの式では完全には再現できません．また出力する材料や光の加減などによっても色の見え方は変化します．よって一般的なイラストソフトやプリンターでは複雑な変換式を用いて色を再現しているそうです．

しかし今回私が製作してみたところ，ある程度の色の範囲ではこの変換式で対応できたので、数値の微調整は行なっているものの基本的にはこの変換式をそのまま利用しました．

色変換の部分は以下のようなコードになっています．

```c++:color_sensor.ino
void loop() {
  ...
  uint16_t clear, red, green, blue;
  tcs.setInterrupt(false);
  delay(60);
  
  //カラーセンサーデータの取得
  tcs.getRawData(&red, &green, &blue, &clear);
  tcs.setInterrupt(true);

  //色情報の正規化
  uint32_t sum = clear;
  float r, g, b;
  r = red; r /= sum;
  g = green; g /= sum;
  b = blue; b /= sum;
  r *= 256; g *= 256; b *= 256; 

  //CMYKへの変換，変換式のまま　（１００を掛けているのは％表示にするため）
  float c, m, y, k;
  k = 256 - r;
  if(k > 256 - g) k = 256 - g;
  if(k > 256 - b) k = 256 - b;

  c = (256 - k - r); c /= 256-k; c *= 100;
  m = (256 - k - g); m /= 256-k; m *= 100;
  y = (256 - k - b); y /= 256-k; y *= 100;
  k /= 256; k *= 100;
  ...
}
```

### 絵の具の出力
計算されたCMYK値をもとに指定した量の絵の具を注ぐ構造がこの製作の核となるポイントであり最も苦労した場所でもあります．液体を扱っているため注ぐ・止めるの動作は精度よく行う必要がありました．水を扱うようなシステムは世の中に多くありますが，ここではArduinoで動作できるような簡単なアクチュエータのみを使うこと，また私のポケットマネーで支払えるような比較的安価なものという制約がありました．

試行錯誤の結果，ゴムチューブの折れをモーターで制御する形をとりました．ゴムチューブは折り曲げると全く水を通さず，折り曲げを止めると再び水を流すという性質があったのでこれを利用しました．まず4つに仕切られたプラスチック容器にCMYKの絵の具をそれぞれを溶かした水を入れておきます．絵の具の入ったタンクからゴムチューブを通してパレットに絵の具を注ぎます．あらかじめゴムチューブを折り曲げて流れを止めておき，指定した時間だけチューブを伸ばすと流れ出る絵の具の量を調節できます．

ゴムチューブの折り曲げはサーボモータを用いて制御しました．サーボモータにゴムチューブをくくりつけておき，モータの回転角度によってチューブの曲げ伸ばしを制御しました．この結果かなり精度の良い絵の具量の調節を実現できました．モーターとゴムチューブの接続の様子は写真のようになっています．モータの角度が変わることでチューブが開き，絵の具が流れます．

ちなみにサーボモータは小型の[マイクロサーボＳＧ９２Ｒ](http://akizukidenshi.com/catalog/g/gM-08914/)，ゴムチューブは[こちら](http://akizukidenshi.com/catalog/g/gP-08635/)を使いました．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470270/e4b50b48-e98f-28bb-bd67-5bbddbcc2619.jpeg" width="480">

# 7.システム全体の流れ
まとめるとシステム全体としてはこのようになっています．
1. カラーセンサーでRGB値の色を読み取ってArduinoに送る
2. Processingがシリアル通信でArduinoから色情報を受け取り，HSB値に変換してディスプレイに出力する
3. ArduinoでRGB値をCMYK値に変換し，モータの角度と時間を指定する
4. モータの回転によってチューブの曲げ伸ばしが行われ絵の具が混ぜられる

[color_sensor.ino](https://github.com/shutakahama/Color_maker/blob/master/color_sensor.ino)ではシステムの状態として4つを定義しており，Arduinoボード上の物理ボタンを押すことで状態を指定できます．

- **State 0**：
 起動時の初期状態．

- **State 1**：
 ディスプレイ表示モード．processingとのシリアル通信を行う．このときLEDが赤色に点灯する。

- **State2**：
 全てのチューブの折り曲げを無くし絵の具を流れさせる状態．デバック用．このときLEDは緑色に点灯する．

- **State3**：
 色生成を始める状態．ボタンを押した時にセンサーが読み取った色を作る．4つのモータを順に指定時間だけ開通させる．この間LEDは青色に点灯する。4つのモーター操作が終わると State=0に戻る．

コードの以下の部分にStateの遷移とモータの動作が書かれています．

```c++:color_sensor.ino
void loop() {
  ...
  //ボタンを押すと指定したStateに移る
  if(buttonState1 == HIGH){
    State = 1;
    delay(500);
  }
  if(buttonState2 == HIGH){
    State = 2; 
    delay(500);
  }
  if(buttonState3 == HIGH){
    State = 3;
    delay(500);
  }

  //State1になっている間，Proessingとの通信を行う
  if(State == 1){
    analogWrite(redpin, 255);  // 赤色LEDの点灯
    if(Serial.available() > 0) {
      Serial.read();
      //RGB値の送信
      Serial.println(String((int)r) + "," + String((int)g) + "," + String((int)b));
    }
  }else{
    analogWrite(redpin, 0);
  }

  //State2の間，全てのモータが絵の具が流れる位置に回転する
  if(State == 2){
    analogWrite(greenpin, 255);  // 緑色LEDの点灯
    
    Servocyan.write(10);  //各モータの絵の具を流す角度は実際にやってみて調節
    Servomagenta.write(20);
    Servoyellow.write(170);
    Servoblack.write(150);
    delay(1000);
  }else{
    //State2以外のときは全て絵の具を流さない
    Servocyan.write(180);
    Servomagenta.write(180);
    Servoyellow.write(0);
    Servoblack.write(-10);
    analogWrite(greenpin, 0);
  }

  //State3のとき，各モータが指定した時間だけ絵の具を流す
  if(State == 3){
    analogWrite(bluepin, 255);  // 青色LEDの点灯
    
    Servocyan.write(40);  //絵の具を流す回転位置
    if(c != 0) delay(100);
    delay(c*40);  //cがシアンの値(量の調整のため40倍している)
    Servocyan.write(180);  //絵の具を流さない回転位置
    delay(1000);
    
    //他の色も同様
    Servomagenta.write(20);
    if(m !=0) delay(100);
    delay(m*40);
    Servomagenta.write(180);
    delay(1000);
    
    Servoyellow.write(170);
    if(y !=0) delay(100);
    delay(y*40);
    Servoyellow.write(0);
    delay(1000);
    
    Servoblack.write(150);
    if(k !=0) delay(100);
    delay(k*20);
    Servoblack.write(-10);
    State = 0;
  }else{
    //State3以外のときは全て絵の具を流さない
    Servocyan.write(180);
    Servomagenta.write(180);
    Servoyellow.write(0);
    Servoblack.write(-10);
    analogWrite(bluepin, 0);
  }
}
```

# 8.Arduinoボードの配線
Arduinoの配線は写真のようになっています．半田付けしたボードの写真が上，配線図が下です．分かりづらいですが

- 色を読み取るカラーセンサー
- 絵の具の量を制御するサーボモータ
- 状態Stateを指定するスイッチ
- 状態Stateを表現するLED

が接続されています．またモーターへの安定した電力を得るため外部電源(単3乾電池2本)を繋いでいます．![Screenshot from 2018-01-29 17-47-44のコピー.png]()

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470270/db5fc027-ebbd-3108-cd2d-7d218179454f.jpeg" width="480">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470270/abc8948c-7d71-c032-9932-2b644f81c711.png" width="480">

# 9.動かしてみた結果
色を生成した様子は冒頭の写真や動画で見ていただければと思うのですが，まとめると，

- 色を読み取る部分，ディスプレイに描く部分は本の内容を参考にしたこともありかなり性能よくできました．
- 色生成に関しては，ある程度の種類の色についてかなり再現度よく色を作ることができました．
- 再現が難しかった色は鮮やかな色（原色に近い赤など）や淡い色（肌色など）です．絵の具に水を混ぜすぎて薄くなりすぎると鮮やかな色が出にくくなるようです．
- 長時間使っていると水漏れ等が起こりメンテナンスが必要です．

# 10.感想
低予算の中でもこれだけの色の再現度の高いものが作れてよかったです．色を再現するだけなら市販のプリンタには及ばないのですが，色を混ぜる行程が目に見えることで視覚的にも面白いものになったのではないかと思います．また「どの色をどのくらい混ぜれば欲しい色が作れるか」がわかるので，絵を描くときの補助教材的な役割を果たせるかもしれません．

改善案ですが，もっと予算があればより正確な量の液体を安定的に注ぐ構造が作れる気がします．色の再現度を高めるためには市販のプリンタのように原色を増やす必要があるかもしれません．