#contents
*課題について [#f1086fd0]
[[課題2>http://yakushi.shinshu-u.ac.jp/robotics/?2017a/Mission2]]はライントレーサーロボットの製作である。
#ref(./Misson_course.png,70%)
&br;各々で2種類ある道順の好きな方を選んで走らせる。
&br;・交差点で一時停止(1秒)や直角コーナーなどで音を鳴らせる。
&br;・ロボットは15×18[cm]であること。
&br; などが条件である。
&br;自分はE地点で直進するルートを選んだ
*機体について [#pa9373d2]
&br;機体は最初に組み立てたサンプルの機体に光センサをそのまま付けた。
#ref(./DSC_0043.JPG,30%,around);
&br;機体とセンサの距離が遠すぎると急カーブが曲がれなかったため、機体の旋回軸とできるだけ近いように調整した。
&br;結果、機体の中心軸とセンサの位置がずれてしまった。
#ref(./DSC_0045.JPG,30%)
*プログラムについて [#n48d05e8]
プログラム全文は[[&size(25){こちら};>./code]]を参照してください
**制御の主な方向性 [#qdde9b73]
センサの値を下図のように4つの閾値でラインの白黒を5つの領域に分け、センサが見ている領域が黒や白であれば白黒の中間の領域に行くようにそれぞれ5通りの処理で制御する。
&br;
#ref(./border_color.png,left,wrap,around,70%)
#ref(./exe_conbi.png,wrap,70%)
#clear
&br;交差点等は処理の1サイクルにかかる時間から判断する。
&br;・白や黒の領域から中間の領域を見に行くまでにかかる時間を測り、設定値よりも長ければ交差点、又は丁字路に差し掛かったと判断するようにした。
&br;・この制御では丁字路か直角などが判断できなかったが、コースは決まっているので何度目の直角・丁字路の判断かという回数で直角か丁字路を判断した。
&br;・急カーブを通るときの時間と直角等にかかる時間をモニタリングして直角などの判断にふさわしい時間を設定した。しかし、急カーブがたまに丁字路にかかる時間より長く時間をかけてしまい、急カーブのところで直角と判断してしまうことがあった。判断した後にスイッチ文で分類するようにしたかったため、急カーブを曲がる時だけ時間の条件を追加して、誤判断をなくした。
&br;・直角に関してはそのままでも曲がってくれたため、直角用の関数は作っていない。
**変数宣言、マクロ、サブルーチンについて [#w913c460]
***変数、マクロ宣言 [#l419ae42]
 #define Rotate_R 55		//四つの閾値
 #define Adjust_R 50
 #define Adjust_L 40
 #define Rotate_L 35
 #define t_border 190		//直角・交差点 判断用の時間
 #define POW_TURN 40		//旋回時の出力
 #define POWER 30		//基本的に使う出力
 #define Gap 38			//機体旋回軸とセンサーの見ているところの距離
 
 #define Pause() Off(OUT_BC); PlaySound(SOUND_DOWN); Wait(2000)	//マクロ群
 #define Snd_up() PlaySound(SOUND_UP)				//サウンド関係
 
 float ANGLE_CALC=178/90;		//1度あたりの旋回時間
 float Calc_long=2.0463;		//角度計算用定数(360/Cir_fere)
 
 sub go_f(int l){			//指定距離前進
 	float Ang=l*Calc_long;
 	RotateMotor(OUT_BC,POWER,Ang);
 	Off(OUT_BC);
 }
 sub turn_R(float angle){		//指定角度左旋回
 	RotateMotorEx(OUT_BC,POW_TURN,-ANGLE_CALC*angle,100,true,true);
 }
Pause()とSnd_Up()は交差点の一時停止などで音を出すのをマクロ化した。
他に関してはコメント文で説明できていると思う。
***rotate_L() , rotate_R() [#n15c6a7b]
 sub rotate_L(){
 	Off(OUT_BC);
 	OnFwd(OUT_B,POWER);
 	OnRev(OUT_C,POWER);
 	until( SENSOR_4>Rotate_L );	//ちょうどいい所まで旋回
 }
左右に旋回する関数。表示しているのは左旋回する関数。
&br;黒と白の中間領域から離れすぎたときに動作する。
***adjust_L() , adjust_R() [#ha209813]
 sub adjust_L(){
 	Off(OUT_BC);
 		OnFwd(OUT_B,POWER);
 	until( (SENSOR_4>=Adjust_L) && (SENSOR_4<=Adjust_R) || (SENSOR_4<Rotate_R));
                       //ちょうどいい所まで右輪だけ前進、or、中間の領域から離れすぎているなら中断
 }
左右どちらかのタイヤを回し、進行方向を少し変える。表示しているのは左に変わる関数。
&br;中間の領域から少しズレたときに動作させる。
&br;初期では中間の領域にいくまで進行方向を曲げるという制御だったが、本来旋回するはずのセンサ値でも旋回してくれなかったため、処理終了の条件に旋回すべきセンサ値になったら中断するように条件を追加した。
***cross() [#k334a3eb]
 sub cross(){					//交差点直進
 	Off(OUT_BC);
 	turn_R(30);				//右に旋回
 	go_f(Gap);				//少し前進
 	OnRev(OUT_B,POW_TURN);
 	OnFwd(OUT_C,POW_TURN);
 	until(SENSOR_4<Adjust_L);		//線上に行くまで右旋回
 }
#ref(./cross.png,right,wrap,around,400x230)
交差点を渡る関数。
&br;交差点と判断した時点では機体の向きが左にかなり傾いているため、右に30°旋回して向きを矯正した。
&br;左に少し傾いている状態で前進すると、センサの見る場所が交差点の向こう側の線の左側になるので、そこから線まで右旋回することで交差点を渡っている。
#clear
***その他関数 [#xe417334]
 sub go_f(int l){			//指定距離前進
 	float Ang=l*Calc_long;
 	RotateMotor(OUT_BC,POWER,Ang);
 	Off(OUT_BC);
 }
 sub turn_R(float angle){		//指定角度左旋回
 	RotateMotorEx(OUT_BC,POW_TURN,-ANGLE_CALC*angle,100,true,true);
 }
これらに関しては前回の流用なので[[&size(18){前回};>2017a/Member/kiryu/Mission1]]の説明を参照してください。
**メインタスク [#k6b91c8e]
 task main(){
 	SetSensorLight(S4);			//センサ定義
 	int N=1;
 	long t0=0;	//変数初期化
 	OnFwd(OUT_BC,POWER);		//前進開始
 	t0=CurrentTick();			//タイマーリセット
 	while(N<=13){
 		if(CurrentTick()-t0<t_border){
 			t0=CurrentTick();		//タイマーリセット
 			if(SENSOR_4<=Rotate_L)		//黒の時(右に行き過ぎ_左旋回)
 				rotate_L();
 			else if(SENSOR_4<=Adjust_L)	//少し黒めの時(ちょっと右寄り_左へ方向をいじる)
 				adjust_L();
 			else if((SENSOR_4>Adjust_L) && (SENSOR_4<=Adjust_R))	//ちょうどいい色の時(維持)
 				OnFwd(OUT_BC,POWER);
 			else if(SENSOR_4>=Rotate_R)	//少し白めの時(ちょっと左寄り_右へ方向をいじる)
 				rotate_R();
 			else if(SENSOR_4>Adjust_R)	//白の時(左に行き過ぎ_右旋回)
 				adjust_R();
 		}
 		else if(CurrentTick()-t0>=t_border ){	//サイクルの時間で直角・交差点か判断
 			switch(N){				//N回目は直角,交差点などの判断をして関数に放り込む
 				case 1:		//F地点
 					Off(OUT_BC);
 					Snd_up();
 					N++;
 					break;
 				case 2:		//Q地点
 					Off(OUT_BC);
 					Pause();
 					N++;
 					break;
 				case 3:		//R地点
 					Off(OUT_BC);
 					Snd_up();
 					cross();
 					N++;
 					break;
 				case 4:		//S地点
 					Off(OUT_BC);
 					Snd_up();
 					N++;
 					break;
 				case 5:		//G地点
 					Off(OUT_BC);
 					Snd_up();
 					N++;
 					break;
 				case 6:		//H地点
 					Off(OUT_BC);
 					if(CurrentTick()-t0<t_border+220)
 						break;			//急カーブ区間は条件を厳しく
 					Snd_up();
 					N++;
 					break;
 				case 7:		//T_1地点
 					Off(OUT_BC);
 					Pause();
 					cross();
 					N++;
 					break;
 				case 8:		//T_2地点
 					Off(OUT_BC);
 					Pause();
 					cross();
 					N++;
 					break;
 				case 9:		//R地点
 					Off(OUT_BC);
 					Pause();
 					N++;
 					break;
 				case 10:	//S地点
 					Off(OUT_BC);
 					Snd_up();
 					cross();
 					N++;
 					break;
 				case 11:	//P地点
 					Off(OUT_BC);
 					Snd_up();
 					N++;
 					break;
 				case 12:	//E地点
 					Off(OUT_BC);
 					Pause();
 					N++;
 					break;
 				case 13:	//A地点
 					Off(OUT_BC);
 					N++;
 					break;
 			}
 		t0=CurrentTick()			//タイマーリセット
 		}
 	}
 	turn_R(60);
 	go_f(Gap*5);
 	Off(OUT_BC);
 	PlaySound(SOUND_DOWN);
 }
交差点や直角かどうかは回数をスイッチ文で分けてそれぞれ制御するようにした。
&br;急カーブ区間は中にif文を追加し、急カーブで直角の判断がされてもはじかれるよう設定した。
&br;最後の4行のコードはAの四角の中に入る動き。
*全体を振り返って [#t2f38465]
**改善点 [#a5d3b6d0]
・1サイクル中に設定時間を越えたら交差点を渡る関数に飛ぶという制御にすべきだった。今回の制御方法では1サイクルにかかる時間で交差点判断をしていたため、サイクル中に設定時間を越えても交差点を渡る関数などに飛ばないという問題があった。トレース自体におおきな影響はないが、判断した時点で機体の見ている向きが結構傾くため、機体の旋回軸とセンサの距離との兼ね合いもあって"cross()"で30°旋回などの調節する文を付け足す必要が出てしまった。
**反省・感想 [#fdaec509]
・今回のライントレースでは、交差点判断や丁字路の判断基準設定に悩まされた。結果的にゴールするプログラムができたが、たまに誤判断してしまうことがあるため、閾値の設定をもう少し確実なものにしたかった。
&br;・制作を通してスケジュールがめちゃくちゃだった。発表日の前日に9時ごろまで10番教室で制作することになってしまった。時間に余裕があれば上記の2つの改善点もリカバリできたと思う。

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS