目次
#contents

*課題3 [#mf123387]
#ref(2018b-mission3.png,,課題コース);
上記のコース上の青のボールは青の缶に、赤のボールは赤の缶にそれぞれ乗せる。缶はロボット起動時にサイコロを振り、サイコロの出た目に対応した位置に置く。ボールを同じ領域内の缶に乗せたかどうか、ボールが缶に当たったか、ダミー缶を使用したかによって点数が付く。&br;
詳しくは[[こちら:http://yakushi.shinshu-u.ac.jp/robotics/?2018b%2FMission3]]



*使用ルート [#p072aaff]
自分たちのグループは以下のようなルート・手順を考えた。
#ref(root.png,,青・赤矢印 移動ルート 薄青・薄赤 索敵範囲);。
+Y内からスタートし、青のボールへ向かう(青矢印)
+青のボールを掴んだら少し前進した後、缶の索敵を行う(薄青)
+索敵後青のボールを青の領域の缶に乗せる
+青のボールを乗せたらEの交差点まで戻る(赤矢印)
+Eの交差点をFの方向へ曲がり進む(赤矢印)
+急カーブ上で赤のボールを掴む
+赤のボールを掴んだらその場で索敵を行う(薄赤)
+索敵後赤のボールを赤の領域の缶に乗せる


*作成ロボット [#i7f25481]
**ロボット全体像 [#r99d2dad]
#ref(robot.jpeg,,作成ロボット);
今回は2グループ合同でのロボット作成だったのでNXTが2台あり、その片方でボールを掴んで持ち上げるアーム部分の操作、もう片方でライントレースを行うタイヤの操作を行った。
タイヤの操作、各センサの値の認識を行うNXTをマスター側、アームの操作を行うNXTをスレーブ側にしている。
-工夫点&br;
今回課題を達成するために行う動作が多く、キットを2台分使用しているためこれまでに比べロボット本体が大きく、重くなってしまった。そのためにタイヤにかかる重量、タイヤを回転させるモーターにかかる負荷も甚大であることが懸念された。それを緩和するため車体を支えるタイヤの数を6個にし、タイヤとモーターの間にギアを入れた。これによりタイヤにかかる重量の分散とモーターにかかる負荷の軽減が実現できた。
#ref(center-tire.jpeg,,工夫点);
#ref(back-tire.jpeg,,工夫点);

右のモーターからタイヤへのギアのつながりを模式的に表すと以下のようになっている。
#ref(gear-tire.png,,A,B,C,Dはギアを示す);
上記の図においてモーターとギアA、ギアBとギアC、ギアDとタイヤは棒でつながっており回転は連動する。そのためモーターを回転させるとギアAが連動して回転し、その回転がギアBに伝わることでギアCも回転し、ギアDを回転させタイヤが回転する、という流れで動力が伝わる。なおモーターとタイヤの回転比は6対1である。

**ボール保持部分 [#m1a1d80c]
以下ボールをつかむ部分をハンド、ボールを持ち上げる部分をアームと呼称する。
***ハンド [#l25b1265]
ハンド部分は上のギアを回転させることで下のギアに動力が伝わり開閉動作を行う。&br;
下の写真はハンドを閉じる場合の動作である。
#ref(hand.jpeg,,ハンドを閉じる場合 緑矢印 ギアの回転方向 赤矢印 ハンドの閉じる方向);
***アーム [#ma519cb5]
モーターAの回転を橙丸部分の計5つのギアによってハンド部分に伝え、ハンドの開閉を行う。
下の写真の緑矢印の方向はハンドを閉じる方向である。&br;
#ref(arm-hand.jpeg,,緑矢印 モーターAの回転方向-ギア回転の伝わり 紫矢印 アームの上下);

モーターB、Cを同時に同じ方向へ回転させることで、アームを上下させる。なおアーム部分の重量も大きいためモーターを2個使い、かかる重量を分散している。
#ref(up-down.jpg,,アームの上下);
図のように下の橙棒をモーターの回転によって傾きを変えることで、ハンドが繋がっている縦棒が上がりボールも上がる。


***ハンドとアームの接合部分 [#w36cfbf8]
モーターAからアームを伝わってきた縦方向の回転は、ハンドのギアにより横回転に変換され開閉の動作を行う。
#ref(gear.jpeg,,接合部分);
**各種センサ [#y2cdbf1e]
今回は超音波センサを2つ、光センサを1つ使用した。S1でラインの明暗を測定、S2で缶の距離を測定、S3でボールの距離を測定する。
#ref(sensor.jpeg,,ロボット前方 各種センサ);
**試行錯誤 [#zb435d06]
ロボット作成当初はアームが長く、前輪タイヤが車体の前方やや外側についており、バランスが悪かった。そのため改善としてアームを短くし、前輪タイヤを中央寄りに取り付けたものを完成品とした。
#ref(bad-robot.jpeg,,没機体);

*動作説明 [#z7957c1b]
今回の課題は前回の課題と類似点が多く存在するため類似点は簡略化し、相違点を詳しく説明する。類似点の詳しい説明は[[前回(Mission2):http://yakushi.shinshu-u.ac.jp/robotics/?2018b%2FMember%2Ftwinrabi%2FMission2]]の動作説明を参照。&br;
なお今回と前回の大きな相違点として引数があり、以下にそれぞれが指定する型について簡単にまとめておく。
+int型     整数を変数とする値を定義する
+float型   浮動小数を変数とする値を定義する
+string型  文字列を変数とする値を定義する
+long型    時間関数を変数とする値を定義する

**NXT間の通信 [#g7520775]
今回は2つのNXTを使用しているので、片方をマスターとしもう片方のスレーブ側をBluetoothで繋げ、連携させている。今回はスレーブ側に{アームを上げる}{アームを下げる}{ボールを掴む}{ボールを放す}の4つの動作を行うプログラムを入れている。マスター側から [RemoteStartProgram(CONN,"スレーブ側のプログラム名.rxe");] を用いて命令を送ることで、スレーブ側に指定したプログラム名の動作を行わせる。

**ライントレース [#g5284910]
今回も前回と同様に線の境目をトレースするプログラムであるが、ライントレースを行う定義関数 
 follow_line(int bw_time,long min_t,int blk_cnt,string invert_info,string sensor_switch) 
において5つの引数を組み込むことで、変数の値によって行われる動作を変更している。以下に5つの変数による動作の変更点をまとめる。なお組み込んであるだけで今回の課題においては使用しておらず、動作の変更を行っていない関数もある。
++bw_timeの値で旋回を行い始めるまでの時間を操作。
++bw_timeの値で旋回を行い始めるまでの時間を操作。また旋回を行ったらそこは交差点であると判断する。
++min_tの値で交差点判断を無視する時間を操作、また-1を指定することで常に交差点判断を行わなくなる。
++blk_cntの値に1を指定すると交差点判断をした時停止しライントレースを終了する。0を指定した時は交差点を直進して通り過ぎた後ライントレースを終了する。-1を指定した時は何も行わない。
++invert_infoにoffを指定した場合は何も行わず左の境目をトレースする。onを指定した場合は光センサの値を反転させることで右の境目をトレースする。
++sensor_switchにonを指定すると、S3に接続された超音波センサが16儖米發両祿科(ボール)を発見した時ライントレースを終了する。offを指定した場合はたとえS3の超音波センサが障害物を認識したとしても何も行わない。&br;


線の境目のトレース方法は前回同様{線外ならば線上に向かう}{線の境目ならば直進}{線上ならば線外に向かう}の3つの動作を光センサの値によって制御することで行い、交差点・急カーブも同様に判断する。

**缶の索敵 [#t95593f1]
缶の索敵に関しては相違点としてタイヤ間の距離と索敵範囲の変更がある。
タイヤ間の距離は単純にロボットが変わり、前回より大きくなったため長くなった。
索敵範囲は前回と違い360度一回転しなくてもよかったため、ライントレースと同様に缶を索敵する定義関数
   search_throw(string side_select,float rotate_angle)
に引数を組み込み、旋回方向と旋回角度を操作できるようにした。
++side_selectにrightを指定すると右旋回、leftを指定するとモーターの回転角度に-1をかけることでモーターを逆転させ、左旋回を行う。
++rotate_angleに指定した角度分だけ旋回を行う&br;

#ref(robot-over.jpeg,,青線 タイヤ間の距離 緑矢印 right時の旋回方向 黄矢印 left時の旋回方向);

旋回角度を算出する式は前回同様 (タイヤ直径/前輪間の距離)*タイヤの回転角度=ロボットの旋回角度 であるが、今回はモーターとタイヤの間にギアをかませており回転比は1対6のため、タイヤの回転角は モーターの回転角/6 で算出される。

**動作時間の測定 [#k1f77543]
動作時間の想定に関しては[[前回(Mission2):http://yakushi.shinshu-u.ac.jp/robotics/?2018b%2FMember%2Ftwinrabi%2FMission2]]とほぼ同一であり変数名が異なるくらいである。前回のt00が今回のstart_tに対応し、t0,t1に関してはそのまま対応する。よって同様に{t1-t0}はロボットが右左折(旋回を含む)を継続して行っている時間を示し、{start_t-t1}はライントレースを開始してから何秒間経過したかを示す。


*使用プログラム [#n73f2320]
**スレーブ側 [#r0af1958]
-アームを上げる(プログラム名[arm_up])

    task main(){
         RotateMotor(OUT_BC,-40,90);  //ボールを持ち上げる
    }

-アームを下げる(プログラム名[arm_down])

    task main(){
         RotateMotor(OUT_BC,10,90);
         Wait(300);
         Off(OUT_BC);
         ResetTachoCount(OUT_BC);
    }

-ハンドを閉じる(プログラム名[hand_close])

    task main(){
         RotateMotor(OUT_A,-30,320);  //ボールを掴む
         Wait(300);
         RotateMotor(OUT_BC,-40,15);
         Off(OUT_ABC);
    }

ハンドを閉じる(ボールを掴む)となぜかアームが下がってしまい、ハンド部分が地面にこすれてしまうので少しアームを上げている。

-ハンドを開く(プログラム名[hand_open])

    task main(){
         RotateMotor(OUT_A,30,320);  //ボールを放す
    }

スレーブ側には上記の単純な4つのプログラムが入っている。
**マスター側 [#j5e525b9]
-使用マクロ
    #define fw Off(OUT_BC);OnFwd(OUT_BC,30); // 直進
    #define lt Off(OUT_BC);OnFwd(OUT_B,35);  // 左回
    #define rt Off(OUT_BC);OnFwd(OUT_C,35);  // 右回
    #define rrt Off(OUT_BC);OnFwd(OUT_C,20);OnFwd(OUT_B,-60); // 右旋回
    #define llt Off(OUT_BC);OnFwd(OUT_B,20);OnFwd(OUT_C,-60); // 左旋回
    #define wait_sec Off(OUT_BC);Wait(1000);                  //一時停止
    #define mini_go Off(OUT_BC);OnFwd(OUT_BC,30);Wait(1500);Off(OUT_BC); //少し直進
    #define def_arm_up until(BluetoothStatus(conn)==NO_ERR);   //アームを上げる
                       RemoteStartProgram(conn,"arm_up.rxe");
                       Wait(4000);
    #define def_arm_down until(BluetoothStatus(conn)==NO_ERR);   //アームを下げる
                         RemoteStartProgram(conn,"arm_down.rxe");
                         Wait(4000);
    #define def_hand_close until(BluetoothStatus(conn)==NO_ERR);   //ボールを掴む
                           RemoteStartProgram(conn,"hand_close.rxe");
                           Wait(4000);
    #define def_hand_open until(BluetoothStatus(conn)==NO_ERR);   //ボールを放す
                          RemoteStartProgram(conn,"hand_open.rxe");
                          Wait(4000);


-定義関数
    const float tire_diameter = 5.75;    //タイヤの直径
    const float tire_axis = 17.7;        //タイヤ間の距離
    const int gear_ratio = 6;            //ギア比
    const float pi = 3.14159;            //円周率
    const int en = 360;                  //円 360度


***NXT間の通信 [#m05b21a9]
    until(BluetoothStatus(CONN)==NO_ERR);   //スレーブ側のアームを上げるプログラムを起動
        RemoteStartProgram(CONN,"arm_up.rxe");
        Wait(4000);

    until(BluetoothStatus(CONN)==NO_ERR);   //スレーブ側のアームを下げるプログラムを起動
        RemoteStartProgram(CONN,"arm_down.rxe");
        Wait(4000);

    until(BluetoothStatus(CONN)==NO_ERR);   //スレーブ側のハンドを閉じるプログラムを起動
        RemoteStartProgram(CONN,"hand_close.rxe");
        Wait(4000);

    until(BluetoothStatus(CONN)==NO_ERR);   //スレーブ側のハンドを開けるプログラムを起動
        RemoteStartProgram(CONN,"hand_open.rxe"); 
        Wait(4000);

[until(BluetoothStatus(CONN)==NO_ERR);]はNXT同士が繋がっていないと以下の動作を行わないように制限する条件文である。

***ライントレース [#k104eca1]
-交差点の通過
    void blk_selection(int blk_select){
        if(blk_select!=1){  //blk_selectが1なら
            wait_sec;         //停止する
        }
        if(blk_select!=0){    //blk_selectが0なら
            mini_go;          //通過する
        }
    }


    void follow_line(int bw_time,long min_t,int blk_cnt,string invert_info,string sensor_switch){
        SetSensorLight(S1);    //光センサ定義
        int right_var;         //光センサ値用変数
        SetSensorLowspeed(S3); //超音波センサ定義
        long t0,t1,start_t; // t0,t1,start_t 時間変数の定義
        t1=CurrentTick(); // 今その時の時刻
        t0=CurrentTick(); // 直進終了・右左折開始時刻
        start_t=CurrentTick(); // ライントレース開始時刻
        while(t1-start_t<min_t || min_t<0){ //ライントレース開始から指定した時間[min_t]以内で繰り返す
            if(sensor_switch=="on"){   //sensor_switchにonを指定していたら
                 if(SensorUS(S3)<16){  //S3が16儖米發望祿科を探知したら
                      break;      //while文から抜け出す    
                 }
            }
            right_var = SENSOR_1; // 光センサ値代入
            if(invert_info=="on"){  //invert_infoにoffを指定したら左の境目をトレースする 
                 right_var = right_var*(-1)+91;} //onを指定したら光センサの値を反転させて右の境目をトレース
            if (right_var>50){
                if (t1-t0>bw_time){ // 右回で指定した時間[bw_time]以上のとき右旋回
                      if (blk_cnt>=0 && blk_cnt<=2 && invert_info=="on"){ //交差点の通過が停止か通過であり、右の境目をトレースしていたなら
                             blk_selection(blk_cnt); //blk_cntに指定された数字に適した動作を行い
                             break;  //ライントレースを終了する
                       }else{
                           rrt;}
                }else{        // 右回
                      rt;}
            }else if (right_var>40){
                fw;
                t0=CurrentTick(); //直進終了・右左折開始時刻 
            }else{
                if (t1-t0>bw_time){   // 左回で bw_time[ms]  以上のとき左旋回
                      if (blk_cnt>=0 && blk_cnt<=2 && invert_info!="on") {
                             blk_selection(blk_cnt);
                             break;
                       }else{
                           llt;}
                }else{            // 左回
                      lt;}}
        t1=CurrentTick();    //今その時の時刻
        }Off(OUT_BC);
    }


***缶の索敵 [#v85d66c0]
    void search_throw(string side_select,float rotate_angle){ 
        SetSensorLowspeed(S2);   //超音波センサ定義
        int d_min=1000,d_gosa=11,side_flag=0;
        long ang_min=0,angle=0,d_angle=0;
        if (side_select=="right"){       //rightを指定していれば右旋回
           side_flag=1;
        }else if(side_select=="left"){   //leftを指定していれば左旋回
              side_flag=-1;
        }
        ResetTachoCount(OUT_BC);
        OnFwdSync(OUT_BC,30,100*side_flag);
        while(((tire_diameter/(tire_axis*gear_ratio))*MotorTachoCount(OUT_C)*side_flag)<=rotate_angle){   //指定した角度[rotate_angle]分旋回
            if(d_min>SensorUS(S2)){
                d_min=SensorUS(S2);    //距離の最小値更新
                angle=MotorTachoCount(OUT_C);
            }
        }
        ang_min=MotorTachoCount(OUT_C)-angle;
        Off(OUT_BC);
        Wait(1000);
        ResetTachoCount(OUT_BC);
        RotateMotorEx(OUT_BC, 40, ang_min, -100*side_flag, true, true);//物体の方を向く
        Off(OUT_BC);
        ResetTachoCount(OUT_BC);
        if(7<SensorUS(S2)){  //缶までの距離が7儖焚爾砲覆
            OnFwd(OUT_BC,40);     //物体の方に近づく
            while(7<SensorUS(S2) || 40>(pi*tire_diameter*MotorTachoCount(OUT_C))/en*gear_ratio){距離が7儖焚爾砲覆襪泙燭40竸覆爐泙
                d_angle=MotorTachoCount(OUT_C);   //物体に近づいた分のモーターの回転角を記録
            }
            Off(OUT_BC);
            RotateMotor(OUT_BC,30,en*2*gear_ratio/(pi*tire_diameter));   
        } 
        def_arm_up;  //アームを上げる
        RotateMotor(OUT_BC,30,en*d_gosa*gear_ratio/(pi*tire_diameter));  //距離の微調整
        Wait(700);
        def_hand_open;  //ボールを放す
        RotateMotor(OUT_BC,-30,en*d_gosa*gear_ratio/(pi*tire_diameter)+d_angle);  //近づいた分戻る
        def_arm_down;  //アームを下げる
        ResetTachoCount(OUT_BC);
        RotateMotorEx(OUT_BC, 40, angle, -100*side_flag, true, true);//元の向きへ戻す
        Off(OUT_BC);
    }

***プログラム全体 [#ua423fd3]
    task main(){          
        string invert_str="off";
        int b_tyokusin=10,r_back=22; //直進する距離と後退する距離を定義
        follow_line(1200, -1,1,invert_str,"off"); //Yから出て青ボールへ向かう
        follow_line(800, -1,0,invert_str,"off");
        def_hand_close; //青ボールを掴む
        ResetTachoCount(OUT_BC);
        RotateMotor(OUT_BC,30,en*b_tyokusin*gear_ratio/(pi*tire_diameter)); //円の中に入る
        Off(OUT_BC);
        search_throw("right",55.0); //右旋回角度55度で索敵
        Wait(2000);
        RotateMotor(OUT_BC,-30,en*b_tyokusin*gear_ratio/(pi*tire_diameter)); //円から出る
        Off(OUT_BC);
        def_arm_up; //アームを上げる
        RotateMotor(OUT_BC,-30,en*r_back*gear_ratio/(pi*tire_diameter)); //Eの交差点まで後退する
        follow_line(600,1200000,-1,invert_str,"on");  //EからFへ向かい急カーブへ行き、赤ボールを認識したらライントレースを終える 
        RotateMotor(OUT_BC,-30,en*3*gear_ratio/(pi*tire_diameter)); //3センチ下がる
        def_arm_down; //アームを下げる
        def_hand_close; //赤ボールを掴む
        search_throw("left",270); //左旋回270度で索敵
        Wait(1000);Off(OUT_BC);
    }
*結果 [#s03258b7]
#ref(result.jpg,,課題3結果);
自分たちのグループ(N1N2)は本番では缶の索敵がずれる、ハンドが開かないなどの不具合が出て、缶に当てることすらできず基本点は0点だった。しかし技術点は多く貰え最終結果としては3位であった。

*感想・反省 [#e7e9bf4c]
ロボット本体に関しては大きく、重くなってしまったが、それによる悪影響を緩和できるような工夫を行え、技術点も多く貰えたので良いほうであるように感じる。しかし索敵角度のズレは缶が遠いところにあることで、超音波センサの反射に時間がかかったことによるものだと考えられる。実際試運転の時も、缶が1,2,3などの比較的近い位置の場合はほぼほぼ誤差なく缶の方向を向けていたのに対し、5,6などの遠い位置では同様にズレが生じていた。それを抑制するために旋回速度を落としてみようともしたが、ロボットが重いせいかモーターの馬力が足りず、タイヤが回転しない等の事態が起こってしまった。そのためいくら重量を緩和できるような工夫ができたからと言っても、やはりロボット本体の軽量化は大事であるように思う。&br;
ハンドの不具合は、おそらくハンドが閉じ切らなかったことで後の動作が行えなくなっていたものと考えられるので、最初にハンドを限界まで開けるか、閉じる角度をもう少し減らしておけば対処できたのではないかと思う。&br;
後期のロボティクス入門ゼミを通して、これまであまりやったことがなかったハード・ソフトの両方を自分で組み立て、課題を達成するという一連の流れを何となく体感でき、どちらもうまくかみ合わせないと正しく動作しないことが分かったので、これから同様のことを行うときに今回学んだことをうまく活かしていきたい。

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