[[2019a/Member]]

目次 
#contents

*課題について [#t653a1ed]
詳しくは[[2019a/Mission2]]を参照してください。
#ref(./2019a-mission2.png,100%,コース図面) 
私は 

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

というコースを線に沿わせてロボット動かしながら、卓球のピンポン玉を回収し、それをゴールまで運びました。

ルールは交差点では1秒間停止し、丁字路では直角方向に進入する時のみ一時停止すること。

*方針 [#be1016a5]
・成功率を高めるために、多少のズレをカバーできる自立して判断するログラム(whileループやif文などの条件分岐による動作決定など)

・プログラムに余計な負担がこないように、小回りが利き、よりコンパクトなロボット設計。(次のロボットの設計で詳しく述べる)
・プログラムに余計な負担がこないように、小回りが利くロボット設計。(次のロボットの設計で詳しく述べる)

・前回の課題1の反省を生かし、周りと情報を共有し、過去のレポート
・前回の課題1([[2019a/Mission1]])の反省を生かし、周りと情報を共有し、過去のレポート
等もしっかり参照する。ネットでも使える情報を集める。(時間の測り方など、今回は先輩のレポートにかなり助けられた)

*ロボットの設計 [#n1c449a6]
コースにはきついコーナーなどがあり、特に今回は例年よりコースが小さくなったので、より小回りが利くことが重要視されました。我々のev3は他のRISやNXTより全体的に大型なこともあり、小回りが利く、小型化がより重要でした。
#ref(./810-10-min-min.png,10%,ロボット)
コースにはきついコーナーなどがあり、特に今回は例年よりコースが小さくなったので、より小回りが利くことが重要視されました。我々のev3は他のRISやNXTより全体的に大型なこともあり、小回りが利くかがより重要でした。

その為に、まずタイヤをよりロボットの中央に近づけることから始めました。その為に、ev3本体の操作がしずらいロボットとなってしまいましたが、小回りの為の致し方のない犠牲です。
その為に、まずタイヤをよりロボットの中央に近づけることから始めました。その為に、ev3本体の操作がしずらい巨大ロボットとなってしまいましたが、小回りの為の致し方のない犠牲です。しかし、この縦長ボディは骨格が多く、さらなる拡張性には対応しています。
#ref(./810-1.PNG,50%,ロボット全体)

次に、重量物のev3、長い回収アームが上面にあるというかなり不安定な構造であるので、細いプラスチックのパーツに負担がかかり、ロボットの剛性が不足しました。そこで負担を和らげるように補強をいたるところにしました。そのことにより、ボディの剛性が上がり、動作の安定にも寄与しました。しかし、これには多数のパーツを要したため、パーツ不足に悩まされました。故に、無駄にパーツを使ってないか、このパーツが使えるかも、などを考えながら限られたパーツのみで作る必要がありました。

次に、ピンポン玉を回収するアームについて説明します。このアームは板と板の間にピンポン玉を挟むようになっています。まず、ピンポン玉の下に潜り込み、球の直系より板と板の隙間が小さいことを生かして持ち上げます。下の画像の青矢印の方向にアームが上がり、ピンポン球を持ち上げます。
次に、ピンポン玉を回収するアームについて説明します。このアームは板と板の間にピンポン玉を挟むようになっています。まず、ピンポン玉の下に潜り込み、球の直系より板と板の隙間が小さいことを生かして持ち上げます。下の画像の青矢印の方向にアームが上がり、ピンポン球を持ち上げます。この先端の形状により、先っぽがちょっと引っかかっただけでも、ひょいと持ち上げてくれました。これが左右にずれても回収してくれました。ので、回収地点にさえ着けばほぼ確実に回収に成功します。回収地点にさえ着けば....。
#ref(./810-2.jpg,50%,ロボットアーム)

このアームは非常に長いので、最初の移動途中にピンポン玉にぶつかったり、固定が甘いのでピンポン玉を振り落とさないように、ロボットの動作速度には配慮しました。
このアームは非常に長いので、最初の移動途中にピンポン玉にぶつかったり、固定が重力と静止摩擦しかないのでピンポン玉を振り落とさないように、ロボットの動作速度には配慮しました。しかし、実際には揺れることもなく、固定はできていました。速度にはあまり配慮しませんでした。もう少し、速度で攻めると、コースの走破時間が短縮できそうです。

カラーセンサーの取り付け位置には気を付けました。これがタイヤから離れると、回転したときなどにカラーセンサーが大きく動いてしまい、その読み取る値まで大きく変わってしまい、以下で述べるライントレースが困難になってしまう。
#ref(./810-3.jpg,50%,カラーセンサの位置)

*ライントレース [#q8fc2041]
今回の課題ではカラーセンサーが一つしか与えられていない。よってコースの黒線が測定エリア内にどれだけ入っているかによって動作を決定する。赤い円の中が測定範囲であり、黒の割合が多くなるほどカラーセンサの値が小さくなり、白の割合が多いほど値が大きくなる。これはカラーセンサが光の反射の度合いを測っているからである。実際の動作については、下の図の,里茲Δ法黒の割合が高いときは白が増えるように動き、△里茲Δ貿鬚粒箙腓高いときは黒の割合が高くなるように動きます。この,鉢△鮓鮓澆坊り返し、ジグザグに動きながら線の片側に沿って移動します。使用言語はPython3なので、この繰り返しはwhile文を使ってカラーセンサーの値の条件を満たせばこれらの動作を繰り返すようにする。
#ref(./810-4.png,50%,ライントレースの図解)

実際のライントレースのサブルーチンは以下のようになります。
 
 def line_trace_left(t,c): #線の左側をライントレース
    x = time.time() #スタートの時刻
    while time.time() - x < t: #現在時刻−スタート時刻がtより小さい間繰り返す
      if cs.value() > c: #カラーセンサーの値がcより大きい、つまり白が多いと右前方に移動
         mL.run_forever(speed_sp=-90)
         mR.run_forever(speed_sp=30)
         x = time.time() #スタート時刻を更新する
      else:        #カラーセンサーの値がcより小さい、つまり黒が多いと左前方に移動。このときスタート時刻は更新しないのが重要。
           mL.run_forever(speed_sp=30)
           mR.run_forever(speed_sp=-90)
    if time.time() - x > t: #現在時刻−スタート時刻がtより大きくなる。つまり黒の割合が多いコーナーだと、スタート時刻が更新されず、この差が大きくなる。これによりコーナーを検知し。モーターの動きが止まる。
       mL.stop()
       mR.stop()
#ref(./810-5.png,50%,line_trace_left) 
 
 def line_trace_right(t,c): #線の右側をライントレース
    x = time.time()     #スタートの時刻
    while time.time() - x < t: #現在時刻−スタート時刻がtより小さい間繰り返す
         if cs.value() > c: #カラーセンサーの値がcより大きい、つまり白が多いと左前方に移動
            mL.run_forever(speed_sp=30)
            mR.run_forever(speed_sp=-90)
            x = time.time() #スタート時刻を更新
         else: #カラーセンサーの値がcより小さい、つまり黒が多いと右前方に移動。このときスタート時刻は更新しないのが重要。
            mL.run_forever(speed_sp=-90)
            mR.run_forever(speed_sp=30)
    if time.time() - x >t: #現在時刻−スタート時刻がtより大きくなる。つまり黒の割合が多いコーナーだと、スタート時刻が更新されず、この差が大きくなる。これによりコーナーを検知し。モーターの動きが止まる。
         mL.stop()
         mR.stop()
#ref(./810-6.png,50%,line_trace_right)

*コーナーを検知しない対策 [#dc247ed9]
基本的にはライントレースのコーナー判定時間を短くし、境界線を黒側に寄せることである。コーナーを判定する時間を短くすると、当然、道中でコーナーと誤判定する確率が上がりますが、より確実にコーナー^で止まりやすくなります。この誤検知の危険性と、停止する確実性の妥協点を探して調整しました。境界線を黒側に寄せると、コーナーについた際に、より、黒色が入る割合が増えます。しかし、同時に黒が多くないと止まらなくなるので、これも、黒を増やす割合と、コーナー判定の厳しさの妥協点を探すことになります。これら2つはt(second),c(cs.value())の2つの変数とし、コースの場所ごとに最適化することが何よりも大切です。
*プログラム [#ee636431]
以下が実際のプログラムである。


 #!/usr/bin/env python3 #使用言語はPython3とする
 from ev3dev.ev3 import * #ev3devをインポートする
 from time import sleep 時間はスリープ関数を使う
 mL = LargeMotor('outA') #Left,つまり左のモータがAポートに繋がっている。移動に使う。
 mR = LargeMotor('outD') #Right、つまり右のモーターがDポートに繋がっている。これも移動に使う。
 mM = MediumMotor('outB') #MediumモーターがBポートに繋がっている。これはピンポン球を上下するアームを動かす。
 cs = ColorSensor('in3') #カラーセンサーがポート3に繋がっている。これはライントレースとコーナーの検知に利用する。
 cs.mode = 'COL-REFLECT' #カラーセンサーが反射した光の量の値を読み取るモードに設定する。
 def motor_init(): #左右のモーターの設定をリセットする。
        mL.reset()
        mR.reset()
 def arm_move(t,y): #ピンポン球を上下に移動するアームを動かす。y>0のときアームは上昇し、y<0のとき下降する。
    mM.run_timed(speed_sp=y,time_sp=t,stop_action='hold')
 def move_forward_long(t): #長い距離を前進するときに利用。時間tの間だけ前進する。tの単位は秒(second)である。
     x = time.time()
     while time.time() - x < t: 
             mL.run_forever(speed_sp=-100)
             mR.run_forever(speed_sp=-100)
     mL.stop()
     mR.stop()
 def move_back_long(t): #長い距離を後進するときに利用。時間tの間だけ後進する。tの単位は秒(second)である。
 
       x = time.time()
       while time.time() - x < t:
             mL.run_forever(speed_sp=100)
             mR.run_forever(speed_sp=100)
       mL.stop()
       mR.stop()
 def move_forward(t): #時間t/1000秒の間だけ前進する。短距離の前進に使用。
    mL.run_timed(speed_sp=-100,time_sp=t,stop_action='brake')
    mR.run_timed(speed_sp=-100,time_sp=t,stop_action='brake')
 def move_back(t): #時間t/1000秒の間だけ前進する。短距離の前進に使用。
    mL.run_timed(speed_sp=100,time_sp=t,stop_action='brake')
    mR.run_timed(speed_sp=100,time_sp=t,stop_action='brake')
 def turn_right(t): #t/1000秒の間だけ右回転する。小さい回転に使用。
    mL.run_timed(speed_sp=-100,time_sp=t,stop_action='brake')
    mR.run_timed(speed_sp=100,time_sp=t,stop_action='brake')
 def turn_right_long(t): #t秒の間だけ右回転する。大きな回転に使用。
    x = time.time()
    while time.time() - x < t:
       mL.run_forever(speed_sp=-100)
       mR.run_forever(speed_sp=100)
    mL.stop()
    mR.stop()
 def turn_left(t): #t/1000秒の間だけ左回転する。小さな回転に使用。
    mL.run_timed(speed_sp=100,time_sp=t,stop_action='brake')
    mR.run_timed(speed_sp=-100,time_sp=t,stop_action='brake')
 def turn_left_long(t): #t秒だけ左回転する。大きな回転に使用。
     x = time.time()
     while time.time() - x < t:
        mL.run_forever(speed_sp=100)
        mR.run_forever(speed_sp=-100)
     mL.reset()
     mR.reset()
 def line_trace_left(t,c): #cの値を境目に線の左側をジグザグ前進し、時間tだけ黒を感知すると動作を停止する。(詳しくは上のライントレースを参照)
    x = time.time()
    while time.time() - x < t:
      if cs.value() > c:
         mL.run_forever(speed_sp=-90)
         mR.run_forever(speed_sp=30)
         x = time.time()
      else:
           mL.run_forever(speed_sp=30)
           mR.run_forever(speed_sp=-90)
    if time.time() - x > t:
       mL.stop()
       mR.stop()
 def line_trace_right(t,c):#cの値を境目に線の右側をジグザグ前進し、時間tだけ黒を感知すると動作を停止する。(詳しくは上のライントレースを参照)
 def line_trace_right(t,c): #cの値を境目に線の右側をジグザグ前進し、時間tだけ黒を 感知すると動作を停止する。(詳しくは上のライントレースを参照)
    x = time.time()
    while time.time() - x < t:
         if cs.value() > c:
            mL.run_forever(speed_sp=30)
            mR.run_forever(speed_sp=-90)
            x = time.time()
         else:
            mL.run_forever(speed_sp=-90)
            mR.run_forever(speed_sp=30)
    if time.time() - x >t:
         mL.stop()
         mR.stop()
 if __name__ == '__main__':
   print (cs.value()) #カラーセンサーの値を表示。この値を毎回確認し、カラーセンサーの値が正常か判断する。
   sleep(1)
   motor_init() #モーターをリセットする
   sleep(1) #1秒間停止。(以下同様に、()の中の数字の秒数だけ停止する。)
   arm_move(1300,100) #コースの途中でピンポン球にアームが当たらないように上げながら移動する。
   sleep(5) 
   line_trace_left(1.1,40) #ライントレースをし、MからKまで移動し、Kでコーナーを検知して停止する。
   sleep(1)
   turn_right(655) #ピンポン球を回収するように角度を調整
   sleep(1)
   move_back_long(4) #Kで停止したままアームを下すと、アームがピンポン球にぶつかってしまうので後進する。
   sleep(5)
   arm_move(700,-100) #アームを下す。
   sleep(1)
   motor_init()
   sleep(1)
   move_forward_long(1.3) #アームをピンポン球の下に潜らせる。
   sleep(1)
   arm_move(1500,100) #アームを上昇させ、ピンポン球を持ち上げる。
   sleep(1)
   move_forward_long(5.5) #Lまで移動する。
   sleep(1)
   turn_left_long(2.4) #Lまで移動する。
   sleep(1)
   line_trace_right(0.8,40) #LからKまで移動し、Kでコーナーを検知して停止する。
   line_trace_right(0.8,40) #LからKまで移動し、Kでコーナーを検知して停止する
   sleep(1)
   turn_left_long(0.5)
   turn_left_long(0.5) #Kのスタート位置まで移動
   sleep(1)
   move_forward_long(1.5)
   move_forward_long(1.5) #Kのスタート位置まで移動
   sleep(1)
   turn_right_long(2)
   turn_right_long(2) #Kのスタート位置まで移動
   sleep(1)
   line_trace_right(0.8,20)
   line_trace_right(0.8,20) #KからJま線の右側をライントレースする。Jでコーナーを検知して停止する。
   sleep(1)
   move_forward(200)
   move_forward(200) #Jからスタートするための移動
   sleep(1)
   turn_left_long(1.3)
   turn_left_long(1.3) #Jからスタートするための移動
   sleep(1)
   line_trace_left(0.87,40)
   line_trace_left(0.87,40) #JからIまでライントレースする。Iでコーナーを検知して停止する。
   sleep(1)
   turn_right(600)
   turn_right(600) #Iのスタート位置まで移動
   sleep(1)
   move_forward(350)
   move_forward(350) #Iのスタート位置まで移動
   sleep(1)
   line_trace_left(0.87,40)
   line_trace_left(0.87,40) #IからHまでライントレースする。Hでコーナーを検知して停止する。
   sleep(1)
   turn_right(600)
   turn_right(600) #Hのスタート位置まで移動 
   sleep(1)
   move_forward(350)
   move_forward(350) #Hのスタート位置まで移動
   sleep(1)
   line_trace_left(0.87,40)
   line_trace_left(0.87,40) #HからGまでライントレースする。Gでコーナーを検知して停止する。
   sleep(1)
   turn_right(1000)
   turn_right(1000) #Gのスタート位置まで移動
   sleep(1)
   move_forward(400)
   move_forward(400) #Gのスタート位置まで移動
   sleep(3)
   turn_left_long(1.5)
   turn_left_long(1.5) #Gのスタート位置まで移動
   sleep(1)
   line_trace_left(1,20)
   line_trace_left(1,20) #GからFまでライントレースする。Fでコーナーを検知して停止する。
   sleep(1)
   move_forward(500)
   move_forward(500) #Fのスタート位置まで移動
   sleep(1)
   turn_left(900)
   turn_left(900) #Fのスタート位置まで移動
   sleep(1)
   line_trace_left(1,20)
   line_trace_left(1,20) #FからEまでライントレースする。Eでコーナーを検知して停止する。
   sleep(1)
   move_forward(500)
   move_forward(500) #Eのスタート位置まで移動する
   sleep(1)
   turn_left(900)
   turn_left(900) #Eのスタート位置まで移動する
   sleep(1)
   line_trace_left(1,20)
   line_trace_left(1,20) #EからDまでライントレースする。Dでコーナーを検知して停止する。
   sleep(1)   
   turn_right(800)
   turn_right(800) #Dのスタート位置まで移動する。ただしライントレースを左側から右側に切り替える。
   sleep(1)
   move_forward(800)
   move_forward(800)#Dのスタート位置まで移動する。ただしライントレースを左側から右側に切り替える。
   sleep(1)
   line_trace_right(1,20)
   line_trace_right(1,20) #DからBまでライントレースする。Cは直進する。Bで停止する。
   sleep(1)
   turn_left(600)
   turn_left(600) #位置調整
   sleep(1)
   move_back_long(5)
   move_back_long(5) #長いアームを下すので後ろに下がる。
   sleep(1)
   arm_move(150,-500)
   arm_move(150,-500) #アームを早く下し、ピンポン球をAにシュートする。
   sleep(1)

*感想と反省 [#p32913e6]
上記のプログラムで難しかったことは、いくつもありました。

1つ目はMからKの間にある急コーナーです。ここをライントレースで突破するために回転半径を小さくする必要があり、そのためにはタイヤとタイヤの間の距離を小さくする必要がありました。そして現在のロボットの形になりました。

2つ目はピンポン球の回収です。回収するためにはピンポン球の場所にまっすぐ侵入してくる必要があり、回収前に前進や後進をすると左右のタイヤの摩擦差により、だんだんと回転してしまい、まっすぐ侵入するのが困難でした。これが成功率を下げる最大の原因でした。アームの先の形状がかなり回収しやすいようになってるので多少のズレは大丈夫でした。

3つ目はHIJGの円でのコーナー検知です。コーナーに侵入するときの角度がかなりあり、白の割合がかなり多いままコーナーにいるので検知しないことが多々ありました。これはline_traceのサブルーチに時間t(second),白と黒の境目の値c(カラーセンサーの値)を変数にすることで、コースの場所によってこれらを変えることで調整することで解決しました。これにより、他の場所でもコーナーの誤検知や、検知しないということがほぼほぼなくなり、成功率の向上に大きく寄与しました。

最後に、今回の課題は過去のレポートやpythonのテキスト=みんなのPython第4版 柴田 淳著(信州大学 工学部 電子情報システム工学科のプログラミング言語Iという2019後期授業で使用予定)を活用することで、ライントレースに必須のwhile文やif文を学んだことで、プログラミングを打つことより、それを修正することに時間を割り当てられたので、期限内に完成することができました。成功率も2/3くらいに向上しました。やはり、ピンポン球の回収で失敗が多いので、そこをなんとかしたかったです。

*参考にした先輩のレポート [#t565491b]
http://yakushi.shinshu-u.ac.jp/robotics/?2018a%2FMember%2Fgear%2FMission2

http://yakushi.shinshu-u.ac.jp/robotics/?2016b%2FMember%2Fbeing%2FMission2

http://yakushi.shinshu-u.ac.jp/robotics/?2016b%2FMember%2Fbicky%2FMission2

http://yakushi.shinshu-u.ac.jp/robotics/?2016a%2FMember%2Fmaria55%2FMission2

*今回の課題で苦労してるEV3の方へ←後輩はこれだけでも読んでください[#ifac8029]
Pythonの基本を、特にwhile文やif文の理解は必須である。Pythonの基本的なテキストを図書館などで読んだり、購入したりすべき。特にwhile文と、if文の箇所を。その後、上記の先輩のレポートを読むとヒントになる。Pythonの基本がなってないと、読んで理解することすら不可能である。最低限これは行うべき。


工学部 電子情報システム工学科所属でEV3を使うことになった方は、後期のプログラミング言語Iで「みんなのPython」を使用するので生協、通販等で先に購入しておくことでPython3の予習をしておくことをおすすめする。後ろの索引からwhile文やif文などのページを探し、ここだけでも読んでおいてほしい。他に方法があるのなら、それでPythonの基本を勉強してください。


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