目次
図の黒い線をなぞって周回するロボットを製作する
主な条件
・交差点が2箇所以上必要
・急なヘアピンカーブが1箇所以上必要
・直角カーブが1箇所以上必要
・黒い線の幅は15〜20mm程度であること
ここでは自分たちの作成したロボットについて紹介していきます。
ロボットのコンピュータ部分を取り除いた全体図です。 なるべくシンプルな構造を目指しました。 更に、重心がなるべく低い位置にくるように高さを調整しました。
では、まずロボットがどのようにライントレースするのかについて説明します。 次の図を見てください。
黒い線を見たとき、皆さんは左側のように見えるはずです。ですが、ロボットは人間と同じように図や色を見て判断することはできません。では、どうやって判断するかというと閾値(しきいち)と呼ばれる値を光センサーによって計測し、その値を基にして判断するのです。つまり、ロボットは黒い線を図の右側のように見ているのです。
ロボットが黒い線をどのように認識しているかは分かったかと思います。では、次にどのようにしてライントレースするのかということを説明したいと思います。次の図を見てください。
図のように閾値が白の値になると右に旋回し、黒の値になると左に旋回しそれを繰り返すことによってラインの境界線を進むことができます。ですが、このままではジグザグ走行となってしまいスムーズに進むことができません。なので次の項ではスムーズに動かすための工夫について説明したいと思います。
今回は、ロボットを滑らかに動かすために黒と白の二種類の閾値をとり、その中間点の閾値を中間点としてそこから離れれば離れるほどモーターの出力が大きくなるようなプログラムを作成しました。これにより、ジグザグ走行を抑えることに成功しました。
実際は中間は灰色ではないのですが、図の中ではわかりやすいように中間が灰色となっています。
コースの難所とその解決方法を紹介します
難所は赤丸と青丸でそれぞれ囲まれている場所です。解決法が異なるため色分けしてあります。
では、まず赤丸のほうから説明します。赤丸で囲んだ部分は直角カーブと急カーブの部分です。先ほどの項でロボットが滑らかに動くように工夫していましたが、その状態でこの二つの難所を走行させるとどうしてもコースアウトしてしまうという問題が発生しました。そこで調べたところある問題点が明らかとなりました。 次の図を見てください。
先ほど説明した滑らかに動かす方法を使った場合が図の左です。これをみると、大きくジグザグ走行していることがわかります。この方法を使うと滑らかに動くのですが、急カーブや直角カーブを曲がりきれずコースアウトしてしまいます。そこで、あえて細かくジグザグ走行させることで曲がれるようにしました。
次に青丸の部分ですが、交差点に差し掛かるとコースを間違えて走行してしまうという問題が発生しました。そこで、解決策として交差点に差し掛かると直進するようプログラムを組んだところうまく解決しました。
今回のライントレースの要ともいえる閾値の定義など、ここで定義しているものは調整にとても苦労しました。
#define Black 27 //黒の閾値1 #define White 57 //白の閾値1 #define Black2 35 //黒の閾値2 #define White2 49 //白の閾値2 #define gray 42 //中間の閾値 #define P_gain 0.38 //定数p #define D_gain 0.040 //定数d #define speed 32 //走行時の基本速度
中間の閾値から離れるほどモーターの出力を上げるプログラムの作成にとても時間がかかりました。
while( CurrentTick()-t0 <= 6500 ){ light0=light; light=Sensor(IN_4); NumOut(30,40,Sensor(IN_4),0); turn=P_gain*(light-gray)*100/(White-Black)+D_gain*(light-light0)/(White-Black)*100; OnFwd(OUT_C,speed-turn); OnFwd(OUT_A,speed+turn); }
上のプログラムが今回のライントレースの中核となる中間の閾値から離れるほどモーターの出力を上げるプログラムの基本形です。走行時の基本速度であるspeedに中間値の距離に比例する関数であるturnを足したり引いたりするという構成となっています。
以下がライントレースの本プログラムです。
task main() { SetSensorLight(IN_4); SetSensorTouch(IN_2); int light,light0; float turn; long t0; t0=CurrentTick(); OnFwd(OUT_A,60); Wait(500); while( CurrentTick()-t0 <= 6500 ){ light0=light; light=Sensor(IN_4); NumOut(30,40,Sensor(IN_4),0); turn=P_gain*(light-gray)*100/(White-Black)+D_gain*(light-light0)/(White-Black)*100; OnFwd(OUT_C,speed-turn); OnFwd(OUT_A,speed+turn); } OnFwd(OUT_AC,speed); Wait(500); while( CurrentTick()-t0 <= 21000 ){ light0=light; light=Sensor(IN_4); NumOut(30,40,Sensor(IN_4),0); turn=P_gain*(light-gray)*100/(White2-Black2)+D_gain*(light-light0) /(White2-Black2)*100; OnFwd(OUT_C,speed-turn); OnFwd(OUT_A,speed+turn); } while( CurrentTick()-t0 <= 26000 ){ light0=light; light=Sensor(IN_4); NumOut(30,40,Sensor(IN_4),0); turn=P_gain*(light-gray)*100/(White3-Black3)+D_gain*(light-light0) /(White3-Black3)*100; OnFwd(OUT_C,15-turn); OnFwd(OUT_A,15+turn); } while( CurrentTick()-t0 <= 26500 ){ light0=light; light=Sensor(IN_4); NumOut(30,40,Sensor(IN_4),0); turn=P_gain*(light-gray)*100/(White-Black)+D_gain*(light-light0)/(White-Black)*100; OnFwd(OUT_C,speed-turn); OnFwd(OUT_A,speed+turn); } OnFwd(OUT_AC,speed); Wait(1500); while( CurrentTick()-t0 <= 33100 ){ light0=light; light=Sensor(IN_4); NumOut(30,40,Sensor(IN_4),0); turn=P_gain*(light-gray)*100/(White2-Black2)+D_gain*(light-light0)/(White2-Black2)*100; OnFwd(OUT_C,speed-turn); OnFwd(OUT_A,speed+turn); } OnFwd(OUT_AC,speed); Wait(500); while( CurrentTick()-t0 <= 37000 ){ light0=light; light=Sensor(IN_4); NumOut(30,40,Sensor(IN_4),0); turn=P_gain*(light-gray)*100/(White-Black)+D_gain*(light-light0)/(White-Black)*100; OnFwd(OUT_C,speed-turn); OnFwd(OUT_A,speed+turn); } while( CurrentTick()-t0 <= 42000 ){ light0=light; light=Sensor(IN_4); NumOut(30,40,Sensor(IN_4),0); turn=P_gain*(light-gray)*100/(White2-Black2)+D_gain*(light-light0) /(White2-Black2)*100; OnFwd(OUT_C,15-turn); OnFwd(OUT_A,15+turn); } while( CurrentTick()-t0 <= 44000 ){ light0=light; light=Sensor(IN_4); NumOut(30,40,Sensor(IN_4),0); turn=P_gain*(light-gray)*100/(White-Black)+D_gain*(light-light0)/(White-Black)*100; OnFwd(OUT_C,speed-turn); OnFwd(OUT_A,speed+turn); } }
結果ですが、難所を解決するためのプログラムをタイマーを使って実行していたので設定時間通りに動かなかった場合違う場所でプログラムが起動したりして、コース全体を通して走行することがあまりできず。安定性に欠ける結果となりました。
最初ライントレースの仕組みを説明してもらったとき理解できたので、すぐにプログラムを作れるだろうと高をくくっていたのですが、いざ実際プログラムを作ってみると、誤作動が多いのとコースの閾値が一様ではないことからなかなか作成できず。悪戦苦闘の毎日が続きました。最終的には何とかトレースすることができたので良かったです。 これからの課題としては、先ほどの結果の項でも話しましたが、タイマーによるプログラム制御がいまいちであったので、そこをもっと改良していけたらと思いました。