工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

ArduinoとRGBセンサーで作る高度な色選別マシン

必要なパーツと用品

パーツ数量
Arduino Nano R31
TCS34725 RGBセンサー1
LM2596 降圧コンバーター1
MG-90S サーボモーター2
MR105ZZ ベアリング (5×10×4mm)3
608RS/608ZZ ベアリング (8×22×7mm)1
6003RS ベアリング (17×35×10mm)1
M3×15mm 皿ネジ1
M2×10 タッピングネジ20
RGB LED (共通アノード)1
220Ω 抵抗3
配線用ワイヤー1式
2.1×5.5mm DCジャック1
熱収縮チューブ1式
デュポンコネクター1式
9Vまたは12V電源 (1A以上)1
試験管 22mm外径×100mm (Danmu Art製)5本
スキットルズ (お菓子)1袋

必要な工具と機械

プロジェクト概要

このプロジェクトが生まれたきっかけは、実は偶然でした。約3年半前、もっと複雑な色選別マシンの設計を始めたものの、多くのプロジェクト同様、未完成のまま窓辺に放置されていました(誰も共感できないですよね、笑)。

数ヶ月前、この古いプロジェクトを再開することにしました。最初に解決すべきだったのは、貯蔵タンクからスキットルズを取り出し、確実に色を判別しながら、システムをできるだけコンパクトにする方法です(前回はこれに失敗しました)。「シンプルな色選別マシンを作って、供給システムの設計を正しくしよう」と思い立ったのが運の尽き。週末2回で終わると甘く考えていましたが、大間違いでした。

ArduinoとRGBセンサーで作る高度な色選別マシン
試行錯誤の末に完成したマシン

2つの全く異なる設計と多数のプロトタイプを経て、ようやく許容できるレベルで動作するものができました(それでも稀にピックアップの問題が発生します)。完璧ではありませんが、結果には満足しています。

ArduinoとRGBセンサーで作る高度な色選別マシン

動作原理

マシンは大きく2つのステージで構成されています。

第1ステージ:供給と色判別

上部には未選別のスキットルズを入れる貯蔵タンクがあります。その底部にある供給機構がスキットルズを1粒ずつ取り出し、カラーセンサーの前で位置決めして色を判別します。貯蔵タンク内の内容物を振動させるアームにより、スキットルズが詰まることなくスムーズに供給口へ流れるよう設計されています(それでも稀に詰まることはあります)。

ArduinoとRGBセンサーで作る高度な色選別マシン
振動アーム付き供給機構

第2ステージ:試験管への振り分け

色が判別されると、第2ステージが作動します。試験管ホルダーが正しい試験管を供給機構の前に移動させ、スキットルズを落とします。このサイクルを貯蔵タンクが空になるまで繰り返します。

自作するには?

詳細な製作ガイドと3Dプリント用ファイルは以下のサイトで公開しています:

https://www.littlefrenchkev.com/colour-sorting-machine

ArduinoとRGBセンサーで作る高度な色選別マシン

組み立て手順

3Dプリントしたパーツをすべて出力すると、以下のような部品が揃います。

ArduinoとRGBセンサーで作る高度な色選別マシン
フロントシェルは見た目が異なる2バージョンありますが、機能に差はありません
ArduinoとRGBセンサーで作る高度な色選別マシン
ベアリング、ネジ、配線などの追加部品

組み立て動画も用意しています(スローモーションの3Dプリンター映像でイントロを作ってみました)。

初期設定

電源を入れると、まず両サーボが初期位置に移動します。初回起動時は、スキットルズを回収するギア/シャフトが最上部にあり、試験管ホルダーが供給機構と一直線になるよう調整してください。

ArduinoとRGBセンサーで作る高度な色選別マシン

トラブルシューティング

LEDが赤点滅して起動シーケンスが始まらない

初期位置移動後、起動シーケンスに入らずLEDが赤点滅する場合、RGBセンサーが検出されていません。配線の問題か、センサー自体の故障です。

試験管が正しく位置合わせされない

原因1:サーボの精度不足またはプリント寸法のばらつき
起動シーケンスで試験管ホルダーが5色分移動しますが、位置がズレることがあります。これはソフトウェア側で colour_angle 変数配列を調整することで補正可能です。各値が各色のサーボ位置に対応しています。

原因2:ギアのバックラッシュ(遊び)
プリント公差によりギアに遊びが生じ、慣性でオーバーシュートすることがあります。これも forward_anti_backlash と backward_anti_backlash 変数で補正できます。回転方向に応じて角度を加減算します。

色が混ざって判別される

ArduinoとRGBセンサーで作る高度な色選別マシン

colour_limit_values 配列を調整してください。各配列は赤・緑・青の最小・最大値を表します。調整方法:1色だけを機械に入れ、PCに接続してシリアルモニターを開きます。スキットルズが判別されるたびにRGB値と判定色が表示されるので、それを参考に閾値を設定します。

こだわりの機能たち

RGB LEDによる視覚フィードバック
ベース部分にRGB LEDを搭載。選別中は判別されたスキットルズの色に合わせて光り、貯蔵タンクが空になると全色を順繰りに表示します。起動時にセンサー異常があれば赤点滅で知らせます。

バックラッシュ補正機能
試験管ホルダーはスパーギアで駆動しますが、プリント公差で完全に遊びをなくすのは困難です。コード側で回転方向に応じて早めに停止することで、オーバーシュートを補正しています。

LEDスムーズ遷移付き遅延関数
set_led(ms, color) 関数は delay(ms) と同様に待機しますが、その間にLEDを滑らかに次の色へ変化させます。試験管ホルダーの回転中に色を変えるのに最適です。

補足ノート

ArduinoとRGBセンサーで作る高度な色選別マシン

ソースコード

LFK_sorting_machine (C/C++)

Arduino IDE用の完全なスケッチです。TCS34725ライブラリとServoライブラリが必要です。

#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include <Servo.h>

// RGBセンサー設定
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_24MS, TCS34725_GAIN_1X);
int colour = 0; // 0=黒/なし, 1=黄, 2=緑, 3=橙, 4=赤, 5=紫
int previous_colour = colour;
int black_count = 0;
int start_measurment_angle = 88;
int measurment_sweep = 2;
int measurment_angle_increment = 4;

// 色判定閾値 [Rmin, Rmax, Gmin, Gmax, Bmin, Bmax]
uint16_t black_limit_values[]   = {0, 200, 0, 200, 0, 200};
uint16_t yellow_limit_values[]  = {850, 2200, 800, 2200, 400, 1100};
uint16_t green_limit_values[]   = {400, 800, 700, 1400, 300, 700};
uint16_t orange_limit_values[]  = {650, 1800, 300, 850, 210, 600};
uint16_t red_limit_values[]     = {400, 950, 150, 450, 150, 400};
uint16_t purple_limit_values[]  = {150, 400, 150, 450, 150, 500};

// RGB LEDピン (共通アノードなのでLOWで点灯)
byte R_pin = 5, G_pin = 6, B_pin = 11;
int current_red_value = 255, current_green_value = 255, current_blue_value = 255;
const int orange[] = {0, 230, 255};
const int red[]    = {0, 255, 255};
const int green[]  = {255, 0, 255};
const int yellow[] = {0, 170, 255};
const int purple[] = {0, 255, 40};

// サーボ設定
int release_delay = 220;
const int colour_angles[] = {10, 46, 82, 118, 154};
const int backward_anti_backlash = 2;
const int forward_anti_backlash = 2;
Servo feeder_servo;
Servo holder_servo;

void setup() {
  Serial.begin(9600);
  pinMode(R_pin, OUTPUT); pinMode(G_pin, OUTPUT); pinMode(B_pin, OUTPUT);
  analogWrite(R_pin, 255); analogWrite(G_pin, 255); analogWrite(B_pin, 255);
  feeder_servo.attach(3);
  holder_servo.attach(2);
  feeder_servo.write(0);
  holder_servo.write(colour_angles[0]);
  
  if (tcs.begin()) {
    Serial.println("Found sensor");
    starting_sequence();
  } else {
    Serial.println("No TCS34725 found ... check your connections");
    while (1) { set_led(300, red); set_led(300, black); } // 赤高速点滅
  }
}

void loop() {
  if (black_count < 10) {
    feeder_servo.write(0);
    delay(450);
    previous_colour = colour;
    get_colour();
    if (colour == 0) { black_count++; shake(); }
    else if (colour == 6) { black_count++; }
    else { move_holder(); release_skittle(); black_count = 0; }
  } else { end_loop(1000); }
}

void get_colour() {
  uint16_t r, g, b, c;
  uint16_t total_r = 0, total_g = 0, total_b = 0;
  feeder_servo.write(start_measurment_angle);
  delay(200);
  for (int i = 0; i <= measurment_sweep; i++) {
    tcs.getRawData(&r, &g, &b, &c);
    feeder_servo.write(start_measurment_angle + i * measurment_angle_increment);
    total_r += r; total_g += g; total_b += b;
    delay(15);
  }
  total_r /= measurment_sweep;
  total_g /= measurment_sweep;
  total_b /= measurment_sweep;
  Serial.print(total_r); Serial.print(" "); Serial.print(total_g); Serial.print(" "); Serial.println(total_b);

  // 色判定ロジック(閾値との比較)
  if (total_r < black_limit_values[1] && total_g < black_limit_values[3] && total_b < black_limit_values[5]) { colour = 0; Serial.println("black"); }
  else if (total_r >= yellow_limit_values[0] && total_r < yellow_limit_values[1] && total_g >= yellow_limit_values[2] && total_g < yellow_limit_values[3] && total_b >= yellow_limit_values[4] && total_b < yellow_limit_values[5]) { colour = 1; Serial.println("yellow"); }
  else if (total_r >= green_limit_values[0] && total_r < green_limit_values[1] && total_g >= green_limit_values[2] && total_g < green_limit_values[3] && total_b >= green_limit_values[4] && total_b < green_limit_values[5]) { colour = 2; Serial.println("green"); }
  else if (total_r >= orange_limit_values[0] && total_r < orange_limit_values[1] && total_g >= orange_limit_values[2] && total_g < orange_limit_values[3] && total_b >= orange_limit_values[4] && total_b < orange_limit_values[5]) { colour = 3; Serial.println("orange"); }
  else if (total_r >= red_limit_values[0] && total_r < red_limit_values[1] && total_g >= red_limit_values[2] && total_g < red_limit_values[3] && total_b >= red_limit_values[4] && total_b < red_limit_values[5]) { colour = 4; Serial.println("red"); }
  else if (total_r >= purple_limit_values[0] && total_r < purple_limit_values[1] && total_g >= purple_limit_values[2] && total_g < purple_limit_values[3] && total_b >= purple_limit_values[4] && total_b < purple_limit_values[5]) { colour = 5; Serial.println("purple"); }
  else { colour = 6; Serial.println("unknown"); }
}

void move_holder() {
  int new_holder_position = colour_angles[colour - 1];
  int holder_delay = abs(previous_colour - colour) * 70;
  if (holder_delay < 0) holder_delay = 0;
  if (previous_colour > colour) {
    holder_servo.write(new_holder_position - backward_anti_backlash);
  } else {
    holder_servo.write(new_holder_position + forward_anti_backlash);
    holder_servo.write(new_holder_position);
  }
  // LED色変更と同期
  if (colour == 1) set_led(holder_delay, yellow);
  else if (colour == 2) set_led(holder_delay, green);
  else if (colour == 3) set_led(holder_delay, orange);
  else if (colour == 4) set_led(holder_delay, red);
  else if (colour == 5) set_led(holder_delay, purple);
}

void release_skittle() { feeder_servo.write(180); delay(release_delay); }

void shake() {
  feeder_servo.write(180); delay(release_delay);
  feeder_servo.write(120); delay(80);
  for (int i = 0; i <= 5; i++) {
    feeder_servo.write(90); delay(80);
    feeder_servo.write(180); delay(80);
  }
}

void starting_sequence() {
  for (int c = 1; c <= 5; c++) { colour = c; move_holder(); set_led(300, (c==1?yellow:c==2?green:c==3?orange:c==4?red:purple)); delay(400); }
  previous_colour = 5; colour = 3; move_holder(); end_loop(200);
}

void end_loop(int duration) {
  set_led(duration, orange); set_led(duration, red); set_led(duration, green); set_led(duration, yellow); set_led(duration, purple);
}

// delay()の代わりにLEDを滑らかに変化させながら待機
void set_led(int duration, int color[3]) {
  int start = millis();
  while (millis() - start < duration) {
    int elapsed = millis() - start;
    analogWrite(R_pin, map(elapsed, 0, duration, current_red_value, color[0]));
    analogWrite(G_pin, map(elapsed, 0, duration, current_green_value, color[1]));
    analogWrite(B_pin, map(elapsed, 0, duration, current_blue_value, color[2]));
  }
  current_red_value = color[0]; current_green_value = color[1]; current_blue_value = color[2];
}

回路図

配線はシンプルです。バックコンバーターの出力が5Vになっていることを確認してから接続してください!

ArduinoとRGBセンサーで作る高度な色選別マシン
電源・センサー・サーボ・LEDの接続図

製造プロセス

  1. EEG装置:設計、製造、新たな応用展開
  2. 自動販売機:366億ドル規模の米国産業パワーハウス
  3. 投票機:歴史、技術、標準
  4. ミシン:19世紀革新から現代自動化までの進化と製造
  5. テレビ:機械式起源からスマート未来へ
  6. スピンドルベアリング:精度と品質保証の要
  7. 旋盤の主要部品と機能:主軸台・心押台・送り機構・チャック等を解説
  8. 旋盤の基礎知識:歴史・原理・種類・部品・安全対策・仕様完全ガイド
  9. フライス盤の主要部品と機能:ベースからラムまで完全解説
  10. 機械とは何か?包括的ガイド
  11. JW Machine:精密CNC受託製造サービス