目次
今回の課題は図のコースをライントレースして紙コップを移動させるロボットを作るものだった。私は第1コースを走るプログラムを作った。(2017b/Mission2参照)
私たちのグループでは画像のようなシンプルなロボットを作った。
付属の説明書に載っているロボットを用いたかったのだが、どうしても紙コップを取るためのモーターがつけられなかった。また一からロボットを作り始めたのだが、駆動モーターと本体、紙コップを取るためのモーター、センサーをどのようにつけるかを同時に考えねばならずとても苦労した。その結果画像のようなロボットが完成した。また、センサーをタイヤと離れすぎず、近すぎない位置に取り付けることができ、きれいなライントレースをすることができた。
前述のように紙コップを取るモーターを取り付けることが難しかった。そこで本体を床と平行に組むことにより安定して固定することができた。また、アームを安定させるためにシャフトの逆側でも固定した。モーターについているタイヤは、ギヤをかましたためにアームを手で動かすときに折れそうだったために取り付けた。
紙コップを取ろうとロボットの少しのずれでうまく取ることができないことが予想された。そこで、アーチ状のパーツを用いることで画像のように、紙コップの縁に引っかからずに枠内に収めることができるようになっている。
今回のプログラムでは「比例制御」を用いることにより、より滑らかなライントレースを行うことができた。初め「比例制御」を用いると滑らかになると聞いたときは仕組みが全くわからなかったが、ふとその仕組みが分かりプログラムを作って実際に動いたのを見て少し感動した。
「比例制御」とは、その名の通り明るさに比例してモーターの回転量を調節することである。例えば黒線の右側をトレースしたい時、明るくなれば右のモーターを強く,左のモーターを弱く、暗くなれば右のモーターを弱く,左のモーターを強くするようにすれば、きれいにトレースすることができる。また、現在の明るさと明るさの中間値との差を基準となる値に足したり引いたりすることで、明るさに比例して回転量を調節している。
このようにセンサーに関係する値だけを定義した。他にも定義した方が楽なものもあるかもしれないが、時間が無くてそこまで手をつけられなかった。
#define sensor SENSOR_2 //センサーの値 #define black 30 //黒の値 #define white 58 //白の値 #define threshold (black+white)*0.5 //閾値(前述の明るさの中間値) #define deflection (sensor-threshold) //センサーの値と閾値(中間値)との差
黒線の右側をトレースするときは、go_forward_in(),左側をgo_forward_out()とした。右左折と間違えないようにin,outとしたのだがright,leftでも良かった気がする。
sub go_forward_in(int x) //前方へ(黒線の右側) { OnFwd(OUT_A,23+deflection*x); OnFwd(OUT_B,25-deflection*x); } sub go_forward_out(int x) //前方へ(黒線の左側) { OnFwd(OUT_A,23+deflection*x); OnFwd(OUT_B,25-deflection*x); }
下の2つは、F-C間のヘアピンカーブをトレースするときに使うために作ったのだが、明るくなったり暗くなったときに、急に片方のモーターが逆回転してしまうためロボットがガクガクしてしまった。
sub go_left(int x) //左前方へ { OnFwd(OUT_A,20+deflection*x); OnFwd(OUT_B,-20-deflection*x); } sub go_right(int x) //右前方へ { OnFwd(OUT_A,-20+deflection*x); OnFwd(OUT_B,20-deflection*x); }
交差点を判別するためのサブルーチンである。センサーが黒線上に一定時間以上ある時に交差点だと識別できるように、センサーが黒線上にあるとき以外はタイマーをリセットしている。このサブルーチンの名前をintertifyとしているが、これはintesection(交差点)、identify(見分ける)を組み合わせた私の造語である。
sub intertify_in() //交差点まで(黒線の右側) { long t=CurrentTick(); while(CurrentTick()-t<=150){ if(sensor>white-5){ go_forward_in(1.5); t=CurrentTick(); }else if(sensor>black+7){ go_forward_in(1.5); t=CurrentTick(); }else{ go_forward_in(1.5); } } Off(OUT_AB); Wait(10); } sub intertify_out() //交差点まで(黒線の左側) { long t1=CurrentTick(); while(CurrentTick()-t1<=150){ if(sensor>white-5){ go_forward_out(-1.5); t1=CurrentTick(); }else if(sensor>black+7){ go_forward_out(-1.5); t1=CurrentTick(); }else{ go_forward_out(-1.5); } } Off(OUT_AB); Wait(10); }
交差点識別のサブルーチンでは交差点ではないところを交差点と認識してしまうところをトレースするためのものである。そのため、明るさが一定値以上,以下の時、違うサブルーチンを用いた。また、交差点と認識してほしくない範囲を通るだいたいの時間の間だけトレースするようにしている。
sub curve_in(int curve_in_time) //急カーブ(黒線の右側) { long ti=CurrentTick(); while(CurrentTick()-ti<=curve_in_time){ if(sensor>white-5){ go_left(1.8); }else if(sensor>black+7){ go_forward_in(1.5); }else{ go_right(1.8); } } Off(OUT_AB); Wait(10); } sub curve_out(long curve_out_time) //急カーブ(黒線の左側) { long to=CurrentTick(); while(CurrentTick()-to<=curve_out_time){ if(sensor>white-5){ go_right(-1.8); }else if(sensor>black+7){ go_forward_out(-1.5); }else{ go_left(-1.8); } } Off(OUT_AB); Wait(10); }
交差点識別した直後、センサーは黒線の真ん中あたりに来る。その後、再びラインとレースするために黒線の縁にセンサーを持ってこなければならない。これらはそのためのサブルーチンである。また、交差点においてセンサーが来る場所は一緒ではないし、もう一方の縁をトレースさせたい場合もあり、これだけの数を作ることになった。確実に黒線の縁にセンサーを持って行くために、ある明るさになるまで動かすようにしている。
sub escape_straight() //黒線からの脱出(直進) { while(sensor<white-10){ OnFwd(OUT_A,25); OnFwd(OUT_B,35); } Off(OUT_AB); Wait(100); while(sensor>black+10){ OnFwd(OUT_A,30); OnFwd(OUT_B,-20); } Off(OUT_AB); Wait(10); } sub escape_CP() //黒線からの脱出(C,P) { while(sensor<white-10){ OnFwd(OUT_A,30); OnFwd(OUT_B,30); } Off(OUT_AB); Wait(100); while(sensor>threshold){ OnFwd(OUT_A,-30); OnFwd(OUT_B,30); } Off(OUT_AB); Wait(10); } sub escape_R() //黒線からの脱出(R) { while(sensor<white-5){ OnFwd(OUT_A,30); } Off(OUT_AB); Wait(10); } sub escape_Q() //黒線からの脱出(Q) { while(sensor<threshold){ OnFwd(OUT_A,30); OnFwd(OUT_B,15); } Off(OUT_AB); Wait(100); while(sensor>black+10){ OnFwd(OUT_A,30); } Off(OUT_AB); Wait(10); } sub escape_F() //黒線からの脱出(F) { while(sensor<white-5){ OnFwd(OUT_A,40); } Off(OUT_A); Wait(10); while(sensor>threshold+7){ OnFwd(OUT_A,30); } Off(OUT_A); Wait(10); }
アームを上げ下げするためのサブルーチンである。ギア比が1:2で180度回したかったので360度モーターを回している。初めて角度を指定するプログラムを用いたのだが、アームが紙コップの縁に当たったときプログラムが止まってしまったが、ロボットを上げてやると再び動き始めた。そこから、角度を指定するとそれだけ回るまで次のプログラムに行かないということを知った。次の課題に生かそうと思う。
void cup(int c) //取るとき+1,置くとき-1 { Wait(500); RotateMotor(OUT_C,50,-360*c); Wait(2000); }
紙コップを取るときはどうしても黒線から外れなければいけないが、私たちのロボットは黒線から90度回転すればちょうど紙コップを取れるように作った(偶然)ので、シンプルにすることができた。
sub turn(int i) //90°時計回り+1,反時計回り-1 { OnFwd(OUT_A,-40*i); OnFwd(OUT_B,40*i); Wait(800); Off(OUT_AB); Wait(100); }
上の2つのサブルーチンを作ったので、正負を代入するだけでキャッチ&リリースすることができた。
sub catch() //紙コップ取る { turn(+1); cup(+1); turn(-1); } sub put() //紙コップ置く { turn(-1); cup(-1); turn(+1); }
この通りである。前述のように引数は実験的に出したものである。サブルーチンの中に入れた方がきれいになるものもあったが、間に合わなかった。B-C間は初め、intertify_inだけであったが電池交換によって交差点識別してしまい、curve_inを用いることになってしまった。
task main() { SetSensorLight(S2); //A-B intertify_in(); //B-C escape_straight(); curve_in(25000); intertify_in(); //C-R escape_CP(); curve_out(31000); intertify_out(); //R-P Wait(500); PlaySound(SOUND_CLICK); escape_R(); intertify_out(); //P-(X)-Q escape_CP(); curve_out(4500); Wait(2000); catch(); intertify_out(); //Q-S escape_Q(); intertify_in(); //S-(Y)-S Wait(500); PlaySound(SOUND_CLICK); escape_straight(); curve_in(12000); Wait(1000); put(); intertify_in(); //S-F Wait(500); PlaySound(SOUND_CLICK); escape_straight(); intertify_in(); //F-C Wait(500); PlaySound(SOUND_CLICK); escape_F(); intertify_in(); //C-D Wait(500); PlaySound(SOUND_CLICK); turn(+1); OnFwd(OUT_AB,50); Wait(1500); Off(OUT_AB); PlaySound(SOUND_CLICK); }
今回の課題は、課題1と比べると格段に難しかったと感じる。難しかったのはいくつかあり、まず、どのように交差点を識別するかであった。今考えると仕組みは割と簡単なように思うが、初めは全く分からなかった。友人に教えてもらったり調べることでようやく分かったのだが、これの調整に最も時間を割いた。次に、ヘアピンを曲がるときにどうしても片方が逆回転してくれなかったことだ。回らなかったときのモーターの強さは-10〜10くらいであった。片方の強さは50くらいだったので今思えば当然のことだった。基準値を負にすることで逆回転できるようにしたのだが、これでは強制的に片方が逆回転してしまい、比例制御を用いているのに滑らかなトレースではなくなっている。とりあえずプログラムの完成を目指して作っていたので、全てを比例制御で滑らかにトレースさせる前に本番が来てしまった。諸々の調節にも時間がかかり、初めて成功したのは前日だった。しかし、直前に走らせたのだが成功しなかった。そして、授業中の最終調整で少しいじってみると見事本番では成功してくれた。最後に、課題1の時もそうだったのだが、やはり時間の使い方が悪かった。一応成功はしたので良かったのだが、もっと気持ちよく喜べたと思う。次の課題は、期末試験と重なり、今まで以上に時間に苦労すると思うが、今度こそは時間をいいわけにしないように取り組みたい。