課題の概要

 詳しくはこちら→課題2

コース

ロボットの説明

ロボ本体1

 今回の課題2はプログラムメインの課題なので、ロボットはそれほど複雑な構造をしていない。前輪2つと後輪1つの定番でシンプルなロボットである。モーター間距離を最小にすることによって小回りの効率性を上げた。センサーを手前に装着することによってライントレース、センサー判断の効率性を上げている。

ロボ本体2

 当初、より小さい黄色のタイヤを使用しようとしたが、なぜか空回りをするので採用を見送った。急カーブ時に専用のプログラムを製作すればよいと判断したため、一段階大きなタイヤを使用している。当初首振り機構にしようと思ったが、うまくいかなかったので、2枚目の写真のようにスライド式にした。スライド式でも曲がることが出来るので、問題ないと判断した。

プログラムの説明

 走行したコースはA→E→F→Q→R→S→G→H→T→T→R→S→P→E→A

マクロ・定数

 

#define THRESHOLD 45 //境45
#define Go OnFwd(OUT_AC);//前進
#define Back OnRev(OUT_AC);//後退
#define Stop Off(OUT_AC);//停止
#define Right OnFwd(OUT_C);Off(OUT_A);//右回転
#define RightQ OnFwd(OUT_C);OnRev(OUT_A);//右旋回
#define Left OnFwd(OUT_A);Off(OUT_C);//左回転
#define LeftQ OnFwd(OUT_A);OnRev(OUT_C);//左旋回
#define nMAX 25//連続値25と定義
#define judge Off(OUT_AC);Wait(100); //ジャッジメント
#define Lo SetPower(OUT_AC,0);//超低速

境の値は計測して最も安定した45とした。ジャッジメントというのは交差点判断時に、停止するだけのプログラムである。超低速としたのは、速すぎるとセンサーの感度が悪くなってしまう可能性があり、交差点を通り過ぎてしまう可能性があるからである。

サブルーチン

 以下の5つのサブルーチンは交差点判断型プログラムに属する

sub L() //L字直交路通過プログラム
{
 Lo;
 PlaySound(SOUND_UP);//サウンドアップ
 RightQ;
 Wait(110);
 Go;Wait(30);
 ClearTimer(1);//タイマーリセット
 Stop(30);
}

これは名の通りF、G、H地点のようなL字カーブで動作する

sub Y() //環状路通過プログラム(環状路手前がYの形に見えたので、Yと定義)
{
 Lo;
 judge;
 PlaySound(SOUND_DOWN);//サウンドダウン
 RightQ;
 Wait(110);
 Go;Wait(50);
 Stop(30);
 ClearTimer(1);
}

環状路手前で止まってサウンド鳴らして動くプログラム

sub traverse() //交差点通過プログラム
{
 Lo;
 PlaySound(SOUND_UP);
 Go;
 Wait(40);
 Stop(30);
 ClearTimer(1);
}
sub traverse2() //交差点通過プログラム(一時停止)
{
 Lo;
 judge;
 PlaySound(SOUND_DOWN);
 Go;
 Wait(40);
 Stop(30);
 ClearTimer(1);
}
sub LQ()   //緩めカーブ通過プログラム
{
 Lo;
 PlaySound(SOUND_UP);
 Right;
 Wait(120);
 Stop(30);
 ClearTimer(1);
}
交差点直行路判断

 これら5つのプログラムは全て同じ原理である。青い線を進行方向とし、赤く塗りつぶしたところ(ブラックゾーン侵入)を通過する際にセンサーがnMAX秒存在しているとき、上記の5つのプログラムが起動するようになっている。これは後述するライントレースのwhile(FastTimer(0)<nMAX)の部分に設定されている。もちろん全てが同時に発動するわけではなく、地点ごとに起動するプログラムを設定している。方法はメインプログラムのコーナーで後述する。

sub Curve() //急カーブ通過プログラム
{
 Lo;
 PlaySound(SOUND_UP);
 until(SENSOR_2<=THRESHOLD+7);//ホワイトゾーン左旋回
 RightQ;Wait(150);
 Go;Wait(30);
 ClearTimer(1);
}

交差点判断を応用してuntilを使用してホワイトゾーン左旋回を行った。ホワイトゾーン旋回というのは、カーブ時にホワイトゾーンに至るまで、曲がり続ける動作を行う旋回である。ここのカーブはラとても急でイントレースのプログラムだけでは無理があり、補助としてこのようなプログラムを組んだ。

以下がライントレース用サブルーチン

sub Line()               //ライントレース 
{
  Lo;
  start play_music()                     //La Campanellaの主題を流す
  ClearTimer(0);                         //タイマーリセット
  while(FastTimer(0)<nMAX)               //タイマーがnMAXを越えるまで以下の動作を継続
     {
        if(SENSOR_2<THRESHOLD-5){              //ブラックゾーンに進入時、右へ
             Right;
        }else if(SENSOR_2<THRESHOLD-3){        //ブラックゾーンに少し進入した時、右旋回
             RightQ;
        }else if(SENSOR_2<=THRESHOLD){         //境界上において直進
             Go;
        }else if(SENSOR_2<THRESHOLD+7){        //ホワイトゾーンに進入時、左旋回
             LeftQ;
        }else{                                 //それ以上の時、左へ
             Left;
         if(SENSOR_2>THRESHOLD-8){              //リセット
             ClearTimer(0);
        }
     }
}
ライントレース

 以下の図のように線ごとに値を決めている。中央を45とし、これの差を用いて値を決める。例えば、45より小さい場合(ブラックゾーンに侵入)のとき、左側へ移動するようにし、45より大きい場合(ホワイトゾーン侵入)、右側へ移動するようにしている。ブラックゾーンに侵入しない時にタイマーリセットを常に行う理由は、交差点&直角路判断に差し掛かる場合のみにタイマーが起動するようにする狙いがあるからである。

メインプログラム

 

task main ()
{
 SetSensor(SENSOR_2,SENSOR_LIGHT);
 ClearTimer(1);                                  //タイマーリセット
 int t=0;                                        // グローバル変数tを定義
 Go;Wait(200);                                   //最初の区間はトレースせずに全速前進
 while(t<13)                                     //t=12まで以下の動作を継続
       {
     Lo;
      if(Timer(1)>250&&t==0)                          //FのL字
       {
        t++;                      //tを+
        L();
       }
     else if(Timer(1)>11&&t==1)                     //環状路手前Q
       {
        t++;
        Y();
       }
     else if(Timer(1)>20&&t==2)                     //R
             {                    
        t++;
        traverse();
       }
     else if(Timer(1)>20&&t==3)                     //S
       {
        t++;
        L();
             }        
     else if(Timer(1)>30&&t==4)                    //GのL字
       {
        t++;
        L();
             }       
     else if(Timer(1)>30&&t==5)                   //GH間の急カーブ
       {
        t++;
        Curve();
             }      
     else if(Timer(1)>130&&t==6)                  //HのL字
       {
        t++;
        L();
             }       
     else if(Timer(1)>20&&t==7)                  //T横断
       {
        t++;
        traverse2();
             }      
     else if(Timer(1)>80&&t==8)                  //2回目のT横断
       {
        t++;
        traverse2();
             }      
     else if(Timer(1)>25&&t==9)                 //環状路手前R
       {
        t++;
        Y();
             }      
     else if(Timer(1)>25&&t==10)                //S
       {
        t++;
        traverse();
             }      
     else if(Timer(1)>12&&t==11)                //P
       {
        t++;
        LQ();
             }       
     else if(Timer(1)>25&&t==12)               //区間EA
       {
         start play_music_coda           //La Campanellaのコーダを流す
         L();
         Go;Wait(60);                              //最後の区間ではループ解除
         Right;Wait(40);    //向きを補正して直進、そしてゴールして停止。
         Go;Wait(240);
         Stop;Wait(100);
         t++;
       }
     else
             {
        Line();
             }                      //普段はライントレース
    }
  Stop;PlaySound(SOUND_DOWN);               //ゴールで歓喜のサウンドダウン
 }

 ここでtを使用して交差点判断型プログラム使用回数を数えることによって、区間毎の走行パターンを決定している。またtと同時にタイマーを設定したのは、センサー判断後、すぐに次のセンサー判断に移行しないようにするためである。タイマー設定時間はばらつきがあるが、これは大雑把に設定している。走行時間などは明確に測っていない。論理和&&を使用することによって、タイマーの条件とtの数値の条件両方を満たす時のみに、交差点、L字カーブなどのライントレース以外の動作が発動するようにしている。こうすることによって、L字と交差点、環状路それぞれ区別可能にし、交差点なのに環状路通過時の動作をするなどの想定外の動作を行わないようにしている。結果的にプログラム自体はとても単純になった。

おまけ:Liszt作曲 La Campanella(Grandes Etudes de paganini,S.141) 

 ここからは完全に自己満足の世界。なぜこの曲を選んだのかという問いに対する答えはない。当初はChopin作曲の幻想即興曲にしようと思ったが、音が多すぎた(今回の数十倍のプログラム数になると思われる)ので断念した。楽譜はスマホのフリーソフトで作ったので少し雑であるが、読めると思われる。原曲聴きたい人はyoutube等で聴くことを推奨。今回製作したLa Campanellaは、ロボットが演奏できるように簡単アレンジしてあるが、ピアノの魔術師と呼ばれたLisztの曲なので原曲はそれなりに難易度が高い。La Campanella(Grandes Etudes de paganini,S.141)のバージョンがとても有名で、誰もが耳にしたことがあると思われる。ちなみに自分は多少は弾けるが、完璧には弾けない。La Campanella(Etudes d'Execution Transcendante d'apres Paganini, S. 140)のバージョンは物凄く難易度が高く、作曲されてから今に至るまで録音できた人は数名らしい。楽譜はもちろんあるので興味がある人は挑戦してみよう。ちなみに自分はこのバージョンは全く弾けない。

ピッチ

 sは#(#は使用出来なかったので...)、数字はオクターブ区別。英・米式表記。

#define Gs0  415
#define A0   440
#define As0  466
#define B0   494
#define C0   523
#define Cs   554
#define D    554
#define Ds   622
#define E    659
#define F    698 
#define Fs   740
#define G    784
#define Gs   831
#define A    880
#define As   932
#define B    988
#define C   1047
#define Cs1  1108
#define D1   1174
#define Ds1  1244
#define E1   1318
#define F1   1396
#define Fs1  1479
#define G1   1567
#define Gs1  1661
#define A1    760
#define As1  1864
#define B1   1975
#define C2   2093
#define Cs2  2217
#define Ds2  2489

主題

 ライントレース中に演奏

主題
task play_music()
{
 while(true)
           {
        PlayTone(Ds2,15);Wait(25);     //1小節目
             PlayTone(Ds2,15);Wait(25);
       PlayTone(Cs2,15);Wait(25);
       PlayTone(B1,15); Wait(25);
             PlayTone(B1,15); Wait(25);
             PlayTone(As1,15);Wait(25); 
             PlayTone(Gs1,15);Wait(25); 
             PlayTone(G1,15); Wait(25); 
             
             PlayTone(Gs1,15);Wait(25);    //2小節目
             PlayTone(As1,15);Wait(25); 
             PlayTone(Ds1,15);Wait(25); 
             PlayTone(Ds1,15);Wait(25); 
             PlayTone(E1,15); Wait(25); 
             PlayTone(Ds1,15);Wait(25);
             PlayTone(Cs1,15);Wait(25); 
             PlayTone(B,15);  Wait(25);
             
             PlayTone(B,15);  Wait(25);   //3小節目
             PlayTone(As,15); Wait(25);
             PlayTone(Gs,15); Wait(25);
             PlayTone(G,15);  Wait(25);
             PlayTone(Gs,15); Wait(25);
             PlayTone(As,15); Wait(25);
             PlayTone(Ds,15); Wait(25);
             PlayTone(Ds1,15);Wait(25);
             
             PlayTone(Ds2,15);Wait(25);   //4小節目
             PlayTone(Ds2,15);Wait(25);
             PlayTone(Cs2,15);Wait(25);    
             PlayTone(B1,15); Wait(25);
             PlayTone(B1,15); Wait(25);
             PlayTone(As1,15);Wait(25); 
             PlayTone(Gs1,15);Wait(25);
             PlayTone(G1,15); Wait(25);
             
             PlayTone(Gs1,15);Wait(25);   //5小節目
             PlayTone(As1,15);Wait(25);
             PlayTone(Ds1,15);Wait(25);     
             PlayTone(Ds1,15);Wait(25);
             PlayTone(E1,15); Wait(25);
             PlayTone(Ds1,15);Wait(25);
             PlayTone(Cs1,15);Wait(25);
            
             PlayTone(Ds1,15);Wait(25);   //6小節目
             PlayTone(Gs1,10);Wait(12);
             PlayTone(B1,10); Wait(12);
             PlayTone(Ds2,15);Wait(25);
             PlayTone(Ds1,15);Wait(25);
             
             PlayTone(G,10);  Wait(12);    //7小節目
             PlayTone(As1,10);Wait(12); 
             PlayTone(Ds2,15);Wait(25); 
             PlayTone(Gs1,90);Wait(100);
           }
}

コーダ 

 ゴール手前の区間で演奏

コーダ
task play_music_coda()
{
 PlayTone(B1,15); Wait(25);    //1小節目
 PlayTone(As1,7); Wait(12);
 PlayTone(B1,7);  Wait(12);
 PlayTone(Cs2,15);Wait(25);
 PlayTone(B1,7);  Wait(12);
 PlayTone(As1,7); Wait(12);
 
  PlayTone(B1,7);  Wait(12);     //2小節目
  PlayTone(Cs2,7); Wait(12);
  PlayTone(Ds2,15);Wait(25);
  PlayTone(Cs2,7); Wait(12);
  PlayTone(B1,7);  Wait(12);
  PlayTone(As1,7); Wait(12);
  PlayTone(B1,7);  Wait(12);  
  
  PlayTone(Cs2,15);Wait(25);   //3小節目
  PlayTone(B1,7);  Wait(12);
  PlayTone(As1,7); Wait(12);
  PlayTone(B1,7);  Wait(12);
  PlayTone(As1,7); Wait(12); 
  PlayTone(Gs1,10);Wait(25);
  
  PlayTone(B1,15); Wait(25);    //4小節目
  PlayTone(As1,7); Wait(12);
  PlayTone(B1,7);  Wait(12);
  PlayTone(Cs2,15);Wait(25);
  PlayTone(B1,7);  Wait(12);
  PlayTone(As1,7); Wait(12);
  
  PlayTone(B1,7);  Wait(12);  //5小節目
  PlayTone(Cs2,7); Wait(12);
  PlayTone(Ds2,7); Wait(25);
  PlayTone(Cs2,7); Wait(12);
  PlayTone(B1,7);  Wait(12);
  PlayTone(As1,7); Wait(12);
  PlayTone(B1,7);  Wait(12);
  
  PlayTone(Cs2,15);Wait(25);  //6小節目
  PlayTone(B1,7);  Wait(12);
  PlayTone(As1,7); Wait(12); 
  PlayTone(B1,7);  Wait(12);
  PlayTone(As1,7); Wait(12);
  PlayTone(Gs1,10);Wait(25);
  
  PlayTone(B1,7);  Wait(12);  //7小節目
  PlayTone(As1,7); Wait(12);
  PlayTone(Gs1,10);Wait(25);
  PlayTone(B1,7);  Wait(12);
  PlayTone(As1,7); Wait(12);
  
  PlayTone(Gs1,10);Wait(100);  //8小節目
}

考察

 結果については最後まで完走させることが出来てよかった。何回も何回も試行錯誤した成果がしっかりと現れて満足している。試行錯誤して苦労している時にパートナーのアドバイス、ヘルプがなかったら、完成させることが出来なかったのではないかと思われる。プログラム自体は複雑な部分は特になく、とてもシンプルにすることが出来たので良かった。La Campanellaを演奏させながら、走行させるのは面白い試みであったと思う。今回の課題において一番苦労したのは交差点認識プログラムの設計で、当初自分は最適な方法を理解しておらず、とても時間を費やした。交差点プログラムが出来た後のプログラムはそれほど苦労はしなかったので、今回の山場はやはり交差点だと感じた。また、電池の消耗によるパワーダウンが原因で、数値が変わってしまうことにも苦労した。今回のプログラムにおいて少し雑になってしまったところがあるので反省している。特に急カーブ専用プログラムで、少し大雑把な曲がり方になってしまった。もう少し時間があったらより正確に曲がることができるかもしれない。次の課題では、今回の課題の反省を活かしてより高度なことに挑戦してみたいと思う。


添付ファイル: fileIMG_6143.PNG 91件 [詳細] fileIMG_6142.PNG 83件 [詳細] fileIMG_6136.PNG 86件 [詳細] fileIMG_6135.JPG 115件 [詳細] fileIMG_6134.JPG 81件 [詳細] fileIMG_6114.JPG 98件 [詳細] fileIMG_6107.JPG 96件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-08-13 (日) 00:23:06