PID制御とは何か、pythonでシミュレーションをしながら解説します。シミュレーションについては以下の記事で紹介しています。
リンク
まず、質量のある物体( 制御対象 )に力(制御入力)を加え、ある値に収束させることを考えます。
下図は縦軸が位置、横軸が時間を表しています。このようにxの値が0に収束するような制御入力の与え方をいろいろと試していきます。

ON-OFF制御
xの値を0に収束させたい。そんなとき、どうすれば良いか。一番に思い付くのは、xが大きければ負の方向に力を加え、xが小さければ正の方向に力を加えることでしょう。
import matplotlib.pyplot as plt
x_list = []
t_list = []
dt = 0.1
m = 1.0 #質量[kg]
x = 1 #初期位置[m]
v = 0 #初速度[m/s]
f = 1 #入力[N]
def F(e): #制御入力
if x>0.8:
u = -f
elif x<-0.8:
u = f
else:
u = 0
return u
for i in range(int(50/dt)):
t = i*dt
x += dt*v
v += dt*F(x)/m
x_list.append(x)
t_list.append(t)
plt.plot(t_list,x_list,color ='b')
plt.title('ON-OFF')
plt.xlabel('t[s]')
plt.ylabel('x[m]')
plt.show()

だめだ、収束しない
P制御( Proportional Controller )
目標値と現在値の差に応じて、力の入力を変えればうまくいくのではないか。つまり、目標値との差に比例した制御入力を与える。
import matplotlib.pyplot as plt
x_list = []
t_list = []
dt = 0.1
m = 1.0 #質量[kg]
x = 1 #初期位置[m]
v = 0 #初速度[m/s]
Kp = 0.3 #比例ゲイン
for i in range(int(50/dt)):
t = i*dt
x += dt*v
F = -x*Kp
v += dt*F/m
x_list.append(x)
t_list.append(t)
plt.plot(t_list,x_list,color ='b')
plt.title('Proportional-Controller')
plt.xlabel('t[s]')
plt.ylabel('x[m]')
plt.show()

これは単振動。"位置に比例した力"はバネと同じなので、収束しない。(抵抗とかあったら収束する)
PD制御( Proportional-Differential Controller )
速度(位置の微分値)に比例した制御入力を追加する。変化を抑制する。
import matplotlib.pyplot as plt
x_list = []
t_list = []
dt = 0.1
m = 1.0 #質量[kg]
x = 1 #初期位置[m]
v = 0 #初速度[m/s]
Kp = 0.3 #比例ゲイン
Kd = 0.3 #微分ゲイン
for i in range(int(50/dt)):
t = i*dt
x += dt*v
F = -x*Kp -v*Kd
v += dt*F/m
x_list.append(x)
t_list.append(t)
plt.plot(t_list,x_list,color ='b')
plt.title('Proportional-Differential-Controller')
plt.xlabel('t[s]')
plt.ylabel('x[m]')
plt.show()

収束した。
ここではノイズを考慮しないが、ノイズがある場合は微分値にローパスフィルタをかける必要がある。 (ノイズでギザギザの値は微分できないから)ローパスフィルタをかけた微分を不完全微分という。ローパスフィルタについては以下の記事で紹介しています。
PID制御(Proportional-Integral-Differential Controller)
PD制御だと、目標値付近ではほとんど入力が無くなる→無限時間経たないと完全には0に収束しない。そこで、積分成分を追加する。いつまでも正の位置にあれば負の方向へ、負の位置に居れば正の方向へ制御入力が増えていく。
import matplotlib.pyplot as plt
x_list = []
t_list = []
dt = 0.1
m = 1.0 #質量[kg]
x = 1 #初期位置[m]
v = 0 #初速度[m/s]
w = 0
Kp = 0.2 #比例ゲイン
Kd = 0.5 #微分ゲイン
Ki = 0.01 #積分ゲイン
for i in range(int(50/dt)):
t = i*dt
x += dt*v
w += dt*x
F = -x*Kp -v*Kd -w*Ki
v += dt*F/m
x_list.append(x)
t_list.append(t)
plt.plot(t_list,x_list,color ='b')
plt.title('Proportional-Differential-Controller')
plt.xlabel('t[s]')
plt.ylabel('x[m]')
plt.show()

あとは、ゲインKp,Ki,Kdをうまく調節すればきれいに収束するようになる。
アニメーションにするとこんな感じ

アニメーションはArtistAnimationを使って作成
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
fig, ax = plt.subplots()
ims = []
dt = 0.01
m = 1.0 #質量[kg]
Kp = 0.0
Kd = 0.0
Ki = 0.0
for j in range(100):
x_list = []
t_list = []
if Kp < 0.2: #比例ゲイン
Kp += 0.01
elif Ki < 0.001: #積分ゲイン
Ki += 0.001
elif Kd < 0.5: #微分ゲイン
Kd += 0.01
x = 1 #初期位置[m]
v = 0 #初速度[m/s]
w = 0
for i in range(int(50/dt)):
t = i*dt
x += dt*v
w += dt*x
F = -x*Kp -v*Kd -w*Ki
v += dt*F/m
x_list.append(x)
t_list.append(t)
tx1 = "Kp=" + str(round(Kp,3))
tx2 = "Ki=" + str(round(Ki,3))
tx3 = "Kd=" + str(round(Kd,3))
title1 = ax.text(0,-0.3,tx1)
title2 = ax.text(0,-0.45,tx2)
title3 = ax.text(0,-0.6,tx3)
im = plt.plot([0,50],[0,0],color='r')
im += plt.plot(t_list,x_list,color ='b')
ims.append(im + [title1] + [title2] + [title3])
ani = animation.ArtistAnimation(fig,ims,interval=100)
plt.ylim(-1,1)
plt.xlim(0,50)
plt.xlabel('t[s]')
plt.ylabel('x[m]')
plt.show()
リンク


コメント