[[2019a/Member]]

目次
#contents
*課題2について [#u74e2052]
A地点を出発し、次の経路を黒い線にそって動くロボットを作成する.交差点では1秒間停止し、丁字路では直角方向に進入する時のみ一時停止すること.

#ref(./s_kadai2_setumei.JPG,70%,課題2説明図)

A地点から出発 → M → K(直進) → L(ピンポン玉をつかむ) → K(右折) → J(一時停止の後、左折) → I(直進) → H(直進) → G(左折) → F → E → D(一時停止の後、直進) → C(直進) → B(一時停止) → シュート→ A地点に入る(ゴール)

詳しくは,[[2019a/Mission2]]をご覧ください.


*ロボットについて [#m5de3aaa]
全体図は下の写真の通りです.今回は,紙のサイズが小さく大型のロボットでは走行しにくいのでなるべくコンパクトになるようにしました.
#ref(./s_zentai1.jpg,50%,全体図1)
#ref(./s_zentai2.jpg,50%,全体図2)


光センサーはここについています.センサーをタイヤと近くすることで,ロボットがより正確に動くようにしました.
#ref(./s_hikari_sensor1.jpg,60%,光センサー位置説明)


**タイヤ(左がA,右がCに接続) [#xf6b7f3f]
動くタイヤは前輪の2輪,後輪は支えているだけです.最初は,4輪のタイヤで動かしていましたがそれでは摩擦が大きく,ロボットの動きのブレが大きかったです.そのため,2輪のみで動くようにし,後輪はゴムを外して摩擦を軽減しスムーズに動けるようにしました.
#ref(./s_taiya.jpg,60%,タイヤ説明)

**アーム(Bに接続) [#x5e87750]
アームはピンポン玉を挟み込む形です.
#ref(./s_arm1.jpg,60%,アーム全体)

アームのギアは,できるだけ省スペースになるようにブロックの上下に付けました.
#ref(./s_arm2.jpg,60%,アームギア横から)

また,回転方向の異なる動きを取り出すためにギアの配置も工夫しました.
#ref(./s_arm3.jpg,60%,アームギア上から)
#ref(./s_arm4.jpg,60%,アームギア下から)



*プログラムについて [#id4fac50]
**ライントレースの仕組み [#ld4d1ed2]
ライントレースとは,あらかじめ引かれた黒い線に沿って進むことを言います.ライントレースをするには,様々な方法が存在します.今回,私は黒い線の左側に沿って進むライントレースを考えました.それでは,仕組みを説明していきたいと思います.

このライントレースはロボット本体に付けられた光センサーの値を用いて行います.RISの光センサーは明るさを0から100までの整数値で得られます.これを用いてコースの黒と白の値を計りました.この結果,黒のライン上はおおよそ38〜40,白の部分ではおおよそ48〜50の値を得られました.これをもとに,黒,黒灰,灰,白灰,白の5段階の値を下の表のように決めました.

|>|BGCOLOR(#696969):|c
||COLOR(white){センサーの値}|
|>|BGCOLOR(#9999ff):|c
|COLOR(){黒}|COLOR(){  〜40}|
|>|BGCOLOR(#99ff99):|c
|COLOR(){黒灰}|41〜42|
|>|BGCOLOR(#ffff99):|c
|COLOR(){灰} |43〜44|
|>|BGCOLOR(#ffcc99):|c
|COLOR(){白灰}|45〜46|
|>|BGCOLOR(#ff9999):|c
|COLOR(){白} |COLOR(){47〜 }|

図にすると以下の通りです.
#ref(./s_sensor_atai.JPG,60%,センサー値図)

ラインとレース中は光センサーの値をもとにロボットの動きが決まるようにします.白の部分にいるときは黒の方へ,逆に黒の線上にいるときは白の方へ移動するようにします.今回は,光センサーの値を5段階で定めたので動きも5種類です.以下の表,図のように動きます.

|>|>|BGCOLOR(#696969):|c
||COLOR(white){センサーの値}|COLOR(white){動作}|
|>|>|BGCOLOR(#9999ff):|c
|COLOR(){黒}|COLOR(){  〜40}|左旋回:左→後進,右→前進|
|>|>|BGCOLOR(#99ff99):|c
|COLOR(){黒灰}|41〜42|左回転:左→停止,右→前進|
|>|>|BGCOLOR(#ffff99):|c
|COLOR(){灰} |43〜44|直進 :左→前進,右→前進|
|>|>|BGCOLOR(#ffcc99):|c
|COLOR(){白灰}|45〜46|右回転:左→前進,右→停止|
|>|>|BGCOLOR(#ff9999):|c
|COLOR(){白} |COLOR(){47〜 }|右旋回:左→前進,右→後進|

#ref(./s_sensor_ugoki.JPG,60%,センサー動き)

ライントレースをするなら,動きは2段階でいいじゃないかと思った人もいるかもしれません.確かに右回転と左回転の2段階の動作でもライントレースをすることは十分に可能です.しかし,今回5段階を用いたのには理由があります.まず,動きの高速化ために直進を入れました.直進は左右両方のタイヤが動くのでよりスムーズにライントレースができるようになりました.次に,急カーブ対策で左右の旋回を入れました.急カーブの時ラインから大きく離れてしまうことがありました.その際,回転だけではラインから離れたままで戻ってくることが難しく,出来たとしても大回りになってしまいました.しかし、旋回を入れることによってコンパクトに回転し急カーブでも上手くライントレースできるようになりました.

ライントレースのイメージです.実際の動きをイメージしたもので,実際とは異なります.
#ref(./line_trace_image.gif,60%,ライントレースイメージ)


このような動作を繰り返し行うことでライントレースをすることができます.



**交差点認識の方法 [#p8c71d7c]
ここでは交差点をどのように認識したかその方法を説明します.まず,直角交差点の方で基本的な仕組みを説明します.次の円形交差点では,直角を応用して円形にした際の直角との違いについて説明します.

***直角交差点 [#zcb84290]
ライントレースをしながら交差点に入ると黒が一定時間以上連続します.今回はこれを交差点と認識することにしました.すなわち,黒が一定時間以上連続したら停止するというプログラムにするということです.(下図参照)

#ref(./s_kousaten_shikumi.JPG,70%,交差点認識の仕組み)

一定時間は今回私たちのロボットでは0.20秒でした.この時間は,とても重要で決めるのは難しかったです.この時間が短すぎると,ライントレースの途中でたまたま黒が連続してしまえば間違って交差点と認識して止まってしまいます.逆に長すぎると交差点でも止まらずにライントレースをし続けてしまいます.誤作動が起きない時間を選ぶ必要があります.


***円形交差点 [#f1b8055c]
直角交差点と円形交差点の違いは止まるまでの一定時間です.下の図を見てください.

#ref(./s_enkei_kousaten.JPG,60%,円形交差点との差)

このように,&color(blue,){直角交差点};と&color(red,){円形交差点};では止まるまでに走行する距離が異なります.円形交差点の方が若干短くなっています.そのため,直角交差点用の一定時間(0.20秒)では長すぎるので交差点を認識しなくなってしましました.そこで,円形交差点では別に一定時間を定めることでこの問題を乗り切りました.ちなみに一定時間は0.13秒でした.

**使用した主なサブルーチン [#pfa6529b]

***定義・マクロ [#c109df9b]

まずグローバル変数のtを定義します.
 int t;
次に交差点を認識するための時間を2種類定義します.
 #define KOUSATEN 20      //直角交差点用
 #define ENKEIKOUSATEN 13 //円形交差点用
ライントレースに使用する光センサーの値を定義します.
 #define kuro 41
 #define kurohai 43   
 #define hai 45
 #define shirohai 47
ライントレースの動作のマクロです.
 #define go_ahead OnFwd(OUT_AC);                                   //直進
 #define turn_left SetPower(OUT_AC,3);Off(OUT_A);OnFwd(OUT_C);     //左回転
 #define turn_right SetPower(OUT_AC,3);OnFwd(OUT_A);Off(OUT_C);    //右回転
 #define Uturn_left SetPower(OUT_AC,3);OnRev(OUT_A);OnFwd(OUT_C);  //左旋回
 #define Uturn_right SetPower(OUT_AC,3);OnFwd(OUT_A);OnRev(OUT_C); //右旋回
アームを閉じるマクロです.アームが途中で開いてくるトラブルを修正するためのものです.
 #define arm_close SetPower(OUT_B,1);OnRev(OUT_B);Wait(10);Off(OUT_B);
一時停止のマクロです.交差点では,1秒間停止しなければならないので,時間は1秒です.
 #define kyukei Wait(100);
ライントレースの1つの動作の継続時間を定義します.
 #define STEP 1

***follow_line1 [#seefd551]
follow_line1はライントレースをして直角の交差点で止まるサブルーチンです.光センサーの値がkuroの状態が一定時間(KOUSATEN)の間続くと止まります.そのために,kuroの動作ではタイマーをリセットせず時間を計り続けます.それ以外の動作の時は1回ずつタイマーをリセットしています.PlaySoundは交差点での停止を確認するためです.
 sub follow_line1()
 {
     SetSensor(SENSOR_2,SENSOR_LIGHT);
   
     ClearTimer(0);           //まずタイマー0をリセット
     while(FastTimer(0)<KOUSATEN)      //タイマー0の値がKOUSATEN(20)未満のとき繰り返す
         {
         if(SENSOR_2<kuro)             //光センサーの値がkuro(41)未満なら
             {
             Uturn_left;        //左旋回
             }
         else if(SENSOR_2<kurohai)    //光センサーの値がkurohai(43)未満なら
             {
             turn_left;ClearTimer(0);  //左回転,そしてタイマー0をリセット
             }
         else if(SENSOR_2<hai)         //光センサーの値がhai(45)未満なら
             {
             go_ahead;ClearTimer(0);   //直進,そしてタイマー0をリセット
             }
         else if(SENSOR_2<shirohai)    //光センサーの値がshirohai(49)未満なら
             {
             turn_right;ClearTimer(0); //右回転,そしてタイマー0をリセット
             }
         else               //それ以外(光センサーの値が49以上)なら
             {
             Uturn_right;ClearTimer(0);//右旋回,そしてタイマー0をリセット
             }
         Wait(STEP);                   //いずれかの動作をSTEP(0.01秒)だけ実行
         }
     Off(OUT_AC);
     PlaySound(SOUND_UP);              //確認音                      
     kyukei;                           //1秒間停止  
 }

***follow_line2 [#a8201e5e]
follow_line2は,tの間だけライントレースをして止まるサブルーチンです.基本的な仕組みはfollow_line1と同じです.follow_line1との違いは,KOUSATENの代わりにグローバル変数tを用いることで任意の時間tの間は止まることなくライントレースを続けます.そのために,それぞれの動作でタイマーをリセットすることはなく,このサブルーチンが始まってからの時間を計り続けます.また,最後のPlaySoundはこのサブルーチンが終わったことを知らせるためのものです.このサブルーチンは想像以上に使いやすく,メインプログラムでは何度も出てきます.困ったときはfollow_line2でほとんど解決できたのではないかというくらい万能でした.

 sub follow_line2()
 {
     SetSensor(SENSOR_2,SENSOR_LIGHT);
   
     ClearTimer(0);          //まずタイマー0をリセット
     while(FastTimer(0)<t)       //タイマー0がt未満の間繰り返す
          {
          if(SENSOR_2<kuro)      //kuro未満の時左旋回
              {
              Uturn_left;
              }
          else if(SENSOR_2<kurohai)  //kurohai未満の時左回転
              {
              turn_left;
              }
          else if(SENSOR_2<hai)    //hai未満の時直進
              {
              go_ahead;
              }
          else if(SENSOR_2<shirohai) //shirohai未満の時右回転
              {
              turn_right;
              }
          else             //それ以外(shirohai以上)の時右旋回
              {
              Uturn_right;
              }
          Wait(STEP);
          }
     Off(OUT_AC);
     PlaySound(SOUND_CLICK);      //確認音
 }

***follow_line3 [#xd0351f1]
follow_line3は,follow_line1とほぼ同じです.ただ,KOUSATENがENKEIKOUSATENに代わっているので円形交差点で止まります.
 sub follow_line3()
 {
     SetSensor(SENSOR_2,SENSOR_LIGHT);
   
     ClearTimer(0);
     while(FastTimer(0)<ENKEIKOUSATEN) //タイマー0がENKEIKOUSATEN未満の時繰り返す
         {               //以下follow_line1と同じ
         if(SENSOR_2<kuro)
             {
             Uturn_left;
             }
         else if(SENSOR_2<kurohai)
             {
             turn_left;ClearTimer(0);
             }
         else if(SENSOR_2<hai)
             {
             go_ahead;ClearTimer(0);
             }
         else if(SENSOR_2<shirohai)
             {
             turn_right;ClearTimer(0);
             }
         else 
             {
             Uturn_right;ClearTimer(0);
             }
         Wait(STEP);
         }
     Off(OUT_AC);
     PlaySound(SOUND_UP);
     kyukei;
 }

***cross_line [#q6eff459]
cross_lineは交差点を直進するためのサブルーチンです.動きは,下の図の通りです.

#ref(./s_cross_line.JPG,50%,cross_line説明)

まず,follow_line1または3で交差点を認識して停止.その時は黒の線上で止まっているので黒の線上であるとき直進をして線を越え,白になると止まります.その後,黒くなるまで右旋回をして黒の線へ戻ります.ここで,右回転ではなく右旋回を用いることで交差点を越えるときにより短い距離で越えることができます.

 sub cross_line()
 {
    SetSensor(SENSOR_2,SENSOR_LIGHT);
    while(SENSOR_2<hai)    //光センサーの値がhai未満の時
        {
        go_ahead;     //直進
         }
    Off(OUT_AC);     
    kyukei;        //一時停止
    while(SENSOR_2>hai)  //光センサーの値がhaiより大きい時
        {
        Uturn_right;    //右旋回
         }
    Off(OUT_AC);
    kyukei;        //一時停止
 }

***kousaten_turn_left [#f031bf2b]
kousaten_turn_leftは交差点を左折するためのサブルーチンです.交差点を認識して停止後,白になるまで左旋回しfollow_lineを再開します.

 sub kousaten_turn_left()
 {
    SetSensor(SENSOR_2,SENSOR_LIGHT);
    while(SENSOR_2<shirohai)     //shirohai以下の時繰り返す
        {
        Uturn_left;          //左旋回
        }
    Off(OUT_AC);
    kyukei;
 }



***pinpon_catch [#ed772a9b]
pinpon_catchはピンポン玉をつかむためのサブルーチンです.アームをゆっくり動かすためにSetPowerを導入しました.

 sub pinpon_catch()
 {
    SetPower(OUT_B,1);
    OnRev(OUT_B);    //アームを閉じる
    Wait(50);
    Off(OUT_B);
    kyukei;
 }
 



**メインプログラム[#ge3070f3]
***1.AからM [#b77ea5d5]
まず,AからM地点を越えてラインにたどり着くために一定時間直進します.

 task main()
 {
    OnFwd(OUT_AC);   //0.8秒間前進
    Wait(80);
    Off(OUT_AC);
    kyukei;

***2.MからK [#k49d89dd]
MからKの間は,急カーブが存在します.その際黒い部分を長い時間通過してしまいます.そのため,follow_line1では途中,交差点でないところでも止まってしまいます.それを防ぐため,follow_line2を用いてカーブを抜ける11秒間は止まらないようにしました.その後fllow_line1でライントレースを続け,K地点の交差点を認識して停止.cross_lineで交差点を直進します.

    t=1100;follow_line2();
    follow_line1();
    cross_line();          //K

***3.KからL [#t72bfd3b]
Kの交差点を直進後,ピンポン玉を取るためLに行きます.そのままライントレースをしていけばいいはずなのですが,タッチセンサーを付けていないのでピンポン玉があることを認識できません.そのため,follow_line2を用いてピンポン玉をつかめる位置までライントレースを行いそこで停止してからpinpon_catchでピンポン玉をつかむというプログラムを組みました.

    t=120;follow_line2();
    pinpon_catch();        //L 

***4.LからK [#v6687261]
ピンポン玉をつかんだ後,そのままライントレースを続けます.L地点をUターンしてK地点で止まります.K地点はラインの左側をトレースする場合,光センサーではタイミングをつかめません.なので,ここでもfollow_line2を用いて停止(STOP1)するようにしました.

K地点での右折について説明します.最初は,停止(STOP1)した位置から光センサーを用いて,白くなるまで右回転,黒くなるまで右旋回というようにセンサーを用いたプログラムを書いていました.しかし,このプログラムでは重大な欠陥が見つかりました.それは,STOP1の位置での光センサーの値が一定でないことです.follow_line2で停止するときスタートからの時間条件だけで止まるのでSTOP1で白か黒かどちらにいるかわかりませんでした.これでは,光センサーの値を用いたプログラムではうまく動きませんでした.よって,一定時間右回転をすることで確実にラインを越えそこからセンサーを用いてラインに戻る動作に改めました.その結果,非常にスムーズに右折をすることができました.動きは下の図を参照してください.

#ref(./s_K_turn_right2.jpg,70%,K地点説明)



    t=470;follow_line2();
    K_turn_right();        //K
    arm_close;       //開かないようアームをちょっと閉じる


 sub K_turn_right()
 {
    OnFwd(OUT_A);   //0.7秒間右に回転
    Wait(70);
    Off(OUT_A);      
    kyukei;    
    while(SENSOR_2>hai) //光センサーの値がhaiより大きいとき繰り返す
        {
        Uturn_right;   //右旋回
        }
    Off(OUT_AC);
    kyukei;
    
 }

***5.KからJ,I,Hを通ってG [#r52ba68b]
Kでの右折後,最初はいきなりfollow_line3(円形交差点用)を用いていました.しかし,それでは右折後すぐに止まってしまう誤作動が多発しました.原因はラインに対して角度をもって入るため,黒の部分を長く通り過ぎ交差点と誤認識するためでした.そのため,ここではfollow_line2を導入し黒になってると思われる間は止まらないようにしました.follow_line2のあと連続してfollow_line3を始めることによって誤って止まることなく,さらに交差点での停止を可能としました.

同様に,Jの左折後,I,Hの直進後にも交差点通過直後のブレによる誤作動を起こさないためにfollow_line2→follow_line3という組み合わせを用いました.

    t=90;follow_line2();
    follow_line3();
    kousaten_turn_left();  //J
    t=120;follow_line2();
    follow_line3();
    cross_line();          //I
    arm_close;       //開かないようアームをちょっと閉じる
    t=100;follow_line2();
    follow_line3();
    cross_line();          //H
    t=60;follow_line2();
    follow_line3();
    kousaten_turn_left();  //G
    arm_close;             //開かないようアームをちょっと閉じる



***6.GからF,Eを通ってD [#ra5ada5c]
F,Eは直角のカーブですが,これを交差点と考えF,Eをkousaten_turn_leftを使って左折するプログラムを考えました.Dは直進をするので,cross_lineを用いました.また,直角の交差点を左折する際にも今までと同様のブレが発生しました.したがってfollow_line2→follow_line1の組み合わせを用いました.下の画像も参照してください.

最初,失敗したとき
#ref(./s_follow_line2_setumei1.JPG,70%,follow_line2失敗説明)

follow_line2を導入したとき
#ref(./s_follow_line2_setumei2.JPG,70%,follow_line2成功説明)

    t=50;follow_line2();
    follow_line1();
    kousaten_turn_left();  //F
    t=100;follow_line2();
    follow_line1();
    kousaten_turn_left();  //E
    t=100;follow_line2();
    follow_line1();
    cross_line();          //D
    arm_close;             //開かないようアームをちょっと閉じる


***7.DからB [#s915a734]
ライントレースをしてCを直進しB地点で停止するプログラムです.今までと同様の理由でfollow_line2を組み込んでいます.

    follow_line1();
    cross_line();          //C
    arm_close;             //開かないようアームをちょっと閉じる
    t=100;follow_line2();
    follow_line1();

***8.BからA [#pfdf3316]
Bの交差点で停止後,ピンポン玉をシュートしゴール地点に入るプログラムです.動きは、下の写真の通りです.アームを開き,ロボットが下がり,そして,アームを閉じる時にピンポン玉をシュートします.最初は,アームを開いてロボット本体でただ押し出すだけだったのですが,それではあまり勢いがつかずシュートと言うにはいまいちだったので改良しました.(下の写真は説明用に撮影したものです.)

1.向きを修正した後
#ref(./s_shoot1.jpg,40%,シュート1)
2.アームを開く
#ref(./s_shoot2.jpg,40%,シュート2)
3.ロボットが下がる(この写真ではちょっと下がりすぎです)
#ref(./s_shoot3.jpg,40%,シュート3)
4.アームを閉じる
#ref(./s_shoot4.jpg,40%,シュート4)
5.シュート!!
#ref(./s_shoot5.jpg,40%,シュート5)

    shoot();               //B
    pinpon_catch();    //アームを閉じる(写真4,5)
    OnFwd(OUT_AC);         //まっすぐ進んでAに入る
    Wait(200);
    Off(OUT_AC);
 }


 sub shoot()
 {
    OnFwd(OUT_A);    //右に回転し,方向を修正(写真1)
    Wait(20);
    Off(OUT_A);
    Wait(10);
    SetPower(OUT_B,1); 
    OnFwd(OUT_B);    //アームを開く(写真2)
    Wait(20); 
    Off(OUT_B);
    Wait(10);
    OnRev(OUT_AC);    //後ろに下がる(写真3)
    Wait(35);
    Off(OUT_AC);
 }



*感想・反省点 [#c3f3176f]
基本的な動作のパーツは比較的簡単に出来ました.しかし、組み合わせてコースを走らせてみるとカーブの途中で止まったり,交差点で止まらなかったりと問題の連続でした.特に,K付近の時間のみで制御するところの時間の調整は本当に難しかったです.また、交差点のあとすぐに止まってしまう問題も解決に時間がかかりました.

結果としては,ほぼほぼ上手く行きました.ただ、同じプログラムなのにさっきは上手くいった,けど今回はダメだったというように,全てが完璧にそろうことはなかなかありませんでした.原因はよくわかりませんでした.次回は,いつでも同じ動きができるようなプログラムや,ロボットを作りたいです.


訪問者数&counter(all); 今日&counter(today); 昨日&counter(yesterday);

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