はじめに
こんにちは、河野です。
前回はPi:Co Classic3をDCモータ化した際の回路とメカの変更点を書きました。
今回はソフトウェアの変更点についてまとめます。
ソフトウェアの変更点
マイコンは変わらずRX631を使用しているため、Pi:Coのサンプルコードを流用しています。
変更した箇所は、ピン割り当てとモータ関係です。それ以外のセンサ値の取得や迷路探索などは、Pi:Coそのままのコードで動かせました。
ピン割り当ての変更
Pi:Coのサンプルコードでは、以下のようにポートと機能をdefineで紐付けています。
#define LED0 (PORTB.PODR.BIT.B0) #define LED1 (PORTA.PODR.BIT.B6) #define LED2 (PORTA.PODR.BIT.B4) #define LED3 (PORTA.PODR.BIT.B0)
I/Oポートの割り当てを変更した場合は、ここを変えて上げるだけで良いです。しかし、今回はPWMなどタイマーを用いる機能のポートも変更されているため、タイマー設定部分も見直す必要がありました。
ポートをそのまま記述していると、何をしているのか読み取りづらく変更も大変になるので、今後マイコンのソフトを開発する際にはこのやり方を参考にしたいなと思います。
モータエンコーダ
これはPi:Coには無い機能なので新しく一から追加する必要があります。
エンコーダからは、モータ軸がどれだけ回ったかという情報を得ることができ、ギヤ比とタイヤ径を掛けることで機体の速度を求めることができます。この情報は後述するモータ制御の際に用います。
エンコーダからはA相、B相と呼ばれる2つの出力があり、この出力の立ち上がり/立ち下がりを見ることで回転方向とカウンタ値を得ることができます。モータによって1周のカウンタ値が決まっているため、このカウント値からモータ軸の回転角度を求めることができます。このあたりの詳しい話は槇原さんの記事で紹介されています。
モータ制御
Pi:Coのサンプルコードでは、大きく分けて以下の2つの処理でモータを制御しています。
- 目標速度と加速度を設定し進みたい距離になるまでスリープする処理
- 1.で設定された値からモータへの指令値に変換しモータへ与える処理
1.の処理は関数化されており、main処理や迷路探索処理などより上位の処理から利用される形になっています。
straight(distance,init_speed,max_speed,target_speed)
このような形で進みたい距離と速度が与えられ、目標距離に到達するまでの時間を計算し、その時間分だけスリープするという処理です。台形加減速による速度と加速度の変化の計算なども行っています。前述したように、ここでは速度と加速度を設定するだけで、実際にモータへの動作指令は出していません。
2.の処理は1[msec]のタイマ割込みで呼ばれます。なので、1.の処理で設定された値を常に監視しているようなイメージです。この処理で実際にモータへの動作指令を出しています。
ということで、初期化を除けばこの2つの処理を、ステッピングモータ用からDCモータ用の処理に変えてあげれば良いです。
1.の処理の変更量は少ないですが、2.の処理は大きく変更する必要があります。ステッピングモータのときは、速度から変換された目標値を与えるだけでおおよそ目標速度となるように動いてくれましたが、DCモータの場合はその動かし方では誤差が発生します。そこで、誤差を小さくするためにPID制御を用います。
以下の計算式で指令値を求めます。今回、Dは使わずにPIのみで制御しました。
指令値(duty比) = P・(目標速度 - 実速度) + I・(目標速度合計 - 実速度合計)
実速度はエンコーダからの値を使います。これを、直進と回転の2つについて求め、指令値としてモータに与えてあげることで目標速度となるように動かすことができます。
おわりに
大まかにですが、Pi:CoをDCモータに改造した際のソフトウェアの変更点をまとめました。
エンコーダの読み方やPID制御を用いたDCモータの制御について学びました。どちらも概念は知っていたのですが、実際にデータシートとにらめっこしながらコードを書いてみて、知識の解像度がとても上がりました。
また、アルゴリズムなどの上位の処理とハードウェア寄りの下位の処理を分ける書き方に触れられたことも良い経験になりました。組み込みシステムのソフトウェアはハードウェアに依存するイメージが強かったのですが、ハードウェアに依存する部分を上手く分けてあげれば、変更や流用に強いコードを書けそうだなと学ぶことができました。