2019a/Member

目次

課題について

詳しくは2019a/Mission3を参照してください。

コース図面

我々の班はロボットを2つずつ、別に動かして、容器に入ってる2つの球と容器の外の2つの球をそれぞれのロボットが回収する分担をしました。私と私のペアは容器の外の2つを回収するのを担当しました。そして、2人で定義を共有し、それぞれ1個分の球を回収する分のプログラミングを書きました。2人の分を合わせてプログラムが完成します。具体的にどこが誰の書いた分かは、下の実際に動かしたプログラムで示します。我我2人が辿ったコースはA'-K'-L'-(1個目の球を回収と同時に発射)-K'-CBの間の線を通過-K-L(2個目の球を回収)-K-I'-(2個目の球を発射)、というルートです。

2つのロボットの連携

MQTT通信による連携を模索していたのですが、今回の課題は制作期間が短い上に、期末試験とかぶってしまったので時間が不足し、資料も不足していたので通信による連携をあきらめました。そのかわり、sleep関数による時間での連携を目指しました。具体的には両方のロボットのプログラムを同時に動かすが、片方はもう片方の作業の邪魔しない位置になるまでの時間分だけsleep()により停止します。お互いぶつからないように、片方だけ先に動かすということです。しかし、ロボットはいつも完全に同じ時間で作業を完了しないため、時間に余裕を持たせました。お互いの回収の成功率などを考慮しながら4人で話し合い、もう片方のロボットで最初にsleep()を入れることで合意しました。

ロボットの説明

ロボットの改修を最初はしていましたが、結果が出ず、結局、課題2とほとんど同じロボットになりました。しかし、あのままではピンポン球を回収できても、射出することはできませんでした。モーターもこれ以上増やせないので、一つのモーターで回収と射出を兼任させました。

robot

ロボット全体↑

robot

回収地点に着く↑

robot

ピンポン球を持ち上げる↑

robot

↑ピンポン球の位置エネルギーとモーターの加速により、カタパルトのように、レールに沿わせながらピンポン球を射出する。持ち上げて射出するまでの一連の動作は、レスリング技のジャーマンスープレックスやバックドロップのように後方に投げるイメージである。

ロボットの出力に関する問題

我々のev3は内臓のリチウムイオンバッテリーで動作しており、モーターやセンサーへの給電もこれにより行われる。しかし、このバッテリーの性能が非常に低く、満充電してもプログラムを実行すれば1、2回程度しかもたない。時間が非常に少ないこの課題の中で、この状態は非常に憤りを感じるものであった。プログラムのトライ&エラー、バグ修正の作業効率が極端に低下した。その上、全て時間経過でモーターを動かしているので、同じ時間でも出力によって動く距離が変化してしまう。これにより、位置調整が狂ったりするようになってしまった。バッテリーの出力の条件を一緒にするとそこまで再現性は低下しないが、ピンポン球を回収するアームの動作が非常に不安定であった。同じ出力にしていても、アームが自身の重さに耐えられなかったり、ピンポン球を吹っ飛ばすほど早く動くことなどがあった。この出力の不安定さには本番直前まで悩まされ、再現性を下げる最大の要因であった。原因は正確にはわかっていないが、バッテリーの劣化などが原因ではないかと推測している。もっと容量が多く、劣化していないバッテリーが必要だと感じた。しかし、これも授業の予算の話などを聞いた後ではあまり強くは言えない。政府はもっと潤沢な資金を大学によこしてほしい。よこせや。

ライントレース

ロボットを2つに分けてしまったので、今回の課題でもカラーセンサーが一つしかない。よってコースの黒線が測定エリア内にどれだけ入っているかによって動作を決定する。赤い円の中が測定範囲であり、黒の割合が多くなるほどカラーセンサの値が小さくなり、白の割合が多いほど値が大きくなる。これはカラーセンサが光の反射の度合いを測っているからである。実際の動作については、下の図の,里茲Δ法黒の割合が高いときは白が増えるように動き、△里茲Δ貿鬚粒箙腓高いときは黒の割合が高くなるように動きます。そしてで,汎韻犬海箸鬚垢襦これらを交互に繰り返し、ジグザグに動きながら線の片側に沿って移動します。使用言語はPythonなので、この繰り返しはwhile文を使ってカラーセンサーの値の条件を満たせばこれらの動作を繰り返すようにする。要は、白にいきすぎたら黒に行き、黒に行きすぎたら白に行くことを繰り返すことで、少しずつ前進する寸法である。

ライントレースの図解

実際のライントレースのサブルーチンは以下のようになります。  

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=-80)
        mR.run_forever(speed_sp=40)
        x = time.time() #スタート時刻を更新する
     else:        #カラーセンサーの値がcより小さい、つまり黒が多いと左前方に移動。このときスタート時刻は更新しないのが重要。
          mL.run_forever(speed_sp=40)
          mR.run_forever(speed_sp=-80)
   if time.time() - x > t: #現在時刻−スタート時刻がtより大きくなる。つまり黒の割合が多いコーナーだと、スタート時刻が更新されず、この差が大きくなる。これによりコーナーを検知し。モーターの動きが止まる。
      mL.stop()
      mR.stop()
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=40)
           mR.run_forever(speed_sp=-80)
           x = time.time() #スタート時刻を更新
        else: #カラーセンサーの値がcより小さい、つまり黒が多いと右前方に移動。このときスタート時刻は更新しないのが重要。
           mL.run_forever(speed_sp=-80)
           mR.run_forever(speed_sp=40)
   if time.time() - x >t: #現在時刻−スタート時刻がtより大きくなる。つまり黒の割合が多いコーナーだと、スタート時刻が更新されず、この差が大きくなる。これによりコーナーを検知し。モーターの動きが止まる。
        mL.stop()
        mR.stop()
line_trace_right

(説明)このサブルーチンを簡単に説明すると、1行目のx=time.time()で現在時刻を記録し、ストップウォッチのように時間を数え始める。白い部分にいる間はストップウォッチをリセットし、黒いところではリセットせず、白のほうに脱出してストップウォッチをリセットする。白のほうにいきすぎると、カラーセンサーでその状況を判断し黒に戻ってくる。黒にきたらまた白のほうに脱出しリセットする。白黒白黒の繰り返しである。雑なイメージで言うと、白にいるあいだは時限爆弾の残り時間が進まず、黒にいるときは時間が進む感じである。しかし、これでは永遠に白黒とループしてしまうのではコーナーでは、白に脱出するのに比較的長い時間がかかるのという性質を利用する。そのストップウォッチの値はコーナーのように黒が多いところでは、リセットができず、経過時間が大きくなる。よって、経過時間がある値に達したらそこは白に脱出しにくいコーナーであると判断する。これにより、コーナーを検知し、停止することができる。コーナーだと時限爆弾の時間が進んでしまい、時間が進まない白まで遠く、時間がかかるので、時間切れになり爆発してロボットが止まるイメージである。

コーナーを検知しない対策

基本的にはライントレースのコーナー判定時間を短くし、境界線を黒側に寄せることである。コーナーを判定する時間を短くすると、当然、道中でコーナーと誤判定する確率が上がりますが、より確実にコーナーで止まりやすくなります。この誤検知の危険性と、停止する確実性の妥協点を探して調整しました。境界線を黒側に寄せると、コーナーについた際に、より、黒色が入る割合が増えます。しかし、同時に黒が多くないと止まらなくなるので、これも、黒を増やす割合と、コーナー判定の厳しさの妥協点を探すことになります。これら2つはt(second),c(cs.value())の2つの変数とし、コースの場所ごとに最適化することが何よりも大切です。今回の課題ではコーナーの数はそこまで多くなく、ライントレースも課題2で書いて、ほとんど失敗しないものを流用しているのであまり問題にはなりませんでした。

パートナーとの作業分担

今回の課題では時間がなく、二人でそれぞれ別のプログラムを制作している時間はなかったので、定義を共有して、プログラミングを分担しました。私のほうの定義は課題2で上手く動いていたので、定義は主に私のものを用いました。私は開始から1個目のピンポン球を回収し射出、2個目のあるコースの手前まで移動、パートナーには2個目の位置までの移動、回収、射出、もう片方のロボットの邪魔をしないように撤退するまでを担当してもらいました。1人ではなく、複数で1つのプログラムを作り上げるという経験ができたのは非常に良かったです。

実際のプログラム

#!/usr/bin/env python3 #python3を宣言
from ev3dev.ev3 import * #ev3devをインポートする
from time import sleep #時間はsleep関数を用いる(例)sleep(x)でx秒経過する
mL = LargeMotor('outD') #左のモーターはDポートから出力する
mR = LargeMotor('outC') #右のモーターはCポートから出力する
mM = MediumMotor('outB') #ミディアムモーターはBから出力する
cs = ColorSensor('in3') #カラーセンサーは3番ポートから出力、入力する
cs.mode = 'COL-REFLECT' #カラーセンサーは反射した光の量を読み取るモードに指定
def motor_init(): #モーターをリセットする
      mL.reset()
      mR.reset()
def arm_move(t,y): #アームを動かす。しかし、これはピンポン玉を保持できない。アームの開放、ピンポン球の射出用に使う。t[s]で動かす時間、yで速度を指定。y>0ならアームは持ち上がり、y<0だとアームは下がる。
  x = time.time()
  while time.time() - x < t:
    mM.run_forever(speed_sp=y)
  mM.stop()
  sleep(1)
def catch_up(x,t): #ピンポン球を回収し、モーターを固定することでアームを固定する。固定しないとアームは自重に耐えられず下がってしまい、ピンポン球を運んだり、ロボットの上に保持できない。stop_action='hold'であることが肝心。xでモーターの速度を指定し、tで時間を指定する。速度の正負による挙動は上のcatch_upと同様。
  mM.run_timed(speed_sp=x,time_sp=t,stop_action='hold')
  sleep(1)
def hold_out(): #上のcatch_upで固定してしまったモーターを開放するためだけの関数。hold状態からout(脱出)するという意味。同名のプロレス入場曲が存在するが関係はない。
  mM.run_timed(speed_sp=100,time_sp=100,stop_action='brake')
  sleep(1)
def move_forward(t): #時間t[s]だけ前進する。whileうんぬんかんぬんは既述のライントレース関連の説明を参照してほしい。時間t[s]だけ前進する。whileループの条件を満たさなくなったら停止する。2つのLarge Motorは速度が正だと前方に行くように回転する、負だと後方に行くように回転する。
   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(t):  #時間t[s]だけ後進する
     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[s]だけ左旋回する 
  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_right(t): #時間t[s]だけ右旋回する
   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_right(t,c): #線の右側に沿って動き、コーナーを検知すると止まる。細かくは既に述べた。
  x = time.time()
  while time.time() - x < t:
    if cs.value() > c:
       mL.run_forever(speed_sp=40)
       mR.run_forever(speed_sp=-80)
       x = time.time()
    else:
         mL.run_forever(speed_sp=-80)
         mR.run_forever(speed_sp=40)
  if time.time() - x >t:
       mL.stop()
       mR.stop()
def line_trace_left(t,c): #線の左側に沿って動き、コーナーを検知すると止まる。細かくは既に述べた。
  x = time.time()
  while time.time() - x < t:
       if cs.value() > c:
          mL.run_forever(speed_sp=-80)
          mR.run_forever(speed_sp=40)
          x = time.time()
       else:
          mL.run_forever(speed_sp=40)
          mR.run_forever(speed_sp=-80)
  if time.time() - x >t:
       mL.stop()
       mR.stop()
def change_paper(): #実際には使用していないが、プログラムから削除していないので掲載した。xに黒線を何回通過したか記録しようとしたが、上手く黒線で停止しなかった。これは移動スピードが速すぎた為、カラーセンサーの値の読み取りが間に合わなかったからだと思われる。結果としては、既述の移動用関数で位置調整することで問題の解決をした。
  x = 0
  while x < 2:
    if cs.value() > 50:
         mL.run_forever(speed_sp=100)
         mR.run_forever(speed_sp=100)
    if cs.value() < 50:
           mL.stop()
           mR.stop()
           x = x + 1
if __name__ == '__main__':
 motor_init() #モーターをリセット
 sleep(1) #1秒間動作を停止する(以下同様)
 hold_out() #何回もデバッグする度にミディアムモーターがhold状態になっていることがあったので、開始条件をそろえるために開放する。
 sleep(1)
 print(cs.value()) #カラーセンサーの値を表示。これは毎回、読み取る値が異常になってないかどうか確認するためのもの。
 sleep(1)
 line_trace_left(1,40) #A'からK’まで線の左側に沿って移動する
 sleep(1)
 turn_right(0.55) #ピンポン球の方向を向くために角度を調整する
 sleep(1)
 move_back(4.2) #前方にアームを展開するとピンポン球にあたるので、一旦下がる。
 sleep(1)
 arm_move(1.5,-400) #アームを展開する
 sleep(1)
 move_forward(1.6) #ピンポン球の回収地点まで前進する
 sleep(1)
 catch_up(550,750) #ピンポン球を回収する
 sleep(1)
 move_forward(5) #前進する。位置調整
 sleep(1)
 turn_right(1.25) #ピンポン球の射出角度を調整
 sleep(1)
 arm_move(1,800) #ピンポン球ー打ちー方はじーめ。ピンポン球を射出する。
 sleep(1)
 hold_out() #一応ミディアムモーターを開放する
 sleep(1)
 turn_left(1.7) #左に旋回する。位置調整
 sleep(1)
 move_forward(0.7) #位置調整
 sleep(1)
 turn_left(1) #位置調整
 sleep(1)
 line_trace_right(1,40) #L'からK'まで線の右側に沿って動く
 sleep(1)
 turn_left(0.5) #ロボットが線に平行になるように左旋回する
 sleep(1)
 move_forward(6)  #コース用紙とコース用紙の境目まで移動し、もう一方のパートナーが作ったプログラムに後を任せる。
 sleep(1)
 move_forward(4.5) #前進する。ここからパートナーが作成しました。
 sleep(1)
 line_trace_left(1,40) #Kの手前から線の左側に沿って動き、Kで停止する。
 sleep(1)
 turn_right(0.6) #Lにまっすぐむくように角度を調整する
 sleep(1)
 move_back(4.2) #ピンポン球にぶつけずにアーム展開をするために後進する
 sleep(1)
 arm_move(6,-200) #アームを展開する
 sleep(1)
 move_forward(2.2) #ピンポン球の回収地点まで前進
 sleep(1)
 catch_up(550,750) #2つ目のピンポン球を回収する
 sleep(1)
 turn_right(2.7) #位置調整
 sleep(1)
 move_forward(3) #位置調整
 sleep(1)
 turn_right(2.5) #位置調整
 sleep(1)
 move_back(2) #位置調整
 sleep(1)
 arm_move(1,300) #L'の手前からピンポン球を射出する。
 sleep(1)
 turn_left(1.5) #もう一方のロボットの邪魔をしないように移動
 sleep(1)
 move_forward(3) #もう一方のロボットの邪魔をしないように移動
 sleep(1) 
 #ここからもう片方の2人によるプログラムとロボットで残った2個を回収、及び投下

感想と反省

今回の課題では非常に時間がなかったうえに、バッテリーと出力に起因するトラブルに最後まで悩まされた上に、本番ではバッテリー切れにより大失敗してしまった。この燦燦たる結果は残念無念極まりないが、本授業で学んだ点は多かった。Pythonの基本にはじまり、ロボットの設計から動作させるためのプログラミング、制作スケジュールの管理、MQTT通信の紹介などなど。私は電子情報システム工学科の情報システムコースに進むつもりなので、今回の経験は将来に渡って非常に有意義であったと思う。放課後大学が閉まるまで残ってするデバッグに次ぐデバッグのつらさや、課題を達成したときの快感もわずかながら感じられた。これからも目標たる情報セキュリティの技術者を目指して精進していきたいと思います。ありがとうございました。


添付ファイル: file1919-3.JPG 6件 [詳細] file1919-5.JPG 8件 [詳細] file1919-4.JPG 7件 [詳細] file1919-2.JPG 6件 [詳細] file1919-1.png 10件 [詳細] file810-6.png 4件 [詳細] file810-5.png 4件 [詳細] file810-4.png 4件 [詳細] file2019a-mission3.png 6件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2019-08-26 (月) 00:09:46