[[2012b/Member]] #contents *メンバー [#bfa47336] -HIBINO -HISASI *課題 [#t27de99e] #ref(2012b/Member/HIBINO/Mission1/12-12-20_004 01.jpg,80%,競技コース) 課題(競技内容)は(1)から(3)までライントレースをしながらボールを運ぶ,というもの.しかし途中にある(2)のような脇道に入りこんではいけないので,それをうまく回避する必要がある. (1)をスタート地点とし,(3)をゴールとする.尚,最終的にゴールにはボールだけが入っていて,本体はゴール外に存在していなければならない. *競技用本体 [#n0b04404] **全体 [#mdb46a9a] #ref(2012b/Member/HIBINO/Mission1/12-12-20_006.jpg,60%,競技用本体) 本体はmindstormsのマニュアルに組立方法が記載されている基本形をベースにしている.また,コードをまとめることができるように各所に固定具を備え付けた. **ライトセンサー周辺 [#gf156651] &ref(2012b/Member/HIBINO/Mission1/12-12-20_005.jpg,60%,ライトセンサー); ライトセンサーは基本形よりできるだけタイヤの軸に近づけることにより,左右に旋回した際の振れ幅を小さくしている.また,センサーの左右にある爪はボールを運ぶ際に固定しておくためのものである. **アーム [#ze6eec7e] &ref(2012b/Member/HIBINO/Mission1/12-12-20_006.jpg,40%,アーム開く); ⇒ &ref(2012b/Member/HIBINO/Mission1/12-12-20_009.jpg,40%,アーム閉じる); アームはこのように開いたり閉じたりすることができる. &ref(2012b/Member/HIBINO/Mission1/12-12-20_010.jpg,40%,アーム閉じる); ボールを運んでいる時はこのようになっている. &ref(2012b/Member/HIBINO/Mission1/12-12-20_007.jpg,60%,アーム内部); アームの内部はこのようにボールが入るだけの空間があり,アームが閉じていればタッチセンサー横の爪と挟み込むような形になってボールを逃がさない.また,ボールを挟んでいてアームが閉じている状態から,アームを開いた時に内部の爪がボールに当たり,ボールを勢いよく前方に転がすことができる. 本来ならばもう少し別の位置にアームを取り付けたかったのだが, モーターが重いのでタイヤの軸付近に取り付け,なるべく遠心力の影響を受けないようにした. *プログラム [#s88c5686] **プログラム概要 [#f7e443f3] 作成したプログラムは役割に応じて3部構成となっている. +ボールを確保する +ライントレースによりゴールまで移動する +ボールをゴール内に入れる ***プログラム全体 [#ra86e639] #define THRESHOLD 35 #define SPEED_H 50 //前進する際の速度 #define SPEED_L 40 //トレースの為に左右に揺れながら動く際の速度 #define OnRL(speedR,speedL) OnFwd(OUT_B,speedR);OnFwd(OUT_C,speedL); #define go_forward OnRL(SPEED_H, SPEED_H); //前進動作 #define turn_left1 OnRL(SPEED_L, -SPEED_L); //その場左回転 #define turn_left0 OnRL(SPEED_L, 0); //左旋回 #define turn_right0 OnRL(0, SPEED_L); //右旋回 #define turn_right1 OnRL(-SPEED_L, SPEED_L); //その場右旋回 #define STEP 1 #define nMAX 800 //連続してラインを認識する回数の限度 #define short_break Off(OUT_BC); Wait(300); #define CROSS_TIME 300 //交差点を直進する時間 #define cross_line OnRL(SPEED_L,SPEED_L);Wait(CROSS_TIME);short_break; //交差点を直進する動作 task main () { SetSensorLight(S2); int nOnline=0; //連続してラインを認識した回数を0の戻す long t0=CurrentTick(); //起動開始時点の時間を記録 OnFwd(OUT_BC,35);Wait(600);Off(OUT_BC); OnFwd(OUT_A,30);Wait(780);Off(OUT_A); OnFwd(OUT_BC,35);Wait(300);Off(OUT_BC); while (true){ while (nOnline < nMAX) { if (SENSOR_2 < THRESHOLD){ turn_left1; nOnline++; }else{ if (SENSOR_2 < THRESHOLD-2){ turn_left0; }else if (SENSOR_2 < THRESHOLD+3){ go_forward; }else if (SENSOR_2 < THRESHOLD+7){ turn_right0; }else{ turn_right1;} nOnline=0; } } short_break; turn_right1; Wait(200); cross_line; nOnline=0; if(CurrentTick() - t0 >= 50000){ OnFwd(OUT_A,-100);Wait(600); } }} ***本プログラムにおける定義 [#e77d59c3] #define THRESHOLD 35 //(1) #define SPEED_H 50 //前進する際の速度 #define SPEED_L 40 //トレースの為に左右に揺れながら動く際の速度 #define OnRL(speedR,speedL) OnFwd(OUT_B,speedR);OnFwd(OUT_C,speedL); #define go_forward OnRL(SPEED_H, SPEED_H); //前進動作 (2) #define turn_left1 OnRL(SPEED_L, -SPEED_L); //その場左回転 (3) #define turn_left0 OnRL(SPEED_L, 0); //左旋回 (4) #define turn_right0 OnRL(0, SPEED_L); //右旋回 (5) #define turn_right1 OnRL(-SPEED_L, SPEED_L); //その場右旋回 (6) #define STEP 1 // #define nMAX 800 //連続してラインを認識する回数の限度 (7) #define short_break Off(OUT_BC); Wait(300); #define CROSS_TIME 300 //交差点を直進する時間 (8) #define cross_line OnRL(SPEED_L,SPEED_L);Wait(CROSS_TIME);short_break; //交差点を直進する動作 (9) 最初に,BとCの端子についてだがそれぞれ(B:右タイヤ C:左タイヤ)に対応している. (1)ライトセンサーがラインの上なのか上でないのかを判断する際に基準となる数値を定義している.ライトセンサーは赤外線を当てた上でどれだけその赤外線が吸収されずに反射するかを判断している.つまり吸収されてあまり反射しない=赤外線が当たった部分は暗い(黒い),吸収されずよく反射する=明るい(白い),とそれぞれ判断し,その反射した量を数値化してより高い数値であればそこはより明るい(白い),ということになる.今回競技で使用した本体についているライトセンサーは大分床面に近づいているため影ができてしまうので全体的に取り扱うライトセンサーの数値は低めになるようにここでの数値も低めにしている. 尚,ライントレースの動き方についてだが &ref(2012b/Member/HIBINO/Mission1/トレース.jpg,70%,ライントレースの動き方); ↑この図のように常にラインの左端を中心に「黒と白」→「黒」→「白」→「黒と白」……とライトセンサーが感知するようにトレースしていくようになっている. (2)では主に「交差点を直進」する際に行われる動作,すなわち前進動作を定義している.また交差点でなくともラインの上を真っ直ぐトレースできているならばこの動作が起こるようにもなっている. (3)では今本体が位置している場所が一本道の上なのか交差点の上なのかを判断する際の「本体の位置を変えずに左に回転してまた更にラインをトレースする」動作を定義している. (4)ではライントレースの際の「黒」→「白」の動作を定義していて,(3)のその場左回転とは違い本体を左にひねるようにして左半身のみ前方に進める動作となっている. (5)は(4)とほとんど同じで「白」→「黒と白」の動作を定義している.これは右半身のみ前方に進める動作となっており,(4)の動作と交互に繰り返すことにより左,右と交互に本体の向きをねじらせながら徐々に進んでいくことが可能となる. (6)ではライトセンサーが白のみを感知した場合にライン(黒)に戻るためにその場で右に回転する動作を定義している.また,交差点に差し掛かって曲がろうとした時にこの動作を行うことにより直進コースに戻る,という脇道に進まない為の重要な動作の一つでもある. (7)は今トレースしているラインが一本道なのか交差点なのかを調べる際に重要な数値を定義している.このプログラムでは一本道か交差点なのかを区別するのに「何回連続してライトセンサーが黒のみを感知するか」を判断材料としている. &ref(2012b/Member/HIBINO/Mission1/交差点.jpg,70%,一本道と交差点の区別について); ↑の図の過程1は一本道についてのトレースを表している.この時は「黒と白」→「黒」→「白」とそれぞれ順番にライトセンサーは感知していることになるのだが,交差点に差し掛かった過程2については「黒と白」→「黒」→「黒」と連続してライトセンサーが黒を感知している.これは略図な為に一本道と交差点での連続して黒を感知する回数の差はわずか1であるが,実際はもっと細かくトレースするために数が多くなっている.すなわちここで定義された数以下連続して黒を感知した場合はそれは一本道とみなし,それ以上連続して黒を感知した場合はそれを交差点とみなす,という数字である. (8)は本体が交差点を交差している道を無視して直進する時間を定義している.この数値が大きすぎるとそれだけ交差している道を無視して=ライントレースをしないで,前進する時間が長くなる為にコースから外れたり,逆にこの数値が小さすぎると直進した後も交差点内に存在している状態になってしまい結局は交差点を直進できない,という結果になり得る.この数値は試行錯誤の中でちょうど良いと思われる数字をあてた. (9)は(8)で定義した時間だけを実際に直進する動作を定義している. ***プログラムの開始,およびプログラム内での設定 [#iff5d614] task main () { SetSensorLight(S2); // (10) int nOnline=0; //連続してラインを認識した回数を0の戻す (11) long t0=CurrentTick(); //起動開始時点の時間を記録 (12) (10)ではライトセンサーが本体の接続部2に接続されていることを設定している. (11)ではライトセンサーが「黒」を感知した際にカウントされていく数をプログラムの頭で0にリセットしている.尚,この数が(7)で定義された数字を上回るか上回らないかで交差点か一本道かを区別する. (12)ではプログラムを開始した瞬間の時間をt0という値に代入している.これはボールをゴール内に転がすプログラムを起こす際にその時点での時間とt0,すなわちプログラムを開始した時間との差で以て起こすか否かを判断するのに使われる. ***ボールを確保するためのプログラム [#gaae9e77] OnFwd(OUT_BC,35);Wait(600);Off(OUT_BC); // (13) OnFwd(OUT_A,30);Wait(780);Off(OUT_A); // (14) OnFwd(OUT_BC,35);Wait(300);Off(OUT_BC); // (15) (13)はボールを爪の中に完璧に入れるための前進動作を表している. &ref(2012b/Member/HIBINO/Mission1/開始位置.jpg,70%,開始位置); 尚,開始位置は↑の図のようにしてある.課題として扱われるほど大層なものではないが"ルール"として「開始する地点はスタート地点の枠の中に本体が収まっている位置であること」となっているためにこうしてある.本体の向きに関しては制限は無い.この位置から(13)の通りに前進をすればボールが丁度爪の真ん中に位置することになる. (14)はアームを閉じる動作を表している.尚,Aはアームに対応した端子である.閉じる際に構造上アームを開いたときにボールを前方に転がすための爪がボールに引っかかって上手くアームが閉じない場合があるので,閉じる速度を遅くし,必要(アームがボールを完璧に包み込む部分)より少し多めにアームが閉じるように時間を設定して,ボールに引っかかったとしてもそのまま力ずくでアームを閉じるようにしてある.(壊してしまったら謝るしかりません.) (14)はアームを閉じる動作を表している.尚,Aはアームに対応した端子である.閉じる際に構造上アームを開いたときにボールを前方に転がすための爪がボールに引っかかって上手くアームが閉じない場合があるので,閉じる速度を遅くし,必要(アームがボールを完璧に包み込む部分)より少し多めにアームが閉じるように時間を設定して,ボールに引っかかったとしてもそのまま力ずくでアームを閉じるようにしてある.(壊してしまったら謝るしかありません.) (15)ではボールを確保した後,ライントレースを開始しやすい位置にまで本体を調整的に動かすプログラムを表している.このプログラムを挿入するかしないかでボールを確保してからのライントレースが成功するかしないかが決まると言っても過言では無い.ライントレースに一番重要なのはライトセンサーがどの位置に当たっているか,である.またこの時の動作の時間も試行錯誤の中で見つけた最良と思われる数字である. ***ライントレースのプログラム [#v145c98d] while (true){ while (nOnline < nMAX) { // (16) if (SENSOR_2 < THRESHOLD){ // (17) turn_left1; // (17') nOnline++; // (18) }else{ if (SENSOR_2 < THRESHOLD-2){ // (19) turn_left0; // (19') }else if (SENSOR_2 < THRESHOLD+3){ // (20) go_forward; // (20') }else if (SENSOR_2 < THRESHOLD+7){ // (21) turn_right0; // (21') }else{ turn_right1;} // (22) nOnline=0; // (23) } } (16)では(11)で説明した「黒を感知した回数」が(7)で定義した「これより大きい回数連続して黒を感知した場合その地点は交差点とみなす数字」より小さい場合にのみ{}内のプログラムが起動するようになっている. (17)は(16)を満たした場合今ライトセンサーが当たっている地点は(1)で定義した基準値よりも小さい(黒い)かを判断している.小さい(黒い)場合はライトセンサーが完璧にラインに当たっているとして,(17')のその場左回転を行いライトセンサーが白に当たるようにする.その際に(18)で(11)で設定した値に1を足すことによって後になって(16)の判断に関わってくる. (19)は(1)で定義した基準値から2を引いた,つまり,より黒い場所にライトセンサーが当たっているかを判断している.基準値より黒い場所とは黒と白,ではなく黒にのみライトセンサーが当たっている場合である.この時には(19')の左回転によって結果として左半身のみが前進したことになる. (20)は(1)で定義した基準値に3を足した,つまりより白い場所にライトセンサーが当たっているかを判断している.この場合はライトセンサーがラインの左端に当たっていると仮定して,(20')の前進動作を行うようになっている. (21)は(1)で定義した基準値に7を足した,つまり(20)よりも白い場所にライトセンサーが当たっているかを判断している.このプログラム全体の性質上,ライトセンサーが感知する場所が白=ラインの左側,となるために(21')の右回転を行う.この時(19')と同じように,今度は右半身のみが前進したこととなる. (22)は(17)(19)(20)(21)のどの判断にも当てはまらない場合,つまりライトセンサーが真っ白な場所に当たっている場合その場右回転をするようにしている.(21)の説明でも述べたが白=ラインの左側なのでその場右回転をしている.また,真っ白=ラインから外れている,ということなのでそのまま進むのでは無く本体の向きをラインに合わせるという意味でその場での右回転となっている. (23)は今までのライントレースの動作全てを終えた段階で(11)の数値を再び0にリセットしている.(23)が無ければ(11)の値は増える機会が増え,逆に減る(0になる)機会が減る為に簡単に(7)の数字を越えるようになってしまい一本道すら交差点と判断してしまう.よってこの一文は今回の課題の上で脇道に外れてしまわないようにする為に非常に大切なものである. 尚,これらの基準値±の値については全て試行錯誤から見つけた数値である. ***交差点を越えるプログラム [#xa12f116] short_break; // turn_right1; Wait(200); // (24) cross_line; // (25) nOnline=0; // (26) (16)において(11)の値が(7)の値を超えた場合「交差点を越えるプログラム」以下のプログラムが起動するようになっている. (24)はその場右回転の動作である.課題のコースでは交差点に差し掛かった場合必ず左折方向に脇道が伸びている.このプログラムが動いているということはそれは交差点の上であるということなので少し脇道をトレースしてその方向に進んでしまった分だけ右に回転(方向を調節)する,というものである.また右に回転す時間は試行錯誤で見つけた. (25)は(24)で方向を調節した上でライントレースを無視し,(9)で定義した動作を行う.(25)をせずにライントレースをしてしまった場合方向を調節したとしても再び交差点で脇道の方をトレースしてしまうため,強引にライントレースを無視して進むべき方向に進む必要がある. (26)では(11)の値を再び0にリセットしている.(25)の段階で交差点は越えた,と仮定しているため,(11)をリセットしなければ交差点を越えたときにまた交差点だと認識して(25)を行ってしまう.それを防ぐ為にここで0にリセットしているのである. ***ボールをゴールに入れるプログラム [#k6f56a49] if(CurrentTick() - t0 >= 50000){ // (27) OnFwd(OUT_A,-100);Wait(600); // (28) (27)は交差点を越えた時,その瞬間の時間から(12)で設定した開始した時の時間を引いた値が50000以上であるかを判断している.すなわち,開始してから交差点を越える瞬間までに50秒以上経っているかどうか,を判断しているのだが,このプログラムはラインの左側しかトレースしないため越えるべき交差点は一箇所だけになる.その交差点は早い段階で越えてしまって,次に現れる交差点は下の図の赤い矢印部分,つまりゴール前のみになる.よって50000以上となっているが,一個目の交差点を越えてから下の図の赤い矢印の部分までの間の秒数であれば何を入れても良い. &ref(2012b/Member/HIBINO/Mission1/02.png,60%,ゴール前交差点); (28)はアームを開く動作を表している.アーム内のボールを前方に転がすための爪によってボールのみをゴール内に入れるのだが,そのためにはある程度の勢いが必要なので-100という速度でアームを動かしている. 尚,ボールを転がす向きであるがゴール前の交差点((27)の説明で使用した図の赤い矢印の部分)に差し掛かって左折しようとしたところで(24)(25)が働き,交差点を直進する=ゴールの方向に向きなおして前進する,ために結果としてゴール方向へボールを転がすことが可能となっている. *感想 [#o8c9e805] ただライントレースをするだけならばアームを取り付ける必要が無く,軽い本体で良いのでスムーズに移動できるのだが,ボールを運ぶ必要があったのでどうしてもアームを取り付ける必要があり,どうしても重くなってしまう.当初はボールをつかんでから動作の時間制御によって無理やりコースに侵入させるようなプログラムであったのだが,その重さ故電池残量,タイヤの開始位置,床の材質によって大きく結果が変わってしまうため,ボールをつかんでからすぐにライントレースを開始するようにした.このように「プログラム上では」成功するはずだが実際にやってみると外的要因によって上手く動作しない,ということが多くて自分にはそれがとても歯がゆかった(実際にゼミ内で披露した際もそれによって失敗に終わった).しかし実際はプログラム,つまりソフトウェアと本体,ハードウェアとの兼ね合いによってロボティクスは成り立つものであると後になってから気づき,どのような状況であってもハードウェアの能力を一定以上引き出し続けることができるソフトウェアを作っていくことが大事なのだと実感できた.もう一つ課題が用意されているが,その際には今回の失敗から学んだことを活かせるようにしたい.