2018b/Member 目次

課題3

  今回の課題は・・・

のボールを運搬して、
それぞれ所定の350ml缶の上に乗せる
・・・だ!     

コース

フィールドの説明

3図_コース.png

  • フィールドは課題2で使用した紙を使用する。
  • 350ml缶(中が入っていても空でよい)は逆さまにして使い、ロボットのスタート直後にサイコロを振って出た目の番号の位置に置く。
  • その番号の次の番号のところにもダミーの缶を置くことができる。ただし6の目が出た場合は6の位置と25cm以上離れていない場所ならどこに置いてもよいものとする。距離は缶と缶のもっとも近い部分で測る。
  • 空き缶には色をつけたり文字や記号を書いてもよい。あるいは周囲に紙を張ってもよい。
  • 赤と青のボールは、図のように所定の場所に置いておく。その際、キットに含まれない小さな輪ゴムを使用するものとする。

ルール

基本ルール

  • 競技時間は審判が続行不能と判断するまで、あるいはリタイアするまで。
  • 図のX地点または(および)Y地点からスタートする。ただし接地している部分はそれぞれの領域内に収まるものとする(線上はOK)。上空部分は領域からはみ出していてもよい。
  • 赤いボールを図のピンクのいずれかに置いた缶に、青いボールを図の水色のいずれかに置いた缶に、それぞれ乗せる。
  • 開始の合図から5秒以内にスタートボタンを押す作業を完了すること。
  • 競技が終了するまで、ロボットに触ったり人間が遠隔で操作してはならない。
  • 途中でうまく動かなくなった場合、1回限り再スタートすることができる(再スタートの際に別プログラムで起動してよい)。

基本得点の計算方法

  • ボールを一つ乗せればそれぞれ10点、二つとも乗せればボーナス点としてさらに10点。
  • ダミーの缶を設置した上で、正しい缶に乗せれば、それぞれさらに6点加点する。
  • ボールを目的の缶に当てることができれば、それぞれ4点。
  • ボールを同じ領域内の間違った缶に乗せた場合は、それぞれ6点。
  • ボールを同じ領域内の間違った缶に当てた場合は、それぞれ2点。
  • ボールを違う領域内の缶に乗せた場合は、それぞれ2点。
  • ボールを違う領域内の缶に当てた場合は、0点。
  • 目的の缶をもとの位置(直径7cmの円)から少し出してしまった場合は1点減点、半分以上出してしまった場合は2点または取得した得点の半分のいずれか少ないほうを減点、その缶を完全にだしてしまったときは点数を半分にする。
  • ダミーの缶がもとの位置から移動しても減点はしない。

技術点の計算方法

  • 以下の動作の精度・スピード・確実性などを含めた技術的な工夫や芸術性について他の全てのチーム(5チーム)が20点満点で採点し、その平均点を求める。 得点の目安:
  • ボール探し取りにいくまでの動作 (3点)
  • ボール掴む動作 (3点)
  • ボールを運ぶ動作 (2点)
  • ボールを缶に置く動作 (2点)
  • 2台のNXT、EV3の連携の良さ(2点)
  • 自立型のロボットとしての形や動作の美しさ、斬新さ(2点)
  • その他 (3点)

ロボット

今回作ったロボットはこれだ!! ☞☞☞3図_ロボ.jpeg

・・・前回同様、アームと車体で構成した。    

アーム

 この課題では、ボールを拾って缶に乗せる。その為、ボールを拾った後に缶の高さまで持ち上げる必要があり、更に、ボールを掴むという動作にモータを一つ使うことになる。
 私達のチームでは、電池の消耗という点からボールを掴むモータを持ち上げるのは難しいという結論に至った。この問題を解決するに当たって、チームメンバーからマジックハンドという案が挙がり、採用された。
 これが実際にできたものである。3図_アームハンド.jpeg

ハンドパーツ

 こちらは、ボールを掴むハンドパーツだ。3図_ハンド.jpeg

 上部にあるギアが回ることで、下の二つのギアがそれぞれ逆方向に回り、開閉する仕組みとなっている。

 下図はハンドパーツを上から見たものである。上部にあったギアの方向に回すと、その下のギアの方向に回る。その為、ハンドパーツが開く。

3図1_ハンド2.png ➡ 3図1_ハンド3.png

アームパーツ

 こちらは、ハンドを上下させるアームパーツだ。3図_アーム.jpeg

 写真の下側のモータがアームを上下させるようになっている。  実際に動かすと、アームの動きは下の図のようになる。黒いパーツがハンドを取りつけるもので、黄緑のパーツがモータの回転によって動くパーツである。

3図1_アーム2.png3図1_アーム3.png3図1_アーム4.png

 モータ一つだと負担が大きいので、二つ並べて使用し負荷を軽減している。



 また、ハンドへ動力を伝える為に、軸となるパーツにギアを取り付けた。このギアを通して、ハンドの開閉を行うことができる。

車体

 車体はブロックでフレームを製作し、そこにモータやタイヤを取り付け、頑丈になるようにした。

3図_車体1.JPG

 しかし、アームが予想以上に重く、車体を支える為アームの下近くに大きなタイヤを二つ追加し、計四つも使用することになった。

3図1_車体2.png



 また、モータへの負担を軽減するため、ここにもギアを使用した。

3図_車体3.jpeg

 ここのギアの配置は次にようになっている。

横から上から
3図1_タイヤ1.png3図1_タイヤ2.png
(この図はギア4とタイヤが、ギア3の左側に設置してあります)       
 


 モータとギア1ギア2ギア3ギア4とタイヤが直結している。各ギアの歯数は次の通り。

ギア歯数
ギア18
ギア216
ギア38
ギア424

 表から、ギア比は、次の式に歯数を代入することで求められる。

3図_ギア1.png
 ここで、ギア2とギア3は直結していて、回転数が等しいので、式は次のようになる。
3図_ギア2.png
 よって、ギア比は6となった。

 つまり、モータにかかる負担が6分の1になったことになる。

センサ

 今回は、缶だけでなくボールも探さないといけないので、超音波センサを二つ使用した。

 一つ目二つ目
場所アームの先端アームの下
写真3図_センサ1.jpeg3図_センサ2.jpeg
役割缶の探索ボールの探索




 また、ライントレースに使用する光センサもアームの下に付けてある。

3図_センサ3.jpeg

プログラム

 このロボットではハンドに一つ、アームに二つ、タイヤに二つモータを使用している。NXT一つではモータを三つまでしか制御できないが、二つのNXTをBluetoothで通信させることで動かす。  Bluetoothを使用する時、片方を"master"、もう片方を"slave"として動作させる。



 ライントレース等、前回の課題のプログラムを流用しているので、新たに追加したり、変更した部分のみ説明する。



 通るコースは次のようにした。

3図1_コース2.png


  1. Yから出てIまで進む。
  2. ボールを取って、円の中心まで進む。
  3. を探して乗せて、戻る。
  4. 円の中心からIまで戻る。
  5. IからEとLの間まで戻る。
  6. Eで曲がり、Fを超えてボールが見つかるまで進む。(Fの先一つ目の急カーブを曲がった辺りで見つけられる。)
  7. ボールを掴む。
  8. その位置のままを探して乗せて、戻る。

車体(master)

マクロ

 Bluetoothの通信はマクロでまとめた。

BluetoothStatus(slave番号)は通信ができているかどうかを示す関数。

RemoteStartProgram(slave番号,実行形式のプログラムファイル名)は指定したslaveの指定プログラムを起動する関数。

/************************************************************
   ライントレ____@('ω' )@彡_____ス・プログラム
*************************************************************/

/*マクロ*/
#define conn 1  //Bluetoothのスレーブ番号
#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_open until(BluetoothStatus(conn)==NO_ERR);\   //放す
                      RemoteStartProgram(conn,"hand_open.rxe");\
                      Wait(4000);
#define def_hand_close until(BluetoothStatus(conn)==NO_ERR);\   //掴む
                       RemoteStartProgram(conn,"hand_close.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度

ボール探索

 基本は課題2のプログラムと同じ

void search_throw(string side_select,float rotate_angle){//最初の一周の部分で、旋回方向と角度を指定する引数を追加した。

    SetSensorLowspeed(S2);
    int d_min=1000,d_gosa=11,side_flag=0;//side_flagは旋回方向を変更する為の変数 d_gosaはアームを上げた後に進む距離[cm]
    long ang_min=0,angle=0,d_angle=0;//d_angleは、缶との距離が遠い場合に近づいた距離を格納
    if (side_select=="right"){     //右旋回なら
       side_flag=1;              //1を旋回角度にかける
    }else if(side_select=="left"){  //左旋回なら
          side_flag=-1;         //-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);       //パワー40で進む
        while(7<SensorUS(S2) || 40>(pi*tire_diameter*MotorTachoCount(OUT_C))/en*gear_ratio){//缶との距離が7センチ以下になるか、進んだ距離が40センチになるまで続ける
            d_angle=MotorTachoCount(OUT_C);   //モータCの回転角を更新
        }
        Off(OUT_BC);//停止
        RotateMotor(OUT_BC,30,en*2*gear_ratio/(pi*tire_diameter));//2センチ進む
    }
    
    def_arm_up;//アームを上げる

    RotateMotor(OUT_BC,30,en*d_gosa*gear_ratio/(pi*tire_diameter));//d_gosaの分だけ進む
    Wait(700);

    def_hand_open;//ボールを放す

    RotateMotor(OUT_BC,-30,en*d_gosa*gear_ratio/(pi*tire_diameter)+d_angle);//d_gosaとd_angleの分だけ戻る

    def_arm_down;//アームを下げる

    ResetTachoCount(OUT_BC);
    RotateMotorEx(OUT_BC, 40, angle, -100*side_flag, true, true);//元の向きへ戻す
    Off(OUT_BC);
}

ライントレース

 基本は課題2のプログラムと同じ

void blk_selection(int blk_select){
    if(blk_select!=1){
        wait_sec;
    }
    if(blk_select!=0){
        mini_go;
    }
}
void follow_line(int bw_time,long min_t,int blk_cnt,string invert_info,string sensor_switch){//sensor_switchはセンサの値が一定以下の時ループから抜けるかどうかを決める引数
    SetSensorLowspeed(S3);
    int bb_cnt;//消し忘れた変数
    int B_cnt=0;//上に同じ
    long t0,t1,start_t; // t0,t1,start_t 時間変数の定義
    SetSensorLight(S1); // 光センサ定義
    t1=CurrentTick(); // 経過時間
    t0=CurrentTick(); // カーブ初期時間
    start_t=CurrentTick(); // 追尾初期時間
    int right_var;        // 光センサ値用変数
    while(t1-start_t<min_t || min_t<0){
        if(sensor_switch=="on"){//sensor_switchがonの時
             if(SensorUS(S3)<16){//超音波センサ2の値が16センチ未満の時
                  break;//ループから抜ける
             }
        }
/*----------------↓繰り返し↓-----------------*/
        right_var = SENSOR_1; // 光センサ値代入
        if(invert_info=="on"){  // ラインの右か左か "off"で左,"on"で右
             right_var = right_var*(-1)+91;}
 
        if (right_var>50){

     /*********右回り↓********/
            if (t1-t0>bw_time){ // 右回でbw_taime[ms]以上のとき右旋回
                  if (blk_cnt>=0 && blk_cnt<=2 && invert_info=="on"){
                         blk_selection(blk_cnt);
                         break;
                   }else{
                       rrt;}
            }else{        // 右回
                  rt;}
     /**********直進↓********/
        }else if (right_var>40){
            fw;
            t0=CurrentTick(); // カーブ初期時間の更新
     /*********左回り↓*******/
        }else{
            if (t1-t0>bw_time){   // 左回で bw_taime[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);
}

メイン

task main(){
//   -Y-
    string invert_str="off";
    int b_tyokusin=10,r_back=22;//b_tyokusinは円に入る時に進む距離 r_backはEとLの間まで戻る時の距離

//blue-ball
    follow_line(1200, -1,1,invert_str,"off");//Eまで進んでEを越える
    follow_line(800, -1,0,invert_str,"off");//Iまで進んで停止

    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));//Iまで戻る
    Off(OUT_BC);

//red_ball 

    def_arm_up;//アームを上げる

    RotateMotor(OUT_BC,-30,en*r_back*gear_ratio/(pi*tire_diameter)); //EとLの間まで戻る

    follow_line(600,1200000,-1,invert_str,"on");//1200秒経つか、センサーの値が一定以下になるまでライントレース
    
    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);
} 

アーム(slave)

 アーム側では四つの簡単なプログラムを容易して、マスター側から通信があった時、任意のプログラムが動作するようになっている。  モータAがハンドの操作、モータB・Cがアームの操作を担当している。

アームを上げる("arm_up.rxe")

task main(){
     RotateMotor(OUT_BC,-40,90);//モータB・Cをパワー40、90°で逆回転させる
}

アームを下げる("arm_down.rxe")

task main(){
     RotateMotor(OUT_BC,10,90);//モータB・Cをパワー10、90°で回転させる
     Wait(300);              //300[ms]待機
     Off(OUT_BC);            //モータB・Cを停止
     ResetTachoCount(OUT_BC);//モータB・Cの回転角をリセット
}

掴む("hand_close.rxe")

task main(){
     RotateMotor(OUT_A,-30,320);//モータAをパワー30、320°で逆回転させる。
     Wait(300);                 //300[ms]待機
     RotateMotor(OUT_BC,-40,15);//モータB・Cをパワー40、15°で逆回転させる
     Off(OUT_ABC);              //モータA・B・Cを停止
}

放す("hand_open.rxe")

task main(){
     RotateMotor(OUT_A,30,320);//モータAをパワー30、320°で回転させる。
}

結果・反省感想

  • 今回の課題は、テスト期間と重なっていたことで、製作時間が少なくなってしまった。更に、その少ない時間の多くをロボットに使った為プログラムが拙いものとなってしまった。
  • 本番では、一つ目のボールを掴むまではできたのだが、ハンドが開かず缶に一つも乗せられないまま終わってしまった。また、缶に向かう際にズレが生じた。こちらは、二段階のサーチをするという話を先生から聞き、とても参考になった。ハンドが開かない問題については、RotateMotor(モータ,パワー,角度);の関数をOnFwd(モータ,パワー);に変えることで回避できたのではないかと今更に思った。発想力が足りないというのはとても歯がゆいものであった。
  • この講義では、結果こそ奮わなかったものの多くのことを学べた。特に、ハードを理解していなければ、ソフトは作れないということをこれでもかと痛感した。ここで学んだことを来年度以降にも活かしていきたいと思う。

添付ファイル: file3図1_コース2.png 15件 [詳細] file3図_コース2.png 8件 [詳細] file3図_センサ3.jpeg 13件 [詳細] file3図_センサ2.jpeg 15件 [詳細] file3図_センサ1.jpeg 9件 [詳細] file3図_ギア2.png 14件 [詳細] file3図_ギア1.png 10件 [詳細] file3図1_ギア2.png 6件 [詳細] file3図1_ギア1.png 9件 [詳細] file3図_車体3.jpeg 9件 [詳細] file3図1_車体2.png 15件 [詳細] file3図1_タイヤ2.png 13件 [詳細] file3図1_タイヤ1.png 14件 [詳細] file3図_車体1.JPG 10件 [詳細] file3図1_アーム4.png 12件 [詳細] file3図1_アーム3.png 11件 [詳細] file3図1_アーム2.png 10件 [詳細] file3図_アーム4.png 7件 [詳細] file3図_アーム3.png 8件 [詳細] file3図_アーム2.png 6件 [詳細] file3図1_ハンド3.png 15件 [詳細] file3図1_ハンド2.png 17件 [詳細] file3図_アーム.jpeg 10件 [詳細] file3図_ハンド.jpeg 11件 [詳細] file3図_アームハンド.jpeg 15件 [詳細] file3図_ロボ.jpeg 15件 [詳細] file3図_コース.png 14件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2019-02-14 (木) 09:34:49