ヴイストン「Beauto Chaser」で学ぶプログラミング入門講座
【第9回】BeautoChaserをC言語でプログラミング!-C言語でプログラムを書いてみよう(後編)
皆さんこんにちは!大阪工業大学電子工学研究部のimokenpiです。連載第9回の今回はC言語プログラムの最終回ということで、前回書ききれなかったモーターの動かし方や、ライントレーサーのプログラムを作成していきたいと思います。
●ついにきました! モーターを使いましょう!
最近の連載ではLEDを光らせたりセンサーを使用したりと動きのないプログラムばかりしてきましたが、そんな動きのない日々もここまでです。ここでは待望のモーターを動かしていきましょう。モーターも前回同様関数を呼ぶだけで動かすことができます。それでは第8回の連載記事と同様にHEW右側のワークスペースから「vs-wrc003.h」を開いて、モーターを動かす関数の宣言を探していきましょう。皆さんもご一緒にお願いします。実際に探してみると、モーターの駆動というそれっぽいコメントのある「Mtr_Run」関数が宣言されています。該当箇所を引用します。
(vs-wrc003.hの引用) /*モーターの駆動 設定した速度でモーターを駆動。呼び出した後はその状態を保持し、制御値に0を与えるまで停止しない。 引数: モーターの制御値 0 :フリー 0x80 :ブレーキ CW_MAX :0x7F (127) CCW_MAX :0x81 (-127) 戻り値: 無し */ void Mtr_Run(BYTE mt1,BYTE mt2,BYTE mt3,BYTE mt4); (引用ここまで)
Mtr_Run関数は引数をmt1からmt4まで計4つ指定するようになっています。VS-WRC003にはモーター端子が2つのみ付いているのでBYTE mt1、BYTE mt2だけ値を指定することになります。つまりモーター端子の搭載されていないBYTE mt3、BYTE mt4は常に0となります。百聞は一見に如かず、実際にMtr_Run関数を使ってみましょう。
(led.cの書き換え箇所) /*メイン関数*********************************************/ void main(void) { const BYTE MainCycle = 60; //CPUの初期設定 Init((BYTE)MainCycle); //CPUの初期設定 Mtr_Run(0,0,0,0); //モーター停止 Wait(2000); //2000msec待つ Mtr_Run(-64,64,0,0); //前進 Wait(2000); //2000msec待つ Mtr_Run(0,0,0,0); //モーター回転方向変更時過負荷防止 Wait(300); //300msec待つ Mtr_Run(64,-64,0,0); //後退 Wait(2000); //2000msec待つ Mtr_Run(0,0,0,0); //モーター回転方向変更時過負荷防止 Wait(300); //300msec待つ Mtr_Run(-64,-64,0,0); //旋回 Wait(2000); //2000msec待つ Mtr_Run(0,0,0,0); //モーター回転方向変更時過負荷防止 Wait(300); //300msec待つ Mtr_Run(64,64,0,0); //旋回 Wait(2000); //2000msec待つ Mtr_Run(0,0,0,0); //モーター停止 } (書き換え箇所終わり)
【動画】モーター駆動テストプログラムの実行の様子 |
コンパイルを行ない書き込んでみましょう。Beauto Chaserが前進、後退、左右旋回を行なったと思います。スピードは最大の127とせず、安全性を考え半分のスピードである64としました。127でガンガン走らせたいところですが、127で動かすとVS-WRC003のモータードライバがけっこう熱くなりましたので、このスピードで動かすのはやめておいたほうがよいでしょう。自動車のスピードメーターも200km/hくらいまでありますが、日常生活で実際に走るのは100km/hぐらいに自重するのと同じイメージで考えてください。
また、モーター回転方向変更時過負荷防止の部分についても触れておきたいと思います。モーターの回転方向を変更する(例えば前進の後に後退するときは左右のモーターの回転方向が変わります)ときは、0.2秒から0.3秒の停止命令を行なってから変更するほうがいいようです。これを怠るとモーターの回転方向が変わるときにモータードライバに多くの電流が流れてしまい、余計な負担がかかってしまいます。
●モータードライバに放熱板を取り付け
放熱板を搭載したVS-WRC003 |
モーター回転方向変更時過負荷防止の処理について先ほど紹介しましたが、この処理をしっかり行おうとすればするほど、C言語でのプログラムは複雑になってしまいます。今回の記事で紹介するプログラムはできるだけ簡単なものにしたいのであえてモーター回転方向変更時過負荷防止の処理を省きたいと思います。この処理を省いたプログラムで動かしても大丈夫だとは思いますが、念のため今回はモータードライバに放熱板(ヒートシンク)を取り付けることにより保険をかけておきたいと思います。モータードライバに貼り付けることにより、モータードライバで発生した熱を放熱板が肩代わりしてくれます。放熱版はロボット専門店のほか、自作パソコンを扱っているお店などで購入してください。
●光を追いかけるプログラムの作成
放熱板も取り付けたところで、さっそくモーターを使ったプログラムを製作してみましょう。まずは光を追いかけるプログラムの作成です。第3回の連載記事にてBeauto Builder NEOで製作したプログラムをC言語で作り直してみました。
光を追いかけるプログラムのフローチャート | 光を追いかけるプログラムをBeauto Builder NEOで製作した場合 |
(led.cの書き換え箇所) /*メイン関数*********************************************/ void main(void) { int date; //変数dateの宣言 //制御周期の設定[単位:Hz 範囲:30.0~] const BYTE MainCycle = 60; //CPIの初期設定 Init((BYTE)MainCycle); //CPUの初期設定 while(1){ //()内の条件が真の間{}内を繰り返す(1は常に真) date=AdRead(0); //変数dateに赤外線センサーの現在の値を代入 if(date<256){ //変数dateが256未満なら{}内を実行 Mtr_Run(-64,64,0,0); //前進(右モーター-64左モーター64) } else{ Mtr_Run(-64,-64,0,0); //左旋回(右モーター-64左モーター-64) } } } (書き換え箇所終わり)
【動画】光を追いかけるプログラムのC言語化 |
前回の記事で紹介しました「Adread()」という関数を使い変数dateに赤外線センサーが取得した値を読み込みます。その値をif文で256より大きいか256未満かを判別します。256未満なら光が赤外線センサーに当たっていて、256より大きいのであれば光が当たっていないということを判別することができます。光が当たっているときの処理は前進(Mtr_Run(-64,64,0,0);)の動作を行ない、それ以外であればelseの左旋回(Mtr_Run(-64,-64,0,0);)を行ないます。
●モーター回転方向変更時過負荷防止の処理を組み込んだ場合
光を追いかけるプログラムに、モーター回転方向変更時過負荷防止の処理を組み込んでみました。
(led.cの書き換え箇所) /*メイン関数*********************************************/ void main(void) { int date; //変数dateの宣言 //制御周期の設定[単位:Hz 範囲:30.0~] const BYTE MainCycle = 60; //CPUの初期設定 Init((BYTE)MainCycle); //CPUの初期設定 //ループ while(1){ //()内の条件が真の間{}内を繰り返す(1は常に真) date=AdRead(0); //変数dateに赤外線センサーの現在の値を代入 if(date<256){ //変数dateが256未満なら{}内を実行 Mtr_Run(0,0,0,0); //モーター回転方向変更時過負荷防止 Wait(200); //0.2秒待つ while(date<256){ //赤外線センサーが取得した値が256未満の間{}内を繰り返す date=AdRead(0); //変数dateに赤外線センサーの現在の値を代入 Mtr_Run(-64,70,0,0); //前進(右モーター-64左モーター64) } } else{ //変数dateが256未満でないなら{}内を実行 Mtr_Run(0,0,0,0); Wait(200); //0.2秒待つ while(date>256){ //赤外線センサーが取得した値が256より大きいなら{}内を繰り返す date=AdRead(0); //変数dateに赤外線センサーの現在の値を代入 Mtr_Run(-64,-70,0,0); //左旋回(右モーター-64左モーター-64) } } } } (書き換え箇所終わり)
このプログラムをフローチャートで表すとこのようになります |
なにやらいきなり複雑になってしまいましたね。しかしやっていることは単純です。if文で光を見つけたときは前進、光を見つけていないときは左旋回の条件分岐を行なっているところは変わりません。しかし前進→左旋回、左旋回→前進などのモーターの回転方向の変更が行なわれた際はかならずMtr_Run(0,0,0,0,)を実行しモーター回転方向変更時過負荷防止の処理を行なうようになっています。また、if文だけではMtr_Run(0,0,0,0,)を何度も実行することになってしまうため、if文の条件分岐の処理の中にwhile文を組み込むことによりこれを防止しています。例えば前進を行なっているときはdateの値が256以上になるまでwhile(date<256)の{}内を実行し続け、dateが256以上になったときだけ、while(date<256)の{}内の処理を終え今度はif文のelseを実行しMtr_Run(0,0,0,0,)を実行してから左旋回の処理を行ないます。
●Beauto Builder NEOで実現できなかった複雑なプログラム
光を追いかけるプログラム(探索方向切り替え機能付き)のフローチャート |
続いて第4回の記事でMONOLAB.の近藤先生におしつけられ……いやいやネタを振っていただいておりました光を追いかけるプログラム(探索方向切り替え機能付き)を作成してみました。
(led.cの書き換え箇所) /*メイン関数***********************************************************/ void main(void) { int date=0; //変数dateの宣言 int i=0; //変数iの宣言 //制御周期の設定[単位:Hz 範囲:30.0~] const BYTE MainCycle = 60; //CPUの初期設定 Init((BYTE)MainCycle); //CPUの初期設定 //ループ while(1){ //()内の条件が真の間{}内を繰り返す(1は常に真) while(i<100){ //i<100の条件が真の間{}内を繰り返す date=AdRead(0); //変数dateに赤外線センサーの現在の値を代入 i=i+1; //iの値を1増やす if(date<256){ //変数dateが256未満なら{}内を実行 Mtr_Run(-64,64,0,0); //前進(右モーター-64左モーター64) Wait(100); //0.1秒待つ } else{ //変数dateが256未満でないなら{}内を実行 Mtr_Run(64,64,0,0); //右旋回(右モーター64左モーター64) Wait(100); //0.1秒待つ } } Mtr_Run(0,0,0,0); //モーター回転方向変更時過負荷防止 Wait(300); //0.3秒待つ i=0; //iの値を0に戻す while(i<100){ //i<100の条件が真の間{}内を繰り返す date=AdRead(0); //変数dateに赤外線センサーの現在の値を代入 i=i+1; //iの値を1増やす if(date<256){ //変数dateが256未満なら{}内を実行 Mtr_Run(-64,64,0,0); //前進(右モーター-64左モーター64) Wait(100); //0.1秒待つ } else{ //変数dateが256未満でないなら{}内を実行 Mtr_Run(-64,-64,0,0); //左旋回(右モーター-64左モーター-64) Wait(100); //0.1秒待つ } } Mtr_Run(0,0,0,0); //モーター回転方向変更時過負荷防止 Wait(300); //0.3秒待つ i=0; //iの値を0に戻す } } (書き換え箇所終わり)
【動画】光を追いかけるプログラム(探索方向切り替え機能付き) |
いやー長いプログラムになってしまいましたね。どこから説明すればよいのやら。while(1)の{}内に2つのwhile(i<100){}が組み込まれてています。つまり2つのwhile(i<100)の{}を100回繰り返す処理を交互に繰り返すことになります。while(i<100)の{}内では前項「光を追いかけるプログラムの作成」のプログラムが組み込まれています。while(i<100)の{}内を実行するたびにiの値を1増やすi=i+1という処理により変数iの値が増加していき、100を超えた時点でwhile(i<100)の{}の処理を終えます。変数iの値を0に戻した後、次のwhile(i<100){}を実行します。while(i<100)の{}内の光を見つけなかったときの処理をそれぞれ右旋回、左旋回2パターン用意しておけば、Beauto Chaserが右旋回、左旋回を交互に行うという仕掛けです。
ちなみにこのプログラムにモーター回転方向変更時過負荷防止の処理を組み込むと「このようなプログラム」になりました。break文も必要になってきますね。
●ライントレースのプログラム(第4回のC言語版)
台車ロボット+C言語プログラムといえばやっぱりライントレース……かどうかはわかりませんが、最後は王道で締めくくりたいと思います。第4回の記事で紹介したBeatuo Builder NEOを使ったライントレースプログラムをC言語で再現してみましょう。
第4回で紹介したライントレースプログラムのフローチャート、今回はC言語でのプログラムをわかりやすく紹介するためにブザーの処理を省きます | 赤外線センサーの数は第4回の記事のときと同じ2個ですが、今回はギヤボックスのギアの組み合わせをDタイプからBタイプに変更し、スピードを上げています |
(led.cの書き換え箇所) /*メイン関数***********************************************************/ void main(void) { int an1date; //変数an1dateの宣言 int an2date; //変数an2dateの宣言 //制御周期の設定[単位:Hz 範囲:30.0~] const BYTE MainCycle = 60; //CPUの初期設定 Init((BYTE)MainCycle); //CPUの初期設定 //ループ while(1){ //()内の条件が真の間{}内を繰り返す(1は常に真) an1date=AdRead(0); //変数an1dateにan1に接続された赤外線センサーの値を代入 an2date=AdRead(1); //変数an2dateにan2に接続された赤外線センサーの値を代入 if(an1date<512){ //変数an1dateっが512未満なら{}内を実行 Mtr_Run(-52,0,0,0); //左カーブ } else if(an2date<512){ //変数an2dateっが512未満なら{}内を実行 Mtr_Run(0,52,0,0); //右カーブ } else{ Mtr_Run(-52,52,0,0); //前進 } if(getSW()==1){ //スイッチが押されたらwhile(1){}のループを抜ける break; //強制的にwhile(1){}のループを抜ける } } Mtr_Run(0,0,0,0); //モーターを止める } (書き換え箇所終わり)
【動画】ライントレースプログラムでの動き |
あえてシンプルにまとめてみました。また、モーターの回転方向が逆になる部分がないためモーター回転方向変更時過負荷防止の処理も必要ありません。黒い床面に設置された白いテープに沿って走るプログラムです。if(an1date<512){}では左の赤外線センサーの値を見ており、値が512未満なら白いテープを検知したことになります。つまりBeauto Chaserが白いラインの右側にずれていることがわかるため、脱線しないように左カーブを行います。else if(an2date<512)では右の赤外線センサーの値を見ており、値が512以下なら白いテープを検知したことになります。つまりBeauto Chaserが白いラインの左側にずれていることがわかるため、脱線しないように右カーブを行います。条件がそれ以外(つまり左右の赤外線センサーが黒い床面を検知している)の場合はelseの{}で前進を行います。また、if(getSW==1)の{}にbreakをつけました。これによりスタートボタンを押すとwhile(1){}のループを強制的に終了させることができます。
●else ifについて
else ifについて説明しておきたいと思います。複数の条件があり、それぞれの処理が必要な場合はelse ifを使います。例えば4つの条件がある場合は下記のように使います。
if(条件1){処理1} //条件1の場合は処理1を実行 else if(条件2){処理2} //条件2の場合は処理2を実行 else if(条件3){処理3} //条件3の場合は処理3を実行 else {処理4} //どれにも該当しない場合は処理4を実行
●break文について
break文についても触れたいと思います。本当は今回作ったライントレースのプログラムにはbreak文を使わなくても実現できるのですが……使いたくて(笑)。break文はwhile文などで{}内を繰り返し実行している際、条件に関係なく強制的に{}内を抜け出したいときに使用します。例えば無限ループを抜け出したい場合は下記のように使います。
While(1){ if(getSW==1){ //スイッチが押されたらifの{}内のbreakが実行され、while文の無限ループを強制的に抜け出す。 break; } }
●最後に
今回でBeauto ChaserのC言語編は終了です。ここまで長かった……。次回はいよいよ最終回です。最後までよろしく!
2009/6/23 14:19