Arduinoの出力電圧をPID制御する
こんにちは、くろべこです。
今回の記事内容は【PID制御】です。
この記事を見ている皆さんであれば、”すでに知っている”または”聞いたことはある”かと思います。PID制御はあらゆる制御方法の基本です。今回は即席で試せるようにArduinoを使用してお手軽にPID制御のデモをしていこうと思います。
後述しますが、PWMのduty比を制御パラメータとしてとして出力電圧を制御したいと思います。
記事の内容
- PID制御の簡単な説明
- Arduinoを使用したPID制御のデモ方法
- PID制御結果
PID制御とは
PID制御とはある目標値に対して制御量をコントロールする古典的な制御方法です。そして、PIDとは比例(Proportional), 積分(Integral), 微分(differential)の三要素の頭文字からとっており、各要素ごとに働きが異なります。
様々な文献でPID制御の例で頻繁に取り上げられているものとして【車のブレーキ制御】があります。今回はこのブレーキ制御を例にして説明しますね。
下の図のように、壁の前方100メートルから車を走らせたと仮定します。ここでの目標値は【壁から5m前方で停止】です。
最初は目標の停止位置から離れているのでブレーキの踏み込み量は0%で走っていますが、ある程度かべに近づいてきたらブレーキを踏み込み減速を行いますね。そして、壁に近づくにつれブレーキの踏み込み量は大きくなり、最終的に壁の5m前方で完全に速度は0となり停止します。
この例では目標値に対してブレーキを制御量としてコントロールを行っています。ブレーキの踏み込み量をPID制御の式で表すと以下のようになります。
■比例項
比例制制御(P)はとても単純です。偏差に対して比例ゲインをかけているだけです。大雑把な制御だったら比例制御でも問題ないのですが、比例制御の欠点として残留偏差が残ってしまい目標値に対して完全には一致した制御を行うことは不可能です。例として目標値を4.5としたある比例制御のグラフを載せます。4.5ピッタリとはならず4.3くらいで安定していますね。この目標値からのずれの0.2を残留偏差と呼びます。この残留偏差をなくすには後述する誤差の蓄積を制御する積分制御が必要となります。
■微分項
微分制御(d)はある区間での偏差の傾きを基に制御量を決めています。瞬間的な応答制御(立ち上がり時)が必要な時は必要ですね。しかし、微分制御はゲインの決め方が難しく制御ゲインの設定を間違えると発振してしまうことがあるので注意です。
■積分項
積分制御(I)は誤差の積分を基に制御量を決めます。なので、比例制御だけでは残留偏差により一定の偏差が残ってしまいますが、積分項を考慮することで残留偏差をなくすことが出来ます。
ArduinoでPID制御をする
今回は任意の目標電圧に対してPWMのduty量(0~255)を制御していきます。まずは、D10とA0をジャンパー線でつないでください。1uF程度のコンデンサがある方はD10-GND間に取り付けましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
const float Target=4.5;//目標電圧 const float Kp=20;//比例ゲイン const float Ki=0.5;//積分ゲイン const float Kd=0.5;//微分ゲイン float x;//読み取り値 float dt; float pretime;//過去時間 float P, I, D, preP; char buf[100]; uint8_t duty; uint8_t a; uint8_t b; void setup() { Serial.begin(115200); pinMode(10, OUTPUT); } void loop() { analogWrite(10, duty); for(int i=0;i<1000;i++){ x+=analogRead(A0); } x=5*x/1000/1023; //制御量計算 dt =(micros() - pretime)/1000000; pretime = micros(); P=Target-x; I+=P*dt; D=(P-preP)/dt; preP=P; duty+= Kp*P+Kd*D+Ki*I; //シリアルモニタ a=(int)x;//整数桁 b=(int)(x*100)%100;//少数桁 sprintf(buf,"\n***結果***\n読み取り電圧:%d.%dV\n",a,b); Serial.print(buf); a=(int)P; b=(int)(P*100)%100; sprintf(buf,"目標値からの差分電圧:%d.%dV\n",a,b); Serial.print(buf); sprintf(buf,"制御量(duty):%d\n",duty); Serial.print(buf); Serial.println(x); } |
PID制御結果
では、プログラムを書き込んでみましょう。シリアルプロッタを使用すれば制御電圧の推移を確認することが出来ます。
ちなみに各ゲイン(Kp, Ki, Kd)についてはトライアンドエラーで決め打ちです。PID制御はこのゲイン調整が一番難しいかもしれません。
結果☟
上から【P制御】【PD制御】【PI制御】【PID制御】です。
P制御では安定した立ち上がりですが、応答性が悪く残留偏差があります。
PD制御では応答性が上がりましたがやや発振気味になっていますね。そして、依然として残留偏差があります。
PI制御はどうでしょうか。比例ゲインを5で統一して比較したかったのですが積分項が加わることで発散してしまうために比例ゲインを15に上げています。グラフからわかる様に残留蓄積が改善されており、ほぼ目標値に制御できました。
最後にPID制御です。応答性を上げて残留偏差をなくすようにトライアンドエラーでゲイン調整を行いました。応答性を上げることでオーバーシュートが発生していますが他の制御に比べて早い立ち上がりが見込めます。
以上をがArduinoを使用したPID制御のデモでした。
このPID制御は【温度管理】【モーターの位置制御】【流量管理】など制御をする部分にはほぼ全部といっていいほど使われている技術です。私個人的な意見ですが、応答性を求めなければPI制御が一番安定して運用できる気がしました。
では、今回はこれで失礼します。
ばいばい!
コメント