PythonでPID制御のクラスを作っていろいろパラメータを変えてみた。
PID制御については以下を参照。
式は以下の通り。
ポイントだけ説明すると、
コードは以下の通り。目標値と各ゲインを与えてインスタンス化し、calc()で操作量を計算する。積分は一定時間だけ行うことをイメージして、過去の積算分に0.8をかけている。このようにすることで昔の影響はだんだん少なくなっていく。微分は過去1サンプルだけで計算するとノイズに弱いため、過去3サンプルの平均で計算している。
class PIDControal: def __init__(self, goal, kp, ki, kd): self.goal = goal self.kp = kp self.ki = ki self.kd = kd self.sum = 0 self.e1 = 0 self.e2 = 0 self.e3 = 0 def calc(self, x): e = self.goal - x self.sum += e u = self.kp * e + self.ki * self.sum + self.kd * \ ((self.e1-e) + (self.e2-e) + (self.e3-e))/3 self.e3 = self.e2 self.e2 = self.e1 self.e1 = e self.sum *= 0.8 return u
初期値0、目標値10でいろいろとパラメータを変えて試してみる。 確認するコードは以下の通り。実際の環境を想定して外乱項を入れている。
x = 0 goal = 10 pidcontroal = PIDControal(goal, kp=0, ki=0.1, kd=0) result = [x] for i in range(100): u = pidcontroal.calc(x) x = x + u + (random.random()-0.5)*0.1 result.append(x) # 制御結果 plt.plot(result) plt.ylim(0, 20) plt.xlim(0, 100) plt.plot([10]*100, "--") plt.ylabel("X") plt.show() # 残差 arr = np.array(result) arr -= goal plt.plot(arr) plt.xlim(0, 100) plt.ylim(-1, 1) plt.plot([0]*100, "--") plt.ylabel("residual") plt.show()
まずはkp=0.05, ki=0, kd=0の時。
かなりきれいにできている。(やってみるとわかるが、今回はランダムな外乱だけで、時定数の大きなゆっくりとした外乱がないので、P制御だけでもうまくいく。)
振動せず偏差が大きい状態が長く続いているので、I制御を入れて収束速度が速くなるか試してみる。kp=0.05, ki=0.02, kd=0の時。
先ほどと比較して収束速度が向上しているのがわかる。
次はkp=1.8, ki=0, kd=0の時。
ガビガビになってしまった。kpが大きすぎて振動している。 ではD制御を入れて急激な制御を抑制してみる。kp=1.8, ki=0, kd=0.1の時。
先ほどと比較して、収束スピードが改善していることがわかる。
最後にkp=1, ki=0, kd=0の時。今回の条件ではこれが一番いい。
今回はランダムな外乱だけだったので、P制御のみで制御できた。しかし、例えばゆっくりと徐々に変化するような外乱であればI制御が効いてくるし、急激に大きな外乱があった場合にはP制御では比例して大きく制御を行なってしまうが、D制御で抑制できるとシステムが安定する。