目次
#contents
*課題 [#n95b1eaf]
ライントレースマシーンの作成

#ref(./2017b-mission2.png,80%,コース)

私は第三コースを担当した。第三コースは、
    Aをスタート
    Bを直進
    Cを直進
    D地点の紙コップを取得して来たコース戻りCへ向かう
    Cを左折
    Fを直進
    Rを左折(一時停止)
    Pを直進
    X地点に紙コップを置いてコースに戻る
    Qを左折
    Sを右折(一時停止)
    Fを左折(一時停止)
    Cを右折(一時停止)
    D地点へ(ゴール)

というコースです。

*ロボットの説明 [#bcf1227f]

&ref(./DSC_0139.JPG,50%,全体);&ref(./DSC_0138.JPG,50%,前);&ref(./DSC_0141.JPG,50%,横);

本体は基本的な3輪型のロボットで基盤の部分に直接アームを動かすモーターを取り付けた。

ロボット本体とセンサーをなるべく近づけ小回りがきくようにしたので、ヘアピンを綺麗に曲がることができた。

また、アーム部分は複雑な構造にせずコップを囲むだけのような構造にした。軽くてシンプルではあるが、少しずれてしまうとアームを下げるときにコップに当たってしまい確保できないこともあったので、プログラミングで微調整するしかなかった。本当は多少ずれても微調整せずに確実に確保できる機構を考えればもっとよかった。

*プログラムについて [#xfe70714]
**プログラム全体 [#eef9cbcc]
 #define SPEED_H 40
 #define SPEED_L 25
 #define THRESHOLD 45
 #define turn_right0 OnFwd(OUT_C,SPEED_L);OnFwd(OUT_A,-SPEED_L)
 #define turn_right1 OnFwd(OUT_C,SPEED_L);Off(OUT_A)
 #define go_forward OnFwd(OUT_AC,SPEED_H)
 #define turn_left0 OnFwd(OUT_A,SPEED_L);OnFwd(OUT_C,-SPEED_L)
 #define turn_left1 OnFwd(OUT_A,SPEED_L);Off(OUT_C)
 sub follow_line0()
 {
     if(SENSOR_4<THRESHOLD-6) {
       turn_left0;
     }else if(SENSOR_4<THRESHOLD-3){
       turn_left1;
     } else if(SENSOR_4<THRESHOLD+3) {
       go_forward;
     } else if(SENSOR_4<THRESHOLD+6) {      
       turn_right1;
     } else{
       turn_right0;
     }
 }
 sub follow_line1(int nMAX)
 {
     SetSensorLight(S4);
     int nOnline=0;
     while(nOnline<nMAX){
     if(SENSOR_4<THRESHOLD-6) {
       turn_left0;
       nOnline++;
     } else{
        if(SENSOR_4<THRESHOLD-3){
       turn_left1;
     } else if(SENSOR_4<THRESHOLD+3) {
       go_forward;
     } else if(SENSOR_4<THRESHOLD+6) {      
       turn_right1;
     } else{
       turn_right0;
     }
     nOnline=0;
     }
     }
 }
 sub follow_line2(long time)
 {
     SetSensorLight(S4);
     long t0;
     t0=CurrentTick();
     while(CurrentTick()-t0<time){
     follow_line0();
     }
     follow_line1(300);
 }
 sub follow_line3(long time)
 {
     SetSensorLight(S4);
     long t1;
     t1=CurrentTick();
     while(CurrentTick()-t1<time){
     follow_line0();
     }
     Off(OUT_AC);
 }
 sub sound()
 {
     Off(OUT_AC);
     Wait(200);
     PlaySound(SOUND_CLICK);
     Wait(1000);
 }
 sub cross_right(int cross_time)
 {
     turn_right0;
     Wait(cross_time);
     OnFwd(OUT_AC,SPEED_H);
     Wait(300);
 }
 sub cross_left()
 {
     turn_left0;
     Wait(500);
     OnFwd(OUT_AC,SPEED_H);
     Wait(200);
 }
 sub catch_cup()
 {
     turn_right0;
     Wait(300);
     Off(OUT_AC);
     Wait(200);
     OnFwd(OUT_AC,SPEED_H);
     Wait(500);
     Off(OUT_AC);
     Wait(200);
     RotateMotor(OUT_B,10,50);
     OnFwd(OUT_AC,-SPEED_H);
     Wait(800);
     turn_right0;
     Wait(700);
     OnFwd(OUT_AC,SPEED_H);
     Wait(350);
 }
 sub release_cup()
 {
    turn_right0;
    Wait(1300);
    Off(OUT_AC);
    Wait(200);
    OnFwd(OUT_AC,SPEED_H);
    Wait(500);
    Off(OUT_AC);
    Wait(200);
    RotateMotor(OUT_B,-10,60);
    OnFwd(OUT_AC,-SPEED_H);
    Wait(900);
    turn_left0;
    Wait(700);
 }    
 task main()
 {
     follow_line1(300);
     Off(OUT_AC);
     Wait(100);
     catch_cup();
     follow_line2(16000);
     sound();
     turn_left0;
     Wait(500);
     OnFwd(OUT_AC,SPEED_H);
     Wait(200);
     follow_line2(1500);
     cross_right(400);
     follow_line3(3500);
     release_cup();
     follow_line2(4000);
     sound();
     cross_right(600);
     follow_line1(300);
     sound();
     cross_left();
     follow_line2(2000);
     sound();
     cross_right(1500);
     OnFwd(OUT_AC,SPEED_H);
     Wait(1000);
     Off(OUT_AC);
 }
    
**マクロ [#jb7afe51]
  #define SPEED_H 40                          //速く移動するとき、おもに直進するとき使用
  #define SPEED_L 25                          //遅く移動するとき、おもにカーブで使用
  #define THRESHOLD 45            //しきい値45
  #define turn_right0 OnFwd(OUT_C,SPEED_L);OnFwd(OUT_A,-SPEED_L)   //右旋回
  #define turn_right1 OnFwd(OUT_C,SPEED_L);Off(OUT_A)             //右に曲がる
  #define go_forward OnFwd(OUT_AC,SPEED_H)              //直進
  #define turn_left0 OnFwd(OUT_A,SPEED_L);OnFwd(OUT_C,-SPEED_L)    //左旋回
  #define turn_left1 OnFwd(OUT_A,SPEED_L);Off(OUT_C)         //左に曲がる

**サブ関数 [#gfb98a48]

交差点を判断するうえで重要になってくるwhile内の nOnline<nMAX と CurrentTick()-t0<time について説明する。

1. nOnline<nMAX について

交差点の判断ではカウンタを利用した。nOnlineという引数が連続してセンサーが黒(この場合閾値ー6以下の数値)を認識した回数をカウントし、認識するとnOnline++でカウンタを増やす、そしてカウンタの数値がnMAXを超えるとそこは交差点である、と認識する。nMAXに代入する数値によってどの程度黒が続けば交差点と認識するかを調整できる。実際にはnMAXに300を代入したので0.3秒間黒を認識し続けると交差点と判断するということである。

2.CurrentTick()-t0<time について

CurrentTick()は行われているサブ関数が開始してから現在までの時間を表しt0は行われているサブ関数が開始した時間である。つまり CurentTick()-t0<time することによって新しい動作を行うたびに時間をはかりtimeで指定した時間経過するまではある動作を、経過後は別の動作をさせるようにできた。こうすることでヘアピンを誤って交差点と認識したり、Q地点でT字路と認識して止まらないようにすることができた。
CurrentTick()は行われているサブ関数が開始してから現在までの時間を表しt0は行われているサブ関数が開始した時間である。つまり CurentTick()-t0<time することによって新しい動作を行うたびに時間をはかり、timeに代入した時間経過するまではある動作を、経過後は別の動作をさせるようにできた。こうすることでヘアピンを誤って交差点と認識したり、Q地点でT字路と認識して止まらないようにすることができた。


 sub follow_line0()                          //ひたすらトレースする関数
 {
     if(SENSOR_4<THRESHOLD-6) {
       turn_left0;
     }else if(SENSOR_4<THRESHOLD-3){
       turn_left1;
     } else if(SENSOR_4<THRESHOLD+3) {
       go_forward;
     } else if(SENSOR_4<THRESHOLD+6) {      
       turn_right1;
     } else{
       turn_right0;
     }
 }
 sub follow_line1(int nMAX)                   //交差点を識別できるトレースする関数
 {
     SetSensorLight(S4);
     int nOnline=0;
     while(nOnline<nMAX){
     if(SENSOR_4<THRESHOLD-6) {
       turn_left0;
       nOnline++;
     } else{
        if(SENSOR_4<THRESHOLD-3){
       turn_left1;
     } else if(SENSOR_4<THRESHOLD+3) {
       go_forward;
     } else if(SENSOR_4<THRESHOLD+6) {      
       turn_right1;
     } else{
       turn_right0;
     }
     nOnline=0;
     }
     }
 }
 sub follow_line2(long time)                 //指定した時間経過するまではひたすらトレースし、経過後は交差点を識別する関数に切り替わる関数
 {
     SetSensorLight(S4);
     long t0;
     t0=CurrentTick();
     while(CurrentTick()-t0<time){
     follow_line0();
     }
     follow_line1(300);
 }
 sub follow_line3(long time)               //指定した時間経過するまではひたすらトレースし、経過したら止まる関数
 {
     SetSensorLight(S4);
     long t1;
     t1=CurrentTick();
     while(CurrentTick()-t1<time){
     follow_line0();
     }
     Off(OUT_AC);
 }
 sub sound()                        //音を鳴らす関数
 {
     Off(OUT_AC);
     Wait(200);
     PlaySound(SOUND_CLICK);
     Wait(1000);
 }
 sub cross_right(int cross_time)            //交差点、T字路を識別した時右に旋回して交差点、T字路を渡る関数
 {
     turn_right0;
     Wait(cross_time);
     OnFwd(OUT_AC,SPEED_H);
     Wait(300);
 }
 sub cross_left()                     //交差点、T字路を識別したとき左に旋回して交差点、T字路を渡る関数
 {
     turn_left0;
     Wait(500);
     OnFwd(OUT_AC,SPEED_H);
     Wait(200);
 }
 sub catch_cup()                  //コップを取りに行き、コースに復帰するまでの関数
 {
     turn_right0;
     Wait(300);
     Off(OUT_AC);
     Wait(200);
     OnFwd(OUT_AC,SPEED_H);
     Wait(500);
     Off(OUT_AC);
     Wait(200);
     RotateMotor(OUT_B,10,50);
     OnFwd(OUT_AC,-SPEED_H);
     Wait(800);
     turn_right0;
     Wait(700);
     OnFwd(OUT_AC,SPEED_H);
     Wait(350);
 }
 sub release_cup()       //コップを指定された場所においてコースに復帰するまでの関数
 {
    turn_right0;
    Wait(1300);
    Off(OUT_AC);
    Wait(200);
    OnFwd(OUT_AC,SPEED_H);
    Wait(500);
    Off(OUT_AC);
    Wait(200);
    RotateMotor(OUT_B,-10,60);
    OnFwd(OUT_AC,-SPEED_H);
    Wait(900);
    turn_left0;
    Wait(700);
 }

**メイン関数 [#rc1b99b4]
この課題を成功させるうえで上でもっとも重要なことは交差点、T字路を識別し、そのまままがって行く時と直進する時を区別することである。私はタイマーを用いて〇秒経過するまではひたすらトレースする関数、〇秒経過以降は交差点、T字路を識別する関数、の二つを組み合わせて一つのサブ関数を作ることで解消した。また、ライントレースを始めるときにセンサーが黒の部分にあるとすぐにカウンタが反応し交差点と認識しやすくなってしまう(特にC地点R地点)現象も同じようにこのタイマーを使うことで解消できた。
 
 task main()
 {
     follow_line1(300);          //AをスタートしてDまで行く、DのT字路を認識して止まる。
     Off(OUT_AC);
     Wait(100);
     catch_cup();             //コップを取り、コースに戻る。
     follow_line2(16000);         //DからRまでいく、途中のヘアピンで交差点と認識してしてしまはないように16秒経過するまではひたすらトレースし、16秒以降は交差点またはT字路を認識すると止まる。
     sound();               //音を出す
     turn_left0;              //左旋回
     Wait(500);
     OnFwd(OUT_AC,SPEED_H);        //前進(これで再びトレースできる位置に戻る)
     Wait(200);
     follow_line2(1500);         //RからPまで行く、トレースし始めは交差点と認識してしまう場合があったので1.5秒だけ認識しないようにした。
     cross_right(400);          //PのT字路を通過
     follow_line3(3500);         //PからPとQの間の位置まで移動、コップを置きに行くためPを出発してから3.5秒経過すると止まる。
     release_cup();           //コップを指定された場所に置きコースに戻る。
     follow_line2(4000);         //コースに戻った位置からSまで行く、QをT字路と認識して止まらないように4秒経過するまではひたすらトレースし4秒以降はT字路を認識すると止まる。
     sound();              //音を出す
     cross_right(600);         //右に旋回し交差点を右折する
     follow_line1(300);         //SからFまで行く
     sound();              //音を出す
     cross_left();           //左旋回して左折する
     follow_line2(2000);        //FからCまで行く
     sound();             //音を出す
     cross_right(1500);        //右折してT字路を右折する
     OnFwd(OUT_AC,SPEED_H);      //前進してゴール
     Wait(1000);
     Off(OUT_AC);
 }

*まとめ [#d1fa0513]
ロボット本体については、思い切ってほぼゼロから組み立てたが、結果構造がシンプルで小回りの利くものができてよかった。

プログラムについては、しきい値とそのプラスマイナスの値でかなりトレースするスピードが変わっていたので最適な値を得るのに時間がかかった。また、交差点、T字路の認識はタイマーを使ってトレースの仕方を変えるという方法を思いついたことでスムーズに解決できた。

反省点は、電池の残量によって動くスピードが変わってしまうので引数の微調整に苦戦してしまったので次回はできる限り電池に左右されないようなプログラムを作りたい。

前回の習字ロボットではうまくいかなかったが今回は本番でもうまくいったので良かった。

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS