PythonでPID制御

PythonでPID制御のクラスを作っていろいろパラメータを変えてみた。

PID制御については以下を参照。

PID制御 - Wikipedia

式は以下の通り。

ポイントだけ説明すると、

  • 目標値に対する偏差から操作量を計算する、フィードバック制御の1つ
  • 操作量はP(比例)、I(積分)、D(微分)で計算された値を合算したもの
    • P(比例): 偏差に係数(Kp)をかけて操作量とする、偏差に比例した制御を行う
    • I(積分): 偏差を積分し係数(Ki)をかけて操作量とする、偏差の積算(積分量)に応じた制御を行う。
    • D(微分): 偏差を微分し係数(Kd)をかけて操作量とする。D制御はオーバーシュートを抑制する目的のものであり、PIと違い単体では制御にならない

コードは以下の通り。目標値と各ゲインを与えてインスタンス化し、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制御で抑制できるとシステムが安定する。