import numpy as np
import matplotlib.pyplot as plt
import control
Standard Feedback loop
Controller $R(s)$:
- Converts the error term into an actuator command
- We are free to choose any control scheme we like.
- As long as the closed loop performance of the system meets our requirements
Note: controller = compensator
- Lead and lag compensators are used quite extensively in control.
- A lead compensator can increase the stability or speed of reponse of a system;
- A lag compensator can reduce (but not eliminate) the steady-state error.
- Depending on the effect desired, one or more lead and lag compensators may be used in various combinations.
- Lead, lag, and lead/lag compensators are usually designed for a system in transfer function form.
- What is phase lead
t = np.arange(0, 10, 0.1)
plt.plot(t, np.sin(t), color='r', label='sin');
plt.plot(t, np.cos(t), color='b', label='cos');
plt.legend();
plt.grid()
- The $cos$ signal is ahead of the $sin$ signal by $90$ deg
- The output leads the input by 90 deg
We can plot the Bode plots:
- Differentiation gives positive phase
Integration gives negative phase (Mirrors the derivative plot)
A zero in a transfer function adds phase
A pole in a transfer function subtracts phase
Lead compensator: adds phase (at least in some frequency range of interest)
- Lag compensator: subtracts phase (at least in some frequency range of interest)
Equations
Lead compensator
$$ R(s) = \frac{\frac{s}{w_z}+1}{\frac{s}{w_p}+1} = \frac{w_p}{w_z}\frac{s + w_z}{s + w_p} $$- one real pole and one real zero
- $w_z < w_p$
- $K=\frac{w_p}{w_z}$ (gain)
- We can take care of the gain easily (e.g., using the root locus method)
Lag compensator
$$ R(s) = \frac{\frac{s}{w_z}+1}{\frac{s}{w_p}+1} = \frac{w_p}{w_z}\frac{s + w_z}{s + w_p} $$- one real pole and one real zero
- $w_z > w_p$
- $K=\frac{w_p}{w_z}$ (gain)
Let's look at the zero-pole contribution separately:
- The zero adds 90 deg and amplifies high frequencies
- The pole subtracts 90 deg and attenuates high frequencies
- Multiplying the two T.F. together means adding everything together on the Bode plot
- Lead/Lag compensator:
- Behaves like a real zero early on, at low frequency
- Until the real pole pulls it back at high frequency
- See blue line for its approximate representation
Note:
- A lead compensator increases the gain at high frequency (but less than a real zero would do)
- This means that it is less noisy than a derivative controller on its own
- A lead compensator adds phase between the two corner frequencies and no phase outside
- Moving the two frequency means we can change where we add our phase
Let's see an example:
w_z = 1
w_p = 10
s = control.tf([1, 0], [1])
R_s = w_p/(s+w_p)*(s+w_z/w_z)
We can plot the zero (blue) and the pole (orange) parts together with the combined Bode plot (green):
fig, axs = plt.subplots(1, figsize=(10,5))
# zero (blue)
control.bode_plot((s+w_z)/w_z, dB=True, omega_limits = [0.1, 100], wrap_phase =True);
# pole (orange)
control.bode_plot(w_p/(s+w_p), dB=True, omega_limits = [0.1, 100], wrap_phase =True);
# compensator (green)
control.bode_plot(R_s, dB=True, omega_limits = [0.1, 100], wrap_phase =True);
# Note: If wrap_phase is True the phase will be restricted to the range [-180, 180) (or [-\pi, \pi) radians)
- What happens when we move the zero closer to the pole?
w_z = 5
w_p = 10
fig, axs = plt.subplots(1, figsize=(10,5))
# zero (blue)
control.bode_plot((s+w_z)/w_z, dB=True, omega_limits = [0.1, 100], wrap_phase =True);
# pole (orange)
control.bode_plot(w_p/(s+w_p), dB=True, omega_limits = [0.1, 100], wrap_phase =True);
# compensator transfer function (green)
R_s = w_p/(s+w_p)*(s+w_z)/w_z
control.bode_plot(R_s, dB=True, omega_limits = [0.1, 100], wrap_phase =True);
- Still a phase lead is present, but much smaller
- What happens if the zero is right on top of the pole?
- if $w_p$ < $w_z$ we obtain a lag compensator
w_z = 50
w_p = 10
fig, axs = plt.subplots(1, figsize=(10,5))
# zero
control.bode_plot((s+w_z)/w_z, dB=True, omega_limits = [1, 1000], wrap_phase =True);
# pole
control.bode_plot(w_p/(s+w_p), dB=True, omega_limits = [1, 1000], wrap_phase =True);
# compensator transfer function
R_s = w_p/(s+w_p)*(s+w_z)/w_z
control.bode_plot(R_s, dB=True, omega_limits = [1, 1000], wrap_phase =True);
- The zero affects the system at higher frequency
- The system behaves like a real pole at lower frequency
- Until the zero comes into effect and "cancels" the pole at higher frequency
- This add lag to the system
Note: The sama transfer function structure can produce phase lead or lag, adjusting the relative position of the pole and the zero
- Design a compensator that uses both a lead and a lag compensator:
w_z = .5
w_p = 1
R_Lead = w_p/(s+w_p)*(s+w_z)/w_z
# Lag compensator
w_z1 = 15
w_p1 = 5
R_Lag = w_p1/(s+w_p1)*(s+w_z1)/w_z1
Plot the pole zero map for the Lead compensator:
control.pzmap(R_Lead);
Plot the pole zero map for the Lag compensator:
control.pzmap(R_Lag);
We construct the Lead-Lag compensator:
R_LL = R_Lead*R_Lag
And we can plot the Bode Plot to see what the phase does:
fig, axs = plt.subplots(1, figsize=(10,5))
control.bode_plot(R_LL, dB=True, omega_limits = [.01, 1000], wrap_phase =True);
- This compensator is leading at low frequency, and lagging at higher frequency
- Let's consider our control loop again:
We have a model of our plant $G(s)$
- we are given a transfer function
- we have identified the model
Design requirements - performance goal:
- Stability
- Rise time
- Settling time
- Max Overshoot
- Damping ratio
- Gain/Phase margin
$G(s)$ alone does not meet our requirements
- We need to design the controller $R(s)$
- How do we choose $R(s)$?
Let's consider:
$$ G(s) = \frac{1}{(s+2)(s+4)} $$- What is the Root Locus of $G(s)$?
s = control.tf([1, 0],[1])
G_s = 1/((s+2)*(s+4))
fig, axs = plt.subplots(1, figsize=(10,5))
control.rlocus(G_s);
- What if we had a Lead compensator?
- How does the root locus change?
Let's choose, arbitrarily, a Lead compensator ($w_z < w_p$):
$$ R(s) = \frac{(s+5)}{(s+6)} $$
Let's see how it looks like with Python:
We define the controller:
R_s = (s+5)/(s+6)
Plot the Root Locus:
fig, axs = plt.subplots(1, figsize=(10,5))
control.rlocus(G_s*R_s);
With a lead compensator:
- We have moved the asynmptotes further into the left half plane
- Increasing the gain $K$ the close loop poles would be more to the left: we have added stability to the system
How does this help us?
- When we use the root locus method we typically know where we would like our dominant closed loop poles to be so that we meet our requirements
- With the root locus method, we first convert our requirements into pole locations
- We need a lead compensator if we need to move our poles to the left of where our current (uncompensated) poles are
- We need a lag compensator if we need to move our poles to the right of where our current (uncompensated) poles are
- If the root locus already goes through the desired locations, we only need to choose the correct gain
- Note: we could still have a steady state error problem, but we know how to fix this already
Given desired poles, solving for the compensator becomes a trigonometry problem:
To be part of the Root Locus: $\sum{\angle Poles} - \sum{\angle Zeros} = 180^o$
For example, for our system we saw that the root locus is:
- There are no zeros (so we do not need to subtract)
- The sum of $\theta_1+\theta_2$=180 because that point is part of the root locus
Given our system: $$ G(s) = \frac{1}{(s+2)(s+4)} $$
If we want poles:
$$s_d = -4 \pm 2j$$
And if we sum up all the angles: $90+(90+\theta)$ = $90+(90+45)$ = 225
- since $\theta = \tan^{-1}(2/2) = \tan^{-1}(1) = 45$
And of course, this is not on the Root Locus.
- To have it on the Root Locus, we need to remove 45 deg of phase
- Using a phase lead compensator we can do it adding a single pole and a single zero:
- $(\theta_p - \theta_z)$ is our lead compensator
- If we pick a zeros at -5
- $\theta_z= 63.43 ^o$
- The pole must go into one specific location:
- $\theta_p=180-225+63.43=18.43$
The compensator for this particular problem is:
$$ R(s) = \frac{s+5}{s+10.015} $$%matplotlib notebook
R_s = (s+5)/(s+10.015)
fig, axs = plt.subplots(1, figsize=(7,7))
control.rlocus(G_s*R_s);
One more step:
- We need to calculate the gain that moves the poles in closed loop where we want them
We can then find:
$$ K = \frac{-1}{1+G(s)R(s)}\Big|_{s=-4 + 2 j} \approx 16 $$We can then sketch bounds: