[[2018b/Member]] 目次 #contents *課題2の概要[#c1701348] 課題2のコース #ref(Inked2018b-mission2_LIj.jpg)↑ ミッション † 次のいずれかのコースで黒い線に沿って動き、途中でボールをゴール付近に立てた350mlの空き缶(黄色で表示)に当てるロボットを製作せよ。 (相棒と違うコースを選ぶこと) 自分はコース2を選択 第2コース † ロボットを正方形Y内におき、Lをスタート Eを一時停止して直進 Iを一時停止して左折 Hを直進 Kを直進 Jを左折 Cを一時停止して右折 Eを一時停止して直進 Gを一時停止して直進 長方形X内に入って停止 (一時停止の指定がある場所は、1秒間停止すること) ボールはロボットが弧IHKJ上にある時にQ地点の空き缶に当てる *扱うロボットの説明 [#hbaee6f9] **全体像 [#lae6a446] #ref(image1 (2).jpeg) NXT本体を設置する場所を考えた結果非常に不安定な位置になってしまったことが反省点の一つ.後にも述べるが超音波センサを設置する場所も誤差を生んでしまう原因となってしまった. **ボールをシュートするための機構 [#xfdcb3c9] カタパルト式の機構.ボールを初めから持って移動できる点を利用しシンプルなプログラムでボールをシュートできるように設計した #ref(image2 (2).jpeg) 実際のシュートした場合はこうなる↓ #ref(image1 (12)_LI.jpg) **超音波センサーの位置 [#zdf6b0bf] #ref(image3.jpeg) 車体の後ろに固定し,シュートするための機構とカラーセンサーを取り付けるため後方に設置.缶の方向に向いた後360度回転しシュートすればよいと初めは考えていたが結局誤差が生じてしまい,失敗するケースの一つとなってしまった. 理想は以下の通り. #ref(7ページプレゼンテーション.jpg) 缶を探した後に・・・ #ref(8ページプレゼンテーション.jpg) #ref(Inked8ページプレゼンテーション_LI.jpg) この180度回転中に誤差が生まれてしまう.超音波センサが前方についていないため2段階のサーチも不可能であるから設計段階での妥協は控えるべきである. **カラーセンサーの位置 [#ae4a7524] 前方につけ,なるべく車体から離さないような位置に取り付けた.あまりに車体から離れてしまうとコースをうまく周れなくなってしまう. #ref(image4.jpeg) *プログラムの説明 [#wff539bb] #ref(image1 (11).jpeg); 今回の課題ではライントレースによりロボットを移動させる.故にラインの右側を通る場合と左側を通る場合の二通りのプログラムを必要とする.また急カーブや直角カーブで交差点判断をしないようなプログラムも必要とする.更に今回はコースの特性上,弧IHKJをうまく一周するプログラムも作成した. カラーセンサーの値は60以上が白,50以上60未満が白と黒の中間なので直進,50未満が黒としてプログラムに記載している. また下のプログラムはt_start(関数が始まった時間)を定義することにより,t1−t_startで動作を行っている時間を表すことが出来るため,t_min間できちんと停止するプログラムとなっている.t1は機体が回転や旋回等の動作を行っている間常に更新され,t0は白or黒の探知し始めた時間を記録.よってt1−t0はセンサーが白or黒を連続で探知し続けた時間であり白と黒の中間,つまり直進した際リセットされる.これによりこの時間がある一定数を超えると白を探知し続けた場合であれば大きくコースから外れている,黒を探知し続けた場合はいくら曲がっても黒ということで交差点であると判断できるようになっている. #REF(image1 (17).jpeg); 上の図のように交差点であれば大きく黒線に入り,カラーセンサが黒を探知する時間が長くなるので基準(t_kosa)を定めて,t1-t0(曲がりor旋回時間)がその時間を超えれば交差点と判断している. **ラインの右側を通るプログラム [#cb3f74d9] void follow_line_migi(long t_min) { SetSensorLight(S1); long t_start=CurrentTick(); //プログラム開始時間を記録 long t0=CurrentTick(); //白or黒の探知し始めた時間を記録 long t_max=150; //連続して黒or白の限界値 long t1=CurrentTick(); //機体が動作を行っている瞬間 while(t1-t_start<t_min) //t_min間この動作を繰り返す { if(SENSOR_1>60){ if(t1-t0<t_max){ OnFwd(OUT_C,40);Off(OUT_A); //少し白の部分に入ったら左折 }else{ OnFwd(OUT_C,30);OnRev(OUT_A,60); //大きく白の部分に出たら軽く左旋回 } }else if(SENSOR_1>50){ OnFwd(OUT_AC,30); //白と黒の中間なので直進 t0=CurrentTick();//白or黒の時間をリセット }else{ if(t1-t0<t_max){ OnFwd(OUT_A,40);Off(OUT_C);//少し黒の部分に入ったら右折 }else{ OnFwd(OUT_A,30);OnRev(OUT_C,60);//大きく黒の部分に出たら軽く右旋回 } } t1=CurrentTick(); //時間更新 } Off(OUT_AC); } **ラインの右側を通過し,交差点し差し掛かったら停止するプログラム [#i26b0f46] void follow_line_migikousa(long t_min) { SetSensorLight(S1); long t_start=CurrentTick(); //プログラム開始時間を記録 long t0=CurrentTick(); //白or黒の探知し始めた時間 long t_max=150; //連続して黒or白の限界値 long t_kosa=200; //この時間を基準に交差点判断 long t1=CurrentTick(); //機体が動作を行っている瞬間 while(t1-t_start<t_min){ //t_min間この動作を繰り返す if(SENSOR_1>60){ if(t1-t0<t_max){ OnFwd(OUT_C,40);Off(OUT_A);//少し白の部分に入ったら左折 }else{ OnFwd(OUT_C,30);OnRev(OUT_A,60);//大きく白の部分に出たら軽く左旋回 } }else if(SENSOR_1>50){ OnFwd(OUT_AC,40); //白と黒の中間なので直進 t0=CurrentTick();//白or黒の時間を0に戻す }else{ if(t1-t0<t_max){ OnFwd(OUT_A,40);Off(OUT_C);//少し黒の部分に入ったら右折 }else if(t1-t0<t_kosa){ OnFwd(OUT_A,30);OnRev(OUT_C,60);//大きく黒の部分に出たら軽く右旋回 } }else{ Off(OUT_AC);//交差点判断したら停止 } } t1=CurrentTick(); //時間更新 } Off(OUT_AC); } **ラインの左側を通るプログラム [#a66021c0] void follow_line_hidari(long t_min) { SetSensorLight(S1); long t_start=CurrentTick(); //プログラム開始時間を記録 long t0=CurrentTick(); //白or黒の探知し始めた時間 long t_max=150; //連続して黒or白の限界値 long t1=CurrentTick(); //機体が動作を行っている瞬間 while(t1-t_start<t_min){ //t_min間この動作を繰り返す if(SENSOR_1>60){ if(t1-t0<t_max){ OnFwd(OUT_A,40);Off(OUT_C);//少し白の部分に入ったら右折 }else{ OnFwd(OUT_A,30);OnRev(OUT_C,60);//大きく白の部分に出たら軽く右旋回 } }else if(SENSOR_1>50){ OnFwd(OUT_AC,40);//白と黒の中間なので直進 t0=CurrentTick();//白or黒の時間を0に戻す }else{ if(t1-t0<t_max){ OnFwd(OUT_C,40);Off(OUT_A);//少し黒の部分に入ったら左折 }else{ OnFwd(OUT_C,30);OnRev(OUT_A,60);//大きく黒の部分に出たら軽く左旋回 } } t1=CurrentTick(); //時間更新 } Off(OUT_AC);//停止 } **ラインの左側を通過し,交差点し差し掛かったら停止するプログラム [#sc381860] void follow_line_hidarikousa(long t_min) { SetSensorLight(S1); long t_start=CurrentTick(); //プログラム開始時間を記録 long t0=CurrentTick(); //白or黒の探知し始めた時間 long t_max=150; //連続して黒or白の限界値 long t_kosa=200; //交差点判断の基準となる時間 long t1=CurrentTick(); //機体が動作を行っている瞬間 while(t1-t_start<t_min){ //t_min間この動作を繰り返す if(SENSOR_1>60){ if(t1-t0<t_max){ OnFwd(OUT_A,40);Off(OUT_C);//少し白の部分に入ったら右折 }else{ OnFwd(OUT_A,30);OnRev(OUT_C,60);//大きく白の部分に出たら軽く右旋回 } }else if(SENSOR_1>50){ OnFwd(OUT_AC,30);//白と黒の中間なので直進 t0=CurrentTick();//白or黒の時間を0に戻す }else{ if(t1-t0<t_max){ OnFwd(OUT_C,40);Off(OUT_A);//少し黒の部分に入ったら左折 }else if(t1-t0<t_kosa){ OnFwd(OUT_C,30);OnRev(OUT_A,60);//大きく黒の部分に出たら軽く左旋回 }else{ Off(OUT_AC);//交差点判断したら停止 } } t1=CurrentTick(); //時間更新 } Off(OUT_AC);//停止 } **コースの弧上を交差点判断せずにスムーズに通過するプログラム [#u08e558c] 基本的には前4つのプログラムと原理は同じであるが,違う点は交差点判断したら一度直進し再びライントレースに戻るようプログラムを設定することで,円周を綺麗に周ることができる. void follow_line_ensyu(long t_min) { SetSensorLight(S1); long t_start=CurrentTick(); //プログラム開始時間を記録 long t0=CurrentTick(); //白or黒の探知し始めた時間 long t_max=150; //連続して黒or白の限界値 long t_kousa=200; //交差点判断の基準となる時間 long t1=CurrentTick(); //機体が動作を行っている瞬間 while(t1-t_start<t_min){ //t_min間この動作を繰り返す if(SENSOR_1>60){ if(t1-t0<t_max){ OnFwd(OUT_A,40);Off(OUT_C);//少し白の部分に入ったら右折 }else{ OnFwd(OUT_A,30);OnRev(OUT_C,60);//大きく白の部分に出たら軽く右旋回 } }else if(SENSOR_1>50){ OnFwd(OUT_AC,30);//白と黒の中間なので直進 t0=CurrentTick();//白or黒の時間を0に戻す }else{ if(t1-t0<t_max){ OnFwd(OUT_C,40);Off(OUT_A);//少し黒の部分に入ったら左折 }else if(t1-t0<t_kousa){ OnFwd(OUT_C,40);OnRev(OUT_A,60);//大きく黒の部分に出たら軽く左旋回 }else{ OnFwd(OUT_AC,40);Wait(200);//交差点判断したら直進 } } t1=CurrentTick();//時間更新 } Off(OUT_AC);//停止 } **缶の方向を調べるプログラム [#sf349a41] また缶に向けて正確にボールをシュートするために正確に超音波センサを用いて缶の方向を向くプログラムを要する.以下のプログラムがそれである. void search_direction() { ResetTachoCount(OUT_ABC);//モーターの記録している角度を初期化 OnFwd(OUT_A,50); OnRev(OUT_C,50); SetSensorLowspeed(S4); //端子4に超音波センサー int d1; d1=1000; //仮の物体への距離 long t0=CurrentTick(); long t1=CurrentTick(); long t2=CurrentTick(); while(t2-t0<=3570){//一周以上して距離の最小値を計測 t2=CurrentTick(); if(SensorUS(S4)<d1){ d1=SensorUS(S4);//仮の最小値よりも近い部分に物体を探知したときそのときの値に最小値を更新 PlaySound(SOUND_CLICK);//音を鳴らして更新が入ったことを知らせる. t1=CurrentTick();//t1更新 } t2=CurrentTick(); } t0=CurrentTick(); Off(OUT_AC); OnFwd(OUT_C,50); OnRev(OUT_A,50); Wait(t0-t1);//最後に最小値を更新した時間分逆回転し位置を戻す. } **実際のプログラム [#pc19d427] task main() { follow_line_migikousa(5000);//QからE OnFwd(OUT_AC,40); Wait(300); follow_line_migikousa(5000);//EからI OnFwd(OUT_C,40);OnRev(OUT_A,40); Wait(700); OnFwd(OUT_AC,40); Wait(300); follow_line_ensyu(14000);//IからJ付近 PlaySound(SOUND_CLICK); search_direction(); RotateMotorEx(OUT_AC,30,360,100,true,true); Wait(3000); RotateMotor(OUT_B, 40, 70); Wait(3000); Off(OUT_AC); Wait(300); RotateMotor(OUT_B, 40, -80); RotateMotorEx(OUT_AC,30,400,100,true,true); follow_line_hidari(6000); PlaySound(SOUND_CLICK); follow_line_hidarikousa(7000);//JからC RotateMotor(OUT_A, 40, 180); RotateMotor(OUT_C, 40, -180); OnFwd(OUT_AC,40); Wait(700); follow_line_migi(23000);//CからD PlaySound(SOUND_CLICK); follow_line_migikousa(10000);//DからE OnFwd(OUT_AC,40); Wait(300); follow_line_migi(26000);//EからFより向こう PlaySound(SOUND_CLICK); follow_line_migikousa(7000);//Gまで PlaySound(SOUND_CLICK); OnFwd(OUT_AC,40); Wait(300); follow_line_migikousa(10000); OnFwd(OUT_AC,40); Wait(300);//ゴール } *反省 [#meb118fb] 今回は本番直前でロボットがうまく作動しなくなってしまい失敗に終わってしまった.原因としては空き缶を探知しボールを当てる関数を作る際に,生じてしまうであろう誤差を想定していなかった点,電池の消耗が激しく機体かプログラムに工夫を加えればもう少し電池の消耗の軽減できたと考えられるがそれが出来なかったため本番でうまく動かなかった点,NXT本体の支えが非常に不安定でありバランスが崩れやすかった点等が挙げられる. この反省を生かして最後の班全員で行う課題に取り組みたいと思う.発表前日は何度も成功していたので前回より悔しさが倍増したので今度こそは成功させたい.