2016b/Member
目次

課題について

マップ
今回の目標は「A,P,Q,R,P,S,D」のルートを通りDにボールをシュートする事です。発表ではRの交差点のときに紙をはみ出してしまったため、コースアウトしてしまいました。しっかりと紙を抑えていなかったため、ボールの取得しているアームが引っかかり、直進してしまったのが原因でした。

機体について

今回の機体の制作には、いかにボールを捕まえる機構に入れるかが問題でした。わざわざライン上にあるボールを取りに行くプログラムを作るより、ライントレース中にボールの捕まえる機構にしてしまった方が、難しい点が少ないです。最初は、ボールを捕る機構をロボットの正面(中心)に作りましたが、ロボットそのものの全長が長くなってしまい、一から作成をしました。

機体の全体像

全体像

ボールの検出と取得の機構

アーム.jpeg
先端に超音波センサーを付けているため、テコの原理のように、ちょっとした振動などで、モーターが回って、測定に誤差が出てしまうので、減速ギアを組み、モーター以外ではアームが動かないようにしました。また減速ギアの効果で、電池残量にあまり影響されずアームの上げ下げができるので、何度も試運転する際に役に立ちました。超音波センサーは、ほとんど地面に対して垂直(12cmほど)にしています。超音波センサーはボールの検出の際、20cm以上(実測)となりますので、プログラムによる判定には、この現象を利用しています。(後述)

ライントレース部分(下半身)

下半身1.jpeg 下半身2.jpeg 前輪.jpeg
ライントレース部分では、ほとんど左右対称に設計してあります。このようにすることで、NXT本体と、ボール取得機構の入れ替えが容易で、ボールをライン上の右回り、左回りの両方からの取得が可能となっています。(今回の課題では、Q直進であるため意味はありません)今回の駆動輪では、タイヤの高さを前輪と合わせるためにギアを組みましたが、減速ギアのため、ライントレースの微妙な調整が、簡単になりました。ボールを安定して捕まえながら前進しなければならないので、前輪は稼働可能な2輪として安定性を高めました。前輪駆動にすれば、4輪にしなくても良かったのですが、ボールとタイヤが引っかかるという点を回避するために後輪駆動となりました。

プログラムについて

プログラムの解説

ライントレースの処理

このプログラムは、「黒、黒に近い、白に少し近い、白に近い、白」の5段階による多段処理をこなっています。黒に近ければ近いほど左旋回を行い、白に近ければ近いほど右旋回を行います。この「白か黒」値は光センサーの中間値からどれぐらい離れているかによって決めています。理論上は指定した中間値に近づこうとする動きをするはずです。今回では少し白側(グレーだけど黒)をライントレースしました。その為、直線等では光センサーは極端な黒の値を出しません。(交差点を除く)

交差点の検出

交差点の検出には、「どの反射率から判定するか」、「どれぐらいの間その反射率が続いたか」の2つによって判断をしています。この判別は、ライントレースのプログラムとは分離しています。ライントレースと交差点判別が分離できていないと、交差点基準を調節した際にライントレースの基準も変わってしまいます。この2つを分けることで、ライントレースの基準となる反射率と、交差点の判別をするプログラムを分けることができ、調節が簡単になります。交差点では、ほとんどラインの中心に一時的に突っ込みます。その為反射率はかなり低くなります。 よって、反射率が低いときに少ない時間ライトセンサーがいるときを交差点とすると、良好に交差点を判別できます。

ボールの検出

ボールの検出には、超音波センサーを利用しました。超音波センサーでは、平面に対しては正確な値を返しますが、ボールなどの球面や、湾曲したものであると、正確な値が取れません。これは超音波センサーの仕組み上の問題ではなく、圧電素子の問題(構造的問題)であると思います。超音波センサーは、出した音が帰ってくるまでの時間で測定するので、微弱でも感知できれば、距離を取得できますが、圧電素子が反応できないほど拡散して微弱であれば、拡散した音波が乱反射等で戻ってくるまでは測定できません。 実際に、本機の超音波センサーで地面を見た時は12cm程ですが、ボールが来た時は20cm以上となります。地面と超音波センサーの距離はセンサーが動いていないので、10cm以上も値がずれることは現実的ではありませんが、ボールを検出したら20cm以上と値を返します。 今回はそのような、正確な値が返ってこないことを利用して、ボールを検出しました。
超音波センサー.jpeg

交差点の処理

交差点では、交差点であることの判別に時間を使って処理をしています。その為、交差点であることが判明するまでは、ライントレースを行うので,里茲Δ米虻遒鬚靴泙后今回のコースでは、6番目(S)の交差点以外は直進をしなければならないので、△僚萢で本体の向きを変えます。そして、で直進をし、い妊薀ぅ鵐肇譟璽垢鬚靴泙后この時、,僚萢が終わった瞬間の本体が向いている方向にはばらつきがあります。その為、終了時点での本体の向きもばらつきが起こるため、い埜鮑硬世糧淑未鮃圓い覆らライントレースを行うと、連続で交差点と誤認する場合があるため、い離薀ぅ鵐肇譟璽垢任蓮1秒間ほど交差点の判別はせずにライントレースを行います。
交差点の処理 交差点の処理

シュートの処理

5個目の交差点(S)を通過後、Dに向けてライントレースを一定以上(実測で1秒ほど)行えば、ラインと同じ向きになります。この時、正確に前進しながら、ボールを離せば、慣性によってシュートができる、という原理に基づきプログラミングをしています。電池残量に影響されますが、ボールを離す時間を調節すればC,Dのゴールエリアライン上からシュートが可能です。

ボール取得のメロディ

ピタゴラスイッチを入れました。ですが、発表までに何処で流せばちょうどよく終わるのかがまだわからなかったので、ボールの取得のときに鳴らしました。このピタゴラスイッチは、コンパイルにBRICsだと40秒ほどかかります。そのため、本体の動作確認中はコンパイルに時間がかかりすぎるため、本文ではコメントアウトしています。(3分レベルのものだと、3000行を超えてメモリオーバーします)やはりEV3には(PCMそのまま流せるので)勝てませんでした。

プログラムの流れ

  1. スタート地点から前進する(枠から光センサーを出す)
  2. ライントレースを数秒交差点判定無しで行う
  3. Pまで行き交差点で直進する
  4. Qでボールを取り2,3の作業をSに行くまで続ける
  5. DQ間のラインにライントレースで同調し、慣性によってシュート

本文

#define THRESHOLD 45   //白と黒の中間の反射率
#define SPEED_Fast 60  // 速いスピード
#define SPEED_Slow 50  // 遅いスピード
#define OnLR(speedR,speedL) OnRev(OUT_A,speedR);OnRev(OUT_B,speedL);   //ABの動作同時指定
 
/*
task pitagora()  //ボールを取ったときのメドレー
{
 //while(true)
 //{

   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1175,140);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1568,140);
   Wait(160);
   PlayTone(1480,130);
   Wait(300);
   PlayTone(1175,130);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(988,130);
   Wait(150);
   PlayTone(880,140);
   Wait(310);
   PlayTone(988,130);
   Wait(150);
   PlayTone(1046,140);
   Wait(310);
   PlayTone(1109,130);
   Wait(150);
   PlayTone(1175,140);
   Wait(460);
   PlayTone(1175,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1568,130);
   Wait(150);
   PlayTone(1480,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1175,140);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(2093,140);
   Wait(160);
   PlayTone(1975,130);
   Wait(300);
   PlayTone(1568,140);
   Wait(160);
   PlayTone(1760,130);
   Wait(300);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,140);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,140);
   Wait(310);
   PlayTone(1568,130);
   Wait(150);
   PlayTone(1480,140);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(988,130);
   Wait(150);
   PlayTone(880,440);
   Wait(460);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1318,130);
   Wait(150);
   PlayTone(1175,440);
   Wait(770);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1175,140);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1568,140);
   Wait(160);
   PlayTone(1480,130);
   Wait(300);
   PlayTone(1175,140);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1175,130);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,140);
   Wait(310);
   PlayTone(2093,130);
   Wait(150);
   PlayTone(1975,140);
   Wait(310);
   PlayTone(1568,130);
   Wait(150);
   PlayTone(1760,140);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1568,130);
   Wait(150);
   PlayTone(1480,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1046,130);
   Wait(150);
   PlayTone(1175,130);
   Wait(300);
   PlayTone(1046,140);
   Wait(160);
   PlayTone(1175,130);
   Wait(300);
   PlayTone(1397,140);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1046,140);
   Wait(160);
   PlayTone(1175,130);
   Wait(300);
   PlayTone(932,130);
   Wait(160);
   PlayTone(1046,130);
   Wait(300);
   PlayTone(932,130);
   Wait(150);
   PlayTone(1046,140);
   Wait(310);
   PlayTone(1244,130);
   Wait(150);
   PlayTone(1175,140);
   Wait(310);
   PlayTone(1046,130);
   Wait(150);
   PlayTone(988,140);
   Wait(310);
   PlayTone(880,130);
   Wait(150);
   PlayTone(784,1050);
   Wait(1380);
   PlayTone(1397,440);
   Wait(460);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1318,140);
   Wait(160);
   PlayTone(1480,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(150);
   PlayTone(1480,140);
   Wait(160);
   PlayTone(1568,440);
   Wait(460);
   PlayTone(1175,130);
   Wait(300);
   PlayTone(988,130);
   Wait(160);
   PlayTone(1046,130);
   Wait(300);
   PlayTone(1046,130);
   Wait(150);
   PlayTone(1175,140);
   Wait(160);
   PlayTone(1109,130);
   Wait(150);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(988,440);
   Wait(770);
   PlayTone(1397,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1318,130);
   Wait(150);
   PlayTone(1480,130);
   Wait(150);
   PlayTone(1318,140);
   Wait(160);
   PlayTone(1480,130);
   Wait(150);
   PlayTone(1568,440);
   Wait(460);
   PlayTone(1175,130);
   Wait(310);
   PlayTone(988,130);
   Wait(150);
   PlayTone(1046,130);
   Wait(310);
   PlayTone(1046,130);
   Wait(150);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1109,130);
   Wait(150);
   PlayTone(1175,140);
   Wait(160);
   PlayTone(988,440);
   Wait(760);
   PlayTone(1175,140);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,140);
   Wait(310);
   PlayTone(1568,130);
   Wait(150);
   PlayTone(1480,140);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,140);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1568,130);
   Wait(150);
   PlayTone(1480,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1568,140);
   Wait(160);
   PlayTone(1480,130);
   Wait(300);
   PlayTone(1175,140);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1175,140);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(1175,130);
   Wait(160);
   PlayTone(1318,130);
   Wait(300);
   PlayTone(2093,130);
   Wait(150);
   PlayTone(1975,140);
   Wait(460);
   PlayTone(1568,140);
   Wait(1230);
//    }
//}

//task pitagora_owari()
//{
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(1175,130);
   Wait(150);
   PlayTone(1318,130);
   Wait(310);
   PlayTone(2093,130);
   Wait(150);
   PlayTone(1975,130);
   Wait(460);
   PlayTone(1568,280);
   Wait(300);
//}
}
*/


sub line_trace(int sensor_light_lv)   //ライントレースのプロセス
{
   if (sensor_light_lv < THRESHOLD-8)
   {
       OnRev(OUT_B,SPEED_Fast);
       OnRev(OUT_A,-SPEED_Fast);
   }
   else if (sensor_light_lv < THRESHOLD-5)
   {
       OnRev(OUT_B,SPEED_Slow);
   }
   else if (sensor_light_lv < THRESHOLD +5)
   {
       OnRev(OUT_AB,50);
   }
   else if (sensor_light_lv < THRESHOLD+8)
   {
       OnRev(OUT_A,SPEED_Slow);
   }
   else
   {
       OnRev(OUT_A,SPEED_Fast);
       OnRev(OUT_B,-SPEED_Fast);
   }
}

sub go_forward_intersection() //交差点を通過する△料犧
{
       OnRev(OUT_AB,SPEED_Slow);
       Wait(410);
       Off(OUT_AB);
}

sub turn_r_intersection()    //交差点の右に車体を向かせる
{
   OnLR(60,-60);
   Wait(450);
}
sub turn_l_intersection()    // 交差点の左に車体を向かせる
{
   OnLR(-60,60);
   Wait(450);
}                            //ボールを取る動作
sub arm_lock()
{
   Off(OUT_ABC);
   OnRev(OUT_AB,25);
   OnFwd(OUT_C,-12);
   Wait(3200);
   Off(OUT_ABC);
}

sub arm_unlock()             //アームを上げ、ボールを自由にする
{
   OnFwd(OUT_C,30);
}

long intersection_judgment(long t0)        //Sensorの値が34(任意の反射率)以上のとき、引数に現在の時刻を代入し返す。それ以外は変更せず引数を返す。
{
   if (SENSOR_1 > 34)
   {
       t0 = CurrentTick();
   }
   return t0 ;
}



task main()
{
   SetSensorLight(S1);               // ライトセンサーの定義
   SetSensorLowspeed(S2);            //超音波センサーの定義
   int intersection_count = 0;       //交差点のカウント変数の定義
   int count_look=0;                 //ボールを掴んでいるかの変数の定義
   long t0 = CurrentTick();          //時間を記憶する変数の定義


   RotateMotor(OUT_AB,SPEED_Slow,-1000);     //スタート地点から前進する
   while(CurrentTick()-t0 < 6000)            //前進から6秒ほど(無条件)ライントレースをすることで、確実に線上に乗る
   {
       line_trace(SENSOR_1);                 //ライントレースを呼び出す
   }
   t0 = CurrentTick();                       // 交差点の判定を始めるためにタイマーをリセット
   
   
   while(true)
       {
       //NumOut(0,0,SensorUS(S2) , true);

           while(CurrentTick()-t0 < 410)        //指定した反射率(intersection_judgmentによる処理)が、410(任意の時間)連続でなければ、繰り返す
           {
               line_trace(SENSOR_1);
               t0=intersection_judgment(t0);
           }

           PlaySound(SOUND_UP);                  //ループを抜けた(交差点の処理が始まる)とき、音を鳴らす
           Off(OUT_AB);                          //車体の停止
           Wait(1000);
           intersection_count +=1;               //交差点をカウントする

           if (intersection_count <=5 )          //交差点が5回以内のときの処理
           {
               turn_r_intersection();            //△僚萢
               go_forward_intersection();        //の処理
               t0 = CurrentTick();               //タイマーをリセット
               while(CurrentTick()-t0 < 1000)    //い僚萢(1秒間ライントレースをする)
               {
                   line_trace(SENSOR_1);
               }


               if (intersection_count ==2)       //2回めの交差点のとき
               {
                   RotateMotor(OUT_AB,SPEED_Slow,500);   //後ろに(正確に)下がる
                   while(SensorUS(S2)<=20)               //超音波センサーが20cm以内のときループ
                   {
                       OnRevSync(OUT_AB,40,0);           //正確に前進
                   }
                   arm_lock();                           //ボールの取得
                   //start pitagora ;                    //メロディを流す
               }
           }

           else if (intersection_count ==6 )             //上のプログラムのgo_forward_intersectionに条件式「if (intersection_count !=6 )」をつければ、コンパクトにはなるが、シュートの処理は調節が必要なので分離
           {
               turn_l_intersection();
               t0 = CurrentTick();
               while(CurrentTick()-t0 < 1500)            //ライントレースでラインに確実に乗る
               {
                   line_trace(SENSOR_1);
               }
               Off(OUT_AB);                              //前進しながら、ボールを離し、慣性によってシュートする
               OnRevSync(OUT_AB,40,0);
               arm_unlock();
               Wait(1500);
               Off(OUT_ABC);
               break;                                    //プログラムの終了
           }
           t0 = CurrentTick();
       }
}

感想

一回目の反省を生かして、処理ごとにサブルーチン(pythonだと関数)を定義しブロック化したため、メンテナンスや、再度呼び出しがとても楽にできました。 ブロック化のため、処理部分を作ってしまえば、あとはそのブロックを組み合わせるので、構造的にも読みやすくすることができたのは、良かったと思います。超音波センサーの現象は推論の域を出ないのですが、確かめるとすれば、まず、どれぐらいの音量から検出が可能なのか、どれぐらい拡散しているのか、乱反射等は起きることは確実だが、ある時間tにおける振動の波と、Δt後の波が合成した際、信号のズレが生じるのではないか(マルチパス)などと言ったことが考えられるので、どのように考慮するのかというようなことを考えなければいけないと思いやめました。


添付ファイル: file前輪.jpeg 106件 [詳細] file超音波センサー.jpeg 124件 [詳細] file下半身2.jpeg 90件 [詳細] file下半身1.jpeg 112件 [詳細] fileアーム.jpeg 97件 [詳細] file全体像.jpeg 90件 [詳細] filekousatenn_1.jpeg 90件 [詳細] filekousatenn.jpeg 122件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-12-22 (木) 18:00:16