背景
在乐高ev3 颜色传感器的原理这篇文章中,我们分析了颜色传感器的硬件设计,获取颜色RGB反射量的方法,这篇文章,我们将在此基础上分析如何通过简单的计算获知被测物体的表面颜色。
RGB到HSV的转换
上文我们提到,乐高的颜色传感器实际上就是在检测反射光或者环境光的强度,其分辨不同颜色的关键在于主动分时发出三种不同颜色的光,再判断被测表面对三种颜色的光线反射强度,从而判断被测物表面的颜色。三种颜色分别为红色,绿色和蓝色,基本的原理是,物体本身是什么颜色,就反射什么颜色的光。例如,红色物体,反射红色最多,绿色和蓝色大部分被吸收。因此我们获得的三种颜色反射强度值,其实就可以认为是物体表面的RGB颜色。
那么,如何根据RGB三个值来计算识别物体具体对应什么颜色呢,这里比较好的办法是将RGB色彩空间转到HSV色彩空间,参考由RGB到HSV的转换详解。我们只需要计算色调H的值,根据H的大小就可以判断物体颜色了。
色调H用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°。其计算方法在参考文献中写的很清楚,就不再赘述。
代码实现
具体到乐高的颜色识别模块,其代码如下(注意,这里是根据乐高颜色传感器的原理推测的算法,并非官方算法和代码):
#define MIN_THR 100
#define BLACK_THR 500
#define WHITE_THR 1500
#define MAX_DELTA 200
int color = COLOR_NON;
//首先对RGB三色值大小进行排序
Sort(colorAmp,sorted_index);
//先处理比较特殊的,没有被测对象、黑色、白色
//首先判断是否没有被测对象,RGB三个值都小于设定阈值MIN_THR,则表示没有被测对象(或被测物体距离太远)
if(colorAmp[sorted_index[MAX]] <= MIN_THR) {
return COLOR_NON;
}
//否则 三个值至少有一个大于MIN_THR
else {
//如果最小值小于等于黑色阈值
if(colorAmp[sorted_index[MIN]]<=BLACK_THR) {
//并且最大值最小值相差不大 判断为黑色
if(colorAmp[sorted_index[MAX]]- colorAmp[sorted_index[MIN]] < MAX_DELTA)
return COLOR_BLACK;
}
//否则,最小值大于黑色阈值 并且最大值不超过最小值的1.2倍
else if((u32)colorAmp[sorted_index[MAX]]*10 < (u32)colorAmp[sorted_index[MIN]] * 12) {
//RGB最小值小于白色阈值 可能是白色物体但是距离太远,还是判断为没有对象
if(colorAmp[sorted_index[MIN]]<=WHITE_THR) {
return COLOR_NON;
}
//RGB最小值大于白色阈值,并且最大值不超过最小值的1.2倍 判断为白色
else {
return COLOR_WHITE;
}
}
//走到这排除了黑色 白色 以及没有被测物/被测物过远,继续判断其它颜色,采用hsv变换
//三个值至少有一个大于黑色阈值,并且至少有一个数小于白色阈值
if(colorAmp[sorted_index[MAX]]> BLACK_THR && colorAmp[sorted_index[MIN]]<= WHITE_THR
&& colorAmp[sorted_index[MAX]] != colorAmp[sorted_index[MIN]]) {
//计算delta
delta = colorAmp[sorted_index[MAX]] - colorAmp[sorted_index[MIN]];
//计算hsv空间色调H
if(sorted_index[MAX] == LED_RED)
H = ((int)colorAmp[LED_GREEN]-(int)colorAmp[LED_BLUE])*60/delta;
else if(sorted_index[MAX] == LED_GREEN)
H = 120 + ((int)colorAmp[LED_BLUE]-(int)colorAmp[LED_RED])*60/delta;
else
H = 240 + ((int)colorAmp[LED_RED]-(int)colorAmp[LED_GREEN])*60/delta;
if(H<0)
H += 360;
//根据色调H判断颜色
switch(H){
case 0 ... 4:
case 355 ... 360:
color = COLOR_RED;
break;
case 6 ... 27:
color = COLOR_ORANGE;
break;
case 34 ... 64:
color = COLOR_YELLOW;
break;
case 83 ... 155:
color = COLOR_GREEN;
break;
case 200 ... 244:
color = COLOR_BLUE;
break;
case 245 ... 314:
color = COLOR_VIOLET;
break;
default:
color = COLOR_NON;
break;
}
return color;
}
}
总的来说,需要先排除黑色、白色和物体距离过远导致反射值很低的情况,这其实就是代表RGB三个值大小接近的情况,这种情况下不适合用HSV空间判断。如果RGB三值相近且都很大,则对应白色;三值相近但是都很小,则对应反射值太低,被测对象太远;三值相近且不算太小,则对应黑色。
排除以上情况后,就可以计算HSV空间的色调H了,由于H值范围已经固定在0-360度,因此算法判断适应性更强。