-
Notifications
You must be signed in to change notification settings - Fork 3
24_PD制御
今回は,前回のPI制御に引き続き,PID制御までの説明を試みます。 試みましたが駄目でした。PD制御までです。
前回,P制御の問題点として"定常偏差"というものを取り挙げました。
これは,外乱等の要因により,最終的な収束値が,目標値とずれることを指します。
定常偏差は,I制御の導入により無くすことができます。
しかしながら,P制御もしくはI制御は,ゲインを大きくしていくと軌道がぶれぶれ(振動的)になります。
やりすぎると,ラインから外れてしまいます。制御では発散と呼びます。
一旦おさらいがてらグラフを描いてみましょうか。
ひとまずP制御だけにしましょう。前回のソースコードで,Ki = 0としてください。
一応,controller.mのコードを以下に示します。
persistent tr_sensor sum_e
Kp = 0.01;
Ki = 0.0; % 積分ゲインを0に
r = 0;
if isempty(tr_sensor)
tr_sensor = -7:1:7;
end
if isempty(sum_e)
sum_e = 0;
end
z = 0;
Nz = ones(1,15)*( value_light_sensor(:,1)<50 );
if Nz > 0
z = ( tr_sensor*( value_light_sensor(:,1) < 50 ) ) / Nz;
end
e = r-z;
sum_e = sum_e + e*delta_t;
u = [1;-1] .* ( Kp * e + Ki * sum_e ) + [0.3; 0.3];実行してみましょう。グラフはこんな感じになりますか?
ラインを一度検知した後も,何度か大きくラインをはみ出しています。
この赤で囲ったはみ出し部分を,制御の用語でオーバーシュートと呼びます。
オーバーシュートが大きかったり,振動的である期間が長いと,例えば通路が狭いときに衝突する危険性があります。
これの補正を試みるのが,D制御です。
D制御(微分制御,Derivative Control)は,その名の通り,偏差を微分します。
あとは今まで通り,定数倍して入力に加えるだけです。
P制御と合わせて数式で見てみると
少し雑ですが離散化して,
大体こんな感じになります。
それでは実装してみましょう。
コードはこんな感じです。Ki = 0として積分項は止めてあります。(自分で実装したい人はそれでもいいですよ)
persistent tr_sensor sum_e pre_e
Kp = 0.01;
% Ki = 0.02;
Ki = 0.0; % 積分ゲインを0に
Kd = 0.0001;
r = 0;
if isempty(tr_sensor)
tr_sensor = -7:1:7;
end
if isempty(sum_e)
sum_e = 0;
pre_e = 0;
end
z = 0;
Nz = ones(1,15)*( value_light_sensor(:,1)<50 );
if Nz > 0
z = ( tr_sensor*( value_light_sensor(:,1) < 50 ) ) / Nz;
end
e = r-z;
sum_e = sum_e + e*delta_t;
u = [1;-1] .* ( Kp * e + Ki * sum_e + Kd * (e - pre_e)/delta_t ) + [0.3; 0.3];
pre_e = e;まず,
persistent tr_sensor sum_e pre_eとして,前回の偏差eの値を記録するために,pre_eという永続変数を作成します。
続いて,
Kd = 0.0001;微分ゲインKdを設定します。
なんでPI制御の時と同様にKpと同じ0.01くらいから試さないんですか?オーダー2つも違いますよ?
と疑問に思った方,その通りです。
これは私の勘が0.01という値に対し警告を発しているためです。
オーダーいじるとどうなるかは後で見てみましょう。一度,Kd = 0.0001でお許しください。
説明を続けます。
if isempty(sum_e)
sum_e = 0;
pre_e = 0;
endの部分でpre_eを初期化します。sum_eが初期化されてなければ、pre_eも初期化されていないので,同じ場所で初期化を行います。
ちなみに,今回,pre_eは初期値を0として扱いますが,pre_eの初期化には十分注意してください。
安直に0にすると,初回時に値が微分項が吹っ飛ぶ可能性があります。
本来,k = 0のとき,e(k-1)が存在しないことが原因です。
安全性を考えるならば,初回時のみKd = 0とするなどして,微分項が作用しないようにした方が良いでしょう。
ここでは,コードのシンプルさを要求すること,初回が0(ライン未検出)としてもそこまで被害は出ないと考えたため,このまま行きます。
u = [1;-1] .* ( Kp * e + Ki * sum_e + Kd * (e - pre_e)/delta_t ) + [0.3; 0.3];で制御を行いますが,微分の項が追加されています。delta_tで偏差の差を割り算します。
積分項も残りっぱなしですが,Ki = 0としているので寄与しません
最後に,
pre_e = e;として,今回の偏差を記録しておきます。
お疲れ様でした。実行してグラフを見てみましょう。
どうですか?オーバーシュートが軽減したことがわかりますか?
横並びで比較したのが次の図です。
左がP制御,右がPD制御です。
P制御の時は510を超えていた山が,PD制御では若干減少しているのがわかりますね。
これが微分項の威力です。
とはいえ,もう少しオーバーシュートを抑えたいですよね?
お待ちかね,チューニングのお時間です。
慣例に従い,オーダーを一つ上げてKd = 0.001として,実行してみましょう
はい素晴らしい!
オーバーシュートがなくなりました!!
大分綺麗に追従するようになりましたね。
ですが,一つ困ったことが生じているのに気が付きますか?
そう,追従が遅くなっているのです。
改めて,Kd = 0.000とKd = 0.001でのグラフを比較してみましょう
このように,最終的な収束値505付近に到達するのは,Dゲインが小さいほうが速いです。
結局微分項は応答をなまらせているため,このような状況になります。
もう一点,D制御には問題点があります。
ここでようやく,Kp = 0.01, Kd = 0.01の同じ値を試してみましょう。
皆さんもやってみてください。
滅茶苦茶になりましたか?
コースアウトすることもあるかもしれませんが,下に示すのはかろうじてゴールできた場合です。
振動を抑えるはずのD制御が,制御を不安定にしてしまっています。
これは,微分項が誤差を増幅してしまう性質があることによります。
そのため,計測誤差やら量子化誤差(測定値の離散化の際に生じる誤差)等によりロボットが大きく振り回されてしまうのです。
以上,お分かり頂けましたか?
D制御はオーバーシュートを軽減し,応答曲線を滑らかにしてくれる一方で,追従の遅れや誤差の増幅につながります。
結局,適切なチューニングが非常に重要ということになります。
さて,いよいよ,P,I,Dそれぞれの良さを組み合わせた,PID制御に入ります。
- 東京工業大学ロボット技術研究会 ロ技研の公式ページです
- Twitter: @titech_ssr ロ技研のTwitterアカウントです
- Twitter: @ssr_welcome ロ技研の新歓Twiterアカウントです
- 東京工業大学 ロボット技術研究会公式ブログ ロ技研の活動を記録しているブログです