LimPIDTest
Test bench for a limited PID controller connected to a plant model with step input.
This test component connects a limited PID controller to a plant model and applies a step input as setpoint and a constant feedforward signal. The PID controller includes derivative, integral, and proportional actions with anti-windup and output limitations. The system response can be observed through the plant output and controller signals.
Usage
LimPIDTest()
Behavior
julia
using BlockComponents #hide
using ModelingToolkit #hide
@named sys = LimPIDTest() #hide
full_equations(sys) #hide
<< @example-block not executed in draft mode >>
Source
dyad
# Test bench for a limited PID controller connected to a plant model with step input.
#
# This test component connects a limited PID controller to a plant model and applies a step input as
# setpoint and a constant feedforward signal. The PID controller includes derivative, integral, and
# proportional actions with anti-windup and output limitations. The system response can be observed
# through the plant output and controller signals.
test component LimPIDTest
# Limited PID controller with configurable parameters
pid = LimPID(Td=0.1, Ti=0.5, y_max=1, y_min=-1, wp=1, wd=0, Nd=10, Ni=0.9, k_ff=1)
# Plant model to be controlled
plant = Plant()
# Step input signal used as setpoint for the controller
signal = Step(height=1)
# Constant signal for feedforward control
signal_ff = Constant(k=1)
relations
# Initial condition for the first state of the plant
initial plant.x1 = 0
# Initial condition for the plant output
initial plant.y = 0
# Connect step signal to controller setpoint input
connect(signal.y, pid.u_s)
# Connect plant output to controller measurement input
connect(plant.y, pid.u_m)
# Connect controller output to plant input
connect(pid.y, plant.u)
# Connect feedforward signal to controller feedforward input
connect(pid.u_ff, signal_ff.y)
metadata {
"Dyad": {"tests": {"case1": {"stop": 10, "expect": {"signals": ["plant.y", "pid.y"]}}}}
}
end
Flattened Source
dyad
# Test bench for a limited PID controller connected to a plant model with step input.
#
# This test component connects a limited PID controller to a plant model and applies a step input as
# setpoint and a constant feedforward signal. The PID controller includes derivative, integral, and
# proportional actions with anti-windup and output limitations. The system response can be observed
# through the plant output and controller signals.
test component LimPIDTest
# Limited PID controller with configurable parameters
pid = LimPID(Td=0.1, Ti=0.5, y_max=1, y_min=-1, wp=1, wd=0, Nd=10, Ni=0.9, k_ff=1)
# Plant model to be controlled
plant = Plant()
# Step input signal used as setpoint for the controller
signal = Step(height=1)
# Constant signal for feedforward control
signal_ff = Constant(k=1)
relations
# Initial condition for the first state of the plant
initial plant.x1 = 0
# Initial condition for the plant output
initial plant.y = 0
# Connect step signal to controller setpoint input
connect(signal.y, pid.u_s)
# Connect plant output to controller measurement input
connect(plant.y, pid.u_m)
# Connect controller output to plant input
connect(pid.y, plant.u)
# Connect feedforward signal to controller feedforward input
connect(pid.u_ff, signal_ff.y)
metadata {
"Dyad": {"tests": {"case1": {"stop": 10, "expect": {"signals": ["plant.y", "pid.y"]}}}}
}
end
Test Cases
julia
using BlockComponents
using ModelingToolkit, OrdinaryDiffEqDefault
using Plots
using CSV, DataFrames
snapshotsdir = joinpath(dirname(dirname(pathof(BlockComponents))), "test", "snapshots")
<< @setup-block not executed in draft mode >>
Test Case case1
julia
@mtkbuild model_case1 = LimPIDTest()
u0_case1 = []
prob_case1 = ODEProblem(model_case1, u0_case1, (0, 10))
sol_case1 = solve(prob_case1)
<< @setup-block not executed in draft mode >>
julia
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.plant.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "LimPIDTest_case1_sig0.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.plant.y], width=2, label="Actual value of plant.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of plant.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.pid.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "LimPIDTest_case1_sig1.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.pid.y], width=2, label="Actual value of pid.y")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of pid.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>