Adding PID controller to a MIMO state space system

I have a state space process with two inputs and one output. The inputs are highly coupled. I would like to add a PID controller to one of the inputs with the output as measurement value. But I’m having a hard time with the feedback function. Could any one guide me in the right direction?

Quick example code:

using ControlSystemsBase, Plots
# Define the state space system
A = [1.0 0.5; 0.2 0.8]
B = [1.0 0.3; 0.4 1.1]
C = [2.0 1.5]
D = [0.5 0.2]

sys = ss(A, B, C, D)

# Simulate the system with lsim
t = 0:1.0:10
u1 = sin.(t)
u2 = cos.(t)
u = [u1 u2]'
y, t, x = lsim(sys, u, t)

plot(y')


# Step 2: Design the PID controller
Kp = 1.0
Ki = 0.5
Kd = 0.1
Tf = 0.1  # Filter time constant
C = pid(Kp, Ki, Kd, Tf=Tf, state_space=true, Ts=1.0, form=:parallel)

G= feedback(sys.sys*C ??????)

If you want feedback a subset of the signals only, you use the advanced interface to feedback

  feedback(sys1::AbstractStateSpace, sys2::AbstractStateSpace;
           U1=:, Y1=:, U2=:, Y2=:, W1=:, Z1=:, W2=Int[], Z2=Int[],
           Wperm=:, Zperm=:, pos_feedback::Bool=false)
                ┌──────────────┐
        z1◄─────â”Ī     sys1     │◄──────w1
   ┌─── y1◄─────â”Ī              │◄──────u1 ◄─┐
   │            └──────────────┘            │
   │                                        α
   │            ┌──────────────┐            │
   └──▹ u2─────▹│     sys2     ├───────▹y2──┘
        w2─────▹│              ├───────▹z2
                └──────────────┘

It would look something like this

sysd = c2d(sys, C.Ts)
U1 = [1] # Index of the input of `sys1` that you want to connect
G = feedback(sysd, C; U1)

Set the other arguments to feedback as appropriate for what you want to include as inputs and outputs of the constructed feedback interconnection.

The output of G in my example above will be Z1 = :, that is, the output of the plant. The inputs will be W1 = :, that is, both inputs of the plant. If you want to keep only the unconnected input of the plant as input to the closed-loop system, set W1 = [2] (since you connected input [1] above).

2 Likes

Thanks for a quick reply, that seems to work. One more question, if I use a u vector as above in lsim would u[:,1] be my reference signal for the controller?

The example I gave have plant inputs as inputs of G. If you want to have the controller input be the input instead, make the controller sys1 and the plant sys2 and choose the signals appropriately.

I would like to set the PID reference, but also give the u[:,2] signal to the plant. Is that possible?

Yes, you can specify any signal as external inputs through the W1, W2 arguments.

Here are two ways to achieve this, one using feedback and one using connect with named systems from RobustAndOptimalControl.jl

using ControlSystemsBase, Plots
# Define the state space system
A = [1.0 0.5; 0.2 0.8]
B = [1.0 0.3; 0.4 1.1]
C = [2.0 1.5]
D = [0.5 0.2]
sys = ss(A, B, C, D)

Kp = 1.0
Ki = 0.5
Kd = 0.1
Tf = 0.1  # Filter time constant
C = pid(Kp, Ki, Kd, Tf=Tf, state_space=true, Ts=1.0, form=:parallel)

sysd = c2d(sys, C.Ts) # Discretize plant

# ==============================================================================
# Connection using feedback
# ==============================================================================
U2 = [1] # Index of the input of plant that you want to connect
W2 = [2] # Index of the plant to keep as external input
W1 = [1] # Index of C to keep as external input
Z1 = []  # Index of C to keep as external output
Z2 = (:) # Index of the plant to keep as external output
G2 = feedback(C, sysd; U2, W1, W2, Z1, Z2)

res = step(G2, 10)
plot(res, layout=2, sp=[1 2])

# ==============================================================================
# Using Named Statespace systems
# ==============================================================================
using RobustAndOptimalControl
Pn = named_ss(sysd, "P")
Cn = named_ss(C, "C")
fb = sumblock("e = r - y", Ts=C.Ts) # Feedback node
connections = [
    :Py => :y
    :e => :Cu
    :Cy => :Pu1
]

z1 = [:Py]          # External outputs
w1 = [:r, :Pu2]     # External inputs
G3 = connect([Pn, Cn, fb], connections; z1, w1)

# ==============================================================================
## Compare
# ==============================================================================
bodeplot(G2)
bodeplot!(G3)

As the plot indicates, the two results are identical.


Edit: I made a new release that allows the user in the latest example with named systems to instead pass external inputs and outputs using the more descriptive keyword arguments external_inputs, external_outputs instead of w1, z1 (the latter option will continue to work).

1 Like

Thanks this was great help