Problem 465741 · hard · Level 04 Non-Linear Data Structures

Closing the Loop Around a Conveyor Motor

series connection · feedback · closed-loop transfer function · stability · final value · steady-state error

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, so bG[0] = 0;
  • the controller C(z) = bC / aC: how the voltage is computed from the speed error. A proportional gain Kp is ([Kp], [1]); Level 3's PI controller is ([Kp + Ki, -Kp], [1, -1]) (the 1 - z^-1 in a is 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] == 0 and aC[0] != 0, aG[0] != 0; every list has at most 6 entries
  • no closed-loop pole is within 1e-4 of the circle abs(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
Starting Python…