Making a PD Controller Component in ModelingToolkit.jl

Thanks!! It is difficult to keep track with the development of the documentation :-).

One more simple question… in the above firstOrder model,

function firstOrder(;name)
    @parameters K τ
    @variables f(t) y(t)
    eqs = [
        D(y) ~ (K*f - y)/τ
        ]
    ODESystem(eqs, name=name)
end

the function creates and returns an ODESystem. Is there a similar DAESystem? I only see headings for ODESystem, SDESystem, and PDESystem in the documentation…

If there is a DAESystem, is it required to specify initial values for the algebraic variables in addition to the states?

OK here is my go of making a PID controller. Notes:

  1. I was completely arbitrary in my choice of A B C D matrices and PID parameters
  2. Defaults for each component could be added
  3. Look how nice ModelingToolkit.jl makes it to access and plot particular variables by name in the solution

Any way this could be made cleaner, let me know.

using ModelingToolkit, OrdinaryDiffEq

## helper functions. reserving u_i and o_i as names of the ith input/output for each system
function get_input(sys, i)
    return getproperty(sys,  Symbol(:u, Symbol('₀' + i)))
end
get_inputs(sys, i) = get_input.((sys,), 1:i)

function get_output(sys, i)
    return getproperty(sys,  Symbol(:o, Symbol('₀' + i)))
end
get_outputs(sys, i) = get_output.((sys,), 1:i)


function linear_plant(A,B,C,D; name=:lp)
    
    num_inputs = size(B,2)
    num_states = size(A,1)
    num_outputs = size(C,1)
    @parameters t
    dt = Differential(t)
    @variables x[1:num_states](t)
    @variables u[1:num_inputs](t)
    @variables o[1:num_outputs](t)
    eqs = vcat(
                dt.(x) .~ A*x .+ B*u,
                o .~  C*x .+ D*u
    )    
    return ODESystem(eqs;
                        name=name
                        )   
end

function PID_controller(Pgain, Igain, Dgain, RC, num_inputs; name=:pid)
    @parameters t
    D = Differential(t)

    @variables u[1:num_inputs](t)
    @variables o[1:num_inputs](t)
    @variables int[1:num_inputs](t)
    @variables mv[1:num_inputs](t)
    eqs = vcat(
        D.(int) .~ Igain.*u, #integrator
        D.(mv) .~ u - RC.*(mv), # low pass filter 
        o .~ Dgain .* (u - RC.*(mv)) + Igain.*int + Pgain.*u
    )
    return ODESystem(eqs; name = name)
end



A = [0 1 0 0; -2 -2 0 0; 0 0 0 1; 0 0 -2 -2]
B = reshape([0 0 1. 0], 4,1)
C = reshape([1 0 0 0],1,4)
D = reshape([0], 1,1)
lp = linear_plant(A,B,C,D)
pid = PID_controller(1., 2.,1.5, 0.5, 1) 

function connect(plant, ref, pid)
    @parameters t 
    eqs = vcat(
            get_input(pid,1) ~ ref(t)  - get_output(plant,1),
            get_output(pid,1) ~ get_input(plant, 1),
    ) 
    return ODESystem(eqs; systems=[plant,pid])
end

all = connect(lp,sin,pid)
final_sys = structural_simplify(all)

u0 = final_sys.states .=> zeros(length(final_sys.states))
prob = ODEProblem(final_sys, u0, (0.,100.), jac=true, sparse=true)
sol = solve(prob, Tsit5())

using Plots
plot(sol, vars = [pid.int₁])
sol[lp.u₁]
1 Like

There is a function DAE_index_lowering that does automated reduction of DAE systems into ODESystems:

https://mtk.sciml.ai/stable/mtkitize_tutorials/modelingtoolkitize_index_reduction/

and yes no worries, I have the same issue with the evolving documentation!

1 Like

Normally, PID controllers are SISO = single input, single output, and most tuning rules are based on SISO systems.

Unconstrained Linear Quadratic Control with infinite horizon has some resemblance with PID control, however…

  • For a SISO system where the system is a double integrator (e.g., Newton’s law), this gives constant gain feedback from the two states (position and velocity), which can be interpreted as perfect PD control.
  • If a state estimator or observer is added, this gives akin to a filtered PD controller.
  • If one adds an extra state which is the integral of the control deviation and puts weight on this in the cost function, the result is a PID controller.

If the real system is not a double integrator, one could in principle assume a double integrator in the control synthesis, which would then lead to a P(I)D controller. But then there is the robustness issue.

1 Like

Thanks, the intuition relating LQR to PID is interesting, I didn’t know about it.
WRT my code…I just put arbitrary PID and ABCD parameters/matrices because I wanted a lazy MWE!

Also wrt finding system types, I find it’s useful to do evaluations of the form
ODESystem |> supertype |> supertype |> subtypes
to see what is available

1 Like

Note: the rapidly evolving documentation is a fantastic thing!, which means that there is rapid progress :-). But it is also tricky to keep up to date :-).

I do not think that the interpretation of an LQ-optimal (state) feedback controller as a PD controller requires a double-integrator model of the plant. What it does require is that the plant is modeled by a second-order state-space model where one of the states is the derivative of the other (in other words, the state variables are velocity and position). Proportional feedback on these two state variables is then, indeed, equivalent to a PD control on the position only.

1 Like

There doesn’t need to be. ODESystem takes care of all DAE stuff automatically. You can even build DAEProblem from an ODESystem if it’s fully implicit.

1 Like

Yeah, I think you’re right.