2016b/Member

概要

今回の課題はライントレースである. "AからD"のコースと"BからC"のコースが選べるので, 私は"AからD"のコースを選択した.

"AからDのコースでは, A(スタート)->P->Q->(ボール捕獲)->Q->R->P->S->D(ゴール)の順に通過する.

マップ

動作の仕組み

ライントレース

今回は光センサーを"ひとつだけ"使用してライントレースを行う必要がある. そのため, 黒線の左側の境界を利用する.

もしセンサーが"黒線上"と判断した時は, 進行方向を左に修正する. 一方センサーが"黒線上でない"と判断した時は, 進行方向を右に修正する. センサーが"黒線との境界"と判断した時には直進する.

ただしこの方法では交差点を検出できない(左屈折と判断して左折してしまう).

交差点の判断

しかし, 光センサーが交差点を通過するとき, 光センサーは一定時間以上"黒線上"と判断し続けるはずである. そこで, 光センサーがX秒以上"黒線上"と判断し続けた場合に限って, カーブではなく交差点であると判断するようにする.

交差点の判断

ハードウェアについて

完成したロボット
ボールを捕獲したところ

今回のローバーは, mindstorm付属のテキストに紹介されている基本的なローバーロボットを改良(小型化及び部品点数の削減)し,

  • 光センサー
  • ボール捕獲用アーム

をフロントにセットした.

NXT本体, ボール捕獲用アームがそれぞれタイヤ駆動用モーター, 前輪上にあるので安定している.

光センサー

光センサーは紙面すれすれに設置することもできたが, 紙面との間に隙間を設けたほうがより安定した動作をすることが判明したため, あえて紙面から離している.

光センサー

黒線の, 左側の境界をなぞっていくので, 光センサーはフロント左側に設置した.

ボール捕獲用アーム

フロント部分にモーターユニットを設置する余裕がなかったので, 最初はモーターをローバーの上部分に設置するつもりだったが, そうするとNXT本体を設置する場所がなくなってしまう. そこで, モーターを縦方向に設置した. 重心が上がってしまうが, 上記の通り前輪上に重心があるため特に問題はなかった.

むしろ, 前輪のグリップ力が上がることで, 他のチームにあった「駆動輪が浮いてしまう」現象をある程度防ぐことができたようである.

ソフトウェアについて

以下がコードである.

#define SPEED 30
#define go_forward OnFwd(OUT_BC, SPEED);
#define l_turn OnFwd(OUT_B, SPEED); Off(OUT_C);
#define r_turn OnFwd(OUT_C, SPEED); Off(OUT_B);
#define timer_r t0 = CurrentTick();

/*
OUT_A -> ハンドル制御
OUT_B -> タイヤ(右)
OUT_C -> タイヤ(左)
SNS_4 -> 光センサー(ライントレース)
*/

//交差点直進
sub CROSS_GO()
{
	RotateMotor(OUT_B, SPEED, -150);
	go_forward; Wait(500);
}

//交差点右折
sub CROSS_R()
{
	RotateMotor(OUT_B, SPEED, -300);
	go_forward; Wait(900);
}

//交差点左折
sub CROSS_L()
{
	RotateMotor(OUT_B, SPEED, 90);
}

//ボールを捕獲
sub BALLGET()
{
	go_forward; Wait(700);
	Off(OUT_BC); Wait(500);
	RotateMotor(OUT_A, SPEED, -50);
	Off(OUT_ABC); Wait(500);
}

//ボールを射出
sub BALLTHROW()
{
	RotateMotor(OUT_B, SPEED, -170);
	RotateMotor(OUT_A, SPEED, 50);
	Off(OUT_ABC); Wait(500);
	OnFwd(OUT_BC, SPEED + 30); Wait(200); Off(OUT_BC);
}

task main()
{
	SetSensorLight(S4);

	long t0 = CurrentTick();
	//交差点を数える
	int cross = 0;

	while(cross <= 7)
	{
		if (CurrentTick() - t0 < 2000)
		{
			if(SENSOR_4 > 48)
			{
				r_turn; timer_r;
			}
			
			else if(SENSOR_4 > 42)
			{
				go_forward; timer_r;
			}
			
			else
			{
				l_turn;
			}
		}

		//交差点とゴール
		else
		{
			Off(OUT_BC); Wait(1000); 

			if ((cross == 0) || (cross == 2) || (cross == 5))
			{
				/* 交差点を直進 */
				CROSS_GO();
			}

			else if (cross == 1)
			{
				/* 交差点を直進後,ボールを捕獲 */
				CROSS_GO(); BALLGET();
			}

			else if (cross == 3)
			{
				/* ヘアピンカーブ */
				RotateMotor(OUT_B, SPEED, 100);
			}

			else if (cross == 4)
			{
				/* 交差点右折 */
				CROSS_R();
			}

			else if (cross == 6)
			{
				/* 交差点左折 */
				CROSS_L();
			}

			else if (cross == 7)
			{
				/* ボール射出 */
				BALLTHROW();
			}

			cross ++;
			timer_r;
		}
	}
}

以下に詳細を説明する.

モーターとセンサーの説明

/*
OUT_A -> ハンドル制御
OUT_B -> タイヤ(右)
OUT_C -> タイヤ(左)
SNS_4 -> 光センサー(ライントレース)
*/

これは, それぞれのモーターの出力とセンサーの対応を示している(コメント).

モーターのトルク,移動等の定義

#define SPEED 30
#define go_forward OnFwd(OUT_BC, SPEED);
#define l_turn OnFwd(OUT_B, SPEED); Off(OUT_C);
#define r_turn OnFwd(OUT_C, SPEED); Off(OUT_B);
#define timer_r t0 = CurrentTick();
  • SPEEDはモーターのトルクの定義(バッテリー電圧の変化を吸収するために, 適宜変更する)
  • go_forwardは前進
  • l_turnは進路を左方向に調整
  • r_turnは進路を右方向に調整
  • timer_rはタイマーをリセットする(交差点の判断に利用)

これ以外の複雑な動作(交差点の進行, ボールの捕獲・射出など)はsubroutineを利用した.

各種動作の定義

//交差点直進
sub CROSS_GO()
{
	RotateMotor(OUT_B, SPEED, -150);
	go_forward; Wait(500);
}

//交差点右折
sub CROSS_R()
{
	RotateMotor(OUT_B, SPEED, -300);
	go_forward; Wait(900);
}

//交差点左折
sub CROSS_L()
{
	RotateMotor(OUT_B, SPEED, 90);
}

//ボールを捕獲
sub BALLGET()
{
	go_forward; Wait(700);
	Off(OUT_BC); Wait(500);
	RotateMotor(OUT_A, SPEED, -50);
	Off(OUT_ABC); Wait(500);
}

//ボールを射出
sub BALLTHROW()
{
	RotateMotor(OUT_B, SPEED, -170);
	RotateMotor(OUT_A, SPEED, 50);
	Off(OUT_ABC); Wait(500);
	OnFwd(OUT_BC, SPEED + 30); Wait(200); Off(OUT_BC);
}

交差点直進・右折・左折

ライントレースを一旦中止し, 進行方向を修正し, ある程度直進する. その後またライントレースを再開する.

ボールを捕獲・射出

ボールは交差点Qの10cm先にあるので, 交差点Qを通過後10cm進んでから, アームをおろす.

射出時は進行方向を修正し, アームをあげて, ローバーを急発進させ, その勢いでボールを射出する.

task main()

task main()
{

光センサーとそのポートを定義.

	SetSensorLight(S4);

交差点の判断にタイマーを利用する. ここでlong型のt0に現在のタイマーの値を代入しておく.

	long t0 = CurrentTick();

通過する交差点が何番目の交差点なのかを判断する. 交差点(ヘアピンカーブとゴールを含む)を通過するたびにcrossの値を増やしていく.

	//交差点を数える
	int cross = 0;

crossが8になった時(=すべての交差点, ヘアピンカーブ, ゴールを通過した時), プログラムは終了する.

	while(cross <= 7)
	{

光センサーの値が

  • 48以上 : 右へ進路変更
  • 42以上48未満 : 直進
  • 42未満 : 左へ進路変更
		if (CurrentTick() - t0 < 2000)
		{
			if(SENSOR_4 > 48)
			{
				r_turn; timer_r;
			}
			
			else if(SENSOR_4 > 42)
			{
				go_forward; timer_r;
			}
			
			else
			{
				l_turn;
			}
		}

タイマーを利用し, "左へ進路変更"が2秒間続くと交差点(ヘアピンカーブ, ゴール)と判断し, ライントレースを中断する. これらについて,

  1. 交差点P(1) : 直進
  2. 交差点Q(1) : 直進後,ボールを捕獲
  3. 交差点Q(2) : 直進
  4. ヘアピンカーブ : 直進
  5. 交差点R : 右折
  6. 交差点P(2) : 直進
  7. 交差点S : 左折
  8. ゴール : ボール射出

である. 各動作のあとにcrossの値を増やし, タイマーをリセットする. また, 課題では,交差点で1秒間停止することになっているので, 交差点と判断した時点で1秒間停止する.

		//交差点とゴール
		else
		{
			Off(OUT_BC); Wait(1000); 

			if ((cross == 0) || (cross == 2) || (cross == 5))
			{
				/* 交差点を直進 */
				CROSS_GO();
			}

			else if (cross == 1)
			{
				/* 交差点を直進後,ボールを捕獲 */
				CROSS_GO(); BALLGET();
			}

			else if (cross == 3)
			{
				/* ヘアピンカーブ */
				RotateMotor(OUT_B, SPEED, 100);
			}

			else if (cross == 4)
			{
				/* 交差点右折 */
				CROSS_R();
			}

			else if (cross == 6)
			{
				/* 交差点左折 */
				CROSS_L();
			}

			else if (cross == 7)
			{
				/* ボール射出 */
				BALLTHROW();
			}

			cross ++;
			timer_r;
		}

	}
}

感想

ローバー自体の設計が遅れてしまい, それが原因で16日の発表に間に合わせることができなかった. 次の, 課題3ではそのようなことが起こらないようにしたい. また, 設置する場所が作れなかったため, 超音波センサーをのせることができなかった. 結果としてはタイマーを使うことでボールの位置を特定したが, 次回はそのような妥協をしないようにしたい.

一方で, ロボットやプログラム自体は当初思っていたよりもしっかりとしたものを作ることができたので, その点は嬉しかった.


添付ファイル: filemap.png 71件 [詳細] filefig5.png 43件 [詳細] filefig4.JPG 75件 [詳細] filefig3.jpg 81件 [詳細] filefig2.jpg 71件 [詳細] filefig1.jpg 80件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-12-31 (土) 17:23:23 (965d)