※以降、パラメータの最適化≒学習と表記している場合あり
PID制御とは
を組み合わせたもの。
: 比例制御項
: 積分制御項
: 微分制御項
: 操作量(指示量)
: 偏差(誤差)
: 比例制御の比例定数 ← 今回はこれを演算によって求める
: 積分制御の比例定数 ← 今回はこれを演算によって求める
: 微分制御の比例定数 ← 今回はこれを演算によって求める
: 積分時間
: 微分時間
: 時間
偏差 e は目標値 − 計測値。比例だけでは目標に届かず、積分がその残りを消し、微分が行き過ぎを抑える、と役割で分けて覚えるとよい。
一方的に温度の変化量を観測した場合。
時刻tの温度 = 時刻tにおける温度変化
::: blockdiag
blockdiag {
// Set labels to nodes.
A [label = "釜"];
B [label = "温度計"];
C [label = "PC"];
D [label = "Graph(CSV)"];
// Set labels to edges. (short text only)
A -> B [label = ""];
B -> C [label = "Temp"];
C -> D [label = "結果"];
}
:::
現象=観測結果なのでそのまま読み取ればよい。
時刻[t-1]のFBによる温度制御により得られた時刻tの温度 = 時刻tにおける温度変化
::: blockdiag
blockdiag {
// Set labels to nodes.
A [label = "釜"];
B [label = "温度計"];
C [label = "PC"];
D [label = "Graph(CSV)"];
// Set labels to edges. (short text only)
A -> B [label = ""];
B -> C [label = "Temp"];
C -> A [label = "補正量"];
C -> D [label = "結果"];
}
:::
現在の観測結果には以前の観測結果によるフィードバックが含まれているため、実際の温度変化量の特徴およびアルゴリズムそのものが現在の観測結果に大きな影響を及ぼす。
静的なデータ(温度変化の結果)を用いてもPID制御には使えない。
PID制御を学習させるには
このループが成立する必要がある。
from control import matlab
import numpy as np
from scipy.optimize import minimize, rosen_hess
この部分が上記「今回の検討におけるハードル」を解決するために使用しているシミュレーター(観測結果ジェネレータ)
逆に考えれば、この評価関数の出力を実機に近づけることでプログラムだけである程度の評価はできるが、疑似データを放り込んでも疑似データに最適化されるだけなので役に立たない。
(簡単に疑似データが作れるような制御であればPID制御すら不要の可能性もある)
def T(x):
"""
Loss Function(評価関数)
parameters
---------------------------------
x[0](=K);proprotional gain
x[1](=Ti);integral time
x[2](=Td);Derivarive time
return
---------------------------------
valuation of Loss Function
"""
"""process model"""
Kt = 10 #Steady-State gain
J = 25 #Time Constant
Cm = 4 #Dead time
num = [Kt] #分子の係数
dem = [J,1] #分母の係数
P = matlab.tf(num,dem)
num2, dem2 = matlab.pade(Cm, 6)
Pade = matlab.tf(num2,dem2)
Process = P * Pade
"""PID controller"""
K = x[0] #Proprotional Gain
Ti = x[1]#Integral Time
Td = x[2] #Derivarive Time
#伝達関数用の係数に変換
kp = K
ki = kp/Ti
kd = Td * kp
num = [kd, kp, ki]
den = [1, 0]
C = matlab.tf(num, den) //伝達関数モデル
"""Feedback Loop"""
sys = matlab.feedback(Process*C,1,-1) //1時刻前の結果を反映
"""Step Response"""
time= 100 #step応答を算出する時間[秒]
t = np.linspace(0,time,1000) #時間を指定する。
y1,T = matlab.step(sys,t)
return sum(T*(y1*1000 - 1000)**2)
**評価関数の中身(伝達関数モデルの読み方)**
- 制御対象 Process は 1次遅れ(時定数 J=25、定常ゲイン Kt=10)に、むだ時間 Cm=4 をパデ近似で足したもの。むだ時間はそのままだと有理関数にならないので、exp(-4s) を分数式で近似している(matlab.pade(Cm, 6))。
- 制御器 C は PID の伝達関数 Kd·s + Kp + Ki/s。ここで Kp=K、Ki=Kp/Ti、Kd=Td·Kp と、上の式の係数に直している。
- feedback(Process*C, 1, -1) で閉ループを作る。第2引数の 1 は帰還路のゲイン、第3引数の -1 は負帰還(目標 − 計測)の意味だ。
- step 応答を 100 秒ぶん(1000 点)計算し、目標値 1 からの二乗誤差を足して損失にしている。sum(T*(y1*1000-1000)**2) は 1000*(y1-1) の二乗を足すのと同じで、行儀よく書くなら sum((y1-1)**2) でよい(定数倍は最適解を変えない)。
- つまり T(x) は「制御を掛けたときの応答の良し悪し」を1つの数値にまとめたもの。これが小さいほど良いパラメータ、というだけで、実機の応答を保証するものではない。
def jacobian(x):
print(type(x))
print(x)
#return x
return np.array([1, 1, 1])
**注意**: この jacobian は常に [1,1,1] を返すだけの仮実装だ。勾配を使う手法(CG、BFGS、Newton-CG、trust-ncg、dogleg)はこの値をそのまま信用するので、収束は当てにできない。実際に使うなら偏導関数を解析的に書くか、有限差分で勾配を求める(jac を渡さない、または 2-point を指定する)のが現実的だ。微分を使わない Nelder-Mead、Powell、COBYLA は jac なしでそのまま動く。
#初期値
init = [1,1,1]
#最適化
nelder_mead = minimize(T,init, method='Nelder-Mead')
powell = minimize(T,init, method='Powell')
cg = minimize(T,init, method='CG')
bfgs = minimize(T, init, method='BFGS')
ewton_cg = minimize(T,init, method='Newton-CG', jac=jacobian)
l_bfgs_b = minimize(T, init, method='L-BFGS-B')
tnc = minimize(T, init, method='TNC')
cobyla = minimize(T, init, method='COBYLA')
slsqp = minimize(T, init, method='SLSQP')
dogleg = minimize(T, init, method='dogleg', jac=jacobian, hess=rosen_hess)
trust_ncg = minimize(T, init, method='trust-ncg', jac=jacobian, hess=rosen_hess)
**手法の使い分け(比較)**
- Nelder-Mead / Powell / COBYLA: 微分不要。関数を何度も呼ぶので遅いが、初期値の影響を受けにくく、評価関数がギザギザでも動く。まずこれで当たりを見るのが安全だ。
- CG / BFGS / L-BFGS-B / TNC: 勾配(またはその近似)を使う。滑らかな関数なら速い。L-BFGS-B と TNC はパラメータに上下限(bounds)を付けられるので、Kp>0、Ti>0 のような制約を入れたいときに使いやすい。
- Newton-CG / trust-ncg / dogleg: 2階微分(ヘッセ行列)まで使う。正しい jacobian と hessian が必須で、仮の値を渡すと意味のない結果になる。
このコードは 11 手法を同じ初期値から走らせて結果を比べているだけで、出力はしていない。実用上は初期値を何点か変えて回し、同じ解に落ちるか(局所解に嵌まっていないか)を確認する。
PID制御の関数はこのまま応用して使うとしたとしても、実際の学習用環境がないと始まらない。
PID制御その他制御理論の理解とコード化(最初は借り物でもいいので自分のものにする)
まずは実機のデータを取得するところから。(特徴を理解し、最適な制御理論を見つける)
この実機には他のフィードバック機構が含まれていても問題ないが、
本番環境では使用しないフィードバックは含まれてはいけない。
(パラメータの最適化に支障が出る)
何らかのシミュレータを用いる(RasPiで疑似チラー+温度計を作るとか)
もしくは本プログラムの評価関数を実機に近づけてシミュレーションする。
本番環境に対する入出力I/Fを確認する(たぶんここが一番ややこしいので先に手を付ける)
出力I/Fは何か。アナログ/デジタル、周期、速度、フォーマット等
最適化でパラメータを探す前に、経験則で当たりを付ける手もある。結果を比べる基準として知っておくとよい。
古典法で初期値を与え、そこから最適化で詰める、という併用が現実的だ。
フィードバックするI/Fは何か。スイッチのOn/OFF? 目標温度のアナログ/デジタル数値? チラーの強度?、周期、速度、フォーマット等
🤖 このページにはAIエージェント(ノア)による編集・加筆が含まれます。内容の正確性は保証できないため、重要な判断は一次情報でご確認ください。