暫くBlockly for Moscoviumにうつつを抜かしていて😍、放置していたロボットカー(下図)を拡張してみます。
今回は、ロボットカーに「数値制御」を適応してみたいと思います。
これまでに作成して来たロボットカーは、主に、スマホの画面から操縦する、ラジオコントロールカー的な物でした(下図)。
なので、「直進させよう」と思って走らせて、「少し右に逸れてるな」と思ったら、ジョイスティックコントロールを左寄りに動かしたら、直進させることができました。
DCモータはPWMによって、供給する電力量を制御しています。
PWMのデューティは0〜255の解像度ですが、左右に同じデューティ255で電力を供給しても、実は、真っすぐ進むとは限りません。
等(↑)によって、同じ電力を供給したつもりでも、左右のDCモータに、ばらつきを生じさせてしまうからです。
また、二輪駆動ロボットカーは、その場で旋回させることもできます。左右のタイヤ(DCモータ)を互いに逆向きに回転させれば良いのです。スマホのリモコン画面で、ジョイスティックコントロールを操作することで、その場で旋回させることもできます。
しかし……、しかし、です。できないのです。
ピッタリ20cm直進させたり、ピッタリ90°回転、させることが……。
インターネット経由の遠隔操縦では、シリアルモニタコマンドの送信タイミングにタイムラグがあるし、モータの回転やロボットカーの走行には慣性力も働くので、止めたいと思ったその瞬間に止めることができず、オーバランする事などに起因します。
勿論、目視で「よっし、20cm動いたぞ止めよう」とか、「90°回転したのでストップ」みたいに、距離や角度を正確に読み取る事が困難なことも一因です。
今回のテーマである「数値制御」を実現するためには、「今、どのくらいの距離を動いたのか」を正確に把握する必要があるのです。
「20cm直進」とか、「90°右旋回」とか、数値指定した距離や角度で、ロボットカーを動かすことを数値制御(NC;Numerical Control)と言います。
導入してから、だいぶ時間が空いてしまいましたが、以前ロボットカーに組み込んだタコメータを以下記事にて復習しておいてください。
「#30-3 ロボットカーを組み立てよう(速度計測)」
タコメータが、数値制御の実行に当たって、とても重要な役割を果たすから、です。
タコメータとは簡単に言うと、DCモータが回転した時に「何回転したのか?」、回転数を知ることのできるセンサです。実際には、回転数をカウントするスロットディスク(下図)に20個のスロットが開いていて、センサ出力が×2倍の40回トグルするため、1回転を40倍の解像度で計測することができます。
つまり、最小1/40回転の単位で回転数を知ることができるのです。
const int ncm_CNTPR=40; // ncm: counts per revolution
40カウントでDCモータが1回転するので、上の定数ncm_CNTPR[カウント/回転]で換算して行きます。
タコメータによって、DCモータの回転数を測定出来たら、次は、DCモータに接続されたタイヤなど、ロボットカーの物理的大きさを知ることが、数値制御の第二段階となります。
ロボットカーのタイヤの大きさ(直径、下図左側のD)を定規で測定してみました。
手元のロボットカーでは、D=6cmでした。
このタイヤが、DCモータの回転軸に直接取り付けられているので、DCモータが1回転するとタイヤも1回転します。タイヤの円周長は直径×円周率=D×π[cm]ですので、タイヤが1回転すると、約18.8cm進む、と考えることができます。
勿論、タイヤがスリップしてしまえば、この前提は狂ってしまいます。手元のロボットカーはゴムタイヤなので、しっかりグリップして、タイヤはスリップしないという前提で進めます。
問題: 「30cm直進させたい場合、タイヤを何回転させれば良いでしょうか?」
簡単な算数の設問ですね?
D=6cm、L=30cmを代入すると、約1.59回転です。
プログラムでは、Dを以下の定数に設定しておきます。
const int ncm_WDIAM=60; // ncm: wheel diameter [mm]
単位は[mm]です。各自の環境に合わせて調整してください。
ちなみに、タコメータ1カウント分の進行距離は以下の通りです。
タコメータ1カウントで進む距離: D×π÷ncm_CNTPR≒4.7[mm]
これがタコメータのセンサ感度なので、これ以上短い距離を制御する事はできません。まあ、この程度の解像度で制御できたら、十分でしょう。
同様に、ロボットカーのタイヤ間距離(下図右側のW)を定規で測定してみました。
手元のロボットカーではW=10cmでした。
このパラメータは、ロボットカーを旋回させたり、左右に曲がったりする時の制御に必要です。左右のタイヤを互いに逆向きに回転させ、旋回させてみましょう。
問題: 「各々のタイヤを何回転させると、240°旋回できるでしょうか?」
こちらも、簡単な算数の設問ですね?
D=6cm、W=10cm、θ=240°を代入すると、約1.11回転です。
プログラムでは、Wを以下の定数に設定しておきます。
const int ncm_WDIST=100; // ncm: wheel distance [mm]
単位は[mm]です。各自の環境に合わせて調整してください。
ちなみに、タコメータ1カウント分の旋回角度は、以下の通りです。
タコメータ1カウントで旋回する角度: 360°÷W×D÷ncm_CNTPR=5.4°
こちらも、この程度の解像度で制御できたら、十分でしょう。
前節までに、直進と、その場旋回を例として説明しました。他にも、以下の様な走行を数値制御で実現することができます。
例)数値制御走行の例
【 】内は、数値制御走行用として追加するシリアルモニタコマンド(詳細は次回)の一例です。
前節の解説をご覧になって、「意外と簡単じゃね?」と思った、そこのあ・な・た。実際にプログラムしようとすると、そう簡単ではありません。コーディング開始する前に、しっかりと検討しておきましょう。
例えば、「20cm直進」させようと思ったら、左右のタイヤを20cm÷(D×π)[回転]させれば良いことは解りました。
ですが、時系列で考えて、例えば以下の様な状態は、どうでしょうか?
この場合、各々のタイヤが20cmづつ走行したのは確かですが、「直進」は、していません。ですよね?
つまり、タコメータを使って回転数を知ることはでき、それによって走行した距離も知ることができるのですが、時系列を合わせて回転させないと「直進」させることができないのです。
上例の様な極端な「ダメな例」には、ならないとしても、最初に指摘した通り、個体差や負荷の差など、様々な要因によって、同じデューティで同時に電力を供給しても、同時に、同じだけ回転してくれる保証は有りません。
直進させようと思ったら、左右のタイヤを同時に同じ速度で回転させなくては、なりません。プログラムには、時系列を合わせながらDCモータの回転を制御することが求められます。
DCモータの回転速度制御には、一般的にPWMが用いられます。PWMのデューティによって、DCモータに供給する電力の平均値を制御することで、回転速度を制御します。
タコメータで計測した左右タイヤの回転数を時系列で比較しながら、同じ速度で回転する様に左右DCモータへ供給するPWMデューティ値(従って平均電力量)を制御して行きます。
DCモータは停止(ストール)した状態から回転開始する時に一番大きな電力を必要とします。ストールした状態なので、まず、静止摩擦力に抗して回転開始するだけの電力が必要なためと考えることができます。モータの仕様表などでは、「ストール電流」として規定されています。
供給する電力が小さいと、ストール状態から回転開始することができません。しかし、一旦回転が始まると、その回転を維持するのは動摩擦力に抗する電力があれば良いので、回転開始するのに必要だった電力より少し小さくしても回転を維持することができます。
供給電力量はPWMのデューティで制御するのですが、この辺の制御が難しいです。
回転開始可能な最小のデューティ値がいくらか?と言う事は、DCモータの特性次第であり、個体差もあります。電源(充電池など)の電圧や電流供給能力にも関係してきます。最小デューティ値を余りギリギリまで攻めると、充電池が経たってきた時に回転開始しなくなったります。
ある程度余裕をもって、デューティの下限値(mtr_MIN_DUTY)を決めておく必要があります。
const int mtr_MIN_DUTY=128; // mtr: minimum duty (>>0)
各自の環境で予備実験して、調整して見てください。
予備実験サンプル:【 】内はシリアルモニタからの送信データ
【stp】
【mtr1,128】
…… 回転開始したか?
一旦モータの回転を停止(【stp】コマンドなど)して、改めて電力を供給(【mtr1】コマンドなど)した時に、回転を「開始」できるかどうか、試して行きます。【mtr1】コマンドの引数がDCモータへ供給するデューティ値なので、この値を上げ/下げしながら、確実に回転開始できる下限値(mtr_MIN_DUTY)を決定してください。
前節で、最小のデューティ値mtr_MIN_DUTYを、ストール状態から確実に回転開始できる値として決定しました。
ひっくり返すと、PWMのデューティ制御では、mtr_MIN_DUTY未満のデューティに設定することができません。時系列で左右モータの足並みを揃えながら制御しなければならないので、例えば以下の様なケースに直面する事になります。
例)左右アンバランスな距離
一般的なPWMデューティ制御だけでは、この様なアンバランスな数値制御を実現できないという事が判りました。
そこで、間欠的PWM(Intermittent PWM)制御という考えを取り込んで見たいと思います。
上図で、dutyは通常PWMのデューティ、duty’は間欠的PWMのデューティです。
下限値mtr_MIN_DUTYに制限される通常PWMのデューティdutyに対して、間欠的PWMのデューティduty’を算入することで、下限値に制限されない総合デューティDuty=duty×duty’(=0〜255、制限なし)を適応可能とします。
Dutyから、dutyおよびduty’への換算(分解)方法:
「間欠的PWM」等と、カッコ良い言葉を使って見たのですが、単なるON/OFF制御と見ることもできます(1^^;)。余り、この機構が機能し過ぎる様だと、恐らく、ギクシャクとした走行となって、美しくないはずです……。
従って、基本的には、通常のPWMデューティ制御によって、モータの回転を制御できることが期待値です。間欠的PWM制御は、左右モータにアンバランスな走行距離を指定された場合の対策用(苦肉の策)です。
数値制御の「肝」に関して考察・解説が終わりました。次回、プログラムを仕上げて実験です。