[Robot] 離散値な古典制御、事始め?

ロボットをどういうレベルで製作するのかにもよるが、自分でファームウエアレベルのコードを書くようになると、避けて通れないのが「制御」って奴になる。今は現代制御とか色々あるのだが、実は基本中の基本である古典制御で済むケースが非常に多いので、それに関して思うトコロをつらつらと…

まず、大前提として古典制御…もしくはPID制御というものがなんなのか?についてやが、詳しいお話は大先生が素晴らしいページを置いてくださってるので、そちらへお任せする(おい)。

熊谷講義ノート

このサイトは、単純な制御のお話しだけではなく、ロボットに関する恐ろしく幅広い「理論に関する」情報が集まっているので、少しでもその手のコトで疑問に思うコトがあるなら、読んでみるといいだろう。左側の目次の恐ろしいほどの量が圧倒してきてビビるかもしれないが(笑)。

で、ヲイラはココで何を書くのかというと、上記の内容を踏まえた上で、少し噛み砕いたお話と、それを実装するときの注意点を書いておこうと思う。

●古典制御の簡単な理解方法

古典制御理論って聞くと難しそうなイメージが湧くが、実は仕組みそのものは(なんでそれで安定するのかはさておき(笑))そう難しいものではない。一般にPID制御とも呼ぶが、実はそれが全てなのね(こら)。

まず根本的な問題として、自分が何を制御したいのか(制御対象は何か)をハッキリさせる必要がある。で、そいつに対してどういう措置をとれば制御対象が変化するのかをハッキリさせる必要もある。

制御器(要は自分で実装したマイコンとファーム)は、指示としての入力(目標値)をもらい、制御対象の現在の値(現在値)を見ながら、それに応じて出力(制御値)を出すワケね。

具体的には、例えばモータの回転数を制御したい場合、欲しい回転数が目標値、なんらかの方法で検知された回転数が制御対象の現在値で、それに対してモータにどれだけ電気を流し込むのかが制御値やな。

その前提の上で…やが

  • P制御…現在値と目標値のズレに応じて制御値を決める。
  • I制御…上記のズレの積分値に応じて制御値を決める。
  • D制御…上記のズレの微分値に応じて制御値を決める。

で、この三つの結果を単純に足し算したモンを、実際の制御値として与えるのね。ただこれだけ(^_^;)。実際には、それぞれに一定の係数を掛け算し、その効果の度合いのバランスを決定する。この係数のコトを「ゲイン」って呼んでる。Pゲイン、Iゲイン、Dゲインって感じ。

でも、マイコンでコレを実装するときにそのまま考えると、そんな微分計算とか積分計算なんてショボいマイコンではできない(最近は強引にやってる例もあるとは聞くが)ワケで、少し考え方を変える必要がある。それが次のお話。

ちなみに、オペアンプ等を用いたアナログ回路においては割と簡単に問題なく微積分できるので、過去にはそれで計算して制御していたと聞く。オペアンプがなぜ「オペ」アンプ(「演算」増幅器)なのかというのは、本来ここいら辺に使うからなのね。

●マイコンにおけるPID制御

マイコンの場合、ご存知の通りアナログな連続量を操作するのは恐ろしく不得意…というか、概ね無理。従って、それをサンプリング(AD変換)し、サンプリングデータ(離散値)として数値列に変換し、ここになんらかの計算を用いてPIDを実装するコトになる。その結果をなんらかのDA変換を用い、アナログ値かそれっぽい何かに変換して実際に操作するワケね。

で、変換する間隔をどうするのか?という問題があるのだけど、実はこの間隔は一定でなくても「どんな間隔であったのか」がわかっていれば、それなりに実装は可能や。しかし、ショボいマイコンで実装する場合は、色々簡略化するために「一定の間隔でサンプリングした値」を使うコトが多い。そしてその情報を使い、一定の間隔(必ずしもサンプリングと同期しなければならないとは限らない)で対象を制御する。この後者の間隔のコトを制御周期と呼んでるかな。

で、これも色々簡略化するために、サンプリング自体も制御周期に合わせてやってるコトが多い。つまり

サンプリング → 一定の演算 → 制御値決定

という手順を、一定の時間間隔で延々と演算し続けるコトになる。だから、大抵の場合はタイマー割り込みを使い、必ず一定の間隔で呼び出す関数を記述してその中でやってる。

簡略化の為に一定の間隔で呼ばれるコトを前提にした以上、その間隔は最大限一定である必要があるので、こういった割り込みで呼ぶ。少なくともfor文で時間を測るなんてのはアウトな。

さて、ここまで簡略化した結果、先の微分値や積分値は、以下のような方法で得た数値で代用できる。

  • 微分値…一回前の現在値と今回の現在値との差分
  • 積分値…現在値と目標値の差分を、毎回加算し続けたもの

無論、P制御は現在値と目標値の差なのは前に書いた通り。これなら、ショボいマイコンでも計算できるだろう。グローバル変数とかローカルなスタティック変数をつかえば、一回前の値とか積算した結果を保持するコトなど簡単だよね。ゲイン計算も単純な掛け算やから、今時のマイコンならまず問題はないし。

●実際のところはPD制御で大抵は済む

とまぁ、ここまで一応PID制御ってコトでPID全部についての実装を書いてきたワケだが、実際には実はI制御はあんまり使わない。なぜなら

「積算で積み上げた結果を元に決めるので、その反応が遅く、常にダイナミックに動き続ける対象にはマッチしないコトが多い」

からなのね。とはいえ、完全に無意味ってワケでもないので、何か制御に行き詰まったら検討してみる価値はあると思う。特に「ジワジワ動かしたいんだけど、最後のツメが甘い」ケースなんかには効果的なので。

あと、その積算した値をどこで無効にしてリセットすべきかという問題もあるんやけど…。

●制御の応答性

で、実際にPD制御を組んでモノを動かすワケやが、ここで一つ性能としての注意点がある。それは制御自体の応答性や。なんのコトかというと

「目標値を与えてから現在値が目標値とイコールになるまでの時間(応答時間)」

と言っていいと思う。これが遅かったりすると、例えば複数同時制御とかしてた場合に、それぞれがバラつきやすくなる。その結果は…例えば外乱も特にないのに4輪オムニな車体がまっすぐ進まなかったりするワケな。

応答時間が充分に早ければ、すぐに一致するワケだからバラついて見えないというコトになるわけや。

で、基本的にはいかにさっさと動かすかがメインになるので、PゲインをデカくしP制御の関与を大きくすれば一気に動こうとする。しかし、そのままでは目標値を通り越したりもする。当然戻って来る方に作用しなおすが、これまた目標値を戻りすぎたりする。何度かフラフラして目標値に落ち着くわけやな。

あ、最悪はそもそもどっちかに張り付いてしまう場合もある。一般にそれを発散という。また、常にフラフラするのを発振、なんとか落ち着くのを収束って言ってるかな。

従って、やらねばならないコトは「いかにさっさと収束させるか」というコトになる。ココで効いてくるのがD制御なのね。

D制御は微分値というコトになっているが、これまでの内容から考えれば気づくと思うんだけど、実際には「現在値の速度」に対する制御と思ってもらって構わないと思う。速度って「単位時間辺りの変化量」ってコトなワケで、毎回同じ時間間隔でサンプリングしているというコトは、前回の現在値と今回の現在値の差ってそのまま「現在値の速度」として扱えるワケだ。一定間隔でサンプリングするコトの意味はココを楽にするためなんよね。面倒で時間コストのデカい割り算が不要になるワケだ。

そして、収束時における「現在値の速度」とはゼロのハズやね。つまり、D制御は「出すぎた現在値の速度の抑制」を行うように作用する。だから、D制御のプラスマイナスは重要だ。現在値から一つ前の現在値を引いた値を加算する形でないとマズい。これを逆に実装すると、簡単に発散してしまう。

一つだけ注意点を書いておくと、わざわざ「現在値の速度」って書いてるのは、単純な速度ではないから。例えばこれが位置を制御しているのであれば移動する何かの速度そのものなんやけど、例えばこれが回転数(=速度)だった場合は回転数の変化する速度のコトであり、つまりは加速度のコトになるのね。

計算式は同じでも、扱う内容が違ってくる場合があるので、そういう意味でも「何を制御したいのか」はチャンと把握しておく必要があるワケ。

●D制御に関する落とし穴

で、まぁたぶんココまでの内容で概ねPD制御を記述することは可能になってると思うのだけど、一つだけ大きな落とし穴があるので、それを指摘しておきたい。

P制御だけを考えたとき、その制御値がプラスなのかマイナスなのかは、あんまり意識しない。なぜなら、現在値が目標値のどちら側にいるのかと、その制御値のプラスマイナスは完全に一致するから。足りなければ加算の方向やし、過ぎていれば減算の方向やな。実にわかりやすい。

しかし、その結果として、現在値と目標値の差のプラスマイナスで制御値のプラスマイナスを決めてしまう…という実装をしてしまうコトがある。例えば何かのDIR端子の値をソレで決める…とかね。これはP制御しか存在しないときには正解であるが、後からD制御を実装したりすると、ドツボにハマる。

どういうコトかというと、特に深くD制御が掛かっている場合、P制御的にはまだプラスの領域でも、D制御が大きなマイナスの値になり、全体としてマイナスの制御値になるコトがあるからだ。

現象としてどういう状況かというと、いわゆる逆転ブレーキが掛かったような状態にある。急ブレーキの最たる奴やね。というか、つまりチャンとPD制御を実装し適切なゲイン調整が済めば、逆転ブレーキなんかは自動的に実装される…というコトでもある。

しかし、ここでDIR端子への指示を先のように実装したままにしていると、当然ブレーキが掛かるどころか、加速するコトになる。そりゃ、安定的に収束するハズがないな。

実装する時には、この辺りによく注意するコトをオススメする。

Zak について

基本的にヲタクです。いや、別に萌えとかいうのではなく、ハマるとトコトン進めようとする癖があるので、自制が必要だという…。
カテゴリー: ソフトウエア, なんか作る, マイコン パーマリンク