目次
#contents


*はじめに [#j9c2c091]
今回は前回よりは早く始めることができ、かなりの余裕を持って機体、ロボット共々完成させることができた。とはいえ、今回も一筋縄ではいかなかった。~
結局、最新のev3dev(2016-10-17版)にアップグレードし、pythonのバージョンをpython3に変更し、windows10からはteraterm、linux mintからは端末からアクセスし、nanoを使ってプログラミングした。~
結局、最新の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へシュート”である。
*課題2(主にライントース)について [#pb1a32ef]
今回の課題は簡単に言えばライントレース+ピンポン玉の確保&シュートである。&size(7){前期より複雑になってる・・・?};~
自分はAスタートであるため、" Aスタート → P直進 → Q直進 → (ピンポン玉をキャッチ) → Q直進 → R右折 → P直進 → S左折 → Dへシュート"である。
今回も機体製作は私、プログラムの雛形はペアのbickyである。

*コースについて [#q45eaa78]
#ref(2016b/Member/being/Mission2/course.png,75%,コース概観)
交差点に番号を振り、各交差点での制御を定義した。自分はAスタートである。~
できるだけ反射率が均一であって欲しかったため&size(7){あと塗るのがめんどくさかったため};、ペンではなくビニールテープを用いラインを引いた。特に不具合はなく、センサーのコースの距離次第ではあるが、ペンで書いたものを同様の値が帰ってくる。

*今回の機体について [#bd46a54a]
**本体 [#a427fead]
今回の機体で重視したのはコンパクトさである。(写真は見やすさのためにケーブルを取り外してある)~
超音波センサー側に進むのだがもともとは反対方向へ進むように作っていたが、これでは車輪とアームの位置が離れてしまう(ライントレース中の機体の左右の揺れが大きくなってしまい、ピンポン玉をはじいてしまう)という指摘を受けたため急遽、逆向きにした&size(15){そのため、後ろ側のほうがいろいろつけられる構造になっており、前側の拡張性が低く苦労した。};~
今回の機体で重視したのはコンパクトさである。なぜならあまり大型であるとピンポン玉を発見する前に機体自体がピンポン玉をはじいてしまうからである。~
モーターの配置は前回同様、駆動にラージモーター、アームにミディアムモーターである。&size(7){モーターの特性上これからも変わらないと思われる};
#ref(2016b/Member/being/Mission2/底面.JPG,75%,機体底面)
(写真は見やすさのためにケーブルを取り外してある)~
この機体の特徴としてまず挙げられるのが、センサーと駆動輪の距離である。この距離が長いほどライントレースの制御は難しくなるため、できるだけ距離を近くした。
#ref(2016b/Member/being/Mission2/kitai1.png,75%,本体概観)


この機体は最終的には超音波センサー側に進むようになったのだが、もともとは反対方向へ進むように作っていた。これでは進行方向側にキャスターがあることで車輪とアームの位置が離れてしまい、
ライントレース中の機体の左右の揺れが大きくなってしまうためにピンポン玉をはじいてしまうという指摘を受けたため急遽、逆向きにした。&size(7){でもほんと、キャスターは便利};~
&size(15){そのため、後ろ側のほうがいろいろつけられる構造になっており、前側の拡張性が低く苦労した。};~

**センサーについて [#q663c934]
使用しているセンサーは超音波センサー、カラーセンサー(赤色発光反射率モード)&size(7){値が条件にも依るが3〜92程度の範囲で返ってくるため、そのまま利用した};ジャイロセンサー(積算モード?)である。
使用しているセンサーは超音波センサー、カラーセンサー(赤色発光反射率モード)&size(7){最初は反射率を0−100に変換せずそのまま返すREF-RAWモードを使用しようとしていたが、条件にも依るが3〜92程度の範囲で返ってくるため、そのまま利用した};~
ジャイロセンサー(積算モード?)である。
***この課題においての各センサーの特徴 [#i56f3286]
超音波センサーは、超音波を発してその超音波が帰ってくるまでの時間から対象との距離を測るセンサーである。今回は機体に地面と水平に取り付け、また値を三回取得し平均を取ることで外的要因や電圧の影響を受けにくくなっている。~
地面と水平に設置することで地面に向けて設置した場合と比べて、ライントレース中の機体のがたつきに影響されることなく、値がおかしくなってゴーストを検知してしまうことがなくなった。~
超音波センサーは、超音波を発してその超音波が帰ってくるまでの時間から対象との距離を測るセンサーである。~
今回は機体に地面と水平に取り付け、また値を三回取得し平均を取ることで外的要因や電圧の影響を受けにくいようにした。~
地面と水平に設置することで地面に向けて設置した場合と比べて、ライントレース中の機体の がたつき に影響され値がおかしくなり、ありもしないものを検知してしまうことを防止している。~
今回の課題の場合対象物が球形であるため反射波が&size(7){ステルス機に当たったレーダー波のように};拡散されてしまう影響か、値が突然大きくなってしまうという現象が散見された。~
われわれの機体は取り付け位置のおかげか特にそのような現象には悩まされなかった。~
われわれの機体は取り付け位置のおかげか特にそのような現象には悩まされなかったが、対処法で一番楽だと思われるのは基準となる値の現在の値の差にabsコマンドを使い絶対値を求め、
その差の絶対値が一定以上であればピンポン玉があると判断する方法である。~

カラーセンサーは、もともと光の三原色を発して色を判定し、その値を色ごとのコードで返してくるものであるが、今回はモードを変更して赤色に発光させ反射率を測定し0〜100で返すモードを使った。&size(7){モードの変更方法は後に記述する};~
~
カラーセンサーは、もともと光の三原色を発して色を判定し、その値を色ごとのコードで返してくるものであるが、今回はモードを変更して赤色に発光させ反射率を測定し0〜100で返すモードを使った。&size(7){モードの変更方法は後に記述};~
~
ジャイロセンサーは本来角速度を検知するものであるため、自分の向いている向きを相対的に&size(7){積分を始めたときの向きが起点として};知るためにはこれを積分しなくてはならず処理速度が一定とはいいがたいEV3で角度を知るのは困難であると思っていた。~
しかし、実際に使ってみるとセンサーの内部で積分しているらしかなり正確な角度を知ることができたが結局自分が最後にほんの少しだけ使うにとどまった。&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パーツである。今回は左右対称には作れなかったが、走行中に歪まず、部品が外れず、分解しやすい機体になっていると思う。
箱にしまえる大きさまで分解するとモーター2個とジャイロセンサーがついたフレーム、アーム、超音波センサー部、ev3の4パーツである。~
今回は左右対称には作れなかったが、走行中に歪まず、部品が外れず、分解しやすい機体になっていると思う。
*ライントレースについて [#w32a74b1]
**ライントレース全般について [#fc967a44]
ライントレースとは、ロボットにセンサーからの情報を基に状況を判断させ、地面に書かれた線の上に追従していくことである。~
ライントレースをすることは難しいことではないが、速さや正確性を求めると調整に手間がかかるものである。~
ライントレースのやり方は多々あるが今回挑戦したものについて触れようと思う。~

今回使用したセンサーは自ら光を発し、その光の反射によって線を判断するセンサーである。~
このようなセンサーにもいくつか種類があるが、今回使用したセンサーは反射率を返すものである。~
ほかにも、センサーには現在の反射率を返し、線があるかないか(もしくは半々か)の判断はプログラムで行うものや、どれくらいの反射で線があると判断するかを事前にハードウェア的に設定しておき、線があるかないかのみをセンサーが判断し、値として返すものなどがある。~

ライントレース自体のやり方も数種類あるが今回は白→黒→白・・・とライントレースしていくようにした。~
この場合、センサーの値は往々にして外的要因に影響されるものであるため、直前に白、黒、グレーの値の範囲を測って設定しておくか、もしくは、プログラム内で自分で設定をするようにしておく必要がある。~
そして、白、黒、グレーのそれぞれの範囲についての行動をプログラミングしておくことで、ライントレースが可能になる。この方式では機体を左右に振りながら進むため、ライントレースがスムーズとはいいがたい。~
原因は動作のパターンが3つしかないためである。黒を検知して白側に旋回する、もしくはその逆か、グレーを検知して直進するという動作だけではどうしてもスムーズに行かない。
&size(7){そもそも今回の課題用のプログラムではグレーの判断すらしていない。処理が間に合わなかったためである。処理速度が遅くなるのはジャイロセンサーが原因だということに気づいたときにはもう間に合わなかった};~
これをスムーズにするためには値の範囲の数を増やせばよい。たとえば値の範囲の数を5個に増やせば旋回するときのパワーの値をずらすことでよりスムーズになる。~
これを繰り返して、範囲の数を増やせば増やすほど滑らかになっていく。これをできるだけ繰り返し、範囲の数を増やせるだけ増やしたのがPID制御である。~
今回の課題は直角なカーブがあるためセンサーの値に制限をつけてPID制御のままでは対応しきれない一定以上急なカーブや直角の時に対応するものも用意したが、処理速度の関係でオミットした。

**ライントレース中の交差点検知について [#o07ffe95]
今回の課題では、ライトセンサーは1つしか使えない。~
これは、ライントレースをしながらそのセンサーで交差点を検知しなくてはならないということである。~
そのために今回は、交差点が必ず直角であり、他の場所に直角はないことを利用した。~
つまり、交差点に侵入するとそこは直角であるため交差点でない場所に比べてライトセンサーが黒を検知し続ける時間が長いということである。~
しかしながら、ev3の反応が遅いためか他と比べてどうしても動きが鈍くなってしまい、交差点とそれ以外を区別するのにかかる時間が他より3倍以上になってしまった。~
また、判断に時間がががるということはその間機体は旋回し続けているため、交差点と検知した時にはほとんど真横を向いてしまっている。~
交差点を右に曲がるときはなんともならないが、直進しなくてはならない時&size(7){圧倒的にこちら多い};は線を踏み越えなければならない。~
線を踏み越えるための動作は確実性の観点から大きく旋回し、線を探す。~
この動きをしたあとに、そのままライントレースに復帰すると交差点を検知した時と逆向きになった状態でライントレースを始めてしまうことが多い。~
そのため、もう一度交差点であると誤検知してしまうことが多発したため、向きを調整する動作を入れてある。

*プログラムについて [#n0984797]
以下のプログラムは''CC BY-SA 4.0''とします。ライセンスは[[こちら:https://creativecommons.org/licenses/by-sa/4.0/legalcode]]&size(7){htmlタグが使えないためこのように表記しておきます};
**自作関数について [#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') 
 

 #behave of ball 
 def ball_catch(): 
 &size(7){各パッケージで自身が使うもののみを定義すればよい。};~
 def ball_catch(): #ピンポン玉を捕まえる関数
 arm.run_forever(speed_sp=-60) 
 time.sleep(4) 
 arm.stop() 

 def shoot(): 
 cs = ev3.ColorSensor('in3') #5~80 
 gy = ev3.GyroSensor('in2') 
 gy_old = gy.value() 
 while abs(gy.value() - gy_old) < 90: 
 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) 
 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) 
     ml.run_forever(speed_sp=100)      #黒線を検知するまで前進する
 while cs.value() < 40: 
     mr.run_forever(speed_sp=0) 
     ml.run_forever(speed_sp=-60) 
     ml.run_forever(speed_sp=-60)      #白になるまで左モーターを後退させる
     mr.stop() 
     ml.stop() 
 arm.run_forever(speed_sp=60) 
     ml.stop()                         #ここまでの一連の動きで機体をゴールにまっすぐに向ける
 arm.run_forever(speed_sp=60)          #アームを上げる
 time.sleep(1) 
 arm.stop() 
 time.sleep(1) 
 mr.run_forever(speed_sp=-200) 
 mr.run_forever(speed_sp=-200)         #機体を後退させアームの前にピンポン玉を出す
 ml.run_forever(speed_sp=-200) 
 time.sleep(2)#adjust 
 time.sleep(2)
 mr.stop() 
 ml.stop() 
 arm.run_forever(speed_sp=-500) 
 ml.stop()             
 arm.run_forever(speed_sp=-500)        #時間がないので一気にアームを下ろす
 time.sleep(0.5) 
 arm.stop() 
 mr.run_forever(speed_sp=150) 
 mr.run_forever(speed_sp=150)          #機体を前進するさせアームにピンポン玉を押し付ける
 ml.run_forever(speed_sp=150) 
 time.sleep(3.2) 
 arm.run_forever(speed_sp=1050) 
 arm.run_forever(speed_sp=1050)        #前進したままで全力でアームを上げることでピンポン玉を打ち出す
 mr.stop() 
 ml.stop() 
 time.sleep(0.5) 
 arm.stop() 
 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 
 cs = ev3.ColorSensor('in3') #5~80 #上に同じく

 GoPw = -120 
 linepowerS = -150 
 linepowerL= -60 
 GoPw = -120       #GoPowerの略 本来は前進用の定数だったが調整の過程で前進ではなく旋回用になってしまった
 linepowerS = -150 #ライントレースのStrongの方のパワー
 linepowerL= -60   #ライントレースのLowの方のパワー


 target = 40 
 power = 100 
 KP = 4.0 
 KP = 4.0          #P制御用の定数郡 ジャイロセンサーを読み込むと処理速度が足りずお蔵入りした ジャイロセンサーを後から読み込めばいいことに気がついたがもう遅かったためそのままである

 #def line_follow():#with P 
 # while cs.value() >= 70: #white 
 #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: #gray?#P 
 #     turn = KP*(cs.value() - target) 
 #     pwl = power - turn 
 #     pwr = power + turn 
 # 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: #black 
 #     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: #white 

 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: #black 
 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(): 
 #behave at intersection(normal:turn_right)   #交差点での振舞いについての関数
 def go_straight():                          #交差点直進時の関数 交差点検知の仕様で右向きになっている
 mr.run_forever(speed_sp=-GoPw) 
 ml.run_forever(speed_sp=GoPw/3*2) 
 ml.run_forever(speed_sp=GoPw/3*2)           #右モーターに比べて左モーターを弱めに動かすことである程度外側に振りながら旋回する 
 time.sleep(2.5) 
 ml.stop() 
 mr.stop() 
 while cs.value() >= 50: #find line 
 while cs.value() >= 50:                      #右モーターのみを回すことで大きく旋回し黒いラインを探す 
     mr.run_forever(speed_sp=200) 
 while cs.value() < 70: #Adjust direction 
 while cs.value() < 70: #Adjust direction     #このままライントレースに復帰すると交差点と誤検知するため白になるまで機体をパワーに差をつけた旋回で旋回させる
     mr.run_forever(speed_sp=-100) 
     ml.run_forever(speed_sp=200) 
 while cs.value() >= 40: 
 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) 
 def turn_left():               #最後の交差点のみ左折する
 mr.run_forever(speed_sp=200)   #一定時間旋し、検知したくない線をまたぐ
 ml.run_forever(speed_sp=-150) 
 time.sleep(2) 
 while cs.value() >= 30: #find line 
 while cs.value() >= 30:        #黒いラインを見つけるまで旋回する
     mr.run_forever(speed_sp=200)   

 

 def start(): 
 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() 
 ml.stop()                                    #停止する
 time.sleep(1) 
 if cs.value() >= 50: 
 if cs.value() >= 50:                         #センサーの値から白と判断すれば黒い線を探すために大きく旋回する
     while cs.value() >= 50: #find line 
         mr.run_forever(speed_sp=200) 
 else: 
     while cs.value() < 50: #Adjust direction 
     while cs.value() < 50: #Adjust direction #黒であったら向きを調整するために左に旋回する
         mr.run_forever(speed_sp=-200) 
         ml.run_forever(speed_sp=200) 
         ml.run_forever(speed_sp=200)
*結果 [#y948682f]
電圧によって調整する必要はあるが、ほぼ毎回成功させることができるようになった。~
しかしながら、ライントレースの方法の影響でタイムを短縮することができないことが残念である。
~
~
~
~
~
~
~
~
*雑記 [#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のバージョンが低いことが原因とわかったためアップデートした。参考としてセンサーの値を表示するプログラムを以下に記述する。~
 #!/usr/bin/env python3 
 from ev3dev.ev3 import * 
 from time import sleep 
 import ev3dev.ev3 as ev3             #ここまでテンプレ

 lcd = Screen()                      #Screen関数に名前をつける

**li-S.py [#af5a8604]
 us = ev3.UltrasonicSensor('in1')   #ポート1に超音波センサーを
 gy = ev3.GyroSensor('in2')      #ポート2にジャイロセンサーを
 cs = ev3.ColorSensor('in3')      #ポート3にカラーセンサーを接続している
 
 while True: 
 	l = str(us.value())           #lに超音波センサーの値を代入
 	m = str(gy.value())	   #mにジャイロセンサーの値を代入
 	n = str(cs.value())           #nにカラーセンサーの値を代入(テスト用のプログラムなので変数名が適当である)
 	x1 = str('Ulrta=',l)          #lの値を前に`Ulrta=`をつけた状態で数字から文字に変換しx1に代入する
 	x2 = str('Gyro=',m)       #上と同じような動作
	x3 = str('Color=',n)      #上と同じような動作
	x = x1+x2+x3          #3つの文字列を統合する
	print(x)            #xの内容をPCに出力する
	lcd.clear()          #LCDをリセットする
	lcd.draw.text((36,80),x)    #lcdの座標の(36.80)に文字列xを出力する
	lcd.update() 
	sleep(1)            #一秒待つ
   
**マルチスレッドについて [#sab79ba6]
特に使用する予定はなかったが使えると便利であるかと思ったのでマルチスレッドについても確認してみた。~
確かめてみると、ある程度は可能であるがstrコマンドのバグがあり、ほかにもバグが多いと聞いたためその時点で断念した。LCDと同様に以下にテストプログラムを記述する。
 #!/usr/bin/env python3 
 from ev3dev.ev3 import * 
 from time import sleep 
 #import ev3dev.font as fonts 
 import ev3dev.ev3 as ev3 
 import threading         #threadingのパッケージをインポート 
 import time 
 import linetrace as move 
 import ball as catch 
 #A to D 

 arm = ev3.MediumMotor('outA') 
 lcd = Screen() 
 
 mr = ev3.LargeMotor('outB') 
 ml = ev3.LargeMotor('outC') 

 us = ev3.UltrasonicSensor('in1') 
 cs = ev3.ColorSensor('in3') #5~80  
 gy = ev3.GyroSensor('in2') 
 cs = ev3.ColorSensor('in3')   #ここまでは今までとほぼ同じ

 x = 0 
 Itime = 1.6 
 def sensors(): 
 while True: 
     l = str(us.value()) 
     m = str(gy.value()) 
     n = str(cs.value()) 
     print('Ulrta=',str(us.value()),' ','Gyro=',str(gy.value()),' ','Ref-aw=',str(cs.value()))   #動作確認のためにPCに値を表示する
     lcd.clear() 
     lcd.draw.text((10,80),l) 
     lcd.draw.text((50,80),m) 
     lcd.draw.text((100,80),n)   #各値を多少ずらして横に並べた状態でLCDに表示する
     lcd.update() 
     sleep(0.5) 

 # 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 
 # ev3.Sound.speak('start!')  

 Sound.play('start.wav') 
 time.sleep(1) 
 move.start() 
 def motor(): #単純なライントレースプログラム
 while True: 
     t0 = time.time() 
     move.line_follow() 
     elapsed_time = time.time() - t0 
     print(elapsed_time, cs.value(),us.value()) 
     if elapsed_time > Itime: 
         x += 1 #count of intersection 
         print('count = ', x) 
         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 
             move.go_straight() 
                 if x == 2: 
                     while True: 
                         us1 = us.value() 
                         us2 = us.value() 
                         us3 = us.value() 
                         print(us1,us2,us3) 
                         move.line_follow() 
                         if (us1 + us2 + us3) / 3 < 70: 
                             mr.stop() 
                             ml.stop() 
                             break 
                     catch.ball_catch() 
                 elif x == 6: 
                     move.turn_left() 
                 elif x == 7: 
                     ev3.Sound.play('neraimasu.wav') 
                     catch.shoot() 
                     break
     if cs.value() => 40: 
         mr.run_forever(speed_sp=-500) 
         ml.run_forever(speed_sp=-800) 
     elif cs.value() < 40: 
         mr.run_forever(speed_sp=500) 
         ml.run_forever(speed_sp=-500) 


 x = threading.Thread(target=sensors) #関数sensorsをthreadingするために処理し、xと名付ける
 y = threading.Thread(target=motor)  #関数motorをthreadingするために処理し、yと名付ける
 
 x.start() 
 y.start() #それぞれを実行する
***結果 [#n27c3d25]
threading自体は成功するようだが、strコマンドについてのOSエラーが発生してしまい対処不可能であった。また、変数の使い方にも制限があるようである。
**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制御に利用したりした。


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