IRC2006?

理論的なこと(パターン認識の話)

色センサーから信号が得られ、それが数値化された後の話である。 2個から4個の数値があり、これをもとに3種類のボールの色のうちどれか識別する。 数値が2個ならば入力が2次元ベクトルである、という。

以下、図は2次元の場合で示す。

同じ色のボールでもセンサーの出力は常に同じになるわけではない。

fig1.PNG

2個のセンサー出力の数値をx_1,x_2とする。 いくつかのボールでデータを取ってプロットしていき、図1のように各ボールでの分布を調べる。だいたい丸で示した範囲に固まっていることがわかったと仮定する。 色識別はこのx_1-x_2平面内に境界線(3次元の場合は境界面、それ以上の場合は境界超平面)を引くことに他ならない。

図1を見て最も簡単に思いつく識別方法は、第1にx_2がt_2より大きければ桃色とし、第2にそうでなければx_1をt_1と比べる方法である。 この場合はこれでよい。

しかし3色が図2のように分布していた場合はどうであろうか。先の例のように水平か垂直に限った直線で切るのは無理である。

fig2.PNG

図2は、x_1,x_2がそれぞれ赤のフィルタと緑のフィルタをかけてとった光の強度と想定した図である。周囲の照明が明るいとx_1,x_2がどちらも増加するので斜め方向の広がりがある。(図は平行移動したように描いたが、実際は違うはずである。)

このような場合、斜めにも切れるようにする必要がある。 適当な一次直線を2本引き、それぞれについてどちら側かを識別すればよい。しかしこの方法では領域がくの字に曲がっているなど直線で切れない場合は困る。直線の本数を増やすか二次曲線を使う等の方法もあるが、大変であろう。(境界線を自動で決める方法もあるが割愛する。興味のある人はパターン認識関連の書籍を参照してほしい。)

最近傍法

一つの方法として最近傍法を紹介する。

fig3.PNG

まず準備として図3のように各色のボールから複数のサンプルを得ておく。 次に本番の識別で、今新たに入ってきたボールの色センサーの値が×印の点であったとする。

fig4.PNG

入力に最も近いサンプルを探し、そのサンプルの色を識別結果とする。 この場合、×の右の方にある緑色の点がそうである。 最も近いのが緑色ということで、今入ってきたボールも緑色と識別する。

なお、距離の基準はここでは直感的にユークリッド距離を用いたが、例えば青色のセンサーはあまりボールの色に関係ないというように各軸ごとに重みが違う。特定の軸(たとえばx_3)方向だけ0.4倍するなどして反映させることもできる。

プログラム例(風兎2005)

風兎2005の色識別部分を示す。 色センサーは携帯電話用カメラTrevaで、出力信号形式はYUVである。Yが輝度、U,Vが色差信号というようにはじめから明るさ情報と色合い情報が分かれている。そのためU,Vしか使っていない。

enum _ball_color {BALL_GREEN, BALL_YELLOW, BALL_PINK, BALL_OTHER};
static inline int squ(int i) {
	return i*i;
}
enum _ball_color classify(int y, int u, int v) {
	const struct {
		int y, u, v;
		enum _ball_color c;
	} data[] = {
		{47, 153, 102, BALL_PINK},
		{46, 148, 102, BALL_PINK},
		{42, 141, 113, BALL_PINK},
		{50, 135, 73 , BALL_YELLOW},
		{ 21, 125, 88, BALL_GREEN}, //データは全部で30個くらいある、中略
		{ 34, 120, 85, BALL_GREEN},
		{-1, 0, 0, BALL_OTHER}
	};
	int min_dist, min_class;
	int i;
	min_dist = 1000000;
	min_class = BALL_OTHER;
	for(i = 0; data[i].y >= 0; i++) {
		int dist = squ(y - data[i].y) * 0
			+ squ(u - data[i].u)
			+ squ(v - data[i].v);
		if(dist < min_dist) {
			min_dist = dist;
			min_class = data[i].c;
		}
	}
	return min_class;
}

以前知能ロボコンで利用した色識別用プログラムbyよっしー

以前、知能ロボコンで利用した、色識別用プログラムです。 去年の1回生向けに、ちょっとだけ修正した気もします。byよっしー

下手くそなプログラムですみません。

/*
TAOSのカラーセンサー用。
PA-3(TIOCB0)にICの出力をつなげた場合。
16ビットタイマのIOならどれでもできると思います。
すなわちPA-2から7の間。
他のポートにつなげた場合はレジスタ名やタイマの番号を
書き換える必要があります。
例えば、PA-4(TIOCA0)にするには
IOB -> IOA
GRB -> GRA
IMFB -> IMFA
ITU0 -> ITU1
STR0 -> STR1
と置き換えればいいはずです。試してません。
表にするとこうです。
     TIOCA TIOCB
ITU0 PA-2  PA-3
ITU1 PA-4  PA-5
IUT2 PA-6  PA-7
*/
//PA-6 ITU2 TIOCAに接続!
void color_itu_init() {
	//GRAをインプットキャプチャレジスタとして使用します。
	//インプットキャプチャすると同時にリセットすることで、
	//常にパルス周期が取り込まれます。
	ITU2.TCR.BIT.TPSC = 0x0; //内部クロックφでカウント(25MHz)
	ITU2.TIOR.BIT.IOA = 0x4; //PA6の立ち上がりエッジでGRAへカウンタを取り込む
	ITU2.TCR.BIT.CCLR = 0x1; //GRAのインプットキャプチャでクリア
	ITU.TSTR.BIT.STR2 = 1; //カウント開始 
}
/******************************************
return 
<color> 
0:green
1:yellow
2:red
3:No Ball
********************************************/
int color(void) {
#include "color_table.txt"
int n, table_number=0; 
int color[4];
unsigned int color_min[2] = {0,0};
unsigned int color_min_temp=0;
color_itu_init();
P6DDR = 0x03;
	for(n=0;n<1;n++) {
	P6DR.BIT.B0 = 1;
	P6DR.BIT.B1 = 1;
//	hprintf("0 0 ");
	//新たにインプットキャプチャが起こって値が更新されるのを待つ。
	ITU.TISRA.BIT.IMFA2 = 0; //フラグクリア [PA3のとき]
	while(ITU.TISRA.BIT.IMFA2 == 0);
	 //セットされるのを待つ [PA3のとき]
	color[0] = ITU2.GRA;
//	printf("%d //", color[0]); // [PA3のとき]
	P6DR.BIT.B0 = 0;
	P6DR.BIT.B1 = 1;
//      hprintf("0 1 ");
	//新たにインプットキャプチャが起こって値が更新されるのを待つ。
	ITU.TISRA.BIT.IMFA2 = 0; //フラグクリア [PA3のとき]
	while(ITU.TISRA.BIT.IMFA2 == 0); //セットされるのを待つ [PA3のとき]
	color[1] = ITU2.GRA;
//	printf("%d//", color[1]); // [PA3のとき]
	P6DR.BIT.B0 = 1;
	P6DR.BIT.B1 = 0;
//	hprintf("1 0 ");
	//新たにインプットキャプチャが起こって値が更新されるのを待つ。
	ITU.TISRA.BIT.IMFA2 = 0; //フラグクリア [PA3のとき]
	while(ITU.TISRA.BIT.IMFA2 == 0); //セットされるのを待つ [PA3のとき]
	color[2] = ITU2.GRA;
//	printf("%d//", color[2]); // [PA3のとき]
	P6DR.BIT.B0 = 1;
	P6DR.BIT.B1 = 1;
//	hprintf("1 1 ");
	//新たにインプットキャプチャが起こって値が更新されるのを待つ。
	ITU.TISRA.BIT.IMFA2 = 0; //フラグクリア [PA3のとき]
	while(ITU.TISRA.BIT.IMFA2 == 0); //セットされるのを待つ [PA3のとき]
	color[3] = ITU2.GRA;
//	printf("%d\n\r", color[3]); // [PA3のとき]
/*------------カラー認識処理----------*/
/*
00 red
01 blue
10 clear
11 green
*/
/*
int color_table[][5]={ {00,01,10,11,<color>},{..}}
<color> 
0:green
1:yellow
2:red
3:No Ball
*/
      if(color[0]<1000)
	{
		//printf("No Ball\n\r");
		return 3;
	}
for( table_number=0 ; color_table[table_number][0] > 0 ; table_number++)
{
	color_min_temp= (color[0]-color_table[table_number][0])*(color[0]-color_table[table_number][0]) 
	+  (color[1]-color_table[table_number][1])*(color[1]-color_table[table_number][1])
	+ (color[2]-color_table[table_number][2])*(color[2]-color_table[table_number][2])
	+ (color[3]-color_table[table_number][3])*(color[3]-color_table[table_number][3]) ;
/*
printf("tabel_number=%d\n\r",table_number);
printf("color_min_temp=%d \n\r",(float)color_min_temp);
printf("color_min[0]=%d \n\r",(float)color_min[0]);
printf("color_min[1]=%d \n\r\n\r",color_min[1]);
*/
	if(color_min[0] >= color_min_temp || table_number == 0)
	{
		color_min[0]=color_min_temp;
		color_min[1]=table_number;
	}
}
	//printf("number.%d\n\r",color_min[1]);
	log_printf("color is %d\n\r", color_table[color_min[1]][4]);
	return color_table[color_min[1]][4];
	}
	return -1;
}
/**********************************************
***********************************************/
/******************************************
return 
<color> 
0:green
1:yellow
2:red
 3:No Ball
4:????
********************************************/
/*カラー認識を10回やり、(ボールなしも含めて)7回以上同じ色ならその色と判定
もし、バラバラなら、3回までやり直し
3回以内に、7回以上同じ色のものがなければ、4を返す
*/
int color_check (void)
{
	int color_count[3];
	int color_temp[4];
//	int color_flag;
	for(color_count[0]=0 ; color_count[0]<3 ; color_count[0]++)
	{
		color_temp[0] = 0;
		color_temp[1] = 0;
		color_temp[2] = 0;
		color_temp[3] = 0;
		for(color_count[1]=0 ; color_count[1]<10 ; color_count[1]++)
		{
				color_temp[ color() ]++;
		}
		for(color_count[2]=0 ; color_count[2]<=3 ; color_count[2]++)
		{
			if (color_temp[color_count[2]] >= 7)
			{
				return color_count[2] ;
			}
		}
	}
	return 4;
}

仕様 メインプログラム側からは color_check (void) を読み出してください。 番号が返ってきます。

あと、color_table.txtを用意してください。 これは、

int color_table[][5]={ {15704,24282,24485,25359,2} ,
		{23294,38387,32762,22766,2},
		{26336,38554,38212,26916,2},
		{11972,18959,21747,12027,1},
		{23324,48764,35329,23526,1},
		{14815,28376,25277,14803,1},
		{40590,37393,60751,40399,0},
		{3489,15696,30868,4629,0},
		{51311,13995,9020,52241,0},
		{52546,17461,11734,52711,0},
		{23710,51516,36395,23690,1},
		{20524,45294,31934,20527,1},
		{26802,45861,37414,26789,2},
		{15875,24005,24114,15564,2},
		{-1,-1,-1,-1,-1}
		};

のような仕様の物です。
このテーブルを作成するプログラムも書いたんだけど、どっか行ってしまいました。
各行最後の数字が色を表します。(この数字がcolor_check (void) で返ってきます)