#contents

*課題について [#k833dc5c]
課題概要については→[[課題2:http://yakushi.shinshu-u.ac.jp/robotics/?2017a%2FMission2]]
#ref(./2017a-mission2.png);
~E地点を右折するプログラムを作成した。
~・E地点右折コース
~E右折(SOUND_UP)
~P一時停止(SOUND_DOWN)&左折
~Q(SOUND_UP)&直進
~R(SOUND_UP)&左折
~T一時停止(SOUND_DOWN)&直進
~T一時停止(SOUND_DOWN)&直進
~H(SOUND_UP)
~G(SOUND_UP)
~S一時停止(SOUND_DOWN)&左折
~P直進(SOUND_UP)
~Q左折(SOUND_UP)
~F(SOUND_UP)
~E直進
~A停止(SOUND_DOWN)
*ロボットの構造について [#kefa40d7]
#ref(./DSC_0402.JPG,10%.);
ロボットの長さは16.9cm、幅は14.0cm、高さは15.5cmである。本体を直立させ、タイヤを幅いっぱいに広げることでより小回りがきくようになっている。本体の背面には支えるためのアームが取り付けてある。タイヤなどではなく、接地面の少ない棒にすることで摩擦が少なく動作を妨げない。1つ問題は光センサーがやや高い位置にあるため、明暗を判別する値の範囲が狭くなってしまったこと。だがこれはプログラムで上手く調整すれば影響はなかった。
*プログラム [#e25666b8]
**定義部分 [#f3c42062]
  #define THRESHOLD 39
  #define HIPOWER 7  
  #define LOWPOWER 2   
  #define set_power_H SetPower(OUT_AC,HIPOWER);
  #define set_power_L SetPower(OUT_AC,LOWPOWER);
  #define go_forward set_power_H;OnFwd(OUT_AC);
  #define turn_right1 set_power_L;OnFwd(OUT_C); OnRev(OUT_A);
  #define turn_right0 set_power_L;OnFwd(OUT_C); Off(OUT_A);
  #define turn_left0 set_power_L;OnFwd(OUT_A); Off(OUT_C);
  #define turn_left1 set_power_L;OnFwd(OUT_A); OnRev(OUT_C);
  #define Break Off(OUT_AC);Wait(100);
まずは閾値(明暗を判別する際の境界の値)を計測の結果から39と設定し、THRESHOLDで定義した。また、スムーズな動作を実現するためset_powerにより二種類の回転速度を設定。直進するときはハイパワーで、曲がるときはローパワーで回転するようにした。turn_right1とturn_left1は両輪を互いに逆回転させその場で回転するような急旋回、turn_right0とturn_left0は片方の車輪のみを回転させやや前進しながらの緩やかな旋回を定義している。Breakは交差点での停止動作をまとめて定義したものである。当然ながら、turn_right1やturn_left1ばかり使っているとその場で左右に振れるばかりで前に進みにくい。よってすべてを組み合わせて直進性を増すことができる。

**サブルーチン [#u7d48f44]
 sub L_LINE()
  {int Black_Time; Black_Time=0;
    while(Black_Time < 6){
     if (SENSOR_2 < THRESHOLD -2) {
       turn_left1; Black_Time++;
      } else if (SENSOR_2 < THRESHOLD -1) {
       turn_left0; Black_Time++;
      } else if (SENSOR_2 < THRESHOLD +1) {
       go_forward;
      } else if (SENSOR_2 < THRESHOLD +2) {
       turn_right0; Black_Time = 0;
      } else {
       turn_right1; Black_Time = 0;
      }
      Wait(1);}
  }
 
 sub R_LINE()
 {int Black_Time; Black_Time=0;
   while(Black_Time < 6){
     if (SENSOR_2 < THRESHOLD -2) {
       turn_right1; Black_Time++;
      } else if (SENSOR_2 < THRESHOLD -1) {
       turn_right0; Black_Time++;
      } else if (SENSOR_2 < THRESHOLD +1) {
        go_forward;
      } else if (SENSOR_2 < THRESHOLD +2) {
       turn_left0; Black_Time = 0;
      } else {
       turn_left1; Black_Time = 0;
      }
      Wait(1);}
  }
まず黒い線の左側をトレースするプログラムと右側をトレースするプログラムの2つをサブルーチンで表した。(Lが左、Rが右)1回の動作は0.01秒で、黒い部分が6回続いたと判断した場合(交差点または直角な曲がり角と判断したとき)にこの動作から抜け出すようになっている。whileの中はint Black_Timeで変数を定義、閾値より光センサーの値が小さい(暗い)時にこの変数に1足して、逆に閾値より光センサーの値が大きい(明るい)時に変数を0に戻す(0を代入する)ようにしている。またこの動作から抜け出したとき(交差点や直角な曲がり角)の後の動作については、それぞれこのサブルーチンを呼び出した直後に入れられる。

**動作プログラム [#y1433ac9]
  task main()
  {
    SetSensor(SENSOR_2,SENSOR_LIGHT); 
    int C_TIME;
    C_TIME = 0;
    int Black_Time;
    Black_Time=0;
  
    if(C_TIME ==0)  //A出発
  { R_LINE();
    C_TIME++;
  }
  
    if(C_TIME == 1)  //E右折
  {
    PlaySound(SOUND_UP);
    ClearTimer(0);
    while(FastTimer(0) < 100)
  {if (SENSOR_2 < THRESHOLD -2) {
        turn_right1; Black_Time++;
       } else if (SENSOR_2 < THRESHOLD -1) {
        turn_right0; Black_Time++;
       } else if (SENSOR_2 < THRESHOLD +1) {
         go_forward;
       } else if (SENSOR_2 < THRESHOLD +2) {
        turn_left0; Black_Time = 0;
       } else {
        turn_left1; Black_Time = 0;
       }
       Wait(1);
  }
    R_LINE();
    C_TIME++;
  }
  
    if(C_TIME == 2)  //P左折
  { 
    PlaySound(SOUND_DOWN);
    Break;
    turn_left1;
    Wait(75);
    go_forward;
    Wait(10);
    L_LINE();
    C_TIME++;
  }
  
    if(C_TIME == 3)  //Q通過
  { PlaySound(SOUND_UP);
    turn_right0;
    Wait(60); 
    L_LINE();
    C_TIME++;
  }
  
    if(C_TIME == 4)  //R左折
  {  PlaySound(SOUND_UP);
     ClearTimer(0);
     while(FastTimer(0) < 100)
    {if (SENSOR_2 < THRESHOLD -2) {
        turn_left1; Black_Time++;
       } else if (SENSOR_2 < THRESHOLD -1) {
        turn_left0; Black_Time++;
       } else if (SENSOR_2 < THRESHOLD +1) {
         go_forward;
       } else if (SENSOR_2 < THRESHOLD +2) {
        turn_right0; Black_Time = 0;
       } else {
        turn_right1; Black_Time = 0;
       }
       Wait(1);
  }
    L_LINE();
    C_TIME++;
  }
  
    if(C_TIME == 5)  //T通過
  {  PlaySound(SOUND_DOWN);
     Break;
     turn_right0;
     Wait(90);
     R_LINE();
     C_TIME++;
  }
  
    if(C_TIME == 6)  //T通過
  {  PlaySound(SOUND_DOWN);
     Break;
     turn_left0;
     Wait(40);
     R_LINE();
     C_TIME++;
  }
    if(C_TIME == 7)  //H通過
  {  PlaySound(SOUND_UP);
     ClearTimer(0);
    while(FastTimer(0) < 100)
  {if (SENSOR_2 < THRESHOLD -2) {
        turn_right1; Black_Time++;
       } else if (SENSOR_2 < THRESHOLD -1) {
        turn_right0; Black_Time++;
       } else if (SENSOR_2 < THRESHOLD +1) {
         go_forward;
       } else if (SENSOR_2 < THRESHOLD +2) {
        turn_left0; Black_Time = 0;
       } else {
        turn_left1; Black_Time = 0;
       }
       Wait(1);
  }
     R_LINE();
     C_TIME++;
  }
  
    if(C_TIME == 8)  //二個目のヘアピン通過
  {  turn_right1;
     Wait(65);
     turn_right0;
     Wait(145);
     R_LINE();
     C_TIME++;
  }
  
    if(C_TIME == 9)  //G通過
  {  PlaySound(SOUND_UP);
     ClearTimer(0);
    while(FastTimer(0) < 100)
  {if (SENSOR_2 < THRESHOLD -2) {
        turn_right1; Black_Time++;
       } else if (SENSOR_2 < THRESHOLD -1) {
        turn_right0; Black_Time++;
       } else if (SENSOR_2 < THRESHOLD +1) {
         go_forward;
       } else if (SENSOR_2 < THRESHOLD +2) {
        turn_left0; Black_Time = 0;
       } else {
        turn_left1; Black_Time = 0;
       }
       Wait(1);
  }
     R_LINE();
     C_TIME++;
  }
  
    if(C_TIME == 10)  //S左折
  { 
    PlaySound(SOUND_DOWN);
    Break;
    turn_left1;
    Wait(75);
    go_forward;
    Wait(10);
    L_LINE();
    C_TIME++;
  }
  
    if(C_TIME == 11)  //P通過
  { PlaySound(SOUND_UP);
    turn_right0;
    Wait(60); 
    L_LINE();
    C_TIME++;
  }
  
    if(C_TIME == 12)  //Q左折
  { PlaySound(SOUND_UP);
    turn_left0;
    Wait(30);
    go_forward;
    Wait(30);
    R_LINE();
    C_TIME++;
  }
  
    if(C_TIME == 13)  //F通過
  {  PlaySound(SOUND_UP);
     ClearTimer(0);
    while(FastTimer(0) < 100)
  {if (SENSOR_2 < THRESHOLD -2) {
        turn_right1; Black_Time++;
       } else if (SENSOR_2 < THRESHOLD -1) {
        turn_right0; Black_Time++;
       } else if (SENSOR_2 < THRESHOLD +1) {
         go_forward;
       } else if (SENSOR_2 < THRESHOLD +2) {
        turn_left0; Black_Time = 0;
       } else {
        turn_left1; Black_Time = 0;
       }
       Wait(1);
  }
     R_LINE();
     C_TIME++;
  } 
  
    if(C_TIME == 14)  //A到着
  {  Off(OUT_AC);
     PlaySound(SOUND_DOWN);
  }
  }
初めにC_TIMEを定義して、何回交差点と判断したかを数え上げる構造にした。0からスタートし、E に差し掛かると1に上がる。それぞれの値と位置の関係はプログラムについている説明を参照。E等の直角な曲がり角の場合、交差点と判断させた上でサウンドを鳴らしそのままトレースを再開させれば曲がれるのだが、ここで最大の難所があった。最初は何が原因かさっぱり分からなかったが、どうやら交差点と判断する→C_TIMEに1を足す→すでに交差点と判断していることから直後にC_TIMEに1を足す→その場で最後まで数え上げてしまう→進まない という状態が発生した。そこでEでC_TIMEに1足した後、タイマーを起動しその後1秒以内はBlack_Timeを考えないただのライントレースプログラムを行うことで解決した。もとからあるライントレースの一部を移植したのでBlack_Timeを数えるようにはなっているが、whileの範囲が時間なので関係ない。(この1秒というのは曲がり角を確実に抜けており、かつ次の交差点に差し掛からない範囲で適当に設定しただけである。)そしてもう1つ、HとGの間にあるヘアピンであるが、この区間は線の右側をトレースしているため、1つ目のヘアピンは外側を問題なく曲がれる。しかし内側を曲がる2つ目のヘアピンは暗い部分が続くため交差点と判断してしまう。これを防ぐのは面倒なのであえて交差点と判断させてしまい、その部分を脱出するプログラムを追加することで解決した。(詳しくはC_TIME==8の部分を参照)最後に完走動画→https://youtu.be/bHBnmEAGRck (最後サウンド2つ鳴らしていますがこれはプログラムが最後まで到達したことを確認するためにほかの部分との違いを付けただけです)

*感想 [#q4a9f14c]
最初はかなり苦労したし挫折しかけた。なにより何が原因で上手くいかないのかを考えるのに苦労した。しかし最後まで走らせることができたときの達成感は格別であった。最初の課題でも使った旋回方法の使い分けが上手くできたと思う。今回は、いや今回もかなりの時間をかけてしまった。もっと基本的なことはスムーズにできるように努力したい。


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS