# Tuning the parameters of a PID controller 
In this notebook you can test your intuition for how to adjust the parameters of a PID controller.

Start by watching [this excellent video](https://www.youtube.com/watch?v=XfAt6hNV8XM).


## Blockdiagram
We consider the model used in the video: Velocity control of a car. The plant model describes how the velocity of the car responds to the position of the accelerator (the gas pedal). In addition to the signal from the accelerator, there are also unknown forces such as wind resistance and gravity when the car is going uphill or downhill. These forces are represented by a disturbance signal entering at the input to the system. 

<!---  -->


## The PID controller
The PID-controller is on so-called *parallel form*
\begin{equation}
F(s) = K_p + \frac{K_i}{s} + K_d s.
\end{equation}

## The closed-loop system from the reference signal to the output
The model is linear and hence the principle of superposition holds. This mean that we can look at the response to the reference signal and the response to the disturbance signal separately. Setting $$d=0,$$ we get a closed-loop response given by
\begin{equation}
Y(s) = \frac{\frac{1}{s(s+1)}F(s)}{1 + \frac{1}{s(s+1)}F(s)}R(s).
\end{equation}

## The closed-loop system from disturbance to the output
Setting $$r=0,$$ the reponse to the disturbance is given by 
\begin{equation}
Y(s) = \frac{\frac{1}{s(s + 1)}}{1 + \frac{1}{s(s+1)}F(s)}D(s)
\end{equation}

## The full closed-loop system
We can find the response to a combination of input signals $r$ and $d$ by summation:
\begin{equation}
Y(s) = \frac{\frac{1}{s(s+1)}F(s)}{1 + \frac{1}{s(s+1)}F(s)}R(s) + \frac{\frac{1}{s(s + 1)}}{1 + \frac{1}{s(s+1)}F(s)}D(s)
\end{equation}




```python
# Uncomment and run the commands in this cell if a packages is missing
!pip install slycot
!pip install control
```


```python
%matplotlib widget
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
import control.matlab as cm
```

## Step response
Below you can manipulate the $K_p$, $K_i$ and $K_d$ parameters of the PID-controller, and see a time-response of the system. At time $t_1=1$ there is a unit step in the reference signal, and at time $t_2=10$ yhere is a negative step in the disturbance signal. Note that since we scaled time using the time constant of the system, the time is not measured in seconds but in the length of time constant. So to get $t_2$ in seconds you will have to multiply with the time constant
\begin{equation}
t_2 = 5 T = 5 \frac{1}{\omega}
\end{equation}
where $\omega$ has unit $1/s$ or $Hz$. 


```python
G1 = cm.tf([1.], [1, 1.])
Gint = cm.tf([1], [1, 0])
G = Gint*G1
print(G)
N = 600
t_end = 30
t = np.linspace(0, t_end, N)  

# The reference signal
r = np.zeros(N)
r[int(N/t_end):] = 1.0

# The disturbance signal
d = np.zeros(N)
d[int(N/t_end)*10:] = -1.0

# set up plot
fig, ax = plt.subplots(figsize=(8, 3))
#ax.set_ylim([-.1, 4])
ax.grid(True)
 
def sim_PID(G, Kp, Ki, Kd, r,d,t):
    """
    Returns the simulated response of the closed-loop system with a 
    PID controller.
    """
    F = cm.tf([Kd, Kp, Ki], [1.0, 0])
    Gr = cm.feedback(G*F,1)
    Gd = cm.feedback(G,F)
    yr = cm.lsim(Gr, r, t)
    yd = cm.lsim(Gd, d, t)
    return (yr, yd)
    
 
@widgets.interact(Kp=(0, 10, .2), Ki=(0, 8, .2), Kd=(0, 8, .2))
def update(Kp = 1.0, Ki=0, Kd=0):
    """Remove old lines from plot and plot new one"""
    [l.remove() for l in ax.lines]
    yr, yd = sim_PID(G, Kp, Ki, Kd, r,d,t)
    ax.plot(yr[1], yr[0]+yd[0], color='C0')
    #ax.plot(yd[1], yd[0], color='C1')


```

    
       1
    -------
    s^2 + s
    



    FigureCanvasNbAgg()



    interactive(children=(FloatSlider(value=1.0, description='Kp', max=10.0, step=0.2), FloatSlider(value=0.0, des…


### Exercise
Try to find PID parameters that give 
1. about 10% overshoot, 
2. settling time of about 4,
3. negligable stationary error at $t=14$ (4 after onset of constant disturbance) 

## Ramp response
A negative unit ramp disturbance starts at time $t_1=0$. 


```python

# The reference signal
dramp = np.linspace(0, t_end, N)
rr = np.zeros(N)

# set up plot
fig, ax = plt.subplots(figsize=(8, 3))
#ax.set_ylim([-.1, 4])
ax.grid(True)
 

@widgets.interact(Kp=(0, 10, .2), Ki=(0, 8, .2), Kd=(0, 8, .2))
def update(Kp = 1.0, Ki=1, Kd=0):
    """Remove old lines from plot and plot new one"""
    [l.remove() for l in ax.lines]
    yr, yd = sim_PID(G, Kp, Ki, Kd, rr,dramp,t)
    ax.plot(yr[1], yr[0]+yd[0], color='C0')
    #ax.plot(yd[1], yd[0], color='C1')


```


    FigureCanvasNbAgg()



    interactive(children=(FloatSlider(value=1.0, description='Kp', max=10.0, step=0.2), FloatSlider(value=1.0, des…


### Exercise
Why does the error in the ramp response keep growing although the controller contains an integrating term?

## Further reading
- [Documentation for the Python Control Systems library](https://python-control.readthedocs.io/en/0.8.3/)
- [PID control on Wikipedia](https://en.wikipedia.org/wiki/PID_controller)



```python

```

818cd2a743cad77896e53b852b6ecd33ce80fbc5
ipynb
Jupyter Notebook
introduction/notebooks/Continuous-PID.ipynb
alfkjartan/control-computarizado
5b9a3ae67602d131adf0b306f3ffce7a4914bf8e
MIT
2020-12-22T09:57:05.000Z
2020-12-22T09:57:05.000Z
introduction/notebooks/Continuous-PID.ipynb
alfkjartan/control-computarizado
5b9a3ae67602d131adf0b306f3ffce7a4914bf8e
MIT
introduction/notebooks/Continuous-PID.ipynb
alfkjartan/control-computarizado
5b9a3ae67602d131adf0b306f3ffce7a4914bf8e
MIT
2019-09-25T20:02:23.000Z
2019-09-25T20:02:23.000Z
Qwen/Qwen-72B
1. YES
2. YES