以上の3つをこなすロボットの作成。
使用できるのは配られたパーツのみ、光センサは1個だけという制限付。
コースの概形は写真のようになっている。
このようにカーブにもゆるいものや急なもの、直角なものなど様々である。
この写真に無くて加工して書いたが、本来このようにT字の交差点が2個あるのだ。
私のプログラムの場合、赤丸の部分がボールの位置(こちらがスタート)で、青いコの字の部分がゴールである。
途中、黄色い丸の部分に障害物がある。
ロボットの中にボールを回収することを考えて作った結果こうなった。
サイズは幅17cm,長さ16cm,高さ14cmと割と大型。
大きすぎて障害物が避けられなくなってしまった。
タイヤ2個とパーツで作った足2個で支えられている。
上の写真は裏側から見た様子。
赤丸の部分にボールを取り込む構造になっている。
モーターを3個、ライントレースをするために光センサ1個を使用。
2つのモーターで左右それぞれのタイヤの制御をしている。
残りの1つのモーターでロボット内にボールの取り込み、シュートするようになっている。
上の写真はボールを取り込み、シュートを行うモーターの構造。
左のアームが下りると右の部分が引っ込み、左のアームが上がると右の部分がボールを弾く仕組みになっている。
このモーターと本体を別々に作った為、下の写真のような欠点ができてしまった。
赤丸の部分では上で説明した仕組みを動かすための重要なゴムが本体のパーツに接触してしまっているのだ。
しかし、直そうにしても中々対処できない。
本体側のパーツは本体の強度に影響するもので、動かそうにも動かせないのだ。
結局今回は動作に影響がなかったのでそのままになっているが、これで良いと自分でも思っていない。
本体から作った事で起こった問題はこれだけではない。
上の写真はロボットとNXT本体の接合部である。
NXTはこの黒いペグ2個だけで無理矢理くっつけている。
そのためNXTを持って上へ持ち上げると簡単に外れてしまう。
本当はNXTにパーツを付けていく方が無難なのだが、本体から作ってしまったためにこのようなことになってしまった。
これも改善すべき点の1つと言えるだろう。
プログラムの前にまずはロボットについてもう1つ話さなければならない。
それはタイヤと光センサとの距離が遠いことだ。
これの距離が近いほどカーブの後の交差点をスムーズに読むことができるのだが、このロボットはその距離が遠く、交差点の判断が難しいということもあり、敢えて交差点を通らないという選択をした。
#define THRESHOLD 44/*閾値*/ #define SPEED 40/*基準の速度*/ #define SPEED_L 25/*旋回時の速度*/
閾値と速度の定義。
閾値はセンサの最高値が57,最低値が38でその中間値前後の数値を使って動かしてみた結果、44に落ち着いた。
速度は通常走行時との基準値と旋回時の速度の2種類を定義している。
#define OnRL(speedR,speedL) OnFwd(OUT_B,speedR);OnFwd(OUT_C,speedL); #define turn_left OnRL(SPEED_L,-SPEED_L);/*左旋回*/ #define turn_right OnRL(-SPEED_L,SPEED_L);/*右旋回*/ #define STEP 1/*while文の動作の待機時間(1/1000秒)*/ #define nMAX 300/*急カーブかそうでないかの境界*/
3種類のモーターを用いているが、Aのモーターはボールの取り込みとシュートを行い、Bのモーターは右のタイヤを、Cのモーターは左のタイヤを制御している。
サブルーチンはボールを取り込むプログラムとボールをシュートするプログラムの2種類を作ってある。
sub get(){ /*ボールを取り込むプログラム*/ OnRL(-SPEED,-SPEED); Wait(1300); Off(OUT_BC); OnFwd(OUT_A,70); Wait(300); Off(OUT_A); OnRL(SPEED,SPEED); Wait(1600); Off(OUT_BC); OnRL(SPEED,-SPEED); Wait(1300); Off(OUT_BC); OnRL(SPEED,SPEED); Wait(300); Off(OUT_BC); }
図で上のサブルーチンを表現するとこのようになる。(画像は2012b/Missionのものを加工して利用。)
ロボットは赤色で、凸の方に光センサがあり、その反対側が取り込む部分である。
アームは上げた状態から始まり、ボールを取り込んだ後枠の中央に戻る。
そしてライントレースする線の方向を向き、枠から出るように僅かに前進させるまでがこの部分の流れである。
sub shoot() { /*ボールをシュートするプログラム*/ OnRL(SPEED,SPEED); Wait(1700); Off(OUT_BC); OnRL(SPEED,-SPEED); Wait(1300); Off(OUT_BC); OnRev(OUT_A,70); Wait(300); Off(OUT_A); }
図で上のサブルーチンを表現するとこのようになる。(画像は2012b/Missionのものを加工して利用。)
最初にも話したが、このロボットはタイヤと光センサの距離が遠い。
従って最後のゴールの枠に対してもまっすぐではなく、斜めに入ってしまう。
これではどこで枠まで来たのかが判断できないということで、最後に通る急カーブを判断基準にした。
変数nOnlineがnMAXより大きくなった後、このプログラムの基準速度で約3秒でロボットの進行方向がライントレースが途切れる枠線にほぼ平行になる。
これを利用して、枠の正面まで動かし、シュートするようにした。
最後までライントレースをしていないのでこれが良いのかどうかは分からないが、私はここ以外に位置を確定できる場所もないだろうからと思い、利用した。
task main() { get(); int count=0;/*ボールを出したことをを表し、while文を抜けるための変数*/ int flag=0;/*説明はプログラム外の文参照*/ int gain;/*センサの値と閾値の差分を入れる変数*/ int nOnline=0;/*黒線を連続で読んだ数*/ long t0=CurrentTick();/*動作を始めた時の現在時間を入れる変数*/ SetSensorLight(S1); while(count!=1){ if(SENSOR_1<=THRESHOLD-5){ if(flag==2&&(CurrentTick()-t0>=3000)){ shoot(); count=1; }else if(flag==1&&(CurrentTick()-t0>=10000)&&( nOnline>nMAX)){ flag=2; t0=CurrentTick();/*一つの時間での処理が終わったと判断し、次の時間での処理の為に現在時間を更新する*/ }else if(flag==0&&(CurrentTick()-t0>=20000)){ flag=1; /*A*/ turn_left; Wait(400); t0=CurrentTick();/*一つの時間での処理が終わったと判断し、次の時間での処理の為に現在時間を更新する*/ }else if(flag==0){ /*センサーの値が40より下の場合は白い面に出るように旋回*/ turn_right; }else{ if(flag==1&&(CurrentTick()-t0>=10000)){ /*B*/ OnRL(SPEED,-SPEED); }else{ turn_left; } } nOnline++; }else if(SENSOR_1>=THRESHOLD+7){ /*センサーの値が51以上の場合は黒線に戻るように旋回*/ if(flag==0){ turn_left; }else{ turn_right; } nOnline=0; }else{ /*センサーの値が40-50の範囲内の場合は比例制御*/ gain=THRESHOLD-SENSOR_1; if(flag==0){ OnRL(SPEED-gain,SPEED+gain); }else{ OnRL(SPEED+gain,SPEED-gain); } nOnline=0; } Wait(STEP); } }
まず、変数flagについてだが、これは主にライントレースをする場所を決めている。
最初(flag=0の時)は黒線の右側でライントレースをし、大体1つ目の交差点を確実に通過する20秒後(ライントレースを始めてからの時間)にflag=1になり、今度は左側でのライントレースになる。
この切り替えの時に、白線の上にいると脱線してしまう。
そこでコメントAの部分で左旋回をし、少なくとも黒線の上に乗るようにしている。
左側でのライントレースをしてから10秒後にflag=2になるが、これは直角のカーブを確実に通過するタイミングで切り替わる。
これでゴール前の急カーブを曲がり切れるようにコメントBの部分で左旋回の速度を上げている。
構想をそのまま形にするのはなかなか難しいことだと感じた。
このロボットでも一応成功はしているが、やはり交差点は判断できるようにしたかった。
プログラムの前に、まず本体の構造を重要視すべきだと実感した。