#contents *はじめに [#j9c2c091] 今回は前回よりは早く始めることができ、かなりの余裕を持って機体、ロボット共々完成させることができた。とはいえ、今回も一筋縄ではいかなかった。~ 結局、最新のev3dev(2016-10-17版)にアップデートし、pythonのバージョンをpython3に変更し、windows10からはteraterm、linux mintからは端末からアクセスし、nanoを使ってプログラミングした。~ また、下書き用にPyCharmのCommunity Editionを利用した。&size(7){相変わらず大学のWifi経由だと不安定で使いにくい。あと、PyCharmほんとに便利・・・}; *課題2について [#pb1a32ef] 今回の課題は簡単に言えばライントレース+ピンポン玉の確保&シュートである。&size(7){前期より複雑になってる・・・};~ 自分はAスタートであるため、" Aスタート → P直進 → Q直進 → (ピンポン玉をキャッチ) → Q直進 → R右折 → P直進 → S左折 → Dへシュート"である。 今回も機体製作は私、プログラムの雛形はペアのbickyである。 *コースについて [#q45eaa78] #ref(2016b/Member/being/Mission2/course.png,75%,コース概観) 交差点に番号を振り、各交差点での制御を定義した。自分はAスタートである。 *今回の機体について [#bd46a54a] **本体 [#a427fead] 今回の機体で重視したのはコンパクトさである。なぜならあまり大型であるとピンポン玉を発見する前に機体自体がピンポン玉をはじいてしまうからである。 #ref(2016b/Member/being/Mission2/kitai1.png,75%,本体概観) (写真は見やすさのためにケーブルを取り外してある)~ 機体は超音波センサー側に進むのだがもともとは反対方向へ進むように作っていた。これでは車輪とアームの位置が離れてしまう(ライントレース中の機体の左右の揺れが大きくなってしまい、ピンポン玉をはじいてしまう)という指摘を受けたため急遽、逆向きにした。~ &size(15){そのため、後ろ側のほうがいろいろつけられる構造になっており、前側の拡張性が低く苦労した。};~ **センサーについて [#q663c934] 使用しているセンサーは超音波センサー、カラーセンサー(赤色発光反射率モード)&size(7){値が条件にも依るが3〜92程度の範囲で返ってくるため、そのまま利用した};ジャイロセンサー(積算モード?)である。 ***この課題においての各センサーの特徴 [#i56f3286] 超音波センサーは、超音波を発してその超音波が帰ってくるまでの時間から対象との距離を測るセンサーである。今回は機体に地面と水平に取り付け、また値を三回取得し平均を取ることで外的要因や電圧の影響を受けにした。~ 地面と水平に設置することで地面に向けて設置した場合と比べて、ライントレース中の機体の がたつき に影響され値がおかしくなりありもしないものを検知してしまうことを防止している。~ 今回の課題の場合対象物が球形であるため反射波が&size(7){ステルス機に当たったレーダー波のように};拡散されてしまう影響か、値が突然大きくなってしまうという現象が散見された。~ われわれの機体は取り付け位置のおかげか特にそのような現象には悩まされなかったが、対処法で一番楽だと思われるのは基準となる値の現在の値の差にabsコマンドを使い絶対値を求め、その差の絶対値が一定以上であればピンポン玉があると判断する方法である~ カラーセンサーは、もともと光の三原色を発して色を判定し、その値を色ごとのコードで返してくるものであるが、今回はモードを変更して赤色に発光させ反射率を測定し0〜100で返すモードを使った。&size(7){モードの変更方法は後に記述};~ ~ ジャイロセンサーは本来角速度を検知するものであるため、自分の向いている向きを相対的に&size(7){積分を始めたときの向きが起点として};知るためにはこれを積分しなくてはならず処理速度が一定とはいいがたいEV3で角度を知るのは困難であろうと思っていた。~ しかし、実際に使ってみるとセンサーの内部で積分しているらしく、かなり正確な角度を知ることができたが結局自分が最後にほんの少しだけ使うにとどまった。&size(7){たぶんこのセンサーと時間制御のみで今回の課題はクリアできる};~ **ピンポン玉関連の機構について [#xc3cbf14] 今回の機体を製作するにあたっていろいろな機構を考えたものの、ピンポン玉を確保し打ち出すという動作をするのに適切な形を求めていった結果このようになった。~ ***ピンポン玉の確保について [#h4dacc8d] 今回の機体はアーム部を上げたまま移動し、Qを通過後に超音波センサーがピンポン玉を検知するとアームを下ろすという非常にシンプルなものになっている。アーム動作用のモーターはev3本体に直接ついている。~ #ref(2016b/Member/being/Mission2/KITAI2.png,75%,ピンポン玉がおさまった状態) 機体は反時計回りに進んでいるため、ピンポン玉は超音波センサーと降りてきたアーム、機体側のバーによって囲まれることで確保される。このときアームを下ろし過ぎないように物理的にリミッターを取り付けてある。 ***ピンポン玉の打ち出しについて [#sdea9034] ピンポン玉の打ち出し手順はシンプルであり、ゴールに到着すると機体の向きを整え、アームを上げてからゆっくりと下がりアームを下げ、前進しながら全力でアームを上げることで打ち出す、という特に難しくもない方式である。~ この方式の課題は、機体がピンポン玉を離しているあいだに何処かへ行ってしまうということと、打ち出すときにアームがピンポン玉と接しておらず打ち出せないときがあることと、変にアームがピンポン玉に当たると意図しない方向に飛んでいってしまうことである。~ これは外的要因によるものであるため動かないようにすることは困難である。 この問題に対応するためたとえピンポン玉が動いてもシュートできるように機体を改造した。~ 簡単に言えばガイドレールの設置とピンポン玉を機体に押し付けることである。ピンポン玉が動いてしまうとはいえそこまで早くは移動しないため多少の時間はある。~ #ref(2016b/Member/being/Mission2/5358228612682.jpg,75%,打ち出し機構) その間にアームを下げ、その状態で前進することにより超音波センサーから伸びているレールとアームから伸びているレールではさみ、機体が前進することでアームとピンポン玉が離れないようにし、安定した打ち出しができるようになった。 **機体の分解について [#ndf053b9] 今回の機体は前回の機体のフレームを立てて、ev3を取り付けたような構造である。今回もゆがまないように、また特定のパーツにだけ負荷がかからないようにした。~ 箱にしまえる大きさまで分解するとモーター2個とジャイロセンサーがついたフレーム、アーム、超音波センサー部、ev3の4パーツである。今回は左右対称には作れなかったが、走行中に歪まず、部品が外れず、分解しやすい機体になっていると思う。 *プログラムについて [#n0984797] **自作関数について [#fb3fe95a] 学校の環境でev3をプログラミングすることの障害は大きく分けて2つある。ひとつはev3devのアップデートによる仕様変更である。これには苦しめられたが対処は可能である。(対処法は後述)~ もう一つはWifiを経由することとev3の性能によるラグである。ev3の性能を上げることは不可能であるため、できるだけ1つのファイルの容量を減らし、処理を軽くし通信の容量を節約することにした。&size(7){あとPyCharmで下書きして修正する手間をできるだけ減らした};~ そのために用いたのが自作関数である。同一ディレクトリに存在しているファイルを使うのであれば普通のライブラリと同じようにimportすればよいため、特に苦労せずに使えるので楽である。~ 本来は別のimportのやり方でなら、関数名を記述するだけでよいが今回は見易さを優先しimport A as Bと記述しA.Bという記述方法を用いた。~ 当然のことだがユーザー関数を使用することでひとつのファイルの容量を半分以下にすることに成功している。 **li-S.py[#af5a8604] &size(7){雛形となったペアのプログラム名がlt.pyであったため(linetrace?)lt-S.pyにしようとしたがミスタイプして現在の形になってしまった}; #!/usr/bin/env python3 from ev3dev.ev3 import * #パッケージのインポート &size(7){lcdと音声ファイルを扱う時はなぜか*でなくてはならない。本来はそれぞれのパッケージをimportすればよいはずだし、パッケージ自体は存在している};~ import ev3dev.ev3 as ev3 import time import linetrace as move import ball as catch #A to D #ペアのプログラムと間違えないようにするためのメモ書き arm = ev3.MediumMotor('outA') #アームの定義 mr = ev3.LargeMotor('outB') #motor right ml = ev3.LargeMotor('outC') #motor left us = ev3.UltrasonicSensor('in1') #超音波センサーの定義 cs = ev3.ColorSensor('in3') #5~80 #カラーセンサーの定義 x = 0 #検知した交差点のカウンタ Itime = 1.6 #この時間以上カラーセンサーが黒であったら交差点と判断する(この時間が長いのがev3の悩み) # A -> P(go_straight) x=1 -> Q(go_straight) x=2 -> ball_catch # -> Q(go_straight) x=3 -> R(turn_R) -> P(go_straight) -> S(turn_L) -> shoot #動作の過程のメモ Sound.play('start.wav') #課題1でev3に喋らせたため、今回は音楽ファイルを再生させた、同時に再生することはできないようである。cvは私物のVOICELOID time.sleep(1) move.start() while True: t0 = time.time() #現在の時間を取得しt0に代入 move.line_follow() #moveと名付けたライブラリからline_followというライントレースをする関数を呼び出す elapsed_time = time.time() - t0 #経過時間(黒であった時間)を計算する(line_followの中でも現在の時間を取得しt0に代入している) print(elapsed_time, cs.value(),us.value() ) #デバック用にPCに現在の経過時間、カラーセンサーの値、超音波センサーの値を表示している。この経過時間から何秒黒であったら交差点と判断するかを求める if elapsed_time > Itime: #経過時間が決められた時間より大きければ交差点と判断する x += 1 #count of intersection #カウンタに1足す print('count = ', x) #交差点と判断したことと現在の位置ををPCに伝る mr.stop() ml.stop() #動きを止める ev3.Sound.play('kousatendesu.wav') #交差点を検知したことを伝える time.sleep(1.2) #待つ・・・ if x == 1 or x == 2 or x == 3 or x == 5: #A to D #またペアと間違えないようにするメモ書き 交差点番号が1,2,3,5の時は直進する move.go_straight() #moveと名付けたライブラリからgo_straightという関数を呼び出し、交差点を直進する この辺のプログラムはペアのものとは大部異なっている if x == 2: #交差点番号が2である時はピンポン玉を探し始める while True: us1 = us.value() us2 = us.value() us3 = us.value() #誤検知防止のために三回値を取得する print(us1,us2,us3) #デバック用にPCに値を送信する move.line_follow() #ライントレースはそのまま if (us1 + us2 + us3) / 3 < 70: #三回の値の平均値が70mm以下のときにピンポン玉と判断する mr.stop() ml.stop() #停止する break #whileを破る catch.ball_catch() #catchと名付けたライブラリからball_catchという関数を呼び出し、ピンポン玉を確保する。 elif x == 6: #自分の場合はペアとは違い左折する必要がある move.turn_left() #moveと名付けたライブラリからturn_leftという関数を呼び出し交差点を左折する elif x == 7: ev3.Sound.play('neraimasu.wav') #シュートをすることを伝える catch.shoot() #catchと名付けたライブラリからshootという関数を呼び出しボールをシュートする ここもペアとはだいぶ異なる break #プログラムを終了する **ball.py [#cddcab06] #!/usr/bin/env python3 import ev3dev.ev3 as ev3 import time arm = ev3.MediumMotor('outA') mr = ev3.LargeMotor('outB') ml = ev3.LargeMotor('outC') &size(7){各ライブラリで自身が使うもののみを定義すればよい。};~ def ball_catch(): #ピンポン玉を捕まえる関数 arm.run_forever(speed_sp=-60) time.sleep(4) arm.stop() def shoot(): #ピンポン玉をシュートするための関数 cs = ev3.ColorSensor('in3') #カラーセンサーを定義する gy = ev3.GyroSensor('in2') #ここでジャイロセンサーを定義する。ジャイロセンサーを最初から定義していると処理が重くなるのか挙動が変わってしまう(体感的には処理速度が3分の2程度になってしまう) gy_old = gy.value() #使用したモードでのジャイロセンサーは電源が入ってからの累積地であるため現在の値を取得する while abs(gy.value() - gy_old) < 90: #値を確認するのが面倒であったため現在のジャイロセンサーの値と取得した値の差の絶対値が90より大きくなるまで機体を右旋回する mr.run_forever(speed_sp=100) ml.run_forever(speed_sp=-100) mr.run_forever(speed_sp=-100) #機体を後退させ距離をとる ml.run_forever(speed_sp=-100) time.sleep(1) while cs.value() >= 40: #white mr.run_forever(speed_sp=100) ml.run_forever(speed_sp=100) #黒線を検知するまで前進する while cs.value() < 40: mr.run_forever(speed_sp=0) ml.run_forever(speed_sp=-60) #白になるまで左モーターを後退させる mr.stop() ml.stop() #ここまでの一連の動きで機体をゴールにまっすぐに向ける arm.run_forever(speed_sp=60) #アームを上げる time.sleep(1) arm.stop() time.sleep(1) mr.run_forever(speed_sp=-200) #機体を後退させアームの前にピンポン玉を出す ml.run_forever(speed_sp=-200) time.sleep(2) mr.stop() ml.stop() arm.run_forever(speed_sp=-500) #時間がないので一気にアームを下ろす time.sleep(0.5) arm.stop() mr.run_forever(speed_sp=150) #機体を前進するさせアームにピンポン玉を押し付ける ml.run_forever(speed_sp=150) time.sleep(3.2) arm.run_forever(speed_sp=1050) #前進したままで全力でアームを上げることでピンポン玉を打ち出す mr.stop() ml.stop() time.sleep(0.5) arm.stop() #アームを止める **linetrace.py [#fa484292] #!/usr/bin/env python3 import ev3dev.ev3 as ev3 import time mr = ev3.LargeMotor('outB') ml = ev3.LargeMotor('outC') cs = ev3.ColorSensor('in3') #5~80 #上に同じく GoPw = -120 #GoPowerの略 本来は前進用の定数だったが調整の過程で前進ではなく旋回用になってしまった linepowerS = -150 #ライントレースのStrongの方のパワー linepowerL= -60 #ライントレースのLowの方のパワー target = 40 power = 100 KP = 4.0 #P制御用の定数郡 ジャイロセンサーを読み込むと処理速度が足りずお蔵入りした ジャイロセンサーを後から読み込めばいいことに気がついたがもう遅かったためそのままである #def line_follow():#with P #P制御とともにライントレースをする関数 # while cs.value() >= 70: #今回カラーセンサーの値が3〜92程度であったためある程度黒よりにトレースする 70以上で白と判断しその場で右旋回する # mr.run_forever(speed_sp=-GoPw) # ml.run_forever(speed_sp=GoPw) # t0 = time.time() # while cs.value() >= 20 and cs.value() < 70: #20以上70未満である時にP制御で前進する # turn = KP*(cs.value() - target) #目標値である40との差を取得し定数を掛け、制御値を求める turnの値は正から負までである # pwl = power - turn #左モーターのパワーを求める # pwr = power + turn #右モーターのパワーを求める # mr.run_forever(speed_sp=pwr) # ml.run_forever(speed_sp=pwl) #それぞれのパワーを代入し、モーターを動かす(パワーではなくてスピード?細かいことはいいんです・・・) # while cs.value() < 20: #70以上で白と判断しその場で左旋回する # mr.run_forever(speed_sp=Gopw) # ml.run_forever(speed_sp=-GoPw*2) def line_follow(): #採用版のライントレース ラインの右側を進み、旋回時のパワーに差をつけることで前進する while cs.value() >= 50: #黒と判断した時 mr.run_forever(speed_sp=-linepowerS) ml.run_forever(speed_sp=linepowerL) t0 = time.time() while cs.value() < 50: #白と判断した時 mr.run_forever(speed_sp=linepowerL) ml.run_forever(speed_sp=-linepowerS) #behave at intersection(normal:turn_right) #交差点での振舞いについての関数 def go_straight(): #交差点直進時の関数 交差点検知の仕様で右向きになっている mr.run_forever(speed_sp=-GoPw) ml.run_forever(speed_sp=GoPw/3*2) #右モーターに比べて左モーターを弱めに動かすことである程度外側に振りながら旋回する time.sleep(2.5) ml.stop() mr.stop() while cs.value() >= 50: #右モーターのみを回すことで大きく旋回し黒いラインを探す mr.run_forever(speed_sp=200) while cs.value() < 70: #Adjust direction #このままライントレースに復帰すると交差点と誤検知するため白になるまで機体をパワーに差をつけた旋回で旋回させる mr.run_forever(speed_sp=-100) ml.run_forever(speed_sp=200) while cs.value() >= 40: #向きを整えるために逆向きに少しだけ動かす mr.run_forever(speed_sp=200) ml.run_forever(speed_sp=-100) #for last intersection def turn_left(): #最後の交差点のみ左折する mr.run_forever(speed_sp=200) #一定時間旋し、検知したくない線をまたぐ ml.run_forever(speed_sp=-150) time.sleep(2) while cs.value() >= 30: #黒いラインを見つけるまで大きく旋回する mr.run_forever(speed_sp=200) def start(): #スタートゾーンAから離脱するための関数 mr.run_forever(speed_sp=220)#Get out of Zone A ml.run_forever(speed_sp=220) time.sleep(1) mr.stop() ml.stop() #停止する time.sleep(1) if cs.value() >= 50: #センサーの値から白と判断すれば黒い線を探すために大きく旋回する while cs.value() >= 50: #find line mr.run_forever(speed_sp=200) else: while cs.value() < 50: #Adjust direction #黒であったら向きを調整するために左に旋回する mr.run_forever(speed_sp=-200) ml.run_forever(speed_sp=200) ~ ~ ~ ~ ~ ~ ~ ~ *雑記 [#ud993dea] 課題に直接の関係はないものの、ev3を扱っている間にわかったこと、調べたことを書く。 **PID制御について [#m22273ff] 課題2がライントレースの課題だと知り、PID制御を実装しようと思ったので[[このサイト:http://www2.denshi.numazu-ct.ac.jp/lego/NXT/nxtOSEK/3_jissen/linetrace2.html]]を参考に製作した。当初の想定と条件が異なっていたためお蔵入りとなってしまい、悔しかったので半ば強引にP制御を導入しようとしてまた失敗してしまった。&size(7){日の目を見ることがあるのだろうか・・・};~ **LCDの使用とev3devのアップデートについて [#f1ba2178] 今回の課題から本格的にセンサーを使用するのでセンサーの値を機体側でも確認しようとLCDを使うことにした。[[ev3devの公式サイト:http://www.ev3dev.org/docs/drivers/]]で調べようとしたが見つけられず、[[ev3devをpyrhonでプログラミングしているサイト:https://sites.google.com/site/ev3python/]]の中の[[ここ:https://sites.google.com/site/ev3python/learn_ev3_python/screen]]を見つけ試行錯誤をした。~ なかなか成功しなかったが、結局ev3devのバージョンが低いことが原因とわかったためアップデートした。参考としてセンサーの値を表示するプログラムを以下に記述する。~ hogehogehogehidehogehoge **マルチスレッドについて [#sab79ba6] 特に使用する予定はなかったが使えると便利であるかと思ったのでマルチスレッドについても確認してみた。結果は、ある程度は可能であったが、strコマンドのバグがあり、ほかにもバグが多いと聞いたためその時点で断念した。LCDと同様に以下にテストプログラムを記述する。 hogehogehogehidehogehoge **ev3devアップデートにかかわる変更点について [#i8ba4c49] 実は私はev3devをアップデートした結果、モーターを特定角度回転させることが不可能になったといわれていた。~ 発表の後の時間で確かめてみると、[[ここ:http://yakushi.shinshu-u.ac.jp/robotics/?EV3#s3b12bed]]にある"モータを特定の角度だけ回す"項の"duty_cycle_sp"を課題のプログラム中に使用した"speed_sp"に変更すると動くすることがわかった。~ その後、ev3dev内の使用可能な命令を調べると"duty_cycle_sp"が"&color(red){_duty_cycle_sp};"に変更になっていた。このような変更点はほかにもあるかもしれないので、適宜確認していく。 **Wifiについて [#eabe08fb] ev3にPCからアクセスする方法はUSB経由とBluetooth経由とWifi経由があるが自分はなぜかWifi以外での接続に成功しないのでWifiを経由していたが&size(3){相変わらず大学のWifiが役に立たない・・・};ので自分でルーターを持ち込んだ。~ 結構快適であるので、自分で持ち込むかもしくは借りたほうがいいと思われる。 **処理速度について [#m19bc65b] ev3は高機能である代わりに処理速度がどうしても遅いようである。NXTやRCXと比べると特にライントレースの周期が長いようだ。負荷を掛けるとさらに遅くなるようであるのでジャイロセンサーのような負荷のかかる処理は最小限にするほうがよいと思われる。 **センサーのモード変更や制御に使えるコマンドの調べ方について [#e9ea8f7e] ec3devはlinuxであるのでコマンドがそのまま使える。/sys/class/内の各フォルダにセンサーやモーターのライブラリがあるためそこをcatやechoを使用することで使用可能なコマンドを確認したり、センサーのモードを変更することができる。たとえばカラーセンサーも値を0-100で返すモードから0-1025で返すモードに変更し、PID制御に利用したりした。