SKY Crew Lab

RoboCup Junior(ロボカップジュニア)参加チーム、SKY Crew(スカイクルー)の技術ブログ。
技術の共有・伝達を目的に、ロボットに関するいろいろな情報やガイドを載せていきます。

2017年大会でRescue Line(レスキューライン) 世界3位、2018年大会でSoccer Lightweight(サッカーライトウェイト) 世界総合5位を獲りました!

2018/09

こんにちは。ソフト担当のSYです。

今回は、以前紹介したエンジェルリングの解説のつづきを話していきたいと思います。

前回のおさらい

エンジェルリングとは
・・・ ラインセンサを隙間なく円形に並べたもの

ラインの位置が正確に分かる!
→→→ 単純なアルゴリズムと動き間違えることなくライン内に戻れる!!

ラインの位置の変化が分かる!
→→→ 外のボールを取りに行くことができる!!

どんなアルゴリズム?

ここでは、実際にどのようなプログラムを書いたかを簡単に説明していきたいと思います。

下記のリンクは世界大会で使ったラインセンサ処理とほぼ同じプログラムの該当部分です。
ラインセンサ処理のソースコード

1.明るさを読み取る

それぞれのラインセンサの値を読み取り、値に応じて白いライン上・緑のコート上・暗い空中のどれなのかを判別します。 具体的には、ライン上(明るい)かどうかとロボットが持ち上げられている(暗い)かどうかを入れたラインセンサの個数分の配列をつくっています。

また、何個が明るく何個が暗いかも計算しています。

2.審判の持ち上げに対応

暗いと判断したセンサが一定個数以上あればロボットが持ち上げられていると判断し、何回か連続で持ち上げられている場合はコート内に戻ったと判断します。 これは押し出しでコート内に戻されたときに対応するためのものです。

3.ライン上のときは・・・

まず、明るいと判断したラインセンサの間隔を調べます。 すると、間隔が最も広い部分の真ん中の方向がコートの方向とわかるので、その方向に行けばコート内に戻れるとわかります。

次に、直前のコートの方向と先ほど計算したコートの方向を比較します。 180度近く変化していた場合、ロボットが半分以上外に出たとわかり、先ほど計算したコートの方向に180度を足したものが正しい現在のコートの方向になります。

また、現在のコートの方向が出た後に、直前のコートの方向との平均を出してコートの方向の急激な変化を抑えています。

4.ライン上でなかったら・・・

ライン上でなく、空中でもない場合は、コート内にいるほぼ完全に外に出たかのどちらかです。 なので、ロボットが半分以上外に出たかどうか=ほぼ完全に外に出たかとなります。 ほぼ完全に外に出たとなった場合は、先ほど計算した現在のコートの方向へ進めばよいということになります。

このようにして、条件分岐もほとんどなく簡単に、アウトオブバウンスを防ぐことができます。

実際のプログラムでは・・・

少し専門的な話になりますので、読み飛ばしてもらっても構いません。 ラインセンサ処理のソースコードをもとに実際のプログラムの説明を行っていきたいと思います。

1.明るさを読み取る

qtyILB は暗いと判断した個数、qtyILW は明るいと判断した個数で、QTY_LINE 回のループでセンサの値を読んでいます。

そして、isLineBlack[] にそれぞれが暗いかどうかの真偽、isLineWhite[] にそれぞれが明るいかどうかの真偽を入れています。

	int qtyILB = 0;
	int qtyILW = 0;
	for(int numLine = 0; numLine < QTY_LINE; numLine ++) {
		//ライン読み取り
		valueLine[numLine] = analogRead(P_LINE[numLine]);
		isLineBlack[numLine] = valueLine[numLine] >= BORDER_BLACK_LINE;
		isLineWhite[numLine] = valueLine[numLine] <= BORDER_WHITE_LINE;
		if(isLineBlack[numLine]) {
			qtyILB ++;
		}
		if(isLineWhite[numLine]) {
			qtyILW ++;
		}
	}

2.審判の持ち上げに対応

countIIA は何回連続で持ち上げられているか、countIIA MAX_CIIA 以上になるとコート内に戻ったと判断し、isOutsideLine isHalfOut dirInside といったラインの方向に関する変数を初期化します。

そして、isLineBlack[] にそれぞれが暗いかどうかの真偽、isLineWhite[] にそれぞれが明るいかどうかの真偽を入れています。

	if(qtyILB >= BORDER_IS_IN_AIR) {
		//持ち上げられている
		countIIA ++;
		if(countIIA >= MAX_CIIA) {
			countIIA = MAX_CIIA;
			isOutsideLine = false;
			isHalfOut = false;
			dirInside = -1;
		}

3.ライン上のときは・・・

まず、posILW[] に何番目のラインセンサがライン上にあるかを入れていき、intvLine[] に連続する2つのposILW[] の差、つまりライン上のラインセンサの間隔の大きさを入れていきます。

	}else {
		//ライン上
		countIIA = 0;
		isOutsideLine = false;
		//白の番号を調べる
		int posILW[qtyILW];
		int numILW = 0;
		for(int numLine = 0; numLine < QTY_LINE; numLine ++) {
			if(isLineWhite[numLine]) {
				posILW[numILW] = numLine;
				numILW ++;
			}
		}
		//白の間隔を調べる
		int intvLine[qtyILW];
		for(int numLine = 0; numLine < qtyILW - 1; numLine ++) {
			intvLine[numLine] = posILW[numLine + 1] - posILW[numLine];
		}
		intvLine[qtyILW - 1] = posILW[0] - posILW[qtyILW - 1] + QTY_LINE;

次に、intvLine[] の値を順々に比べていくことで、どの間隔が一番広いかがわかり、posMaxIntvL に間隔の場所、maxIntvL に間隔の大きさを入れています。これによって、ロボットが戻る方向が計算でき、その結果をdirInside に入れています。

ちなみに、simplifyDeg()は角度を0度~360度に収めるための自作の関数なので気にしないでください。

		//ラインの方向を調べる
		int maxIntvL = 0;
		int posMaxIntvL = 0;
		for(numILW = 0; numILW < qtyILW; numILW ++) {
			if(maxIntvL < intvLine[numILW]) {
				maxIntvL = intvLine[numILW];
				posMaxIntvL = numILW;
			}
		}
		double numDirInside = posILW[posMaxIntvL] + maxIntvL * 0.5;
		dirInside = simplifyDeg(numDirInside / QTY_LINE * 360);

次に、dirInside prvDirInside + 110 prvDirInside + 250 の範囲にあれば半分以上外に出たとわかり、isHalfOut は半分以上外に出ているかどうかを表しています。prvDirInsideには事前に直前のdirInside を入れておきます。

		//前回と比較
		if(prvDirInside >= 0) {
			//半分以上外か
			isHalfOut = false;
			if(insideAngle(dirInside, prvDirInside + 110, prvDirInside + 250)) {
				dirInside = simplifyDeg(dirInside + 180);
				isHalfOut = true;
			}

最後に、誤差等を減らすためにprvDirInsidedirInside の平均値的なものを計算します。

			//平均値計算
			if(abs(dirInside - prvDirInside) <= 180) {
				dirInside = prvDirInside * MULTI_AVG_DI + dirInside * (1 - MULTI_AVG_DI);
			}else {
				dirInside = prvDirInside * MULTI_AVG_DI + dirInside * (1 - MULTI_AVG_DI)
								+ 360 * (dirInside >= prvDirInside ? MULTI_AVG_DI : 1 - MULTI_AVG_DI);
			}
			dirInside = simplifyDeg(dirInside);
		}
	}

4.ライン上でなかったら・・・

countIIA を初期化し、isOutsideLine というライン外かどうかの変数にはisHalfOut という半分以上外に出ているかどうかの変数をそのまま入れます。

dirInside という戻るべき方向を示す変数には、ライン外ならprvDirInside を入れて直前のままにし、ライン内なら-1 を入れて戻るべき方向は存在しないとします。

	}else if(qtyILW <= 2) {
		//ライン上でない
		countIIA = 0;
		isOutsideLine = isHalfOut;
		dirInside = isOutsideLine ? prvDirInside : -1;

以上でプログラムの説明は終わりです。お疲れさまでした( ´∀` )ノ

エンジェルリングのここがよくない...(´・ω・`)

配線が大変

円形上にセンサを配置するため、特にプリント基板の場合は配線をきれいに行うのが難しいです。

重い

センサも多く、センサそれぞれに抵抗をつける必要もある上、円形なので基板自体のサイズも大きいことから、普通より重くなってしまうという欠点があります。

モーターとの物理的干渉を考慮しないといけない

隙間なくセンサを配置する必要があるため、モーターの内側か下にセンサを配置できるような設計が必要です。

まとめ

エンジェルリングはハード的には欠点もいくつかありますが、うまくいけば大変有能な武器になってくれると思います。

何かわかりにくいところや質問があればTwitterの質問箱やブログのコメントにてよろしくお願いしますm(_ _)m

それではさようなら!(´∀`)ノシ

こんにちは。二人目のソフト担当のSYです。ラインセンサや戦略といった全体的なプログラミングをしています。

今回は、SKY Crewの世界大会機の最大の特徴の一つである、円形ラインセンサ(エンジェルリングについて話していきたいと思います。

エンジェルリングを用いることで、絶対にアウトオブバウンスせず(理論上)、さらにラインの外のボールもとれるようになるのです!

エンジェルリングって何?

そもそもエンジェルリングとは、下の写真のようにラインセンサを隙間なく円形に並べ、ライン上では常にどれかしらのラインセンサが反応するようにしたものです。

世界大会では20個のラインセンサ素子と20個のLEDを半径約5㎝の円周上に並べました。

エンジェルリング

何ができるの?

1. ラインの位置が正確に分かる!

まず、エンジェルリングを用いることで、ロボットの向きがどうであろうと、どのような状態でライン上に乗っているかを正確に知ることができます。

下の図1のように、十字状に並べたラインセンサでは(ジャイロなしでは)自分が角にいるかなどはわからず、ラインの位置も大雑把でしかわからないため、アウトオブバウンスしてしまうことがあります。

また、場合分けなども多くなってしまい、動きが複雑になってしまいます。

十字ラインの説明

一方、エンジェルリングでは、下の図2のように線の形や位置を明確に区別することができ、どちらに進めばライン内に戻れるかを間違えることは(おそらく)絶対にありません。

さらに、円形であるのでジャイロセンサや距離センサを使わずとも、単純なアルゴリズムと動きでライン内に戻れます。

エンジェルリングの説明

2. ラインの位置の変化が分かる!

エンジェルリングを使えば、ロボットのどの部分が線の外に出ているのかを連続的に知ることができます。

そして、どのようにラインの位置が変化しているかが分かれば、ラインセンサが反応する間はロボットが完全に外に出ていないと分かるので、その間は外のボールを取りに行くことができるのです。

エンジェルリングの説明2

最後に

後編では、実際にはどのようなプログラムを書いたのか、エンジェルリングの改善点や欠点といったことについて話そうと考えています。

何かわかりにくいところや質問があればTwitterの質問箱やブログのコメントにてよろしくお願いします。

それでは後編をご期待ください!(´∀`)ノシ

kossanです。

twitterで既に少しバラしていますが、ここでチームとして公式に発表します。

SKY Crewは、RoboCupJunior2019 Soccer Lightweightに出場します!

経緯

次シーズン、どの競技に出場するかは世界大会中からチームで話し合ってきました。

具体的には、次とさらにその次シーズン(これが最後になると思われる)にどの競技に出るかで意見をぶつけてきました。

次回Lightweightに出場することに落ち着いた最大の理由は、Lightweightでやりたい事をやり切れていないからです。

今シーズンも大会ごとに機体を新造するスタイルを取ったために、ハードにかける時間に圧迫されてソフトや確実性を向上させることができませんでした。

次回はハードをコロコロ新造することはやめて、ソフトに時間を割こうと考えています。流石に世界大会機は足からボロボロなので使いませんが。(←あれさっきなんて言ったっけ( ̄∇ ̄); )

なぜOpenに出ないのか

もちろんOpenに未練がないわけでもありません。ただ私たちのロボットを見ての通り、現在はOpenに出るだけのハード力がありません。次の大会に向けてハード力を鍛えても、またソフト屋が窮屈な思いをすることが十分に予想されます。

また私たちのチームでは、「アイデアで勝とう」という精神をより重要視しています。

そのため、よりゲームに細かい動きやすばしこい動きがあるLightweightでアイデア勝負を仕掛けに行こうと思っています。(あと単純に世界一を取りたいです(笑))

もしかしたら ...

これからも全方位カメラを搭載したロボットを作っていくつもりなので、暇があれば(あるわけないが)オレンジボールを追いかけることもできるかもしれません。そんな奇跡が起きた時には、Openの方々とお手合わせして頂きたいです♪

では次のシーズンも、Lightweightの皆さんよろしくお願いします!!m(_ _)m

↑このページのトップヘ