[[2018a/Member]]
#contents

*課題2の説明 [#g77517d1]
**内容 [#n00a28e5]
下の図のようなコースを各チームで作成し、「ミッション」を遂行するためのロボットを作成する。
***コース [#ycc153c5]

&ref(2018a/Member/gear/Mission2/2018a_mission2.png,80%,課題2のコース);
&ref(2018a/Member/gear/Mission2/2018a_mission2_route.png,90%,ルート);~
***コースの説明 [#t240a8af]

-缶のキャッチングまで
&size(18){Aをスタート ⇒ Bを直進 ⇒ Cで一時停止の後、直進 ⇒ Dで一時停止の後、Xの空き缶をキャッチしてD地点に戻る};~
-缶をホールドしたままライントレース
&size(18){⇒ DからEに向かい、Eを直進 ⇒ FをGで一時停止の後、左折 ⇒ Hで一時停止の後、右折 ⇒ Iで一時停止の後、右折}; 
-缶をリリースのち帰還まで 
&size(18){⇒ Lを直進Kを直進 ⇒ Jで一時停止の後、空き缶をYに置きてJに戻りBに向かう⇒ Bで一時停止の後、左折 ⇒ Aで停止};



**方針 [#t6b0dfa9]
-出来るだけ正確かつスピードの速いライントレースをするために,動きが単調になるとみられる部分では(例えばAからDまでにある緩いカーブや直線)スピードを速くし,そうでない部分は正確にトレース出来る最大の速さを用いることにし,これらの値はいくつかの実験を通して調節する.~
~
-トレースの仕方はラインの縁を付属のカラーセンサーを用いて「黒か白」の判別を行い,それぞれの場合においての動きを組み合わせることで前方に進行させる(例えば「黒」を検知したとき斜め前方方向に曲がり「白」を検知したときその逆方向へ曲がる,といったジグザグ走行が挙げられ,今回はこれを採用した).~
~
-指定の交差点で一時停止するときを考えると,上述のジグザグ走行をする場合に,スピードが小さく動きが遅くなるときにおいては右折または左折をすると推測できる(スピードが速いと「黒」を検知後も「黒」の領域を越えて直進する).この曲がるときを考えると「黒」の検知が継続していることを示している.今回はそのことから「黒」の検知時間を計測する関数を挿入し,「ある値以上になると交差点である」というコマンドを組み込むことで交差点を検知することにした.~
&ref(2018a/Member/gear/Mission2/explanation_line.png,86%,ライントレースの様子);~

*扱うロボットの説明 [#c131aaf1]
**ロボットの全体像 [#ued88287]
今回で扱うロボットは課題1と同様にEV3である.以下の写真のようなロボットを製作した.~
&ref(2018a/Member/gear/Mission2/robot_figure.jpg,86%,ロボットの全体像);
&size(18){アームを開くとこんな感じ→};&ref(2018a/Member/gear/Mission2/robot_figure2.jpg,80%,アーム開いたとき);~
~
缶のキャッチングのためのアームは,両脇より挟む様式を用いたものにした.当初,缶が収まるような大きさの枠を用いて,引っ掛けるようにしてキャッチさせようとしたが,この場合には枠の真正面に缶が位置しなければ確実にキャッチできないことが分かったため変更した.変更後のものは,缶がアームを広げた範囲にあれば確実に回収できる(下図参照)&size(9){それに外見がカッコイイ};.なお,アームは本体左後方に取り付けたモーターで動かす.~
&ref(2018a/Member/gear/Mission2/figure_can_catch.png,80%,アームの比較);
&ref(2018a/Member/gear/Mission2/figure_can_catching.png,80%,アームの比較);
**使用する諸機器 [#ic81df57]
***&size(17){カラーセンサー}; [#ped5caca]
今回の課題完遂のための最重要部分である.~
~
&ref(2018a/Member/gear/Mission2/figure_Color-Sensor.jpg,40%,カラーセンサー);~
~
なるべくタイヤの前方につけることで,色を検知してジグザグ走行をするときに車体の振れ幅が小さくなり,正確なライントレースができる.
***&size(17){超音波センサー}; [#l55bd07d]
缶のキャッチやリリースの際に使用する.~
~
&ref(2018a/Member/gear/Mission2/figure_Ultrasonic-Sensor.jpg,35%,超音波センサー);~
~
本体の真正面についていないことに注意する.
*ライントレースにあたって [#l58a721b]
**地点間の移動 [#fb77948]
-&color(blue){&size(18){A→B→C};};~
単調な長いカーブがあるので,交差点の手前までにかかりそうな時間を &size(17){テキトーに};&size(7){(微調整ナンテシテマセン)}; 指定し,速度を大きくしてトレースさせる.交差点手前からは正確にトレースさせて,交差点を正確に検知,一時停止させる.~
-&color(blue){&size(17){C→D→X地点にある缶をキャッチ};};~
急カーブ手前の直線は速度を大きくさせて &size(17){テキトーに}; ある時間だけトレースさせ,カーブに差し掛かったところで速度を小さくさせて正確にトレースさせる.このとき,D地点で一時停止させるのに,超音波センサーを用いて缶を検知したときに停止させるようにする.その場合にはカーブに差し掛かる前にアームを開いた状態でスタンバイさせなければならない.~
-&color(blue){&size(17){D→E→F→G};};~
缶をキャッチした後にサークルから離脱,トレースをさせてE,F地点を停止しないように通過させてG地点で一時停止させる.~
-&color(blue){&size(17){G→H};};~
二番目の急カーブまでは正確にトレースさせる.最後の直線に入って,速度を大きくさせて &size(17){テキトーに}; ある時間だけトレースさせる.交差点手前からは正確にトレースさせて,交差点を正確に検知,一時停止させる.~
-&color(blue){&size(17){H→I};};~
H地点で一時停止した後,右方向へ車体を向けて正確にトレース,I地点で交差点を検知,のち一時停止させる.~
-&color(blue){&size(17){I→J};};~
L,K地点では一時停止する必要はないが,簡易的に扱うために交差点検知用のプログラムを応用して検知させる.サークルでの検知回数を指定し,J地点までトレースさせ,交差点検知後に一時停止させる.~
-&color(blue){&size(17){Y地点に缶をリリース→B};};~
J地点で一時停止後,車体を旋回させて缶をY地点の真上に位置させる.アームを開いてリリースした後,後退して旋回,方向を転換してB地点までトレースさせ,交差点を検知して一時停止させる.
-&color(blue){&size(17){B→A};};~
B地点で一時停止後,左折してAに向かい,T字の部分で交差点として検知し,旋回,後方からAに進入,停止させる.~
~
&size(15){※なお,スタート時のカウントダウンや交差点検知にはEV3に周波数を指定した音を出させる.};

**注意点 [#df9c0499]
***ロボット本体について [#ha31b1a6]
今回の課題で扱うロボットは最初の写真から分かる通り重装備で,特にアームも大きいため縦に長くなっている.これによって缶のキャッチングの前やリリーシングの後のロボットの動きで,車体やアームが缶を引っ掛ける可能性がある.したがって,これに十分に配慮したライントレースのプログラムを作成する必要がある.~
&ref(2018a/Member/gear/Mission2/example_accident.png,80%,例えばこのような事故が);
***ライントレース時について [#y73ef421]
ライントレースでは&color(red){車体のスピードと切り返しの角度};が重要な点になる.例えば,スピードが速い場合は交差点での実際の検知継続時間が通常のライントレース時の検知継続時間と等しいまたは下回る可能性がある.この時,交差点検知は困難になる.また,スピードが速くてカーブを曲がる際にタイヤの切り返しが小さい場合に,ライン上にカラーセンサーが留まり続けられず,ライントレースが出来なくなる可能性がある.さらに,交差点進入時の車体の角度によっては「黒」の検知継続時間が基準よりも短くなる可能性もある.したがって,左右のタイヤのスピードはいくつかの実験を通して,最適値を導き出す必要がある.
*実行プログラムの説明 [#ub70388e]
**プログラムの導入 [#o596b472]
今回はEV3を扱っているため,プログラミングにTera TermというソフトからPythonという言語を用いる.~
以下は定義までの導入である
 1#!/usr/bin/env python3
 from ev3dev.ev3 import *     #EV3を扱うモジュールをインポート
 from time import             #timeモジュールをインポート

 mL = LargeMotor('outA')      #A端子側の移動用モーターをmLとおく
 mR = LargeMotor('outB')      #B端子側の移動用モーターをmRとおく
 mA = MediumMotor('outC')     #C端子側のアーム開閉用モーターをmAとおく
 cs = ColorSensor('in1')      #1端子側のカラーセンサーをcsとおく
 us = UltrasonicSensor('in2') #2端子側の超音波センサーをusとおく
**定義の挿入 [#v2fcb8ff]
***トレースの各場面で基本的に扱うもの [#p1b968fc]
 def motor_init():            #各モーターをリセットする
     mL.reset()
     mR.reset()
     mA.reset()
 
 def turn(r):                 #その場で角度 r だけ旋回する(右向きを正とする)
    mL.run_to_rel_pos(position_sp=r,speed_sp=100,stop_action='hold')
    mR.run_to_rel_pos(position_sp=-r,speed_sp=100,stop_action='hold')
    sleep(1)
 
 def line_follow_fast(t):  #指定時間 t だけ速度を大きくしたライントレースする
     motor_init()
     tS = time.time()
     while time.time() - tS < t:
         if cs.value() <= 30:
           mL.run_forever(speed_sp=130)
           mR.run_forever(speed_sp=40)
         if cs.value() >= 30:
           mL.run_forever(speed_sp=40)
           mR.run_forever(speed_sp=130)
     if time.time() - tS > t:
        mL.stop()
        mR.stop()
        sleep(1) 

以下は交差点検知用トレースプログラムである.なお,カラーセンサーによる「黒」と「白」の識別用基準値は 30 とし,検知継続時間の基準値は 0.47 秒とする.交差点を検知した後のターンの方向によって,トレースの位置(右側or左側)を変更させる必要があるため予め二種類のプログラムを作成しておく.

 def inter_find1():         #ラインの左側をトレースするとき交差点検知した後,一時停止する
     motor_init()
     tS = time.time()               #検知開始時刻を tS とおく(time.time()はその時の時刻)
     while time.time() - tS < 0.47:  #検知継続時間が0.4秒未満のときトレースを続ける
         if cs.value() <= 30:       #「白」っぽい色を検知したら右に曲がりラインを探す
           mL.run_forever(speed_sp=100)
           mR.run_forever(speed_sp=-40)
         if cs.value() >= 30:       #「黒」っぽい色を検知したら左に曲がりラインを出る
           mL.run_forever(speed_sp=-30)
           mR.run_forever(speed_sp=70) #正確に検知継続時間を計測するため速度は小さめに
           tS = time.time()         #タイムカウント開始         
     if time.time() - tS > 0.47:     #検知継続時間が0.4秒を超えたときwhileを抜け停止する
       mL.stop()
       mR.stop()
       sleep(1)

 def inter_find2():         #ラインの右側をトレースするとき交差点検知した後,一時停止する
     motor_init()
     tS = time.time()       
     while time.time() - tS < 0.47:
         if cs.value() <= 30:
           mL.run_forever(speed_sp=-40)
           mR.run_forever(speed_sp=100)
         if cs.value() >= 30:
           mL.run_forever(speed_sp=70)
           mR.run_forever(speed_sp=-30)
           tS = time.time()
     if time.time()- tS > 0.47:
       mL.stop()
       mR.stop()
       sleep(1)

以下は各交差点を検知するためのプログラムである.
 
 def inter1_straight():     #第一交差点(C地点)
     inter_find2()
     sing_go()
 
 def inter4_left():         #第四交差点(G地点)
     inter_find1()
     sing_go()
  
 def inter5_right():        #第五交差点(H地点)
     inter_find2()
     sing_go()
 
 def inter6_right():        #第六交差点(I地点)
     inter_find2()
     sing_go()
     turn(60)
     sleep(1)

B地点の交差点での検知については工夫した点があるので後述で説明する.


***アームを開閉させる時に扱うもの [#e238d6ac]
 def can_catch():             #アームを内側へ開く(キャッチングの時には開いた状態から)
     motor_init()
     mA.run_to_rel_pos(position_sp=46,speed_sp=40,stop_action='hold')
     sleep(1)

 def can_release():           #アームを外側へ開く(アームを閉めた状態から)
     mA.reset
     mA.run_to_rel_pos(position_sp=-46,speed_sp=50,stop_action='hold')
     sleep(1) 
      
***EV3で音を出すときに扱うもの [#j500eed2]
今回は交差点を検知したときとスタートまでのカウントダウンに音を出すことにした.
 def sing_start():          #3秒間のカウント
     for i in [1,2,3]:                #後述のコマンドを3回繰り返す
         Sound.tone(600, 300).wait()  #600ヘルツを0.3秒間発声
         sleep(0.7)  

 def sing_go():             #高音域を発声
     Sound.tone(1200, 400).wait()     #1200ヘルツを0.4秒間発声

 def sound_start():         #スタート3秒前カウント(イメージは「ド,ド,ド,レー↑」)
     sing_start()
     sing_go()

 
***円滑なライントレースのために [#z25983bd]
基本的にトレースの行程に沿って説明する.~
トレース序盤
 def curve_fast():          #第一カーブのトレース
     line_follow_fast(11)
 
 def straight_fast_inter1(): #第一交差点通過後の直線のトレース
     line_follow_fast(10) 
以下は第二カーブから缶のキャッチ,G地点の交差点検知にかけて
 def curve_follow_catch(): #第二カーブをトレース,D地点で停止するよう超音波センサーで
      motor_init()                 缶を検知(なお,値の単位は mm(ミリメートル) である)
     while us.value() >= 55: #缶との距離が55mm超の場合,トレースを続けて缶にアプローチ
       if  cs.value() <= 30:
         mL.run_forever(speed_sp=-40)
         mR.run_forever(speed_sp=90)
       if  cs.value() >= 30:
         mL.run_forever(speed_sp=90)
         mR.run_forever(speed_sp=-40)
     if us.value() <= 55: #55mmに達したらwhileを抜けて一時停止,キャッチのために缶との距離を詰める
         mL.stop()
         mR.stop()
         sing_go()
         sleep(1)
         mL.run_to_rel_pos(position_sp=80,speed_sp=80,stop_action='hold')
         mR.run_to_rel_pos(position_sp=80,speed_sp=80,stop_action='hold') 
 
 
 def inter_find_through1(): #キャッチした後にサークルから離脱,G地点の交差点までトレース
     motor_init()
     mL.run_to_rel_pos(position_sp=-80,speed_sp=80,stop_action='hold')
     mR.run_to_rel_pos(position_sp=-80,speed_sp=80,stop_action='hold')
     sleep(0.5)
     turn(-90)
     inter4_left()

↓curve_follow_catch() から inter_find_through1() までの様子~
~
&ref(2018a/Member/gear/Mission2/figure_catching1.jpg,50%,キャッチングの様子);
&ref(2018a/Member/gear/Mission2/figure_catching2.jpg,50%,キャッチングの様子);
&ref(2018a/Member/gear/Mission2/figure_catching3.jpg,50%,キャッチングの様子);
&ref(2018a/Member/gear/Mission2/figure_catching4.jpg,50%,キャッチングの様子);
&ref(2018a/Member/gear/Mission2/figure_catching5.jpg,50%,キャッチングの様子);~
~

以下はG地点からH地点までの連続カーブをトレースするためのプログラムである.
第四カーブ通過後すぐの直線を速度の大きいトレースに切り替えたいと考えたが,そのための条件として,左側をトレースし続けて第四カーブで曲がるときにカラーセンサーがラインの外側にあることを利用する.つまり,&color(red){「白」の検知継続時間に注目した};バージョンのものを作成した. &size(9){これはライントレースと言える...のだろうか?};
 def zigzag_follow():      #G地点からH地点までの連続する第三,第四カーブをトレース
     motor_init()
     tS = time.time()
     while time.time() - tS < 2.5:
       if cs.value() <= 30:
         mL.run_forever(speed_sp=-40)
         mR.run_forever(speed_sp=90)
         tS = time.time()
       if cs.value() >= 30:
         mL.run_forever(speed_sp=90)
         mR.run_forever(speed_sp=-40)
     if time.time() - tS > 2.5:
       turn(100)          #H地点手前で交差点検知に移るため,ラインの左側から右側へシフト
       sleep(0.5)
       line_follow_fast(5) #第四カーブ通過後の直線を速度が速いトレースで通過する
       sleep(0.5) 


 def inter_find_through2(): #L,K,J地点を交差点として検知して直進する
     for i in [1,2,3]:      #L,K,Jの3地点で後述のコマンドを繰り返す
         inter_find2()      #↓交差点を検知した後,トレースを続行させるために体勢を整える
         sleep(1)
         mL.run_to_rel_pos(position_sp=40,speed_sp=40,stop_action='hold')
         mR.run_to_rel_pos(position_sp=160,speed_sp=120,stop_action='hold')
         sleep(1.2)   
         turn(-20)
     sleep(0.5)

実は全くの偶然だが,inter_find_through2でのトレースの仕方は inter_find_through1 と同一にも関わらず,交差点を検知する(これは,交差点を検知せずにトレースするinter_find_through1に対しても同じ).サークルのトレースに入る車体の角度によるものと考えられる.~
なお,次の地点までのトレース前に体勢を整えていなければ,ラインの右側をトレースしている場合に,交差点検知後は右折をしてそのままトレースし続けてしまう.したがって,今回は交差点検知後に,各地点の右へ延びる直線をいったん跨ぐようにして,次の地点へのトレースに復帰するようにした.(下図参照)~
&ref(2018a/Member/gear/Mission2/inter_find_through2.png,50%,トレースの軌跡);
~
以下は缶のリリースと方向転換の過程である.~
 def follow_release():
     inter_find_through2()  #I地点からJ地点までサークルをトレース
     sing_go()
     turn(-200)             #Y地点に缶をリリースする角度に旋回(直線BJに対して少し斜め)
     sleep(0.5)
     can_release()          #缶をリリース
     sleep(0.5)             
     mL.run_to_rel_pos(position_sp=-200,speed_sp=80,stop_action='hold') #後退※
     mR.run_to_rel_pos(position_sp=-200,speed_sp=80,stop_action='hold')
     sleep(1) 
※このとき,旋回しても車体が缶に当たらない距離まで下がるための準備に入る.これをしないと悲劇が...→  &ref(2018a/Member/gear/Mission2/example_accident.png,50%,例えばこのような事故が(再び));
~
~
車体の方向を転換してB地点へ向かう.

 def leave_circle():
     motor_init()
     while us.value() <= 120: #超音波センサーで,旋回しても缶に当たらない距離まで下がる.
         mL.run_forever(speed_sp=-100)
         mR.run_forever(speed_sp=-100)
     if us.value() >= 120:
       mL.stop()
       mR.stop()
       sleep(0.1)
       turn(90)                #少し車体の角度を変える
       while cs.value() >= 30: #ラインを検知するまで進む
         mL.run_forever(speed_sp=80)
         mR.run_forever(speed_sp=80)
       if cs.value() <= 30:   
         mL.stop()             #ラインを検知したら一時停止
         mR.stop()
         sleep(0.1)            #↓ラインを跨ぐ
     mL.run_to_rel_pos(position_sp=90,speed_sp=80,stop_action='hold')
         mR.run_to_rel_pos(position_sp=90,speed_sp=80,stop_action='hold')
         sleep(1) 
以下の図は後退から旋回に移るまでの過程の様子である.~
~
&ref(2018a/Member/gear/Mission2/turn_locus.png,90%,カラーセンサーの軌跡);

 def inter10_find_left():
     motor_init()
     while cs.value() >= 30:   #ラインを検知するまで(検知しなかったら)旋回
          mL.run_forever(speed_sp=80)
          mR.run_forever(speed_sp=-80)
     if cs.value() <= 30:
       mL.stop()
       mR.stop()
       sleep(0.1)
       inter_find2()           #ラインを検知したら,B地点の交差点検知に入る
       sing_go()               
       mL.run_to_rel_pos(position_sp=80,speed_sp=80,stop_action='hold') #↓※※
       mR.run_to_rel_pos(position_sp=80,speed_sp=80,stop_action='hold')
       sleep(0.5)
       turn(-58)
以下の図は inter10_find_left の動作の様子である.~
~
&ref(2018a/Member/gear/Mission2/inter10_trace_locus3.png,80%,第十交差点での検知の様子);~

※※このとき,検知後に曲がってラインから外れるだけでは次トレースの体勢は整わない.トレースが始まり,ラインに入るときに誤検知してしまう角度で入るためである.(下図参照)~
~
&size(25){×};&ref(2018a/Member/gear/Mission2/inter10_trace.jpg,65%,誤検知);←ラインに対する車体の角度が大きいと「黒」の検知が長くなる
&ref(2018a/Member/gear/Mission2/trace_faulse.png,40%,誤検知の過程);~
&size(25){〇};&ref(2018a/Member/gear/Mission2/inter10_trace_success.jpg,65%,トレース成功);←通常のトレースに近い状態~
~
以下はスクエアを探して後方より入庫するまで
 def find_square():
     inter_find1()      #突き当りを交差点として検知
     sleep(1)
     turn(-360)         #方向転換
     sleep(0.3)
 
 def enter_square():    #後方より入庫
     motor_init()
     mL.run_to_rel_pos(position_sp=-250,speed_sp=80,stop_action='hold')
     mR.run_to_rel_pos(position_sp=-250,speed_sp=80,stop_action='hold')
     sleep(2)
     can_catch()  


**実際の作動プログラム [#r97b7025]

 sound_start()          #スタート合図
 curve_fast()           #緩やかな第一カーブを速くトレース
 inter1_straight()      #第一交差点を検知&停止→直進
 straight_fast_inter1() #検知後の直線を速くトレース
 turn(-40)              #ラインの右側トレースから左側トレースに転換 ※※※
 can_release()          #アームを開いてキャッチングの準備(releaseはまだです)
 curve_follow_catch()   #急な第二カーブを通過後,第二交差点で停止,缶をキャッチ
 inter_find_through1()  #最初のサークルを辿り,第四交差点を検知&停止→左折
 zigzag_follow()        #急カーブが連続する第三カーブを通過
 inter5_right()         #第五交差点を検知&停止→右折
 inter6_right()         #第六交差点を検知&停止→右折
 follow_release()       #サークルを辿り,第九交差点で停止&缶をリリース
 leave_circle()         #缶のリリース後,サークルより離れて第十交差点に向かう
 inter10_find_left()    #第十交差点を検知&停止→左折
 find_square()          #ベーススクエアまで戻る
 enter_square()         #T字路で検知&停止からの方向転換して後方より入庫

※※※キャッチに入る前になぜトレースの位置を変更するかというと,超音波センサーが缶より見て正面左側についており,交差点に進入してすぐに缶を検知して停止してほしいため,なるべく缶の正面のほうを向きやすくなるようにする必要があったためである.

*感想 [#aa2002a8]
今回はハードウエアに対して比較的時間をかけなかったので,円滑にライントレースのプログラムを作成することができ,発表会にも間に合わせることができた.結果としては最後の最後で,後方入庫という最大の見せ場で失敗してしまった.これは,最後の交差点からのトレースのために施した工夫が仇となったためである.具体的には,スクエアのT字路に入る車体の角度がずれてしまったことだと考えられる.ある場面での調整は後々の影響が大きいということを痛感した.

本日&counter(today);昨日&counter(yesterday);合計&counter(all);

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