[PYTHON] [Introduction to PID] I tried to control and play ♬

PID control seems to be a technology that has been around for a long time. So, I carefully formulated it and played with it, so I will summarize it.

【reference】 ①PID controller ② [PID control](https://ja.wikipedia.org/wiki/PID%E5%88%B6%E5%BE%A1#:~:text=PID%E5%88%B6%E5%BE%A1 % EF% BC% 88% E3% 83% 94% E3% 83% BC% E3% 82% A2% E3% 82% A4% E3% 83% 87% E3% 82% A3% E3% 83% BC% E3 % 81% 9B% E3% 81% 84,% E6% 96% B9% E6% B3% 95% E3% 81% AE% E3% 81% 93% E3% 81% A8% E3% 81% A7% E3% 81% 82% E3% 82% 8B% E3% 80% 82)

It can be easily represented by the following graph. From the above reference ① By Arturo Urquizo - PID_en.svg.png Here, each quantity is defined as follows.

\begin{align}
r(t)&;Target amount\\
y(t)&;After operation, the current amount of target plants and processes\\
e(t)&=r(t)-y(t);Current amount-Residual calculated with target amount\\
u(t)&=P+I+D;Operation amount\\
          &;Operations on target plants and processes\\
        &;The value of the current that flows to raise and lower the temperature and the angle for operating the handle and valve. ..\\
P&=K_pe(t);Proportional control\\
I&=K_i\int_0^t e(\tau )d\tau;Integral control\\
D&=K_d\frac{de(t)}{dt};Derivative control\\
\end{align}

Here, in the case of computer control, $ y (t) $ is a discrete value, so it is better to define integral control and differential control by numerical integration and numerical differentiation as follows. At sampling time $ t = t_n $, place as follows. The numerical differentiation may be of a higher order, but the following may be used in consideration of practicality and reactivity.

\int_0^te(\tau)d\tau = \Sigma_{i=0}^ne(t_i)\Delta t\\
(\frac{de(t)}{dt})_{t_n}=\frac{e(t_n)-e(t_{n-1})}{\Delta t}\\
\Delta t = t_n-t_{n-1} ; const=Set to 1

Try to drop it in the code

First, the control targets are as follows. In other words, it is an image of temperature control of electric furnaces.

def ufunc(M,u=0,t=0):
    M0=0.2
    m=0.05
    return M - m*(M-M0-u)

The controller side considers the following PID control. Part of the code is based on the following. 【reference】 Try PID control with Python

t = 100
params_list = [(0,0,0)]  #Kp,Ki,Set Kd
for kp,ki,kd in params_list:
    Kp,Ki,Kd=kp,ki,kd
    M0 = 2
    M=M0
    goal = 1.00
    e = 0.00 
    e1 = 0.00 
    u=0
    es=0
    x_list = []
    y_list = []
    u_list = []
    x_list.append(0)
    y_list.append(M)
    u_list.append(u)
    for i in range(1,t,1):
        M1 = M
        e1 = e
        e = goal - y_list[i-1] #Deviation (e)=Objective value (goal)-Present value
        es += e #Integral is addition
        u = Kp * e + Ki * es + Kd * (e-e1) #Derivative is subtraction
        M = ufunc(M,u,i) #Response from the target
        x_list.append(i)
        y_list.append(M)
        u_list.append(u)
    plt.hlines([goal], 0, t, "red", linestyles='dashed') #The goal is displayed with a red dotted line
    plt.plot(x_list, y_list, color="b") #The time change of the response is displayed in blue
    plt.title('Kp={}, ki={}, kd={}'.format(Kp, Ki, Kd))
    #plt.plot(x_list, u_list, color="black") #Operation amount is displayed in black
    plt.ylim(-0.5, goal*2+0.2) #Graph height adjustment
    plt.pause(0.2)
    plt.savefig("output/Kp,Ki,Kd/imageKp{}Ki{}Kd{}M0{}.png ".format(Kp,Ki,Kd,M0))    
    plt.close()

See the target reaction with params_list = [(0,0,0)]

When t = 0, if M = 2 and room temperature M0 = 0.2, it gradually approaches M = 0.2 as follows. imageKp0.0Ki0.0Kd0.0M02dt1.png

This system is expressed by the following equation.

\frac{dM}{dt}=-m(M-M0)

The solution is

M=M0+(2-M0)e^{-mt}\\
t=When 0, M=2

Ziegra Nichols limit sensitivity method

See the critical vibration of the object According to Wikipedia, if Ki = Kp / Ti and Kd = Kp · Td, the optimum parameters are determined by the marginal sensitivity method of Ziegra Nichols as follows.

Control method Kp Ti Td
P 0.50Ku - -
PI 0.45Ku 0.83Tu -
PID 0.6Ku 0.5Tu 0.125Tu

Here, Ku and Tu are defined as follows. "The proportional gain Kp is gradually increased from 0, and when the control amount reaches the stable limit and the constant amplitude vibration is maintained, the increase of Kp is stopped. The proportional gain at this time is Ku (limit sensitivity). , If the vibration cycle is Tu, When the critical vibration is calculated for the above object, it is calculated as Tu = 2.0 when Ku = 39 as shown below. imageKp39.0Ki0.0Kd0.0M02dt1.png

If you rewrite the above table with Ki, Kd,

Control method Kp Ki Kd
P 0.50Ku - -
PI 0.45Ku 0.54Ku/Tu -
PID 0.6Ku 1.2Ku/Tu 3KuTu/40

result

In fact, if you try with the above parameters, P control Kp19.5Ki0.0Kd0.0M02dt1 imageKp19.5Ki0.0Kd0.0M02dt1.png PI control Kp17.55Ki10.57Kd0.0M02dt1 imageKp17.55Ki10.572289156626507Kd0.0M02dt1.png PID control Kp23.4Ki23.4Kd5.85M02dt1 imageKp23.4Ki23.4Kd5.85M02dt1.png In other words, PID control fails for some reason This seems to be due to the rounding error.

dt precision extension and recalculation

So, extend dt so that it can be calculated in arbitrary increments as shown below.

def ufunc(M,u=0,t=0,dt=1):
    M0=0.2
    m=0.05
    return M - m*(M-M0-u)*dt

t = 100
dt=0.1

Ku=39
Tu=2
params_list =[(0.5*Ku,0,0),(0.45*Ku,0.54*Ku/Tu,0),(0.6*Ku,1.2*Ku/Tu,3*Ku*Tu/40)]
for kp,ki,kd in params_list:
    Kp,Ki,Kd=kp,ki,kd
    ...
    for i in range(1,int(t/dt),1):
        ...
        e = goal - y_list[i-1] #Deviation (e)=Objective value (goal)-Last realized value
        es += e*dt
        u = Kp * e + Ki * es + Kd * ((e-e1)/dt )
        M = ufunc(M,u,i,dt)

        x_list.append(i*dt)
        y_list.append(M)
        u_list.append(u)
        ...
    plt.savefig("output/Kp,Ki,Kd,dt/imageKp{}Ki{}Kd{}M0{}dt{}.png ".format(Kp,Ki,Kd,M0,dt))    
    plt.close()

Re-executed with dt = 0.1, I was able to draw well as expected. PID control Kp23.4Ki23.4Kd5.85M02dt0.1 imageKp23.4Ki23.4Kd5.85M02dt0.1.png Since the big change ends at t = 0-10, it is now possible to calculate the dt step in detail, so let's recalculate with the step dt = 0.01. P control Kp19.5Ki0.0Kd0.0M02dt0.01 imageKp19.5Ki0.0Kd0.0M02dt0.01.png PI control Kp17.55Ki10.57Kd0.0M02dt0.01 imageKp17.55Ki10.572289156626507Kd0.0M02dt0.01.png PID control Kp23.4Ki23.4Kd5.85M02dt0.01 imageKp23.4Ki23.4Kd5.85M02dt0.01.png

Graph reproduction of PID control

Try to reproduce the following graph of English Wikipedia. Change_with_Ki.png It seems that the conditions are not drawn, so when I draw variously under the most likely conditions, the Ki dependency becomes almost similar under the following conditions.

def ufunc(M,u=0,t=0,dt=1):
    M0=0
    m=0.4
    return M + m*u*dt - m*(M-M0)*dt

The calculation is done with the same parameter values as shown. Three graphs were drawn at the same time with a legend. The drawing range of the graph is adjusted.

t = 20
dt=0.01
params_list =[(1,0.5,1),(1,1,1),(1,2,1)]
for kp,ki,kd in params_list:
    Kp,Ki,Kd=kp,ki,kd
    M0 = 0
    M=M0
    goal = 1.00
    e = 0.00 
    e1 = 0.00 
    u=0
    es=0
    x_list = []
    y_list = []
    u_list = []
    x_list.append(0)
    y_list.append(M)
    u_list.append(u)
    for i in range(1,int(t/dt),1):
        M1 = M
        e1 = e
        e = goal - y_list[i-1] #Deviation (e)=Objective value (goal)-Last realized value
        es += e*dt
        u = Kp * e + Ki * es + Kd * ((e-e1)/dt )
        M = ufunc(M,u,i,dt)
        x_list.append(i*dt)
        y_list.append(M)
        u_list.append(u)
    plt.hlines([goal], 0, t, "red", linestyles='dashed') #The goal is displayed with a red dotted line
    plt.plot(x_list, y_list,label="Kp{}Ki{}Kd{}".format(Kp,Ki,Kd)) 
    plt.legend()
    plt.ylim(-0.1, 1.5) #Adjust the height of the graph
    plt.pause(0.2)
plt.savefig("output/Kp,Ki,Kd,dt/imageKp{}Ki{}Kd{}M0{}dt{}2.png ".format(Kp,Ki,Kd,M0,dt))    
plt.close()

On the other hand, the Kp dependency can be drawn as follows, and it cannot be reproduced without selecting the parameters. imageKp1.6Ki1Kd1M00dt0.011.png And the Kd dependency seems to be almost reproduced as follows. imageKp1Ki1Kd2M00dt0.013.png

About the control target

This time, the control target was set by imagining the temperature of the electric furnace. In actual control, you need to control something more dynamic. In that case, I think that it can be realized by rewriting ufunc () introduced this time. Also, depending on the control target, I think that there are targets that cannot be PID controlled. We need to pursue further the limits of that area.

Summary

・ I tried playing with PID control ・ Control parameters were determined by the marginal sensitivity method of Ziegra-Nichols. ・ I tried to reproduce the graph posted on Wikipedia ・ I was able to explicitly define a function that describes the controlled object. ・ Introduced sampling time dt

・ I would like to actually apply it to temperature control and motor control.

Recommended Posts

[Introduction to PID] I tried to control and play ♬
[Introduction to infectious disease model] I tried fitting and playing ♬
I implemented DCGAN and tried to generate apples
I tried to debug.
I tried to paste
I tried to control the network bandwidth and delay with the tc command
[Introduction to AWS] I tried porting the conversation app and playing with text2speech @ AWS ♪
I tried to read and save automatically with VOICEROID2 2
I tried pipenv and asdf for Python version control
I tried to implement and learn DCGAN with PyTorch
I tried adding post-increment to CPython. Overview and summary
I tried to automatically read and save with VOICEROID2
I tried adding system calls and scheduler to Linux
[Introduction to Pytorch] I tried categorizing Cifar10 with VGG16 ♬
I tried to install scrapy on Anaconda and couldn't
[Introduction to AWS] I tried playing with voice-text conversion ♪
I tried to learn PredNet
[I tried using Pythonista 3] Introduction
I tried to organize SVM.
I tried to implement PCANet
Introduction to Nonlinear Optimization (I)
I tried to reintroduce Linux
I tried to introduce Pylint
I tried to summarize SparseMatrix
I tried to touch jupyter
I tried to implement StarGAN (1)
I tried to predict and submit Titanic survivors with Kaggle
I tried to combine Discord Bot and face recognition-for LT-
[Introduction to pytorch] Preprocessing by audio I / O and torch audio (> <;)
I tried to get Web information using "Requests" and "lxml"
[Introduction to simulation] I tried playing by simulating corona infection ♬
[Django] I tried to implement access control by class inheritance.
[Introduction to Pandas] I tried to increase exchange data by data interpolation ♬
I tried to illustrate the time and time in C language
I tried to display the time and today's weather w
Mongodb Shortest Introduction (3) I tried to speed up even millions
I tried to enumerate the differences between java and python
I tried to make GUI tic-tac-toe with Python and Tkinter
[Introduction to Mac] Convenient Mac apps and settings that I use
I tried to implement Deep VQE
I tried to create Quip API
I tried to touch Python (installation)
[Introduction to Python3 Day 1] Programming and Python
I tried to implement adversarial validation
I tried to explain Pytorch dataset
I tried to touch Tesla's API
I tried to implement hierarchical clustering
I tried to organize about MCMC.
I tried to implement Realness GAN
I tried to move the ball
I tried to estimate the interval.
I tried to visualize bookmarks flying to Slack with Doc2Vec and PCA
[Introduction to Python] I compared the naming conventions of C # and Python.
[Introduction to simulation] I tried playing by simulating corona infection ♬ Part 2
I tried to let Pepper talk about event information and member information
I tried to make a periodical process with Selenium and Python
I tried to create Bulls and Cows with a shell program
I tried to extract players and skill names from sports articles
I tried to make a mechanism of exclusive control with Go
I tried to create a linebot (implementation)
I tried to summarize Python exception handling