目次

コース

自分はコース1を担当した。赤線はロボットの軌道を表していて、軌道を貫いている棒は停止位置を表している(停止時間1秒)。青の円は球を発射する位置。

map.jpg

ロボット

robot.jpg

車体は説明書通りになっており、車体上部にボールを発射するためのスロープを設けてある。

robot2.jpg

車体の前方に光センサーと超音波センサーをを取り付けている。

車体上部にミディアムモーターを取り付け画像のように機能する。

mediummotor.jpg

プログラム

ライントレース(基本編)

今回の課題の根幹となる部分のプログラム

c1=cs.value()
x=(abs(c1-10)/70)*100 #だいたい10<c1<80といった範囲だったので、0<x<100になるようにした。絶対値がついているのはc1<10の時でも負の値にならないように。
if x>100:
   x=100 #最大値が100を超えないようにする。
l=(x/100)*210-30 #1
r=150-l #2
ml.run_forever(speed_sp=l,stop_action='brake') #lの速さで進む。
mr.run_forever(speed_sp=r,stop_action='brake') #rの速さで進む。

#1 左タイヤの速度を設定する部分。x=0(黒い時)でl=-30,x=100(白い時)でl=180となり-30<l<180の速さで走る。

#2 右タイヤの速度を設定する部分。x=0(黒い時)でr=180,x=100(白い時)でr=-30となり-30<r<180の速さで走る。 こうすることで、センサーの値に応じて速さが変化するため直線も曲線も滑らかに移動できた。

交差点の認識

滑らかに移動できたことは、ライントレースをしている間はカラーセンサーの値はほとんど変わっていないということである。よってこのライントレースにwhile c1>13という条件を与えることにすると、

def linetrace() :
       c1=cs.value()
       while c1>13: #c1が黒を認識するまでループ
               c1=cs.value()
               x=abs((c1-10)/70)*100
               if x>100:
                       x=100
               r=(x/100)*210-30
               l=150-r
               ml.run_forever(speed_sp=l,stop_action='brake')
               mr.run_forever(speed_sp=r,stop_action='brake')
       stop()

というプログラムになり、交差点のようなカラーセンサーが急に黒を認識する時に止まるようになる。

このプログラムに至った経緯

はじめは下のプログラムのようにカラーセンサーの値の増加量を読み取らせてラインを判別していた。

t1=time.time()
s1=cs.value()
sleep(0.1)
t2=time.time()
s2=cs.value()
x=(s2-s1)/(t2-t1) #カラーセンサーの0.1秒間の増加量

if x>0:
 a=200
 b=200-x
 forward(a,b) #増加量の大きさの分だけ速さが減少して進む
if x=0:
 a=200
 b=200
 forward(a,b)
if x<0:
 a=200+x
 b=200
 forward(a,b)

しかし増加量を取ると、少しの明るさの違いで大きくジグザグした移動してしまうし、かといって増加量の秒間を増やすとラインをはみ出してしまうため、そもそもsleep関数をプログラムに入れてはダメだと分かった。

ライントレース(応用編)

基本編でのプログラムは線の左側にいるときにしか使えない上に、丁字路や急カーブを曲がり切れないので、条件別に4つに分類した。

定義名のl,rの違いは線の右に居るか左に居るかで、in,outの違いは交差点を認識するか境界線を認識するか

def linetrace_rin(): #左側にいて、交差点を認識すると終わる
 c1=cs.value()
 while c1>13:
  c1=cs.value()
  x=(abs(c1-10)/70)*100
  if x>100:
   x=100
  l=(x/100)*210-30
  r=150-l
  ml.run_forever(speed_sp=l,stop_action='brake')
  mr.run_forever(speed_sp=r,stop_action='brake')
 stop()
def linetrace_rout(): #左側にいて、境界線を認識すると終わる
 c1=cs.value()
 while c1<40:
  c1=cs.value()
  x=(abs(c1-10)/70)*100
  if x>100:
   x=100
  l=(x/100)*120-40
  r=40-l
  ml.run_forever(speed_sp=l,stop_action='brake')
  mr.run_forever(speed_sp=r,stop_action='brake')
 stop()
def linetrace_lin(): #右側にいて、交差点を認識すると終わる
 c1=cs.value()
 while c1>13:
  c1=cs.value()
  x=abs((c1-10)/70)*100
  if x>100:
   x=100
  r=(x/100)*210-30
  l=150-r
  ml.run_forever(speed_sp=l,stop_action='brake')
  mr.run_forever(speed_sp=r,stop_action='brake')
 stop()
def linetrace_lout(): #右側にいて、境界線を認識すると終わる。
 c1=cs.value()
 while c1<40:
  c1=cs.value()
  x=(abs(c1-10)/70)*100
  if x>100:
   x=100
  r=(x/100)*120-40
  l=40-r
  ml.run_forever(speed_sp=l,stop_action='brake')
  mr.run_forever(speed_sp=r,stop_action='brake')
 stop()

lout,routのスピードの値の範囲は-40<l<80 -40<r<80となっており最小値がより低くなっている

このように分類することで、直線や緩やかなカーブの時でもある程度のスピードを保ちつつ、急カーブでは減速して曲がり切れるようになる。文字では分かりにくいので画像を載せておく。

丁字路

linetrace_lin()
linetrace_lout()
linetrace_lin()
curve1.jpg

急カーブ

linetrace_rin()
linetrace_rout()
linetrace_rin()
curve2.jpg

画像の緑の丸あたりで関数が切り替わっている。

※lが右側に居て、rが左側に居るというのは誤植ではない。白い紙の上にロボットを置いたとき、どの方向に回転するかでl,rを決めている。

lr.jpg

このプログラムに至った経緯

最初はGからEにかけてのコースを次のようなプログラムで走らせようと思った。

i=0
while i<300: #iが300になった時点で終了
 c1=cs.value()
 if c1<13: #カラーセンサーが黒を認識したときにiのカウントが1増える
  i=i+1
 else:
  i=i #カラーセンサーが灰色や白を認識したときはカウントは増えない

 x=(abs(c1-10)/70)*100
 if x>100:
  x=100
 l=(x/100)*60-20 #カーブを曲がり切れるようにさっきよりは速度が控えめ
 r=20-l
 ml.run_forever(speed_sp=l*5,stop_action='brake') #最初はここでlの値の大きさを調整していた。
 mr.run_forever(speed_sp=r*5,stop_action='brake')
stop()

これはループするうちに急カーブなどで黒を認識した数だけカウントを蓄積し、交差点の黒を認識するとき止まれるようにしている。この場合は黒線上でwhileが300回ループしたときに止まる。

しかしこのプログラムだとEV3のバッテリー量に左右されやすく、成功するかしないかは日によって違ったため安定性に欠けた。しかも丁字路や急カーブも曲がれる速度に設定してあるため、直線や緩やかなカーブも遅かった。なので条件ごとに分けた。

ボール射出のプログラム

def shoot(): #ボールを放つプログラム
       mm.run_to_rel_pos(position_sp=-60,speed_sp=150,stop_action='hold') #上の画像のようにモーターを回転させることでスロープを作る
       time.sleep(3)
       mm.run_to_rel_pos(position_sp=60,speed_sp=150,stop_action='hold') #元の位置に戻す
       time.sleep(3)
def discover():
       t0=time.time() #回転し始めで時間の計測を始める
       u1=us.value() #超音波センサーの値をwhile関数でで使うため定義しておく
       ml.run_to_rel_pos(position_sp=-700,speed_sp=150,stop_action='hold')
       mr.run_to_rel_pos(position_sp=700,speed_sp=150,stop_action='hold') #360度回転
       t2=time.time() #whileの条件で使うため、あらかじめ定義しておく
       while t2-t0<4.7: #一回転するまでループする(4.7秒)
               u2=us.value() #u2としてもう一つの値を定義しておく。
               if u2<u1:
                       u1=u2 #n回目の観測距離(u2)がn-1回目の(u1)より小さい場合、u1の値を更新する
                       t1=time.time() #更新した時の時間を計測
               t2=time.time() #whileでかかる時間をt2-t0として計測
       time.sleep(1)
       ml.run_timed(time_sp=(t1-t0)*1000,speed_sp=-150,stop_action='hold') #t1-t0が回転し始めてから一番近い物体を捉えるまでの時間を表しているので、その分回転をする
       mr.run_timed(time_sp=(t1-t0)*1000,speed_sp=150,stop_action='hold')
       time.sleep(t1-t0) #回っている時間にプログラムが進まないようにしている
       time.sleep(1)
       shoot() #上の関数を起動
       ml.run_timed(time_sp=(t1-t0)*1000,speed_sp=150,stop_action='hold') #回った分だけ戻る。
       mr.run_timed(time_sp=(t1-t0)*1000,speed_sp=-150,stop_action='hold')
       time.sleep(t1-t0)
discover.jpg

プログラム全体

#!/usr/bin/env python3
from ev3dev.ev3 import *
import time #time.time()とtime.sleep()を使う
ml=LargeMotor('outA')
mr=LargeMotor('outD')
mm=MediumMotor('outC')
cs=ColorSensor('in4')
ts=TouchSensor('in1')
us=UltrasonicSensor('in2')

ロボットに情報を打ち込むところ。

def motor_init(): #モーターの値を初期化
       mr.reset()
       ml.reset()

def stop(): #止まる
       ml.stop()
       mr.stop()

def linetrace_rin():
       c1=cs.value()
       while c1>13:
               c1=cs.value()
               x=(abs(c1-10)/70)*100
               if x>100:
                       x=100
               l=(x/100)*210-30
               r=150-l
               ml.run_forever(speed_sp=l,stop_action='brake')
               mr.run_forever(speed_sp=r,stop_action='brake')
       stop()

def linetrace_rout():
       c1=cs.value()
       while c1<40:
               c1=cs.value()
               x=(abs(c1-10)/70)*100
               if x>100:
                       x=100
               l=(x/100)*120-40
               r=40-l
               ml.run_forever(speed_sp=l,stop_action='brake')
               mr.run_forever(speed_sp=r,stop_action='brake')
       stop()

def linetrace_lin():
       c1=cs.value()
       while c1>13:
               c1=cs.value()
               x=abs((c1-10)/70)*100
               if x>100:
                       x=100
               r=(x/100)*210-30
               l=150-r
               ml.run_forever(speed_sp=l,stop_action='brake')
               mr.run_forever(speed_sp=r,stop_action='brake')
       stop()

def linetrace_lout():
       c1=cs.value()
       while c1<40:
               c1=cs.value()
               x=(abs(c1-10)/70)*100
               if x>100:
                       x=100
               r=(x/100)*120-40
               l=40-r
               ml.run_forever(speed_sp=l,stop_action='brake')
               mr.run_forever(speed_sp=r,stop_action='brake')
       stop()

def shoot():
       mm.run_to_rel_pos(position_sp=-60,speed_sp=150,stop_action='hold')
       time.sleep(3)
       mm.run_to_rel_pos(position_sp=60,speed_sp=150,stop_action='hold')
       time.sleep(3)

def discover():
       t0=time.time()
       u1=us.value()
       ml.run_to_rel_pos(position_sp=-700,speed_sp=150,stop_action='hold')
       mr.run_to_rel_pos(position_sp=700,speed_sp=150,stop_action='hold')
       t2=time.time()
       while t2-t0<4.7:
               u2=us.value()
               if u2<u1:
                       u1=u2
                       t1=time.time()
               t2=time.time()

       time.sleep(1)
       ml.run_timed(time_sp=(t1-t0)*1000,speed_sp=-150,stop_action='hold')
       mr.run_timed(time_sp=(t1-t0)*1000,speed_sp=150,stop_action='hold')
       time.sleep(t1-t0)
       time.sleep(1)
       shoot()
       ml.run_timed(time_sp=(t1-t0)*1000,speed_sp=150,stop_action='hold')
       mr.run_timed(time_sp=(t1-t0)*1000,speed_sp=-150,stop_action='hold')
       time.sleep(t1-t0)

def runtime(l,r): #指定した時間だけ真っ直ぐに進む 主に交差点を進む用
       ml.run_timed(time_sp=l*1000,speed_sp=70,stop_action='brake')
       mr.run_timed(time_sp=r*1000,speed_sp=70,stop_action='brake')
       time.sleep((l+r)/2) 

def angle(l,r): #指定した数値の角度だけ進む 主に向きを変える用
       ml.run_to_rel_pos(position_sp=l,speed_sp=70,stop_action='hold')
       mr.run_to_rel_pos(position_sp=r,speed_sp=70,stop_action='hold')

実際に進むために必要なプログラム。

def AtoB(): AからBに行くプログラム
       runtime(2,2) #Pの枠を越えるために少し進ませる
       linetrace_lin() #B地点までライントレース
       print("AtoB") #ちゃんと関数が終了したか確かめる

def BtoK():
       linetrace_lout() #B地点に進入した際、linetrace_lin()では曲がりきれないため切り替える
       linetrace_lin() #白と黒の境界線に達した時、より速いlinetrace_lin()に戻してKまで進む
       print("BtoK") #BからKへ正常に進めたら表示
       time.sleep(1) #停止地点で1秒止まる

def KtoJ():
       angle(-120,120) #交差点を跨ぐために向きを変える
       time.sleep(2.5) #上の動作中に次のプログラムへ進まないようにする
       linetrace_rin() #ラインの左側にいて境界線上を走るので、これでKからJへ
       print("KtoJ") #

def JtoI():
       angle(150,20) #交差点を跨ぐために向きを変える
       time.sleep(2) #跨いでいる間にプログラムが次へ進まないようにする
       linetrace_rin() #ラインの左側にいて、境界線上を走るので、これでJからIへ
       print("JtoI")

def ItoH():
       angle(100,20) #Iの交差点を跨ぐ
       time.sleep(2) #跨いでいる間にプログラムが次へ進まないようにする
       linetrace_rin() #ラインの左側にいて、境界線上を走るので、これでIからHへ
       print("ItoH")

def HtoG():
       linetrace_rout() #Hの交差点に進入した際、linetrace_rin()では曲がりきれないため切り替える
       linetrace_rin() #境界線に達したとき、切り替える
       time.sleep(1) #停止地点で停止する
       print("HtoG")

def GtoE():
       angle(0,180) #車体の位置を交差点Gの右上から右下に移動する
       time.sleep(2) #向きを変えている間にプログラムが次へと進まないようにする
       linetrace_lin() #ラインの右側にいて、境界線上をを走る 
       linetrace_lout() #2つ目の急カーブは曲がりきれなくなるので切り替える
       linetrace_lin() #急カーブを曲がり終えて境界線に達した時切り替える
       time.sleep(1) #停止位置で停止
       print("GtoE")

def EtoL():
       linetrace_lout() #Eの交差点に進入した際、曲がりきれないので切り替える
       linetrace_lin() #境界線に達した際、切り替える
       print("EtoL")

def Goal():
       runtime(5,5) #Qに入るために少し進む

def start(): #全てをまとめた
       AtoB()
       BtoK()
       KtoJ()
       JtoI()
       discover()
       ItoH()
       HtoG()
       GtoE()
       EtoL()
       Goal()

start() #起動

反省

今回のライントレースで、速度の範囲を決めるときに、正の値の範囲を増やすと角で曲がり切れなくなったり、負の値の範囲を増やすとライン上でジグザグした動きになって進んでくれなかったりなどのジレンマがあり、調整が大変だった。今思うとプログラムばかりいじっていると感じ、ロボットのセンサーの位置をいじると改善されていたのかもしれないと思った。センサーの増加量を利用しようとしていた時はtanhの関数を使おうなどの努力をしていたが、うまくいかずに断念したのでもっと利用する場面を見極めたい。

動画

https://youtu.be/-_e3MriktZk

総計:934


添付ファイル: filerobot2.jpg 197件 [詳細] filediscover.jpg 209件 [詳細] filelr.jpg 208件 [詳細] filerobot.jpg 200件 [詳細] filemediummotor.jpg 196件 [詳細] filemap.jpg 232件 [詳細] filecurve2.jpg 81件 [詳細] filecurve1.jpg 173件 [詳細]

トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2019-02-12 (火) 13:32:16