マイコンによるロボット制御::PID

大層な名前が付いていますが,そんなに高度なことは書きません. マイコンでロボットにライントレースをさせたり,物を動かしたりといったときに, どうすれば安定して動作するかといった話です. 計算が楽そうなPIDに絞って説明します.

そもそもPIDってのは?

制御の理論にPID(ピーアイディー)というものがあります. PIDという名前自体は「Proportional-Integral-Differential(Derivative)」の頭文字で, 「比例」「積分」「微分」です.

なぜこんなものが必要かといえば…Google(注1)で検索してください. と,言ってしまうと終わってしまうので,雰囲気をつかんでもらうため,簡単に説明します. そもそも,わたしは「ソフトウェア工学」とかいう全く専門外のことをやってる人間 ですので,この辺の知識は怪しいです(ちゃんとした本を読んだこともありませんし…). 正確な知識は,大学の授業か専門書から得るのが妥当だと思います.

制御のしかた

ラインの上を辿るライントレースに限らず,ロボットを動かすときは, 本来の経路から外れた場合に元の経路に戻すという操作が必要になります. ラインから外れる原因はいろいろと考えられますが, モータの特性によるものかもしれませんし,誰かがロボットを横から押したのかもしれませんし, ラインが曲っていたのかもしれません.

ロボットが何らかの原因で本来の経路から右に反れた場合を考えます. この場合,右のモータの出力を上げるか,左のモータの出力を下げて,左に曲ることになります. 逆の場合も同様です

P制御

ロボットが本来の経路から外れた場合,出来る限り早く本来の経路に戻る必用があります. さもないと障害物に衝突するかもしれませんし,崖下に転落するかもしれません. まぁ,崖から落ちるかどうかは知りませんがそういう設定とします. あと,なめらかに動いた方が効率がよさそうです.

すぐに思いつくのは,誤差が大きいときは,大きな力で元の位置に戻そうとし, 誤差が小さいときは,小さい力で戻るという方法です. ここでいう誤差というのは,「目標とする位置」と「現在の位置」の差のことです. 「目標とする位置」と「現在の位置」の差に定数をかけたものを, 左右のモータの出力の差とすれば実現できます.

これが比例(P)制御です.

一見,うまく行きそうですがこれで実際に動かしてみると,一度誤差が出ると振動がなかなか 収まらなかったり,最悪の場合発散してしまったりするので上手く動かないことが多いです. モータで動くものを作った経験がある方なら,その解決に苦労した人もいると思います. 右にずれると,左に曲がってもとの位置にもどりますが,するとそのまま行き過ぎて, 今度は左にずれて,右に戻そうとして…という繰り返しが発生しないようにする必要があります.

それならPD

そこで登場するのが微分項(D)です.

誤差を微分した成分を加算することで,上の問題は呆気なく解決します.

こんどは試しに左にロボットがずれる場合にDの項がロボットに与える影響を考えます. 左にずれつつあるときは,右に曲るように作用して,本来の経路に近づくときは右方向動いている ことになるので今度は,P成分の働きを抑えて左に曲るように作用します.

このPD制御だけでも,かなり安定したライントレースができるようになります.

P制御とPD制御

ライントレースの動作を比較すると,だいたいこんな感じになります. PD制御は一旦ラインから外れても,すぐにライン上で安定します.

Iが必用とされるとき

ロボットに常に一定の力が加わっている時や,長いカーブを曲っているときは, PD制御だけでは,外から加わる力と戻そうとする力が釣り合った地点に収束してしまいます. 左右のモータで微妙に個体差があったりするときもですね. 問題にならないかもしれませんが,本来の経路とは少しずれた部分を走り続けることになります.

そこで,誤差を積分していって,最終的に本来の位置に持っていくことができます. 右に少し寄ったところを走っているなら,時間とともに積分された値が増えていって, 左に少しずらすことになります.

デジタルな処理系での実現方法

PIDをマイコンで実現しようと思うと,細かい部分ですが厄介な問題にぶつかると思います. まず,普通のマイコンというのは8ビットとかのコンピュータなので, データを離散的な数値として扱う必用があります. floatを使えばほぼ解決ですが,コードサイズやメモリ使用量がどんどん大きくなるのでお勧めはしません (最近のマイコンは計算自体は高速なのでメモリさえ気にすればなんとかなると思いますが…). まぁ,これは私がこの文章を書いている2006年の話です. でも,クロックと違って,メモリをケチればそれだけコストが下がるのは事実なので, 低価格なマイコンを選ぶと状況はあまり変わらないかもしれません. そもそも,最近のSoCとかFPGAとかいう技術を見ていると素人が扱えるマイコンが どれだけ生き残っていられるかも心配です.

まぁ,話を戻しましょう. 離散的であるという問題を解決する必要があります. マイコンの場合,離散的なのは入出力だけでなく,時間も含まれます. また,ライントレースに使うセンサーが少ない場合も入力の 状態の数がかなり限られて極端な階段状になることもあります.

images/pid_fig1.png

P成分はロボットがその位置にいれば,その値が取得できるので, あまり問題になることはありません.

D成分は,時間との関係があるので少し真面目に考える必要があります. 比較的軽いロボットならば,前回調べた値との差分を微分値としても動きますが, 常にそれで解決とはいかない…ような気がします. 特にロボットが重くて時定数が大きい場合,モータのパワーが動作に影響するまで かなりの時間を要します. しかし,階段状の入力を微分してもインパルスが現れるだけなので, そのままモータ出力に適用しても上手く行かない場合が多いです.

images/pid_fig2.png

制御の周期を落として,ロボットに合わせるか,十分滑らかになるようにセンサーを 増やすという方法も一つですが, 他の処理との関係もあるので,ここでは別の方法でいきます.

ここでは,D成分が一瞬しか出てこないのが問題なので,一定時間残すことにします. 変化があってから,一定期間を測るために変数を用意したり, D成分が0になるまえに連続して変化があった場合の処理を書くのも馬鹿らしいので, もう少し真っ当な方法でやります. 変化があるたびに,D成分に加算していって, 定期的に,1以下の数を掛ければ時間とともにどんどん小さくなっていきます.

images/pid_fig3.png

この動作は電気で出てくる微分回路に良く似ています. また,出来る限りループの処理時間を一定に保った方が計算が楽なのは言うまでもありません.

PIDのIは積分値で,一度大きくなると,後々まで影響がでるパラメータなので, 値が一定以上にならないように気をつけたりしないと,思ってもいない動作を する原因になります. 本来は,スタート時からのデータをひたすら積分する(足していく)必要がありますが. 一定期間のデータから求めるというもので良いと思います. あと,真面目に計算するよりも,プログラムの利点を生かして,色々な値を 放り込んでしまってもいいかもしれません. 例えば,ロボットの重さと回転半径が分かるなら,Iに初期値を与えてしまって, 早く安定するように調整するという手もあります.

終わりに

まだ校正中です.そのうちグラフや図を書きます.

この文書の履歴

Copyright © 瓶詰堂 all rights reserved.