A conveyor belt's motor speed is controlled by a microcontroller, once every 10 ms. Both parts are transfer functions, each a pair of coefficient lists (b, a) in powers of z^-1:
- the plant
G(z) = bG / aG: how the belt speed responds to the motor voltage. It cannot react within the same sample, sobG[0] = 0; - the controller
C(z) = bC / aC: how the voltage is computed from the speed error. A proportional gainKpis([Kp], [1]); Level 3's PI controller is([Kp + Ki, -Kp], [1, -1])(the1 - z^-1inais the running sum).
In the loop, the error e = r - y (target speed minus measured speed) goes into the controller and then the plant. The two in series are the loop transfer function L = C * G, so bL = bC * bG and aL = aC * aG (polynomial products). Closing the loop, Y = L * (R - Y), gives Y * (1 + L) = L * R, and the closed-loop transfer function from target to speed is
L bL / aL bL
T = ------- = ------------- = -----------
1 + L 1 + bL / aL aL + bL
So bT = bL and aT = aL + bL (a polynomial sum, the shorter list padded with zeros at the end). Finally divide both lists by aT[0] so that aT[0] = 1.
If the target speed steps from 0 to 1 and the closed loop is stable (every root of aT has abs < 1 - 1e-6; use the setup's roots_of), the speed settles at the final value T(1) = sum(bT) / sum(aT), and 1 - T(1) is the steady-state error. If the loop is not stable, there is no final value.
Write closed_loop(controller, plant) that returns (bT, aT, final), with final = None for a loop that is not stable. To see the step response, paste your run_transfer from the seismometer problem and press Run with plot(run_transfer(bT, aT, [1] * 80), kind="step").
Examples
Input: controller = ([2], [1]), plant = ([0, 0.2], [1, -0.8])
Output: ([0.0, 0.4], [1.0, -0.4], 0.6666666666666667)
Explanation: L = 0.4 z^-1 / (1 - 0.8 z^-1); aT = [1, -0.8] + [0, 0.4] = [1, -0.4]: a pole at 0.4,
stable. The speed settles at 0.4 / 0.6 = 2/3 of the target: proportional control leaves an offset.
Input: controller = ([2.5, -2], [1, -1]), plant = ([0, 0.2], [1, -0.8])
Output: ([0.0, 0.5, -0.4], [1.0, -1.3, 0.4], 1.0)
Explanation: a PI controller (Kp = 2, Ki = 0.5): poles 0.8 and 0.5, and no offset at all.
Input: controller = ([12], [1]), plant = ([0, 0.2], [1, -0.8])
Output: ([0.0, 2.4], [1.0, 1.6], None)
Explanation: too much gain: the closed-loop pole is at -1.6, and the speed oscillates ever harder.
Constraints
bG[0] == 0andaC[0] != 0,aG[0] != 0; every list has at most 6 entries- no closed-loop pole is within
1e-4of the circleabs(z) = 1 - 1e-6 - answers are compared with a tolerance of
1e-6; lists may be given as tuples
Goals
- Connect a controller and a plant in series by multiplying their transfer functions
- Form the unity-feedback closed loop T = L / (1 + L) with polynomial arithmetic
- Predict where the step response settles from T(1), but only when the loop is stable