今回の課題は、前回の課題で使用したライントレースの紙をつかって行う。二枚を合わせて一つのコースとする。 赤と青のボールを二箇所に設置し、一台のロボットがボールをつかみ向こう側の缶に当てる。その時ボールを放つロボットは真ん中の線を越えてはならない。そしてもう一台のロボットは、放たれ缶にあたったボールを拾いゴールまで持っていく。
私達のグループでは、二人がロボットの組み換え作業を、もう二人がプログラムを組むという体制で課題に取り組んだ。
ロボットは二台必要なので、ライントレースをした後にボールを察知し拾うロボット(左)と、ボールを探した後にゴールまで持っていくロボット(右)を使用した。
このロボットの特徴はアームと首(超音波センサー)が一緒に動くところである。腕が閉じている状態、つまりボールを掴んでいる状態ならば、次にするべき動作は缶を探す動作なのでセンサーを上に向くよにした。反対に、腕を開いているときはボールを探している時なので、ボールだけを探せるようにセンサーが下向きになるようにした。
また、ボールをつかむことを最優先としたロボットを組んだためアームによってボールを打ち出すことが難しくなってしまった。そこでロボット本体でボールを押し出す形となった。
まず、モーターAによって腕の開閉(超音波センサーの上下)を行うことができるようにした。 よく使う動作であるため、開閉を定義した(get=腕を閉める=超音波センサーを上げる,open=腕を広げる=超音波センサーを下げる)。モーターAが動くことにより、歯車で接続してある左腕と超音波センサーが動く仕組みになっている。
また、腕の片方を90°に曲がったパーツにし、足元に日本の補助パーツを入れたことで、ボールをキャッチした際に腕の中でボールが暴れないようにした。ボールが腕の中で暴れてしまうと、ボールを放つ際に、ロボットが再び腕を広げ一時停止をしたときに腕の中からボールが逃げてしまうことがあるためである。そうすると、押し出して放つはずのボールがまっすぐに飛ばなくなってしまう。
次にモーターBCでタイヤ2つを動かし、下の部分につけた光センサーによってライントレースをするようにした。(プログラムは課題2のものを使用)
#define straight OnFwd(OUT_BC,30); #define turn_left OnFwd(OUT_B,30);Off(OUT_C); #define turn_right OnFwd(OUT_C,30);Off(OUT_B); #define TURN_LEFT OnFwd(OUT_B,30);OnRev(OUT_C,30); #define TURN_RIGHT OnFwd(OUT_C,30);OnRev(OUT_B,30); #define THRESHOLD 35 #define get OnFwd(OUT_A,45);Wait(300);Off(OUT_A);Wait(1000); #define open OnRev(OUT_A,45);Wait(300);Off(OUT_A);Wait(1000);
(此処から先の缶を探すプログラムは授業中に配布されたプリントを利用した。)
const float diameter=5.45; const float track=10.35; const float pi=3.1415;
void fwdDist(float d) { long angle; angle=d/(diameter*pi)*360.0; RotateMotorEx(OUT_BC,30,angle,0,true,true); }
void turnAng(long ang) { long angle; angle=track/diameter*ang; RotateMotorEx(OUT_BC,30,angle,100,true,true); }
int searchDirection(long ang)
{ long angle,tacho_min=0,tacho_corr; int d_min; d_min=55; angle=(track/diameter)*ang; turnAng(ang/2); ResetTachoCount(OUT_BC);
OnFwdSync(OUT_BC,30,-100); while(MotorTachoCount(OUT_B)<=angle){ if(SensorUS(S4)<d_min){ PlaySound(SOUND_CLICK); d_min=SensorUS(S4); tacho_min=MotorTachoCount(OUT_B); } } OnFwdSyncEx(OUT_BC,30,100,RESET_NONE); until(MotorTachoCount(OUT_B)<=tacho_min||SensorUS(S4)<=d_min);
Wait(14); Off(OUT_BC);Wait(500); return d_min; }
task main(){ SetSensorLight(S3); SetSensorLowspeed(S4); int catch=0;
(出発と同時にアームを開けるのは、超音波センサーを下に向けボール以外の物体の誤認を防ぐためである)
straight; Wait(500); RotateMotor(OUT_A,30,-80);
(超音波センサーが12cm未満、つまりボールを検出するまでライントレースを続ける)
while(true){ if(SensorUS(S4)>=12){ if(SENSOR_3<THRESHOLD-9){ TURN_LEFT; }else if(SENSOR_3<THRESHOLD-5){ turn_left; }else if(SENSOR_3<THRESHOLD+5){ straight; }else if(SENSOR_3<THRESHOLD+9){ turn_right; }else{ TURN_RIGHT; } }else{ Off(OUT_BC); get; Wait(1000); catch++;
(catchという数は、何個目のボールつかむかによって、その後の動作を変えるために利用した。しかし、時間が足りなかったため二個目のプログラムを書くことができなかった)
if(catch==1){ RotateMotorEx(OUT_BC,40,-200,0,true,false); RotateMotorEx(OUT_BC,40,200,-100,true,false); Off(OUT_BC); Wait(500); RotateMotorEx(OUT_BC,40,700,0,true,false); Off(OUT_BC); Wait(500); int d=searchDirection(130); PlaySound(SOUND_UP);
(缶を探した時、50cm以下の最小値の方に向かうようにした)
if(d<=50){ Off(OUT_BC); Wait(500); RotateMotorEx(OUT_BC,40,-300,0,true,false); Wait(6000); open; RotateMotorEx(OUT_BC,90,340,0,true,false); Off(OUT_BC); Wait(1000); RotateMotorEx(OUT_BC,65,-730,0,true,true); RotateMotorEx(OUT_BC,40,150,100,true,false); RotateMotor(OUT_A,60,50); straight; Wait(2500); } } } } }
初めに、紙のはしまで来て缶の前のボールを探す。察知したらボールを取りに行く。もし察知しなかった場合、つまり缶の後ろにボールが行ってしまった場合、ロボット自身が反対側に移動しボールを探す。ボールをつかんだ後は、途中までラインとレースをしたのちゴールまで一直線に進む。
ボールを探しに行くため、超音波センサーはボールを放つロボットに比べて上を向いている。またボールをがっちりつかむため、アームは「くまで」のような形をしていて、ボールを見つけると右と左のパーツが交互に組み合わさり、ボールが外に出て行かないような構造になっている。
まず、すべての値に対して数値の微調整に苦労した。特に超音波センサーの距離の設定が大変だった。小さすぎる値に設定すれば反応してくれなくなってしまい、大きすぎる値に設定すると目標とは異なるものに反応してしまっていた。
またバッテリーの残量によって動きがだいぶ左右されてしまうので、モーターの回転角度や速度を少しずつ治していくのが大変だった。
構造の部分にも書いたが、ボールを放つ側のロボットの内部に、ボールの振動を抑えるためにパーツを組み込んだことで、ボールを掴んでから放つまでの動作の誤差がだいぶ減った。
いままで一度もロボットに触れたことがなく、かつプログラムなんて書いたこともなかったが、この授業のおかげで両方に触れ合うことができた。今後も機会があったら積極的に取り組んでいきたい。