[[2019a/Member]]

目次
#contents

*課題について [#v7f72e33]

詳しくはリンクを参照[[2019a/Mission3]]

*ロボットについて [#s311aae2]

今回のロボットは,2つのNXTを通信させることでモーターを4つ使用できるようにし,2つを移動用,2つをボールをキャッチするアーム用とすることでスムーズに動くように工夫した.

#ref(./s_S__46137369.jpg)

#ref(./s_S__46137370.jpg)

↑機体を前方から撮影した図

#ref(./s_S__46137371.jpg)

↑機体を後方から撮影した図

#ref(./s_S__46137368.jpg)

↑アームを上下させる部分の図

**センサーの取付け位置 [#p951d4cb]

明るさを読み取るための光センサーをロボットに取り付ける際の工夫として,紙に書かれたラインを正確に読み取るために紙に対して垂直にセンサーを取り付けた.

また,センサーの取り付け位置をタイヤの軸にできるだけ近づける事によって旋回した際のセンサーの振り幅を小さくし,センサーによる判断をより繊細にできるようにした.

**アームについて [#x6afe20f]

このロボットでは,ボールをきれいにつかめるように開閉するアームとそれを上下に動かす機構の2つを組合わせたものを採用した.

開閉するアームは,同じ大きさのギアをつなげ一つのモーターで左右に開閉する動作を可能にした.(機体を前方から撮影した図を参照)

アームを上下させる機構は,ひし形の角の点を回転させることにより上下の動作を可能にした.
また,モーターを直に取り付けて上下させようとすると先端についているアーム(とそれを動かすモーター)の重みで上下させるためのモーターに負荷がかかってしまうので,モーター側に小さなギア,ひし形の角の側に大きなギアを使いつなげることでモーターにかかる負荷をより少なくし,安定した動作とパーツのダメージ軽減を可能にした.(アームを上下させる部分の図を参照)

*プログラミングについて [#k0cfaf75]

このロボットは,2つのNXT(マスター側とスレイブ側)で通信を行い,マスター側は機体の移動,スレイブ側はアームの開閉・上下をそれぞれ担当している.

今回作ったプログラムではライントレースする際判定をより繊細にしなめらかに動作するように
,センサーの値を5段階で判定した.

まずNXTのモニター機能でセンサーが完全に黒の線上にある時と完全に白の紙の上にあるときの数値を計測し,それぞれをblack・whiteと定義する.これにより,black以下の数値のときは線上,white以上の数値のときは線外,間の数値のときは線と紙の境目の灰色のところにセンサーがあることになる.

次に,bkackから+5の値までを黒寄りの灰色,whiteから−5の値までを白よりの灰色と定義することによって,5段階の判定が可能となった.

**マスター側のプログラム [#z1c84b5d]

***定義 [#t1ad70b0]

まずdefineで次のように定義した.

 #define black 30                                         //完全に黒のときの値
 #define white 50                                         //完全に白のときの値
 #define speedH 60                                        //速いスピード
 #define speedL 30                                        //遅いスピード
 #define speedM 20                                        //逆回転ときのスピード
 #define mae OnFwd(OUT_BC,speedH);                        //前進の動作
 #define ushiro OnRev(OUT_BC,speedH);                     //後退の動作
 #define turnRRR OnFwd(OUT_C,speedH);OnRev(OUT_B,speedM); //一番キツい右曲がり
 #define turnLLL OnFwd(OUT_C,speedH);OnRev(OUT_C,speedM); //一番キツい左曲がり
 #define turnRR OnFwd(OUT_C,speedH);Off(OUT_B);           //右曲がり
 #define turnLL OnFwd(OUT_B,speedH);Off(OUT_C);           //左曲がり
 #define turnR OnFwd(OUT_C,speedH);OnFwd(OUT_B,speedL);   //緩めの右曲がり
 #define turnL OnFwd(OUT_B,speedH);OnFwd(OUT_C,speedL);   //緩めの左曲がり
 #define step 1                                           //ライントレースの動作時間
 #define jikan 100                                        //完全に黒で居続けられる時間の最大値(しきい値)
 #define teishi Off(OUT_BC);Wait(300);                    //一時停止
 #define CONN 1                                           //スレイブの番号
 #define SIGNAL1 11                                       //スレイブとやり取りする信号の番号
 #define SIGNAL2 12                                       //同上
 #define SIGNAL3 13                                       //同上
 #define SIGNAL4 14                                       //同上
 #define curveL OnFwd(OUT_B,20);OnRev(OUT_C,10);          //左に回転してカーブする動作

***直線のライントレース(交差点に差し掛かるとループから抜ける)のサブルーチン [#w6a2482d]

このプログラムではラインの左側をトレースしている.

トレースをする際黒のライン上にいるときは左に曲がるように設定しているが,交差点に差し掛かったとき黒であるとロボットが判断している時間が普通のライントレースをしているときより長いことを利用し,黒である時間のしきい値を定め,左曲がりしている時間がその値より長ければ交差点,短ければ普通のライントレースの最中であると定めた.

CurrentTickを使うこととによって完全に黒い線上にいる時間を測り,その値がjikanで定義したしきい値を超えた場合それを交差点と判断しwhileループから抜け出し次のプログラムに行ける様になっている.

ここでは黒でいる時間の上限をt_maxとし,そこにjikanの値を代入してしきい値の役割を与えている.

 void linetraceS()                      //完全に黒がjikan以上だったら外れる
 {
     long t0=0,t_black=0,t_max=jikan;
 
     t0=CurrentTick();                  
     while(t_black<t_max){              //t_blackがt_maxの値より大きくなったらループから抜ける
         if(SENSOR_3>white){            //完全に白のとき
          turnRR;                       //右曲がり
         } else if(SENSOR_3>white-5){   //白よりの灰色のとき
          turnR;                        //緩い右曲がり
         } else if(SENSOR_3>black+5){   //灰色のとき
          mae;                          //前進
         } else if(SENSOR_3>black){     //黒寄りの灰色のとき
          turnL;                        //緩い左曲がり
         } else{                        //完全に黒のとき
          turnLL;                       //左曲がり
          t0=CurrentTick();             //現在時間をt0とする(時間を測り始める)
          until((SENSOR_3<black+5)&&(SENSOR_3>black));//黒寄りの灰色になるまで測り続ける
          t_black=CurrentTick()-t0;     //現在時刻からt0の値を引いたもの(右曲がりしていた時間)をt_blackとする
         }
      Wait(step);
     }
 }

***カーブのときのライントレース(交差点に差し掛かるとループから抜ける)のサブルーチン [#m6908bb5]

交差点の判断は直進のときと同様の方法で行っている.

基本的には直線のときと同じ条件での構成になるが,今回制作したロボットの旋回半径がコースのカーブの半径より大きくなってしまい,内側のタイヤを逆回転する必要が出てきたが,それを直線でも行ってしまうと動作がなめらかでなくなってしまうので,カーブのときのライントレースは直線のときとは別のサブルーチンを使用した.

 void linetraceS()                      //完全に黒がjikan以上だったら外れる
 {
     long t0=0,t_black=0,t_max=jikan;
 
     t0=CurrentTick();                  
     while(t_black<t_max){              //t_blackがt_maxの値より大きくなったらループから抜ける
         if(SENSOR_3>white){            //完全に白のとき
          turnRRR;                      //キツい右曲がり
         } else if(SENSOR_3>white-5){   //白よりの灰色のとき
          turnRR;                       //右曲がり
         } else if(SENSOR_3>black+5){   //灰色のとき
          mae;                          //前進
         } else if(SENSOR_3>black){     //黒寄りの灰色のとき
          turnLL;                       //左曲がり
         } else{                        //完全に黒のとき
          turnLLL;                      //キツい左曲がり
          t0=CurrentTick();             //現在時間をt0とする(時間を測り始める)
          until((SENSOR_3<black+5)&&(SENSOR_3>black));//黒寄りの灰色になるまで測り続ける
          t_black=CurrentTick()-t0;     //現在時刻からt0の値を引いたもの(右曲がりしていた時間)をt_blackとする
         }
      Wait(step);
     }
 }

***交差点を直進して突破するサブルーチン [#tb79d878]

前に示した2つのサブルーチンでは,完全に黒でいる時間が長いとき交差点と判断しループから抜けている.つまり交差点と判定された瞬間は線上で進行方向に対し斜め左を向いていることになる.

その状況を突破するために,まず白よりの灰色になるまで前進しその後黒寄りの灰色になるまで右曲がりを続けるというプログラムを組んだ.

 void crossline()
 {
     mae;                    //前進
     until(SENSOR_3>white-5);//白よりのグレーまで
     turnRR;                 //右曲がり
     until(SENSOR_3<black);  //黒寄りのグレーまで
 }

***交差点を曲がるサブルーチン [#oeacb03d]

・右に曲がるときのサブルーチン

このときも同様に右斜め前を向いて始まるので,そこから完全に白になるまでキツめに左に曲がり突破する.

 void Rcurve()
 {
     turnRRR;                //キツい右曲がり       
     until(SENSOR_3>white);  //完全に白になるまで
 }


・左に曲がるときのサブルーチン

このときもやはり斜め右を向いた状態から始まるので,そのまま完全に白になるまで回転して突破する.

 void Lcurve()
 {
     curveL;                 //左曲がり
     until(SENSOR_3>white);  //完全に白になるまで
 }

***回転するサブルーチン [#x94161f2]

コースのL・L'の地点において,ボールを掴んだあと道を引き返す際回転するサブルーチン

ボール回収後,ラインから外れるために一度前進しその後ラインに近づくまで回転することによって,ゴールに向けての方向転換を実現した.

void kaiten()
 {
    mae;                               
     Wait(900);                         
     OnFwd(OUT_C,20);OnRev(OUT_B,10);   
     until(SENSOR_3<black+5);          
 }

***ボールを掴む・離す際のサブルーチン(マスター側) [#k3bb955f]

このロボットは,ボールを掴んで離す一連の動作をスレイブ側で行っているので,マスター側はボール(もしくはゴールの箱)がある位置に機体が来たらスレイブ側に掴む(または離す)ような命令を送り,掴む(または離す)動作が完了したというメッセージが帰ってくるまで止まるという動作を一つのサブルーチンとしてまとめた.

ここでは信号の混線を避けるため,掴む動作と離す動作で異なるMAILBOX,信号を用いている.

・ボールを掴む際のサブルーチン

掴む際マスターとスレイブでやり取りする信号をmsg1としてそこにSIGNAL1(ここではボール前についたことを知らせる信号),SIGNAL2(ここでは掴む動作が終了したことを知らせる信号)を代入し,それをMAILBOX1を通してやり取りしている.

 void catch()
 {
     int msg1;                                    //msg1を定義する
 
     SendRemoteNumber(CONN,MAILBOX1,SIGNAL1);     //CONN(ここではスレイブ1)にMAILX1を通してSIGNAL1(ここではボール前についたことを知らせる信号)を送信する
     while(msg1!=SIGNAL2){                        //msg1の値がSIGNAL2(ここでは掴む動作が終了したことを知らせる信号)になったらループを抜ける
         ReceiveRemoteNumber(MAILBOX1,true,msg1); //MAILBOX1でスレイブから信号を受け取る
         Off(OUT_BC);                             //ループの間(掴む動作が終了したことを知らせる信号がスレイブから帰ってくるまで)機体を停止させる
     }  
 }

・ボールを離す際のサブルーチン

離す際マスターとスレイブでやり取りする信号をmsg2としてそこにSIGNAL3(ここではゴールの箱の前についたことを知らせる信号),SIGNAL4(ここではボールを離す動作が終了したことを知らせる信号)を代入し,それをMAILBOX2を通してやり取りしている.

 void release()
 {
     int msg2;                                   //msg2を定義する
 
     SendRemoteNumber(CONN,MAILBOX2,SIGNAL3);    //スレイブにMAILBOX2を通してSIGNAL3(ここではゴールの箱の前についたことを知らせる信号)を送信する
     while(msg2!=SIGNAL4){                       //msg2の値がSIGNAL4(ここではボールを離す動作が終了したことを知らせる信号)になったらループを抜ける
         ReceiveRemoteNumber(MAILBOX2,true,msg2);//MAILBOX2でスレイブから信号を受け取る
         Off(OUT_BC);                            //ループの間(離す動作が終了したことを知らせる信号がスレイブから帰ってくるまで)機体を停止させる
     }  
 }

***プログラム全体 [#r730834f]

今回の課題では,箱に入っているボールは一つだけのとき位置が確定できないので一つだけ取ることにし,その他L,L'のボールの計3つをゴールする作戦をとった.

task main()
 {
    SetSensorLight(S3);


    mae;                                    //前進
    Wait(300);
    linetraceC();                           //M to K
    Lcurve();                               //箱の方に向きを変える
    linetraceC();                           //K to J
    OnRev(OUT_C,speedL);                    //向きの微調整
    Wait(500);                              //上に同じ
    catch();                                //ボールを掴む
    linetraceC();                           //J to I
    Lcurve();                               //Iの方に向きを変える
    linetraceC();                           //I to C
    Lcurve();                               //Bの方に向きを変える
    linetraceS();                           //C to B
    Rcurve();                               //中央の線の方に向きを変える
    linetraceC();                           //B to 中央の線
    Rcurve();                               //A'の方に向きを変える
    linetraceS();                           //to A'
    Lcurve();                               //B'の方に向きを変える
    linetraceC();                           //to B'
    Lcurve();                               //C'の方に向きを変える
    linetraceS();                           //B' to D'
    Rcurve();                               //H'の方に向きを変える
    linetraceC();                           //D' to H'
    OnRev(OUT_C,speedL);                    //向きの微調整
    Wait(500);                              //上に同じ
    release();                              //ボールをはなす
    ushiro;                                 //H'toD'
    Wait(2000);                             
    OnFwd(OUT_C,20);OnRev(OUT_B,10);        //D'で回転
    until(SENSOR_3>white-5);                //白よりの灰色になるまで
    linetraceC();                           //D'toC'
    crossline();                            //C'
    linetraceS();                           //C'toB'
    Lcurve();                               //B'
    linetraceS();                           //B'toM'
    Rcurve();                               //B'とM'の間の角
    linetraceS();                           //B'toM'
    Lcurve();                               //M'
    linetraceC();                           //M'toK'
    crossline();                            //K'
    OnFwd(OUT_BC,30);Wait(2900);Off(OUT_BC);//2900の時間前進 K'toL'
    catch();                                //L'でボールを掴む
    OnRev(OUT_BC,30);Wait(2900);Off(OUT_BC);//2900の時間後退
    Lcurve();                               //K'で左曲がり
    linetraceS();                           //K'toJ'
    release();                              //J'でボールを離す
    ushiro;                                 //後退
    Wait(2000);                             //2000の時間
    OnFwd(OUT_C,20);OnRev(OUT_B,10);        //K'で回転
    until(SENSOR_3>white-5);                //白よりの灰色になるまで
    linetraceC();                           //K'toM'
    Rcurve();                               //M'
    linetraceS();                           //M'toM'とBの間の角
    Lcurve();                               //M'とBの間の角
    linetraceS();                           //to二枚の間の線に差し掛かるところ
    Rcurve();                               //二枚の間の線に差し掛かるところ
    linetraceS();                           //間の線上
    Lcurve();                               //間の先から右のコートに抜けるところ
    linetraceS();                           //toB
    crossline();                            //B
    linetraceS();                           //BtoM
    Lcurve();                               //M
    linetraceC();                           //MtoK
    crossline();                            //K
    linetraceS();                           //KtoL
    Wait(2900);                             //2900の時間
    catch();                                //Lでボールを掴む
    kaiten();                               //Lで回転
    linetraceC();                           //LtoM
    Rcurve();                               //M
    linetraceS();                           //toBとMの間の角
    Lcurve();                               //BとMの間の角
    linetraceS();                           //to二枚の間の線に差し掛かるところ
    Rcurve();                               //二枚の間の線に差し掛かるところ
    linetraceS();                           //間の線上
    Lcurve();                               //間の先から左のコートに抜けるところ
    linetraceS();                           //toB'
    Lcurve();                               //B'
    linetraceS();                           //B'toD'
    Rcurve();                               //D'
    linetraceS();                           //D'toH'
    release();                              //H'でボールを離す
 }

**スレイブ側のプログラム [#red22c09]

***定義 [#i1135d38]

 #defineで次のように定義した
 #define SIGNAL1 11  //マスターとやり取りする信号の番号
 #define SIGNAL2 12  //同上
 #define SIGNAL3 13  //同上
 #define SIGNAL4 14  //同上
 #define tukamu OnRev(OUT_A,20);Wait(800);//ボールを掴む
 #define hanasu OnFwd(OUT_A,20);Wait(300);//ボールを落とす
 #define up OnFwd(OUT_B,50);Wait(1800);//アームを上げる
 #define down OnRev(OUT_B,50);Wait(900);//アームを下ろす

***プログラム全体 [#t4b8d068]

マスター側と同様,掴むときの変数をmsg1,信号をSIGNAL1,SIGNAL2とし,MAILBOX1でやり取りを,離すときは変数をmsg2,信号をSIGNAL3,SIGNAL4とし,MAILBOX2でやり取りを行っている.

 task main ()
 {
    int msg1;  // 受け取った値を格納する変数
    int msg2;  // 同上
    while(true){
        ReceiveRemoteNumber(MAILBOX1,true,msg1);//MAILBOX1で受け取った信号をmsg1に格納
        ReceiveRemoteNumber(MAILBOX2,true,msg2);//MAILBOX2で受け取った信号をmsg2に格納
        if(msg1==SIGNAL1){                      //msg1にSIGNAL1が入っていたなら
            down;   
            tukamu;
            up;
            Off(OUT_AB);                        //モーターを停止させる
            SendResponseNumber(MAILBOX1,SIGNAL2);//マスター側のMAILBOX1にSIGNAL2を送る
        }
        if(msg2==SIGNAL3){                      //msg2にSIGNAL3が入っていたなら
            hanasu;
            Off(OUT_A);                         //モーターを停止
            SendResponseNumber(MAILBOX2,SIGNAL4);//マスター側のMAILBOX2にSIGNAL4を送る
        }
    }  
 }

*感想・反省点 [#oec31ff0]

今回初めて扱ったNXT間での通信は手こずったが,最終的にうまく行ってよかったと思う.
アーム部分や移動する部分などの機体は頑丈でしっかり動くよう工夫して作れていた点,他チームより良くできたと感じている.

しかし,本番直前にセンサーの位置を変更しそれに対応したプログラムが間に合わず,肝心のライントレースがうまく動作しなかったのが残念だった.
また,チームで分担してプログラミングを行ったのだが,各自でつくったものを合わせる際それぞれ違った考えのもとプログラミングをしていて,その点意思疎通が足りなかったことを反省している.

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