#author("2020-02-13T14:24:45+09:00","ameame","ameame") #author("2020-02-13T14:29:26+09:00","ameame","ameame") [[2019b/Member]] #contents *課題2 ライントレース [#be995a5d] 指定されたコースを走り,コース上のボールをスタート地点に持ち帰る.~ コースは[[こちらのページ>2019b/Mission2]]の2人目である. *成果 [#e5171237] 光センサの値に合わせて比例的にタイヤの速さを変えライントレースをした.そのため,緩やかなカーブはなめらかに曲がることができた.また,なめらかに曲がるため速く移動することができた.しかし,その速さのせいで十字路や曲がり角などは不安定であった.場所に合わせて速度を変えられるようにしたがそれでも数回に1度しか成功しなかった. *ロボットについて [#x7653024] #ref(image0.jpeg) タイヤと本体は講義内で配布されたNXTの説明書に載っているものと同じように出来ている. 光センサ,超音波センサ,ボールを取るためのアームが写真のようについている.光センサはライントレース,超音波センサはボールを見つけるためにつけている.アームは普段はモーターで上に持ち上げている.~ 光センサは周りの明るさの影響を減らすために地面に平行に取り付けた.しかし,あまり成果は得られなかった.アームでボールを捕まえた後ロボットが動くとボールがアームから飛び出してしまうことがあったためアームに写真のように柱を取り付けた. 光センサは周りの明るさの影響を減らすために地面に平行に取り付けた.しかし,あまり成果は得られなかった.アームでボールを捕まえた後ロボットが動くとボールがアームから飛び出してしまうことがあったためアームに写真のように柱を取り付けた.~ プログラムに時間を割くためロボットにはあまり時間をかけていないが,アームの摩擦による誤差や光センサの位置による値の変化などで不具合があったため.ペアの人と相談して修正するべきであった. *ライントレースの仕組み [#eb4d28c1] 黒い線の縁,つまり黒色と白色の境目をトレースしている.線の左側を進む場合,ロボットの下が白ならば左に曲がり,黒ならば右に曲がる. *プログラムについて [#jb2d6ae3] **使用したプログラム [#kfd765f5] #define TIME1 120 //この時間を超えて黒の部分にいると十字路と判断する #define TIME2 120 //この時間を超えて白の部分にいるとその場で旋回する #define SPEED 50 //ロボットのベースとなる速さ #define centor 49 //白と黒の境目の光センサの値 #define distance 12.5 //タイヤ間の距離 #define diameter 5.5 //タイヤの直径 void crossroad(int t) { Off(OUT_BC); Wait(2000); OnFwd(OUT_BC,65); Wait(t); Off(OUT_BC); } void change_line(int curve){ while(SENSOR_1>centor-10){ OnRev(OUT_B,60*(-curve/abs(curve))); OnFwd(OUT_C,60*(-curve/abs(curve))); } } void follow_line(int curve,int slowdown) { int a=(curve/25)*(SENSOR_1-centor); int vb=(SPEED-a)/(slowdown/10); int vc=(SPEED+a)/(slowdown/10); if(vb>100){vb=100;} if(vc>100){vc=100;} if(vb<-100){vb=-100;} if(vc<-100){vc=-100;} OnFwd(OUT_B,vb); OnFwd(OUT_C,vc); } void follow_linePlus(int CURVE,int SLOWDOWN,long tmin,long tmax) { long t0=CurrentTick(); long tb=CurrentTick(); long tw=CurrentTick(); while(CurrentTick()-tb<TIME1 && CurrentTick()-t0<tmax){ follow_line(CURVE,SLOWDOWN); if(SENSOR_1>centor+5 || CurrentTick()-t0<tmin){tb=CurrentTick();} if(SENSOR_1<centor+5){tw=CurrentTick();} if(CurrentTick()-tw>TIME2){ while(SENSOR_1>centor-5){ OnRev(OUT_B,60*(CURVE/abs(CURVE))); OnFwd(OUT_C,60*(CURVE/abs(CURVE))); tb=CurrentTick(); tw=CurrentTick(); } } } } void fetch_ball(int CURVE,int SLOWDOWN,long tmax) { long t0=CurrentTick(); while(SensorUS(S4)>11 && CurrentTick()-t0<tmax){ follow_line(CURVE,SLOWDOWN); } Off(OUT_BC); RotateMotor(OUT_A,10,130); t0=CurrentTick(); while(CurrentTick()-t0<800){ follow_line(CURVE,SLOWDOWN); } RotateMotorEx(OUT_BC,50,180*distance/diameter,-100,true,true); } task main() { SetSensorLight(S1); SetSensorLowspeed(S4); //以下はルートの説明の写真内の番号 follow_linePlus(50,10,6000,6000); //1 change_line(-50); fetch_ball(-50,10,5000); //2 follow_linePlus(-200,20,1800,10000); //3 crossroad(250); follow_linePlus(-200,20,3000,3000); //4 change_line(50); follow_linePlus(200,20,4000,6000); //5 crossroad(300); follow_linePlus(200,20,2000,5000); //6 crossroad(0); follow_linePlus(200,20,3500,4500); //7 crossroad(700); } **ルートの説明 [#o0fc34f8] #ref(roat.jpeg) 写真のように頻繁にトレースするラインの左右を入れ替えている.その理由は曲がりやすさと十字路の右左折のためである.白い部分にいる時間が長いとその場で黒い部分に着くまで旋回するようになっているため,カーブの外側であれば急でも曲がりきることができる.また,白黒の境界をトレースするため,十字路の前では曲がりたい方向をトレースしていなければいけない. **関数の説明 [#je09e5ac] 5つの関数を使っている. ***follow_line [#c0841384] void follow_line(int curve,int slowdown) { int a=(curve/25)*(SENSOR_1-centor); //光センサの値と白黒の境目の値(centor)の差に比例する値 int vb=(SPEED-a)/(slowdown/10); //モータBの速さ int vc=(SPEED+a)/(slowdown/10); //モータCの速さ if(vb>100){vb=100;} //モータの速さを-100〜100に制限 if(vc>100){vc=100;} if(vb<-100){vb=-100;} if(vc<-100){vc=-100;} OnFwd(OUT_B,vb); OnFwd(OUT_C,vc); } SPEED:ロボットのベースとなる速さ~ 光センサの値によって左右のモータの速さをそれぞれ-100〜100の間で1次関数的に変化させる関数.~ 引数は2種類あり,~ curve:ロボットの曲がりやすさを決める.''値の正負で沿うラインの左右が決まる''.~ slowdown:ロボットを全体的に減速させる ***follow_linePlus [#xc2437e0] void follow_linePlus(int CURVE,int SLOWDOWN,long tmin,long tmax) { long t0=CurrentTick(); //総経過時間を計る long tb=CurrentTick(); //黒の部分にいる時間を計る long tw=CurrentTick(); //白の部分にいる時間を計る while(CurrentTick()-tb<TIME1 && CurrentTick()-t0<tmax){ follow_line(CURVE,SLOWDOWN); if(SENSOR_1>centor+5 || CurrentTick()-t0<tmin){tb=CurrentTick();} if(SENSOR_1<centor+5){tw=CurrentTick();} if(CurrentTick()-tw>TIME2){ //白の部分に長くいるとその場で旋回する while(SENSOR_1>centor-5){ OnRev(OUT_B,60*(CURVE/abs(CURVE))); //CURVEの正負で回転方向を反転 OnFwd(OUT_C,60*(CURVE/abs(CURVE))); tb=CurrentTick(); tw=CurrentTick(); } } } } ライントレースをする.白の部分にいる時間がTIME2より長くなるとその場で黒の部分に着くまで旋回する.経過時間がtmin以降で十字路につく,またはtmaxを超えると関数が終了する.~ 引数は4つあり,前2つはfollow_line内の値を決め,後2つは前文で説明したtminとtmaxを指定する.CURVEは白の部分で旋回する方向を決めるのにも使われている. ***change_line [#n0629891] void change_line(int curve){ while(SENSOR_1>centor-10){ OnRev(OUT_B,60*(-curve/abs(curve))); OnFwd(OUT_C,60*(-curve/abs(curve))); } } 沿うラインの左右を変えるときに使う.センサーがラインの外にあるときに沿う側を変えてしまうとコースアウトしてしまうため一度ラインの上に戻さなくてはならない.そのため,センサーが白の部分の値を取る間その場で旋回する.~ 引数は1つあり,左から右に移る時は負の値,右から左の時は正の値を入れる. ***crossroad [#ge9d7b43] void crossroad(int t) { Off(OUT_BC); Wait(2000); OnFwd(OUT_BC,65); Wait(t); Off(OUT_BC); } 十字路で2秒一時停止をし,その後t秒前進する. ***fetch_ball [#pe8e540b] void fetch_ball(int CURVE,int SLOWDOWN,long tmax) { long t0=CurrentTick(); while(SensorUS(S4)>11 && CurrentTick()-t0<tmax){ follow_line(CURVE,SLOWDOWN); } Off(OUT_BC); RotateMotor(OUT_A,10,130); t0=CurrentTick(); while(CurrentTick()-t0<800){ follow_line(CURVE,SLOWDOWN); } RotateMotorEx(OUT_BC,50,180*distance/diameter,-100,true,true); //その場で180度旋回 } 超音波センサがボールを見つけるまでライントレースを行い,見つけたらアームを下す.その後少し進んでからUターンする.~ 引数は3つあり,前2つはfollow_lineの値を指定し,もう一つはtmaxを指定する.経過時間がtmaxを超えるとその場でアームを下し旋回する. *まとめ [#w9151a3e] 今回の方法では緩やかなカーブをなめらかにすばやく曲がることができた.そのため,せっかくなのでできるだけ速くミッションを達成できるようにしたいと考えた.結果,速く達成することはできたが安定性が低かった.今回は作業時間のほとんどをプログラミングに割いたが,ロボット側にも改善点があったと考える.アームの位置が重心から遠いためカーブの際にずれが生じていた.また,移動が速いため,交差点と判断する時間が短くそこでも誤差が生じていた.速さを求めるのであれば別の判断方法を考えるべきであった.