Python
python3
PID
制御工学
PID制御

PythonでPID制御をやってみる

はじめに

最近、独学で制御工学を勉強し始めました。
理解を深める為にはプログラミングで実装してみるのが一番早い、というのが私の理念です。
なので、今回は最近勉強したPID制御をPythonを使って実現してみます。
なお、この記事ではPID制御や制御工学の原理・理論の説明はしません。
Pythonでの実装にのみフォーカスを当ててます。

matplotlibとnumpyを使います。
OSは、Ubuntu16.04とMacOS High Sierra10.13で動作確認済みです。
Python2.7, Python3.6の両方で動きます。
一応、GitHubにもコードを上げておきます。

変数の説明

M : 与える操作量
M1 : 一つ前に与えた操作量
goal : 目的値
e : 偏差(目的値と現在値の差)
e1 : 前回に与えた偏差
e2 : 前々回に与えた偏差
Kp : 比例制御(P制御)の比例定数
Ki : 積分制御(I制御)の比例定数
Kd : 微分制御(D制御)の比例定数

操作量(M)の求め方

操作量(M)の求め方ですが、本来ならば、微積の計算をして操作量(M)を求めます。
しかし、それには莫大な計算が必要になる上、そもそもコンピュータで扱えるのは基本的に離散的な値です。
そこで、サンプリング方式
M = M1 + Kp * (e-e1) + Ki * e + Kd * {(e-e1) - (e1-e2)}
で操作量(M)を求めます。

プログラムコード

それでは、いよいよプログラムを書いていきます。

まずは、必要なモジュールをimportします。

import numpy as np
from matplotlib import pyplot as plt
from numpy.random import *

次に、メインの部分を書いていきます。

M = 0.00 
M1 =  0.00 
goal = 50.00  
e = 0.00 
e1 = 0.00 
e2 = 0.00 
Kp = 0.1 
Ki = 0.1 
Kd = 0.1 

t = 100

M, M1, e, e1, e2は最初は0.00に設定しておきます。
goalは目的の値でとりあえず今回は50.00にしておきましょう。
Kp,Ki,Kdはパラメータとなります。最初は様子をみて全て0.10にしておきましょう。
謎の変数tが出てきましたが、これは時間(t)です。for文のループ回数を設定する為に使います。

次に、グラフで表示するための準備をします。

x_list = []
y_list = []

x_list.append(0)
y_list.append(0.00)

x_listには時間(t)が入ります。
y_listには与えた操作量(M)が入ります。
時間(t)が0の時には、操作量も0.00なので、for文で回す前に事前にリストに格納しておきます。

そして、いよいよPID制御に入ります。
と言っても、先ほどのサンプリング方式の式に代入するだけです。

for i in range(1,t):
        M1 = M
        e1 = e
        e2 = e1
        e = goal - y_list[i-1] #偏差(e) = 目的値(goal) - 前回の操作量

        M = M1 + Kp * (e-e1) + Ki * e + Kd * ((e-e1) - (e1-e2))

        x_list.append(i)
        y_list.append(M)

偏差(e)の求め方は、制御対象によって変わるので注意してください。
とりあえず今は、(目的値)と(前回与えた操作量)の差を偏差(e)としています。

最後に、グラフで表示してみましょう。

plt.plot(x_list, y_list)
plt.ylim(0, goal*2) #見やすくするため
plt.show()

Figure_1.png

最初の段差が少々気になりますが、綺麗な曲線で目的値まで届いていると思います。

パラメータを変えてみよう

試しに、Kp, Ki, Kdをそれぞれ0.01と0.50に変えてみました。
届かない.png
パラメータが0.01だと、目的値50.00まで届かないです。

ハンチング.png
パラメータが0.50だとハンチングが発生してしまいました。

ノイズが入った場合だとどうなるか

ランダムでノイズを入れてみましょう。
コードはGitHubに上げておきます。
オーバーシュートやハンチングを防ぐにはKi, Kdの細かい調整が必要になりそう。

最後に

簡単ではありますが、PythonでPID制御をやってみました。
PID制御自体があまり勉強できていないので、もしかしたら間違えている部分があるかもしれません(コメントお待ちしております)。
2018/07/03 altさん、編集リクエストありがとうございます。

参考にしたサイトを載せておきます。
モータのPID制御法
【分かりすぎる】PID制御の基礎(YouTube動画)

一応、ソースコードも載せておきます。

pid.py
# coding:utf-8
import numpy as np
from matplotlib import pyplot as plt
from numpy.random import *

def main():
    M = 1.00
    M1 =  0.00

    e = 0.00
    e1 = 0.00
    e2 = 0.00

    Kp = 0.10
    Ki = 0.10
    Kd = 0.10

    t = 100

    goal = 50.00

    x_list = []
    y_list = []

    x_list.append(0)
    y_list.append(0.00)

    for i in range(1,t):
        M1 = M
        e1 = e
        e2 = e1
        e = goal - y_list[i-1]

        M = M1 + Kp * (e-e1) + Ki * e + Kd * ((e-e1) - (e1-e2))

        y_list.append(M)
        x_list.append(i)

    plt.plot(x_list, y_list)
    plt.ylim(0, goal*2)
    plt.show()

if __name__ == "__main__":
    main()