目次
私は課題2の1人目のコースを行った。
まず全体像を下に示す。
NXT本体とセンサをなるべく低い位置につけ、とにかく重心を低くし、安定性を上げた。しかし、センサを低く設置したことで、ロボットの前にスペースがなくなり、ボールを回収する機構は後ろに付けることになった。超音波センサはボールにうまく反応しなかったため、設置場所に悩んだが、ボールの高さに合わせるのが一番反応しやすいと考えたため、ボールの高さで床と水平になるように設置することにした。
ボールの回収は、上の写真のようにアームを作り、モータによるアームの上げ下げでボールを囲い、そのまま引っ張っていく行くことにした。
#define spd 30 //スピード #define go_right() OnFwd(OUT_A,spd); #define turn_right() OnFwdSync(OUT_AC,spd,-100); #define go_left() OnFwd(OUT_C,spd); #define turn_left() OnFwdSync(OUT_AC,spd,100); #define go() OnFwdSync(OUT_AC,spd,0); #define up OnFwd(OUT_B, -30);Wait(500) //ボールキャッチャー上げる #define down OnFwd(OUT_B, 30);Wait(700) //ボールキャッチャー下げる const float diameter = 5.45; //タイヤの直径 const float track = 11.05; //タイヤ間の幅 const float pi = 3.1415; //円周率
この定義は主に、下で順次説明する関数の中で使う。
void fwd(float d) //距離dcm前進する関数 { long angle; angle = d/(diameter*pi)*360.0; RotateMotorEx(OUT_AC,spd,angle,0,true,true); } void turnAng(long ang) //ang度(指定した角度)旋回する関数 { long angle; angle = (track/diameter)*ang; RotateMotorEx(OUT_AC,spd,angle,100*sign(ang),true,true); }
下から2行目のRotateMotorExの中に「sign(ang)」とあるが、これを付けることでマイナスの角度を指定すると逆回りに旋回するようになる。
float search_ball(long ang) //現在の方向を中心にang度の範囲で探し障害物までの距離を返す { SetSensorLowspeed(S2); long tacho_min; float d_min; d_min = 100000; //仮の最小値 turnAng(ang/2); //指定された角度の半分だけ旋回 ResetTachoCount(OUT_AC); //角度計測をリセット int kakudo; float Sens; Wait(900); kakudo = 0; while(kakudo <= ang){ //ang/20度ずつ旋回し、ボールまでの距離を測る turnAng(-ang/20); Wait(900); Sens = SensorUS(S2); if(Sens < d_min){ d_min = Sens; //仮の最小値を更新 tacho_min = MotorTachoCount(OUT_A); } kakudo = kakudo + ang/20; } Wait(1000); turnAng( - (MotorTachoCount(OUT_A) - tacho_min + 50));//ボールの向きを向く *50は補正値 Off(OUT_AC); Wait(100); return d_min; }
tacho_minはモータの回転角で、d_minは指定された角度の範囲中で最短距離にある障害物までの距離を表す。初めは止まることなく旋回しボールまでの最短距離を出そうとしたが、その方法だと超音波センサがうまくボールを見つけられず反応しなかったため、少し旋回し、止まって超音波センサを動かし、再び少し旋回するというのを繰り返す方法に変更した。
void follow_line(long tmin = 0,long tmax = 10000)//スタートからの時間がtmin以下では、交差点を無視してライントレースし、tmax経てば、交差点に達しなくても停止 { long t_start,t1; t_start = CurrentTick(); SetSensorLight(S3); int intencity; t1 = CurrentTick(); while((CurrentTick() - t1 < 300||CurrentTick() - t_start < tmin)&&(CurrentTick() - t_start < tmax)){ intencity = SENSOR_3; if(intencity < 33){ //黒 turn_left(); //左旋回 }else { if(intencity > 43){ //白 turn_right(); //右旋回 }else if(intencity > 38 && intencity <= 43){ //白灰 go_right(); //右折 }else if(intencity >= 37 && intencity <= 38){ //灰 go(); //直進 }else { go_left(); //左折 } t1 = CurrentTick(); } } turn_right();Wait(500);//微調整 Off(OUT_AC);Wait(2000); }
私が行った1人目のコースはラインの左側境界をトレースするだけでゴールに到達できるコースだったので、右側境界をトレースするプログラムはを用いなかった。工夫したポイントとして、上から7行目のプログラムのwhileの中に「または」を意味する記号「||」や、「かつ」を意味する記号「&&」を使うことで、スタートからの時間がtmin以下では交差点を無視してライントレースし、tmax経てば交差点に達しなくても停止するようなプログラムを簡潔に書いた。また、黒の部分が0.3秒間以上続くと交差点と判断し、止まるようにしたが、その際少し左に旋回してから止まってしまうので、少し右に旋回し、交差点に対して垂直になってから止まるように微調整した。
void fetch_ball()//ボールを見つけ回収し、戻ってくる { float d = search_ball(90); fwd(d - 17); up; turnAng(200); down; turn_left();Wait(1500); go();Wait(500); follow_line(16000,30000); }
90度の範囲でボールを探すようにしたが、実際はもう少し狭い範囲の方が誤差が少なくなったと思う。旋回してボールを回収した後はラインの左を沿わせるために、少し左を向き少し進むようにした。また、帰る途中で交差点と間違えて止まってしまう箇所があったため、16秒間は止まらずライントレースするようにした。
task main() { follow_line(8500,20000);//AからD go();Wait(600); follow_line(9000,20000);//DからG go();Wait(400); turnAng(90); follow_line(0,3500);//GからH turnAng(90); OnFwd(OUT_AC,-spd);Wait(700); fetch_ball();//HからJ go();Wait(2500); Off(OUT_AC); }
途中、交差点と間違えて止まってしまったり、交差点で止まらなかったりする箇所があったので、tminとtmaxをうまく使って調整してある。
ボールを回収する機構をロボットの後ろに付けたことで、ボールを回収するために旋回する時、ボールにロボットが当たらないように気をつけないといけなかったり、旋回した後にアームがボールを回収できる範囲にいなければならなかったりなど、ボールを回収する際に考慮するべきことが多くなってしまった。また、私たちのロボットは光センサが少し地面から離れていたため、教室の明るさが違うと光センサの値が変わってしまい、閾値を決めるのに苦労した。他のチームのロボットを見ると超音波センサは下ではなく上の方にボールを見下ろす形で付けていて、ボールを感知できていたので、センサの位置も重要であると感じた。今回は電池の残量にも気を使い、ロボットの動きで大きな変動はなかったため、前回の失敗を生かすことができたと言える。
最後に、課題2ではプログラムというソフトの面ではうまく工夫できたが、ロボットの構造というハードの面でやや問題が生じたため、次回の課題3では今回の反省を生かし、ロボットの構造やセンサの位置に気をつけて取り組もうと思う。