ライントレースとは、地面に引かれている白線ないしは黒線を追従(トレース)していく競技です。光センサ等を用いてラインと機体のずれを相対的に算出し、そのずれを制御量にして機体を動かしていくのが一般的です。
ライントレースは様々な方法があります。ここではごく簡単なライントレースの方法を挙げていきましょう。
もっとも初歩的で、簡単なライントレース方法です(図1)。この制御法では、フォトインタラプタ等のセンサを1つで行うことが出来ます。ラインに乗っていれば左のモーターを動かし、ラインに乗っていなければ右のモーターを動かすといった制御方法で実現できます。しかし、この制御方法で振動を小さくするのは難しく、ラインの境界線を走るために少しずれた位置を走ることとなり、またラインのもう片方の境界線まで外れると、トレースが出来なくなります。
比例制御によるライントレースとは、ラインと機体のずれに応じた制御量で動かしていく制御方法です。ラインと機体のずれが大きければ大きく修正するようにし、ラインと機体のずれが小さければ小さく修正するようにします。比例制御ではセンサを沢山に付けないと、ずれの量が検出できないので注意が必要です。最低でも2つ以上はセンサを付けないと上手く出来ません。
以下に簡単なプログラムを示します。
// //センサが4つ付いている場合の簡単なライントレース // // getLineDat() // …現在のラインセンサの値を下位4ビットに格納されたデータを取得できる // // motorOutDat( signed char dutyL , signed char dutyR ) // …百分率で左右のモーターを動かす関数 unsigned char lineDat; //ラインセンサの値を保持する変数 while( 1 ){ lineDat = getLineDat(); //現在のラインセンサの値が、 //下位4ビットに格納されている。 //真ん中2つのセンサが反応していたら if( lineDat == 0b00000110 ){ motorOutDat( 60 , 60 ); //60% 60% 出力 //左端2つのセンサが反応していたら }else if( lineDat == 0b00001100 ){ motorOutDat( 40 , 80 ); //40% 80% 出力 //左端1つのセンサが反応していたら }else if( lineDat == 0b00001000 ){ motorOutDat( 20 , 90 ); //20% 90% 出力 //右端2つのセンサが反応していたら }else if( lineDat == 0b00000011 ){ motorOutDat( 40 , 80 ); //80% 40% 出力 //右端1つのセンサが反応していたら }else if( lineDat == 0b00000001 ){ motorOutDat( 20 , 90 ); //90% 20% 出力 //其れ以外 }else{ motorOutDat( 60 , 60 ); //60% 60% 出力 } }
比例制御は、ずれに応じた制御量にする必要があります。しかしセンサの反応毎に制御量を個別に指定するのはとても困難です。センサの数が2個の場合は、2の2乗=4通りとなり、その程度であれば場合分けで制御量を個別指定しても良いと思いますが、センサが6個付いている場合は、2の6乗=64通りとなり、とても場合分けで対応できる数ではありません。64個も制御量を個別指定するとなると、気が滅入ってしまいますね。
そこで、制御量を計算で出す方法をご紹介したいと思います。その方法とは重み付き平均です。ここでいう重み付き平均とは一般的な加重平均とは違います。
どのように制御量を計算するのかと言うと、まず機体の中心ラインと各センサの距離をそのセンサの重みとします。例えば、あるセンサは機体の中心ラインから10mm離れていたとしたら、そのセンサの重みは10となります。このように重みを割り振って行くと、機体の中心ラインからの距離に応じた重みが各センサに割り振られると思います。
そして、反応しているセンサの重みの合計値の平均をとってあげれば、その時のずれの値が距離で得られます。
実は、目標値(ライン上)に収束させるために、ずれを偏差として制御量にするのが比例制御というものであり、場合分けして制御量を個別に指定するのは、比例制御とは言いません。比例制御はその名の通り「比例」するので、ずれの量に比例した値が制御量となるべきなのです。
以下に、比例制御のプログラムを示します。
// //センサが4つ付いている場合の比例制御トレース // // getLineDat() // …現在のラインセンサの値を下位4ビットに格納されたデータを取得できる // // motorOutDat( signed char dutyL , signed char dutyR ) // …百分率で左右のモーターを動かす関数 signed short lineGain[4] = {-60,-20,20,60};//中心ラインと各センサの距離 unsigned char lineDat; //ラインセンサの値を保持する変数 signed short error; //制御量 signed char sensorCount; //反応しているセンサの数 unsigned char i; //カウンタ用 unsigned char duty = 60; //基準デューティー比 while( 1 ){ lineDat = getLineDat(); //現在のラインセンサの値が、 //下位4ビットに格納されている。 error = 0; //0に初期化 sensorCount = 0; //0に初期化 for( i = 0 ; i < 4 ; ++i ){ //i = 0~3 if( ( lineDat >> i ) & 1 ){ // lineDatのiビット目の値の真偽を見る error += lineGain[ i ]; //真の場合はセンサが反応しているので、重みを付けて制御量に加算 sensorCount++; //最後に平均をとるためのカウンタをインクリメント } } error /= sensorCount; //反応しているセンサの重みの合計値の平均をとる motorOutDat( duty - error , duty + error ); //出力 }
比例制御は、個別に制御量を指定する必要がないので簡単ですね。