2018a/Member

ライントレース

課題

与えられたコースを走行するロボットとそのプログラムを作成する。

走行するコース

第1コースを選択した。

第1コース

ロボット本体

ロボット本体 ロボット本体 ロボット本体 ロボット本体

ロボット本体

胴体部分と、缶をつかむ部分(以下、アーム)からなる。

胴体部分とアームはNXT知能ブロックによってしっかりと連結している。

知能ブロック部分を外すと胴体部分とアームの連結がゆるく、ぷらぷらになってしまう。

缶をつかんだ時の様子。

モーターAの回転により、アームを開閉する。

人間の手が缶を持つときのような形でつかむ。

横から見た図。

アーム部分はかなり重く、下に支えがないと機体が前に傾いてしまう。

缶を認識するためのセンサーをアームの下にくるように取り付け、支えとした。

これにより安定した走行が可能になった。

プログラム

黒線の左側をライントレースするプログラムを作成した。 最後まで終わらなかった。Xで缶をつかむところまで作成した。

定義した定数

明るさの判断をするためのしきい値

#define w 58 //これ以上は真っ白 
#define lw 57 // これ以下は少し白
#define g 50 //これ以下は灰
#define lb 44 //これ以下は少し黒
#define b 36 //これ以下は真っ黒
  • 36以下は「真っ黒」とし、このしきい値の定数名はbとした。
  • 44以下は「少し黒」とし、このしきい値の定数名はblとした。
  • 50以下は「灰」とし、このしきい値の定数名はgとした。
  • 57以下は「少し白」とし、このしきい値の定数名はwlとした。
  • 58以上は「真っ白」とし、このしきい値の定数名はwとした。

明るさの区間ごとの、ロボットの走行の仕方

#define s 30
#define L OnFwd(OUT_B,40);OnRev(OUT_C,40);//左に曲がる
#define R OnFwd(OUT_C,s);OnRev(OUT_B,s);//右に曲がる
#define LL OnFwd(OUT_B,s);Off(OUT_C);//少し左に曲がる
#define LR OnFwd(OUT_C,s);Off(OUT_B);//少し右に曲がる
#define S OnFwd(OUT_BC,s);//直進
  • Lは左旋回
  • Rは右旋回
  • LLは左折
  • LRは右折
  • Sは直進

回転スピードs ・・・速すぎると単位距離あたりの「明るさの認識」の回数が減り、うまくライントレースできない。

   遅すぎるとタイヤが空回りすることがよくあり、うまく進まない。

   30が丁度よいと判断した。

Lにおける回転スピード ・・・左折するためにsより速い回転。詳しくは後述のサブルーチンのところで解説。

サブルーチンの基本形

{
    SetSensorLight(S3);
    
    
    while(true){
        if(SENSOR_3<b){
           L;                      //真っ黒なら左へ                             
        }else if(SENSOR_3<lb){
           LL;                   //少し黒なら少し左へ                                  
        }else if(SENSOR_3<g){
           S;                       //灰なら直進
        }else if(SENSOR_3<lw){
           LR;                      //少し白なら少し右へ
        }else if(SENSOR_3>w){
           R;
        }                            //白なら右へ
    }
}

この形(左側ライントレース)を基本とし、これをコースの各部分ごとに適したサブルーチンとなるよう調節した。

各サブルーチン

sub stop_cross()          //交差点で一秒止まる、左側ライントレース
{
    SetSensorLight(S3);
    int n1=0;
    
    while(n1<1){
        if(SENSOR_3<b){
           long t0=FirstTick();
           L;                      //真っ黒なら左へ                             
           long t1=CurrentTick();
           if(t1-t0>100){Off(OUT_BC);Wait(1000);S;Wait(300);n1++;}//交差点で一秒止まる
        }else if(SENSOR_3<lb){
           LL;                   //少し黒なら少し左へ                                 
        }else if(SENSOR_3<g){
           S;                    //灰なら直進
        }else if(SENSOR_3<lw){
           LR;                   //少し白なら少し右へ
        }else if(SENSOR_3>w){
           R;
        }                            //白なら右へ
    }
}

AからCへの部分。

黒線の左側をライントレースし、交差点に差し掛かったら一秒間停止するプログラム。

「真っ黒」を認識し続ける時間(t1-t0)が0.1秒を超えると止まるようにした。

t1は明るさが「真っ黒」である時間を数え始める点。

t2は明るさが「真っ黒」であるのが終わる時間を記録する。

このプログラムで確かに交差点Cで一時停止したが、なぜうまくいったのかは

実のところよく分からない。

交差点を認識するというより、「真っ黒」であるのを認識すると止まるのであると考える。

現に、ライントレースで軌道修正する際に「真っ黒」を認識するようなことがあると

一時停止し、次のサブルーチンに移ってしまっていた。

センサーが(おそらく)「灰」以上に明るい位置からライントレースを始めないとうまくいかないプログラムであった。

このサブルーチンが1回実行されると次のサブルーチンに移行するようにするため、変数n1を導入した。

交差点を認識し一秒間停止するとn1が1ふえる。すると次のサブルーチンへ移る。

sub turn_cross()  //曲がり角で止まらず左折する
{
    SetSensorLight(S3);
    int n2=0;

    while(n2<3){
        if(SENSOR_3<b){
           L;
           Wait(450);
           OnRev(OUT_BC,s);
           Wait(400);
           n2++;                  //黒なら左へ 、曲がり角左折                               
        }else if(SENSOR_3<lb){
           LL;                   //少し黒なら少し左へ                                 
        }else if(SENSOR_3<g){
           S;                    //灰なら直進
        }else if(SENSOR_3<lw){
           LR;                   //少し白なら少し右へ
        }else if(SENSOR_3>w){
           R;
        }                            //白なら右へ
    }
}

Cから次の曲がり角への部分。

交差点や曲がり角を曲がる際、旋回している途中にセンサが黒線上から外れてうまく曲がれなかったため、

「少し曲がっては後ろに下がる」ことを繰り返して黒線上から外れずに曲がるようにした。

失敗することも多いプログラムであった。

左折し終えたら次のサブルーチンに移行するように、変数n2を導入した。

少し左に曲がって下がる、を1回行うとn2が1ふえる。

3回繰り返す(つまり曲がり角を左折する)と次のサブルーチンに移るよう、

n2<3が真である間、whileの中身のプログラムを実行するようにした。

sub go_cross()    //曲がり角スルー
{
     SetSensorLight(S3);
     int n3=0;

     
     while(n3<1){
       if(SENSOR_3<b){
          long t2=CurrentTick();
          L;                      //真っ黒なら左へ                             
          long t3=CurrentTick();
          if(t3-t2>500){n3++;}                           
        }else if(SENSOR_3<lb){
           LL;                   //少し黒なら少し左へ                                 
        }else if(SENSOR_3<g){
           S;                    //灰なら直進
        }else if(SENSOR_3<lw){
           LR;                   //少し白なら少し右へ
        }else if(SENSOR_3>w){
           R;
        }                            //白なら右へ     
     }
 }

曲がり角からXの缶へ。

上記のとおり、「曲がっては後ろに下がる」を繰り返さないと左折できないため、「サブルーチンの基本形」をほとんどそのまま用いれば左折しない。

円をライントレースしていかないようにするためこのようにした。

円の外側に到達すると一瞬左折しようとするができない。その間センサーは「真っ黒」を認識している。

この「真っ黒」の時間が0.5秒続くとn3が1ふえる。

「真っ黒」の時間は上記t1-t0と同様にt3-t2によって数える。

このサブルーチンが1回実行されると次のサブルーチンに移行するようにするため、変数n3を利用。

n3が1以上になると次のサブルーチンに移る。

sub get_can()   //缶をつかむ
{
    OnRev(OUT_A,s);
    Wait(700);
    Off(OUT_A);
 }

缶を掴む。

アームは最初は胴体部分に垂直に開いている。

0.7秒モーターAを回転させて缶を掴む。

センサーは利用していない。

一つ前のサブルーチンを終えた時点で眼の前に缶が来るようになっているので、

ただアームを閉じれば缶を掴み取れる。

mainのタスク

各サブルーチンを順番に並べた。

task main()
{
    stop_cross();    //AからC
    turn_cross();    //Cからカーブ、曲がり角曲がる
    stop_cross();    //Dで止まる
    get_can();       //缶をつかむ
    
    Off(OUT_BC);
}

反省

ロボット本体の作成はほとんどパートナーに任せっぱなしだった。

相変わらず課題を後回しにし、その点でもパートナーに迷惑をかけた。

十分に時間を確保しなかったためにプログラムの作成が終わらなかった。

ひとつひとつのサブルーチンごとに、思った通りにロボットが動くか確認する作業は大変であった。

左折、一時停止、急カーブの走行など、どうプログラムを作ればうまくいくのか・何が原因で失敗するのかを考えるのが難しかった。失敗する原因を探す際、ロボットが今どのプログラムを実行しているのかが分からないことがあった。

タイヤが空回りしたり回らなかったりすることがあったが、タイヤと機体の胴体部分の距離を縮めると改善した。タイヤが機体の胴体部と離れすぎるとうまく働かなくなることがあるとわかった。


添付ファイル: fileIMG_0568.JPG 37件 [詳細] fileIMG_0567.JPG 27件 [詳細] fileIMG_0573.JPG 33件 [詳細] file2018-07-18.png 47件 [詳細] file2018a-mission2.png 31件 [詳細] fileIMG_0571.JPG 20件 [詳細] fileIMG_0570.JPG 41件 [詳細] fileIMG_0569.JPG 34件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-08-18 (土) 05:57:46