解魔方的机器人攻略23 – 识别颜色(上)
由 动力老男孩 发表于 2010/04/05 20:39:40今天看到架子上的萝卜头,已经落了很多灰尘。想起萝卜头的攻略还剩几篇迟迟没有写完。前一段时间一直在试验小爱的手机遥控器功能,从今天开始准备陆续把萝卜头的攻略补完,给博客也打扫打扫灰尘。
说起来真是很惭愧,颜色识别在萝卜头制作过程中是花费时间最多的部分。其中还有一段小插曲:
我在淘宝上买的颜色传感器,在NXT上测试时,发现只有用强光照射在魔方表面的时候,传感器才有读数。那时候在网上很难找到相关的资料,不知道是我买了次品,还是设置不当。后来我猜想传感器中心的那个透明小灯泡是光源,就擅自去电子市场买了一个LED小灯,然后把这个500块钱的传感器敲开换上。一通电,嘿,灯居然亮了,然后我就把拆下来的小灯扔到垃圾桶继续测试。结果…..这次传感器彻底废了。接下来是从垃圾桶里翻那个透明的小灯泡,非常悲剧的是那天正好吃了虾和鱼,我把整垃圾桶的虾皮鱼骨摸了两遍,才找到那个透明的小灯泡,把它洗洗干净又换上了。后来才知道,这个灯泡其实是用来读取颜色的,而不是照明的。而我买的那个颜色传感器确实是个次品,必须用灯光照射才能勉强读数。所以你们看到第一版的萝卜头,在悬臂上是带有一个照明灯的。
在此提醒一下朋友们:颜色传感器在普通的光照环境下,应该是有读数的,而且很敏感,读数会不断小幅跳动。如果你买的传感器读数一直是0或者跳动非常大,那么请尽快找奸商退换。另外,在这里感谢一下北京西觅亚公司,他们给我提供了几个测试用的颜色传感器,并且给我换了一个新的。我也因此了解了一些颜色传感器的特性。
好了,进入正题。上一篇介绍了如何在电脑和NXT之间使用蓝牙进行通讯。有了蓝牙,我们就可以把颜色传感器的读数发送给电脑,然后用电脑识别颜色后调用解魔方的算法。
1,从NXT发送颜色数据到电脑
在之前的一篇博客里,我介绍了三个函数:ReadAllSide,ReadOneSide和SendColorToPC。现在蓝牙已经调通,可以改写SendColorToPC函数用来发送数据。其中getRed()和getRawRed()等函数的说明,请参考颜色传感器的API文档
//Send colors to PC public static void SendColorToPC(int center, int n) throws Exception { //get the x,y of n int y = n % 3; int x = (n - y) / 3; //send to PC by bluetooth byte[] data = new byte[9]; data[0] = (byte)center; //center表示是魔方的某一面 data[1] = (byte)x; //x 表示魔方这一面3*3的色块中,第x行的色块 data[2] = (byte)y; //y 表示魔方这一面3*3的色块中,第y列的色块 data[3] = (byte)color.getRed(); data[4] = (byte)color.getGreen(); data[5] = (byte)color.getBlue(); data[6] = (byte)(color.getRawRed() / 3); data[7] = (byte)(color.getRawGreen() / 3); data[8] = (byte)(color.getRawBlue() / 3); BlueTooth.WriteBytes(data); }
2,在PC端接受颜色数据
PC程序中的BlueToothDataReceived函数,用来响应接受到蓝牙数据的事件。我们加上下面这段函数:
else if (length == 9) { int i = data[0]; int j = data[1]; int k = data[2]; int r = data[3]; int g = data[4]; int b = data[5]; int rawR = data[6]; int rawG = data[7]; int rawB = data[8]; ColorItem newItem = new ColorItem(i, j, k, r, g, b, rawR, rawG, rawB); colorDistinguish.ColorItems.Add(newItem); DisplayMessage += newItem.ToString() + "\r\n"; Status = "成功获取数据:" + i + "," + j + "," + k; }
其中用到了两个类 ColorItem 和 ColorItemDistinguish。这两个类的作用后面再说,总之这里把所有的颜色数据都先保存到一个阵列(Array)里,最后统一识别颜色。
3,解析颜色的方案
细心的朋友可能在API中看到了getColor()函数,我们何必要全部保存颜色后再统一分辨呢,直接读一个分辨一个不是更好?事实证明这个函数基本没什么用,红色和橙色都会解析成红色,而且环境光线变化时影响很大。还有一些朋友建议用HSV颜色模型,这种方案我也试过了,基本上也很难分辨。为什么呢?请看下面一组读数:
Red [0,1,2]=>RGB=(23,0,0),RawRGB={45,1,8} [0,2,2]=>RGB=(30,0,0),RawRGB={60,1,5} [0,2,1]=>RGB=(25,0,0),RawRGB={49,3,12} [0,2,0]=>RGB=(32,0,0),RawRGB={63,2,6} [0,1,0]=>RGB=(22,0,0),RawRGB={43,2,11} [0,0,0]=>RGB=(25,0,0),RawRGB={59,3,3} [0,0,1]=>RGB=(30,0,0),RawRGB={58,5,17} [0,0,2]=>RGB=(31,0,0),RawRGB={61,8,17} [0,1,1]=>RGB=(31,0,0),RawRGB={62,15,22} Orange [2,1,2]=>RGB=(28,0,0),RawRGB={55,12,8} [2,2,1]=>RGB=(30,0,0),RawRGB={57,14,14} [2,0,1]=>RGB=(32,0,0),RawRGB={62,15,13} [2,1,0]=>RGB=(32,0,0),RawRGB={63,16,12} [2,2,2]=>RGB=(42,0,0),RawRGB={83,24,10} [2,2,0]=>RGB=(41,0,0),RawRGB={82,24,13} [2,0,0]=>RGB=(41,0,0),RawRGB={80,23,10} [2,0,2]=>RGB=(39,0,0),RawRGB={76,22,13} [2,1,1]=>RGB=(41,5,0),RawRGB={81,30,21}
这是在自然光条件下,对红色和橙色的9个色块分别读数的结果。可以看到,它们的Green和Blue分量全部是0,只有红色分量有差别。但是红色的red分量从23~32,橙色的red分量从28~42,它们中间是有重叠的。对于这些读数,HSV完全没用。
有一段时期我几乎已经绝望了,不过终于在最后让我找到了一点区别:红色的RawBlue分量基本上比RawGreen分量稍大,而橙色恰好相反。另外请对比一下[0,0,0]和[2,2,1],它们的RawBlue分量和RawGreen分量是相同的,但是仍然可以找到区别:按公式R+2*RawG-2*RawB计算,橙色的永远比红色大!
也就是说,我们单独取到一组颜色数值时,很难直接知道它是什么颜色,只有对一组数进行排序后,才能区分出不同的颜色。就像刚才这18个数,我们按照R+2*RawG-2*RawB从大到小排序,最终结果的前9个是橙色,后9个就是红色。类似的,我们还可以定义出分辨颜色的判断规则:
1,假设RGB三个值的最小值为Min,按Min从大到小排序,前9个是白色
2,剩下的颜色,按照G分量从大到小排序,前9个是黄色(有意思吧,绿色分量最大的是黄色)
3,剩下的颜色,按照B分量从大到小排序,前9个是蓝色(这个还算靠谱)
4,剩下的颜色,按照R分量从小到大排序,前9个是绿色
5,剩下的颜色,按照R+2*RawG-2*RawB从大到小排序,前9个是橙色
6,剩下的颜色全是红色
当光线从弱到强变化时,这些值基本会成比例的变大,所以这些规则依然有效。
有兴趣的朋友可以查看一组完整的颜色读数,来验证以上这些规则:http://www.diy-robots.com/rubiksolver/readcolors.txt
下一篇继续介绍这种分辨方式的具体代码实现。
没想到颜色识别这么麻烦, 是因为传感器不好导致的么
嗨,老男孩,盒子博客的地址已经更改,请将链接名改为:老四,链接为http://bulog.org
搞定
工作开小差啊你
color sensor这东西太不好买了。。。
^&%#^%@^@%$#
我在用USB摄像头做,读数更夸张… = =
摄像头噪点很多,我估计需要把一个范围内的多点做一下平均
各部分零件有没有卖
我只知道LEGO官网上所有零件都是可以单买的
楼主,好久不见!
我是那杯纠结的橙C,今天又看了一遍你的视频,又在想你的颜色识别算法其实是一个对54个小块的排序算法,那么如果在读六个面的时候外界光强发生变化,比如第三个面,会不会导致这个面上的9个小块在54块的排序中会相对排序更靠前(比如读第三个面的时候外界光线很强,会不会第三个面上的黄色被识别为白色)?
应该会的,我用Hue+Brightness来组合判断,会好一点
—第1点:—
Color Value=7586023
Hue=39.82759,Brightness=91
ColorValue=7586023
Hue=39.82759,Brightness=91
R=231,G=192,B=115
Value=385
sRGB=1,3,0
橙=4
—第2点:—
Color Value=9539616
Hue=180.531,Brightness=57
ColorValue=9539616
Hue=180.531,Brightness=57
R=32,G=144,B=145
Value=30
sRGB=0,0,20
绿=3
—第3点:—
Color Value=5672390
Hue=29.46429,Brightness=78
ColorValue=5672390
Hue=29.46429,Brightness=78
R=198,G=141,B=86
Value=308
sRGB=1,4,0
橙=4
—第4点:—
Color Value=15497277
Hue=219.7714,Brightness=93
ColorValue=15497277
Hue=219.7714,Brightness=93
R=61,G=120,B=236
Value=-171
sRGB=1,0,8
蓝=1
—第5点:—
Color Value=7035326
Hue=349.3069,Brightness=75
ColorValue=7035326
Hue=349.3069,Brightness=75
R=190,G=89,B=107
Value=154
sRGB=3,1,0
红=2
—第6点:—
Color Value=16777200
Hue=180,Brightness=100
ColorValue=16777200
Hue=180,Brightness=100
R=240,G=255,B=255
Value=240
sRGB=1,1,1
白=0
—第7点:—
Color Value=10157780
Hue=85.2,Brightness=100
ColorValue=10157780
Hue=85.2,Brightness=100
R=212,G=254,B=154
Value=412
sRGB=1,2,1
黄=5
—第8点:—
Color Value=8177131
Hue=39.45946,Brightness=92
ColorValue=8177131
Hue=39.45946,Brightness=92
R=235,G=197,B=124
Value=381
sRGB=1,3,0
橙=4
—第9点:—
Color Value=6575275
Hue=348.9655,Brightness=67
ColorValue=6575275
Hue=348.9655,Brightness=67
R=171,G=84,B=100
Value=139
sRGB=2,1,0
红=2
color.getRed()与 color.getRawRed() 了两个函数有啥区别啊?
color.getRawRed()/3,除以3理解不了?