Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

PID control

So far, we have seen P, I, and PI control strategies. We will now discuss PID control, which also includes a derivative term.

PID control

A PID controller in transfer function form looks like

C(s)=U(s)E(s)=kp+kis+kdsC(s) = \frac{U(s)}{E(s)} = k_p + \frac{k_i}{s} + k_d s

The parameters kpk_p, kik_i, and kdk_d are the proportional, integral, and derivative gains, respectively. Depending on which gains are nonzero we have:

We can also write the PID control law in the time domain:

u(t)=kpe(t)+ki0te(τ)dτ+kde˙(t)u(t) = k_p e(t) + k_i \int_0^t e(\tau)\, \dd\tau + k_d \dot e(t)

Present-past-future interpretation

u(t)=kpe(t)present+ki0te(τ)dτpast+kde˙(t)futureu(t) = \underbrace{k_p e(t)}_{\textsf{present}} + \underbrace{k_i \int_0^t e(\tau)\, \dd\tau}_{\textsf{past}} + \underbrace{k_d \dot e(t)}_{\textsf{future}}

Example: drone altitude control

In this example, we look at the qualitative effects of adjusting proportional, integral, and derivative gains in a PID controller for controlling the altitude of a drone. No numbers or equations for now!

The user provides a reference altitude r(t)r(t), and the drone should adjust its thrust to achieve the desired altitude y(t)y(t) based on the feedback error e(t)=r(t)y(t)e(t) = r(t) - y(t).

Interactive demonstration. The interactive demo below shows the step response for the drone altitude PID controller. The drone starts on the ground and is trying to reach the reference altitude (dashed line). You can tune kpk_p, kik_i, and kdk_d by moving the sliders.

  1. Start by increasing kpk_p. The steady-state error decreases and the response is faster, but overshoot and oscillation increase. Pick kpk_p about half-way up the slider.

  2. Now increase kik_i. Notice the steady-state error goes to zero, but the integral gain introduces even more overshoot and oscillation, and the settling time gets longer. Let’s pick kik_i about half-way up the slider as well.

  3. Now increase kdk_d. Notice the damping effect; overshoot and oscillation decrease, and the settling time gets shorter. Try increasing kdk_d all the way up and see what happens! The system becomes overdamped and sluggish, so we don’t want to go that far. Let’s pick kdk_d about half-way up the slider as well.

Try experimenting with different combinations of kpk_p, kik_i, and kdk_d to see how they affect the transient response and steady-state error.

 

Video explanations of PID

The two videos below provide excellent explanations of PID control in the context of a drone and a self-driving car, respectively. They also provide intuitive explanations of the roles of the proportional, integral, and derivative terms in a PID controller.

Video explaining PID in the context of the drone example.

Video explaining PID in the context of a self-driving car.

PID control of second-order systems

Consider the second-order plant

G(s)=ωn2s2+2ζωns+ωn2G(s) = \frac{\omega_n^2}{s^2 + 2\zeta \omega_n s + \omega_n^2}

The transfer function of a PID controller can be written as

C(s)=U(s)E(s)=kp+kis+kds=kds2+kps+kis\begin{aligned} C(s) &= \frac{U(s)}{E(s)} = k_p + \frac{k_i}{s} + k_d s \\ &= \frac{k_d s^2 + k_p s + k_i}{s} \end{aligned}

So a PID controller introduces an integrator (pole at the origin) and two zeros (the roots of the numerator) to the loop transfer function. The closed-loop transfer function is:

GCL(s)=G(s)C(s)1+G(s)C(s)=ωn2(kds2+kps+ki)s(s2+2ζωns+ωn2)1+ωn2(kds2+kps+ki)s(s2+2ζωns+ωn2)=ωn2(kds2+kps+ki)s(s2+2ζωns+ωn2)+ωn2(kds2+kps+ki)=ωn2(kds2+kps+ki)s3+(2ζωn+ωn2kd)s2+ωn2(kp+1)s+ωn2ki\begin{aligned} G_\textsf{CL}(s) &= \frac{G(s)C(s)}{1 + G(s)C(s)} = \frac{\frac{\omega_n^2(k_d s^2 + k_p s + k_i)}{s(s^2 + 2\zeta \omega_n s + \omega_n^2)}}{1 + \frac{\omega_n^2(k_d s^2 + k_p s + k_i)}{s(s^2 + 2\zeta \omega_n s + \omega_n^2)}} \\ &= \frac{\omega_n^2(k_d s^2 + k_p s + k_i)}{s(s^2 + 2\zeta \omega_n s + \omega_n^2) + \omega_n^2(k_d s^2 + k_p s + k_i)} \\ &= \frac{\omega_n^2(k_d s^2 + k_p s + k_i)}{s^3 + (2\zeta\omega_n+\omega_n^2 k_d) s^2 + \omega_n^2(k_p+1) s + \omega_n^2 k_i} \end{aligned}

Pole placement

If we know where we want to place the poles of the closed-loop system (for example, to achieve a desired settling time, percent overshoot, or peak time), we can do it directly by finding the characteristic polynomial we want and matching coefficients to find the corresponding kpk_p, kik_i, and kdk_d values.

For example, suppose our open-loop plant G(s)G(s) had ζ=12\zeta = \frac{1}{2} and ωn=1\omega_n = 1. Substituting these values into the closed-loop transfer function (6), we have

GCL(s)=kds2+kps+kis3+(kd+1)s2+(kp+1)s+kiG_\textsf{CL}(s) = \frac{k_d s^2 + k_p s + k_i}{s^3 + (k_d+1) s^2 + (k_p+1) s + k_i}

If we want to place the poles at -1, -2, and -3, then the characteristic polynomial is

(s+1)(s+2)(s+3)=s3+6s2+11s+6(s+1)(s+2)(s+3) = s^3 + 6 s^2 + 11 s + 6

Matching coefficients, we have

kd=5,kp=10,ki=6\begin{aligned} k_d &= 5, & k_p &= 10, & k_i &= 6 \end{aligned}

Canceling the plant poles

As we saw with PI control of a first-order system, we can use the zeros of the PID controller to cancel out the poles of the plant if they are stable. If we use a PID controller of the form:

C(s)=k(s2+2ζωns+ωn2)sC(s) = \frac{k(s^2 + 2\zeta \omega_n s + \omega_n^2)}{s}

then the loop transfer function is

G(s)C(s)=ωn2s2+2ζωns+ωn2k(s2+2ζωns+ωn2)s=kωn2s\begin{aligned} G(s)C(s) &= \frac{\omega_n^2}{s^2 + 2\zeta \omega_n s + \omega_n^2} \cdot \frac{k(s^2 + 2\zeta \omega_n s + \omega_n^2)}{s} \\ &= \frac{k\omega_n^2}{s} \end{aligned}

which is a first-order system with a single pole at the origin. This means we have successfully canceled the two poles of the second-order plant, and we have a much simpler system to analyze and control. The closed-loop transfer function is

GCL(s)=G(s)C(s)1+G(s)C(s)=kωn2s1+kωn2s=kωn2s+kωn2G_\textsf{CL}(s) = \frac{G(s)C(s)}{1 + G(s)C(s)} = \frac{\frac{k\omega_n^2}{s}}{1 + \frac{k\omega_n^2}{s}} = \frac{k\omega_n^2}{s + k\omega_n^2}

so the closed-loop system is a first-order system with DC-gain 1 and time constant τ=1kωn2\tau = \frac{1}{k\omega_n^2}. This means we can make the response arbitrarily fast by increasing kk and there will never be overshoot.

2-DOF PID controller (bonus)

One way to get around the issue of the zeros is to use a 2-DOF PID (two degree-of-freedom PID) controller, which introduces additional tuning parameters bb and cc. The time-domain control law is

u(t)=kp(br(t)y(t))+ki0te(τ)dτ+kd(cr˙(t)y˙(t))u(t) = k_p (b r(t) - y(t)) + k_i \int_0^t e(\tau)\, \dd\tau + k_d (c \dot r(t) - \dot y(t))

Remember that e(t)=r(t)y(t)e(t) = r(t) - y(t), so the proportional term is now kp(br(t)y(t))k_p (b r(t) - y(t)) instead of kpe(t)k_p e(t), and the derivative term is now kd(cr˙(t)y˙(t))k_d (c \dot r(t) - \dot y(t)) instead of kde˙(t)k_d \dot e(t). This is more complicated than standard PID because the controller needs independent access to r(t)r(t) and y(t)y(t), not just the error e(t)e(t). If we pick b=1b=1 and c=1c=1, we recover the standard PID controller.

Let’s see what this additional complexity buys us. In the Laplace domain, the 2-DOF PID controller and plant equations look like:

U(s)=(kpb+kis+kdcs)R(s)(kp+kis+kds)Y(s)Y(s)=ωn2s2+2ζωns+ωn2U(s)\begin{aligned} U(s) &= \left( k_p b +\frac{k_i}{s} + k_d c s \right) R(s) - \left( k_p + \frac{k_i}{s} + k_d s \right) Y(s) \\ Y(s) &= \frac{\omega_n^2}{s^2 + 2\zeta \omega_n s + \omega_n^2} \cdot U(s) \end{aligned}

Eliminating U(s)U(s) and simplifying, we obtain the closed-loop transfer function:

GCL(s)=Y(s)R(s)=ωn2(kdcs2+kpbs+ki)s3+(2ζωn+ωn2kd)s2+ωn2(kp+1)s+ωn2kiG_\textsf{CL}(s) = \frac{Y(s)}{R(s)} = \frac{\omega_n^2(k_d c s^2 + k_p b s + k_i)}{s^3 + (2\zeta\omega_n+\omega_n^2 k_d) s^2 + \omega_n^2(k_p+1) s + \omega_n^2 k_i}

Compare this to the closed-loop transfer function for the standard PID controller in (6). The DC gain is still 1 (zero steady-state error), so the integral term is doing its job. The denominator is unchanged, but the numerator now includes bb and cc. Adjusting these parameters allows us to place the zeros of the controller independently of the poles, so we have ultimate flexibility in placing the poles and zeros anywhere we like! We can even pick b=c=0b=c=0 to eliminate the zeros entirely if we so choose.

The trade-off here is that 2-DOF PID controllers are more complex to implement and tune, since we now have five knobs to adjust instead of three, and we need independent access to r(t)r(t) and y(t)y(t), which may not always be possible.

Example: inverted pendulum

Consider an inverted pendulum. The equations of motion look like

m2θ¨(t)mgsinθ(t)=u(t)m \ell^2 \ddot\theta(t) - mg \ell \sin\theta(t) = u(t)

where θ=0\theta=0 represents the upright position and u(t)u(t) is the torque applied at the pivot. Linearizing around θ=0\theta=0 and making all the coefficients equal to 1 for simplicity, we obtain the transfer function

G(s)=Θ(s)U(s)=1s21G(s) = \frac{\Theta(s)}{U(s)} = \frac{1}{s^2-1}

The poles are located at s=±1s = \pm 1, so the system is unstable (of course!). Let’s design a controller step by step, adding more complexity only when necessary.

Control architecture

Let’s start with the standard unity feedback control architecture, but we’ll add an additional input d(t)d(t), which represents a disturbance. The disturbance occurs at the input uu because it is a force/torque. For example, it could be a person tapping the pendulum with their finger, or a constant wind force pushing the pendulum.

Block diagram of a feedback control system with a disturbance input.

Figure 3:Block diagram of a feedback control system with a disturbance input.

Our goal is not to track a reference signal r(t)r(t). We just want to stabilize the pendulum at the upright position. So we will use r(t)=0r(t)=0. Rather, our goal is to reject the disturbance d(t)d(t). In other words, when d(t)d(t) is nonzero, we want the output θ(t)\theta(t) to stay as close to zero as possible. We might have:

d(t)={δ(t)(a single tap at time t=0)H(t)(a constant wind starting at time t=0)d(t) = \begin{cases} \delta (t) & \textsf{(a single tap at time $t=0$)} \\ H(t) & \textsf{(a constant wind starting at time $t=0$)} \end{cases}

This is a common control problem known as disturbance rejection. Let’s redraw the block diagram to reflect this goal.

Modification of  with r=0 and the disturbance d treated as the reference.

Figure 4:Modification of Figure 3 with r=0r=0 and the disturbance dd treated as the reference.

The closed-loop transfer function from the disturbance d(t)d(t) to the output θ(t)\theta(t) is

GCL(s)=Θ(s)D(s)=G(s)1+G(s)C(s)G_\textsf{CL}(s) = \frac{\Theta(s)}{D(s)} = \frac{G(s)}{1 + G(s)C(s)}

P control

With proportional control, the closed-loop transfer function is

GCL(s)=1s211+kps21=1s2+(kp1)G_\textsf{CL}(s) = \frac{\frac{1}{s^2-1}}{1 + \frac{k_p}{s^2-1}} = \frac{1}{s^2 + (k_p-1)}

Depending on the value of kpk_p, the poles of the closed-loop system can be in different locations. Remembering our definitions of stability:

The usual next step is to add an integral term to eliminate the steady-state error, but this is a different controller architecture so we shouldn’t blindly apply our previous results from the table on steady-state errors. From Eq. (19) and the FVT, the output we are trying to drive to zero is given by

limtθ(t)=lims0sΘ(s)=lims0sG(s)D(s)1+G(s)C(s)\lim_{t\to\infty} \theta(t) = \lim_{s\to 0} s \Theta(s) = \lim_{s\to 0} \frac{s G(s)D(s)}{1 + G(s)C(s)}

Let’s look at the two disturbance cases.

PD control

With proportional-derivative control, the closed-loop transfer function is

GCL(s)=1s211+kp+kdss21=1s2+kds+(kp1)G_\textsf{CL}(s) = \frac{\frac{1}{s^2-1}}{1 + \frac{k_p + k_d s}{s^2-1}} = \frac{1}{s^2 + k_d s + (k_p - 1)}

Using our stability tests, we will have a stable system whenever kp>1k_p > 1 and kd>0k_d > 0. We can place the closed-loop poles anywhere we like!

What if we wanted to reject a step disturbance perfectly? We would need an integrator, which means we would need to use a PID controller instead of a PD controller.

PID control

With proportional-integral-derivative control, the closed-loop transfer function is

GCL(s)=1(s21)1+kds2+kps+kis(s21)=ss3+kds2+(kp1)s+kiG_\textsf{CL}(s) = \frac{\frac{1}{(s^2-1)}}{1 + \frac{k_d s^2 + k_p s + k_i}{s(s^2-1)}} = \frac{s}{s^3 + k_d s^2 + (k_p - 1) s + k_i}

Using our stability tests, we will have a stable system whenever kp>1k_p > 1 and kd>0k_d > 0 and ki>0k_i > 0 and kd(kp1)>kik_d(k_p-1) > k_i. Again, we can place the closed-loop poles anywhere we like by tuning kpk_p, kik_i, and kdk_d.

Interactive demonstration

The demo below shows the impulse or step response of the inverted pendulum system with PID control. You can adjust the gains and see how they affect the transient response and steady-state error. The axes on the right also show the pole locations of the closed-loop system.

  1. Verify that with only P control, the system is unstable for kp1k_p \leq 1 and marginally stable for kp>1k_p > 1.

  2. Add a derivative term and verify that the system is stable for kp>1k_p > 1 and kd>0k_d > 0, has zero error to an impulse input, but finite error to a step input.

  3. Add an integral term and verify that the system has zero error to both impulse and step inputs.

 

Practical considerations

When implementing PID control in practice, there are a few details worth knowing about. We will mention them briefly here so you are aware of them, but we won’t go into too much detail.

Integrator windup

In an ideal scenario, the integral term in a PID controller helps eliminate steady-state error. However, some things can go wrong in practice. Here is an example:

Suppose we are holding the drone in our hands and we turn on the controller. We get distracted and end up waiting a few minutes before releasing the drone. During this time, the error e(t)e(t) is very large because the drone is far from the reference altitude, so the integral term starts accumulating a large value. This is called integrator windup. By the time we release the drone, the integrator has accumulated an enormous value, which saturates the thrusters. The drone shoots up but by the time it reaches the desired altitude, the integral term is still very large, so it keeps shooting up and overshoots the target altitude by a lot. We then get a highly oscillatory response with massive overshoot, or worse, the drone crashes into the ground on the way back down!

To prevent this, we can use an anti-windup strategy, which typically involves limiting the value of the integral term to prevent it from growing too large. This can be done by introducing a saturation limit on the integral term, or by using a conditional integration approach where the integral term is only updated when the control input is not saturated.

Noise sensitivity

The PID controller looks like this in the time domain:

u(t)=kpe(t)+ki0te(τ)dτ+kde˙(t)u(t) = k_p e(t) + k_i \int_0^t e(\tau)\, \dd\tau + k_d \dot e(t)

The derivative term kde˙(t)k_d \dot e(t) can be problematic since differentiation amplifies high-frequency noise. The error signal e(t)=r(t)y(t)e(t) = r(t) - y(t) is typically measured using sensors, and these measurements can be noisy. There are several ways to mitigate this issue:


Test your knowledge

Solution to Exercise 1 #

A PID controller can be written as

C(s)=kp+kis+kds=kds2+kps+kis.C(s)=k_p+\frac{k_i}{s}+k_d s=\frac{k_d s^2+k_p s+k_i}{s}.

To cancel the stable plant poles at s=1s=-1 and s=4s=-4, choose the controller numerator to be proportional to (s+1)(s+4)=s2+5s+4(s+1)(s+4)=s^2+5s+4, i.e.

C(s)=k(s+1)(s+4)s=ks2+5s+4s.C(s)=k\,\frac{(s+1)(s+4)}{s}=k\,\frac{s^2+5s+4}{s}.

Matching coefficients with kds2+kps+kis\frac{k_d s^2+k_p s+k_i}{s} gives

kd=k,kp=5k,ki=4k.k_d=k,\qquad k_p=5k,\qquad k_i=4k.

Now compute the loop transfer function:

G(s)C(s)=1(s+1)(s+4)k(s+1)(s+4)s=ks.G(s)C(s)=\frac{1}{(s+1)(s+4)}\cdot k\frac{(s+1)(s+4)}{s}=\frac{k}{s}.

So the closed-loop transfer function is first-order:

GCL(s)=G(s)C(s)1+G(s)C(s)=ks1+ks=ks+k.G_{\textsf{CL}}(s)=\frac{G(s)C(s)}{1+G(s)C(s)} = \frac{\frac{k}{s}}{1+\frac{k}{s}} = \frac{k}{s+k}.

This has DC gain GCL(0)=1G_{\textsf{CL}}(0)=1, so the step steady-state error is zero.

For a first-order system with pole at k-k, the settling time is approximately

ts4k.t_s \approx \frac{4}{k}.

Setting ts=2t_s=2 gives k=2k=2. Therefore,

kd=2,kp=10,ki=8.k_d=2,\qquad k_p=10,\qquad k_i=8.
Solution to Exercise 2 #

The loop gain is

G(s)C(s)=kps(τs+1)+ki(τs+1)+kds2(s+1)s(τs+1).G(s)C(s)=\frac{k_p s(\tau s+1)+k_i(\tau s+1)+k_d s^2}{(s+1)s(\tau s+1)}.

Thus the closed-loop characteristic polynomial is the denominator of GC1+GC\frac{GC}{1+GC}, which is

(s+1)s(τs+1)+(kps(τs+1)+ki(τs+1)+kds2).(s+1)s(\tau s+1)+\Big(k_p s(\tau s+1)+k_i(\tau s+1)+k_d s^2\Big).

Collecting powers of ss, we have

τs3+(1+τ+kpτ+kd)s2+(1+kp+kiτ)s+ki.\tau s^3+\Big(1+\tau+k_p\tau+k_d\Big)s^2+\Big(1+k_p+k_i\tau\Big)s+k_i.

Because the leading coefficient is τ\tau, we match this polynomial to τ(s+2)3\tau(s+2)^3. Since

τ(s+2)3=τs3+6τs2+12τs+8τ,\tau(s+2)^3=\tau s^3+6\tau s^2+12\tau s+8\tau,

coefficient matching yields

1+τ+kpτ+kd=6τ,1+kp+kiτ=12τ,ki=8τ.\begin{aligned} 1+\tau+k_p\tau+k_d &= 6\tau,\\ 1+k_p+k_i\tau &= 12\tau,\\ k_i &= 8\tau. \end{aligned}

Now substitute τ=110\tau=\frac{1}{10}. From ki=8τk_i=8\tau we get

ki=45.k_i=\frac{4}{5}.

From 1+kp+kiτ=12τ1+k_p+k_i\tau=12\tau,

kp=12τ1kiτ=65145110=325.k_p=12\tau-1-k_i\tau=\frac{6}{5}-1-\frac{4}{5}\cdot\frac{1}{10} =\frac{3}{25}.

From 1+τ+kpτ+kd=6τ1+\tau+k_p\tau+k_d=6\tau,

kd=6τ(1+τ)kpτ=351110325110=64125.k_d=6\tau-(1+\tau)-k_p\tau=\frac{3}{5}-\frac{11}{10}-\frac{3}{25}\cdot\frac{1}{10} =-\frac{64}{125}.

Answer

kp=325,ki=45,kd=64125(τ=110).\boxed{ k_p=\frac{3}{25},\qquad k_i=\frac{4}{5},\qquad k_d=-\frac{64}{125}\qquad(\tau=\tfrac{1}{10})}.

(yes, the derivative gain is negative! This is a consequence of the derivative filter, which adds a zero to the controller that can affect the transient response. The negative derivative gain helps to counteract this effect and achieve the desired closed-loop pole placement.)