用Arduino检测颜色

很多机器人竞技及爱好者制作过程中都需要能辨别颜色的传感器,比如赛道中的区域和信标的识别,又或者一些爱好者希望制作一款能够自动还原实物魔方(Rubik’s Cube)的机器人。
关于颜色的基本问题,笔者经验很多初学者还混淆“光的三原色及颜料的三原色”,这可能会影响颜色传感器使用的理解,下面先做一简单介绍。
我们看到叶子是绿色的,并不是因为叶子发出绿色的光,而是因为白色阳光中混合了各种颜色的光,除了绿色被反射以外,其它颜色的光都被吸收了。

color01

光的三原色(RGB) 颜料的三原色(CMYK)
将橙红和绿的色光混合,可得到黄色光;绿和蓝紫光混合可得到青绿色光;橙红和蓝紫混合可得到红紫色光。若将三原色光混合,则会变成白光。这些色光混合后,会得到比原来色光更明亮的色光,因此色光的混合,又称为“加色混合”。
色彩有减法,是由于物体表面上的颜料,吸收了日光中一部份的光波,反射日光其他的色光,当两种或多种颜料混合的时候,有更多的色光被吸收,越少的色光被反射,因而形成暗色或黑色。色彩的减片法是运用在颜料的混合,亦广泛地运用在印刷技术之中。同时色彩的减法又称为CMYK,CMYK分别代表三原色中彩蓝C (Cyan),洋红(Magenta),黄(Yellow),以及黑(Black)。黑色虽然不属于三原色的一种,但在印刷上,要加上黑颜料才能调出真正的黑色。
由上面的三原色感应原理可知,如果知道构成各种颜色的三原色的值,就能够知道所测试物体的颜色。对于 TCS3200来说,当选定一个颜色滤波器时,它只允许某种特定的原色通过,阻止其它原色的通过。例如:当选择红色滤波器时,入射光中只有红色可以通过,蓝色和绿色都被阻止,这样就可以得到红色光的光强;同理,选择其它的滤波器,就可以得到蓝色光和绿色光的光强。通过这三个值,就可以分析投射到 TCS3200 传感器上的光的颜色。仔细观察芯片的放大照片就可以看到。在透明塑料封装的芯片中央有一个硅片,其中的电极用纯金跳线和外部引脚相连。因为此芯片需要透光工作,不同于其它黑色塑料封装的芯片,恰好能够一窥集成电路IC芯片的内部结构。这是笔者故意设此一节讲解该芯片的原因之一。
我们还可以看到硅片中央有8×8=64个颜色不同的方块,这其实是64个小光敏二极管,不过上面都有滤镜,只能分别透过红(R)、绿(G)、蓝(B)三种颜色光中的一种,以检测这三种光线分量的强度。三种颜色光的光敏二极管等量均布在64块方砖中。

color02color03

Texas Advanced Optoelectronic Solutions(TAOS)公司是全球公认的光传感技术创新厂商,其所生产的TCS3200颜色识别芯片在机器人爱好者中得到广泛的应用。许多开源硬件模块提供商利用这块芯片制成颜色模块出售。这些模块大同小异,在使用前比较重要的还是了解此款芯片的基本工作原理。在AllDataSheet网站中中查找此款芯片的资料,可以了解到以下几点:

color04color05color06

该芯片有8个引脚,其中OE用于选择此芯片是否正常工作,因为其它引脚可以和其它传感器共用Arduino引脚,在OE为有效状态时,整个芯片才工作。无效状态时,其它引脚都处于高阻状态,相当于断开连接。OE上划一横线表面,OE是在低电平时有效,而非高电平。
VDD和GND是电源正负极,如欲知道此专业名词来历请查阅晶体管工艺相关书籍,由于DataSheet中指明工作电压范围2.7V~5.5V,所以和Arduino连接时直接接5V端口即可。
OUT是唯一一个输出端口,输出固定频率的方波来传达检测所得颜色信息。
S0、S1、S2、S3此四个都是输入的设置端口,具体功能下面两表介绍

color07
根据DataSheet可知,100%的频率输出的时候,典型的方波频率是600kHz,占空比50%,但是由于一些控制器无法检测频率过高的频率,所以可以通过设置S0、S1两个输入引脚的电平,按相应比例降低输出方波的频率,以便于低速的信号捕捉电路能够侦测统计相关信号,但传感器信号的刷新速度也会随之降低。由于Arduino的速度已经足够快较准的捕捉100%频率的TCS3200信号,故S0、S1均设置为高电平(H/HIGH)。

color08
S2和S3输入引脚依照一定的组合,可以分别获得红(R)、绿(G)、蓝(B)三种颜色光的强度,所以在使用颜色传感器的过程中,只要在较短的时间内逐次扫描分别获得三种色光的分量即可初步判断物体的大致颜色了
下面给出TCS3200各控制引脚与Arduino控制器的硬件连线与程序源代码。

color09

//测试颜色识别模块TCS3200
const int s0 = 8;
const int s1 = 9;
const int s2 = 12;
const int s3 = 11;
const int out = 10;
   
//定义LED端口
int pinRed = 2;
int pinGreen = 3;
int pinBlue = 4;
    
//RGB颜色色值
int red = 0;
int green = 0;
int blue = 0;
    
void setup()   
{  
  pinMode(s0, OUTPUT);  
  pinMode(s1, OUTPUT);  
  pinMode(s2, OUTPUT);  
  pinMode(s3, OUTPUT);  
  pinMode(out, INPUT);  
  pinMode(pinRed, OUTPUT);  
  pinMode(pinGreen, OUTPUT);  
  pinMode(pinBlue, OUTPUT);  
  Serial.begin(9600);  
  digitalWrite(s0, HIGH);  
  digitalWrite(s1, HIGH);  
}  
    
void loop() 
{  
  color();
  //输出RGB各色值
  Serial.print("Red:");  
  Serial.print(red, DEC);  
  Serial.print("Green:");  
  Serial.print(green, DEC);  
  Serial.print("Blue:");  
  Serial.print(blue, DEC);  
  Serial.println();  

  //检验结果是否红色 
  if (red < blue && red < green && red > 50)  
  {  
   Serial.println("Red");  
   digitalWrite(pinRed, HIGH); //点亮红色LED 
   digitalWrite(pinGreen, LOW);  
   digitalWrite(pinBlue, LOW);  
  }  

  //检验结果是否绿色
  else if (blue < red && blue < green)   
  {  
   Serial.println("Blue");  
   digitalWrite(pinRed, LOW);  
   digitalWrite(pinGreen, LOW);  
   digitalWrite(pinBlue, HIGH); //点亮绿色LED 
  }  

  //检验结果是否蓝色
  else if (green < red && green < blue)  
  {  
   Serial.println("Green");  
   digitalWrite(pinRed, LOW);  
   digitalWrite(pinGreen, HIGH); //点亮蓝色LED 
   digitalWrite(pinBlue, LOW);  
  }  
  Serial.println();  

  //延时两秒后关闭所有LED
  delay(2000);   
  digitalWrite(pinRed, LOW);  
  digitalWrite(pinGreen, LOW);  
  digitalWrite(pinBlue, LOW);  
 }  
    
void color()  
{  
  //设置好S2、S3端口,准备读取颜色值
  digitalWrite(s2, LOW);  
  digitalWrite(s3, LOW);  
  //红色光RED
  red = pulseIn(out, digitalRead(out) == HIGH ? LOW : HIGH);  
  digitalWrite(s3, HIGH);  
  //蓝色光BLUE  
  blue = pulseIn(out, digitalRead(out) == HIGH ? LOW : HIGH);  
  digitalWrite(s2, HIGH);  
  //绿色光GREEN  
  green = pulseIn(out, digitalRead(out) == HIGH ? LOW : HIGH);  
}