Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for PID component to accept symbolic parameters #231

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Blocks/Blocks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export Limiter, DeadZone, SlewRateLimiter
include("nonlinear.jl")

export Integrator, Derivative, FirstOrder, SecondOrder, StateSpace
export PI, LimPI, PID, LimPID
export PI, LimPI, PID, PD, LimPID
include("continuous.jl")

export AnalysisPoint, get_sensitivity, get_comp_sensitivity,
Expand Down
54 changes: 37 additions & 17 deletions src/Blocks/continuous.jl
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,15 @@
end

"""
PID(;name, k=1, Ti=false, Td=false, Nd=10, int__x=0, der__x=0)
PID(with_I = true, with_D = true; name, k=1, Ti=0.1, Td=0.1, Nd=10, int__x=0, der__x=0)

Text-book version of a PID-controller without actuator saturation and anti-windup measure.

# Parameters:

- `k`: Gain
- `Ti`: [s] Integrator time constant (Ti>0 required). If set to false, no integral action is used.
- `Td`: [s] Derivative time constant (Td>0 required). If set to false, no derivative action is used.
- `Ti`: [s] Integrator time constant (Ti>0 required). If `with_I` set to false, no integral action is used.
- `Td`: [s] Derivative time constant (Td>0 required). If `with_D` set to false, no derivative action is used.
- `Nd`: [s] Time constant for the derivative approximation (Nd>0 required; Nd=0 is ideal derivative).
- `int__x`: Initial value for the integrator.
- `der__x`: Initial value for the derivative state.
Expand All @@ -234,29 +234,42 @@

See also [`LimPID`](@ref)
"""
@component function PID(; name, k = 1, Ti = false, Td = false, Nd = 10, int__x = 0,
@component function PID(with_I = true, with_D = true; name, k = 1, Ti = 0.1, Td = 0.1,

Check warning on line 237 in src/Blocks/continuous.jl

View check run for this annotation

Codecov / codecov/patch

src/Blocks/continuous.jl#L237

Added line #L237 was not covered by tests
Nd = 10, int__x = 0,
der__x = 0)
with_I = !isequal(Ti, false)
with_D = !isequal(Td, false)
pars = @parameters begin
k = k
Ti = Ti
Td = Td
Nd = Nd
int__x = int__x
der__x = der__x

Check warning on line 246 in src/Blocks/continuous.jl

View check run for this annotation

Codecov / codecov/patch

src/Blocks/continuous.jl#L241-L246

Added lines #L241 - L246 were not covered by tests
end

@named err_input = RealInput() # control error
@named ctr_output = RealOutput() # control signal
!isequal(Ti, false) &&
(Ti ≥ 0 || throw(ArgumentError("Ti out of bounds, got $(Ti) but expected Ti ≥ 0")))
!isequal(Td, false) &&
(Td ≥ 0 || throw(ArgumentError("Td out of bounds, got $(Td) but expected Td ≥ 0")))
Nd > 0 || throw(ArgumentError("Nd out of bounds, got $(Nd) but expected Nd > 0"))

@named gainPID = Gain(k)
with_I &&
(@symcheck Ti ≥ 0 ||

Check warning on line 253 in src/Blocks/continuous.jl

View check run for this annotation

Codecov / codecov/patch

src/Blocks/continuous.jl#L253

Added line #L253 was not covered by tests
throw(ArgumentError("Ti out of bounds, got $(Ti) but expected Ti ≥ 0")))
with_D &&
(@symcheck Td ≥ 0 ||

Check warning on line 256 in src/Blocks/continuous.jl

View check run for this annotation

Codecov / codecov/patch

src/Blocks/continuous.jl#L256

Added line #L256 was not covered by tests
throw(ArgumentError("Td out of bounds, got $(Td) but expected Td ≥ 0")))

@symcheck Nd > 0 ||
throw(ArgumentError("Nd out of bounds, got $(Nd) but expected Nd ≥ 0"))

@named gainPID = Gain(; k)
@named addPID = Add3()
if with_I
@named int = Integrator(k = 1 / Ti, x = int__x)
@named int = Integrator(; k = 1 / Ti, x = int__x)
else
@named Izero = Constant(k = 0)
@named Izero = Constant(; k = 0)
end
if with_D
@named der = Derivative(k = Td, T = 1 / Nd, x = der__x)
@named der = Derivative(; k = Td, T = 1 / Nd, x = der__x)
else
@named Dzero = Constant(k = 0)
@named Dzero = Constant(; k = 0)
end
sys = [err_input, ctr_output, gainPID, addPID]
if with_I
Expand Down Expand Up @@ -286,7 +299,14 @@
else
push!(eqs, connect(Dzero.output, addPID.input3))
end
ODESystem(eqs, t, [], []; name = name, systems = sys)
ODESystem(eqs, t, [], pars; name = name, systems = sys)
end

with_I(type::Union{AbstractString, Symbol}) = contains(lowercase(string(type)), "i")
with_D(type::Union{AbstractString, Symbol}) = contains(lowercase(string(type)), "d")

function PID(type::Union{AbstractString, Symbol}; kwargs...)
PID(with_I(type), with_D(type); kwargs...)
end

"""
Expand Down
38 changes: 22 additions & 16 deletions test/Blocks/continuous.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Test
using ModelingToolkit, ModelingToolkitStandardLibrary, OrdinaryDiffEq
using ModelingToolkitStandardLibrary.Blocks
using OrdinaryDiffEq: ReturnCode.Success
Expand All @@ -12,9 +13,13 @@ an integrator with a constant input is often used together with the system under
=#

@testset "Constant" begin
@named c = Constant(; k = 1)
@named int = Integrator(x = 1)
@named iosys = ODESystem(connect(c.output, int.input), t, systems = [int, c])
pars = @parameters begin
k = 1
x = 1
end
@named c = Constant(; k)
@named int = Integrator(; x)
@named iosys = ODESystem(connect(c.output, int.input), t, [], pars; systems = [int, c])
sys = structural_simplify(iosys)
prob = ODEProblem(sys, Pair[], (0.0, 1.0))
sol = solve(prob, Rodas4())
Expand Down Expand Up @@ -167,9 +172,11 @@ end
end

@testset "PID" begin
@parameters Ti=0.5 Td=1 / 100
@named pid_controller = PID(; k = 3, Ti, Td)

re_val = 2
@named ref = Constant(; k = re_val)
@named pid_controller = PID(k = 3, Ti = 0.5, Td = 1 / 100)
@named plant = Plant()
@named fb = Feedback()
@named model = ODESystem([
Expand All @@ -178,8 +185,7 @@ end
connect(fb.output, pid_controller.err_input),
connect(pid_controller.ctr_output, plant.input),
],
t,
systems = [pid_controller, plant, ref, fb])
t, [], [Ti, Td]; systems = [pid_controller, plant, ref, fb])
sys = structural_simplify(model)
prob = ODEProblem(sys, Pair[], (0.0, 100.0))
sol = solve(prob, Rodas4())
Expand All @@ -188,15 +194,14 @@ end
@test sol[plant.output.u][end]≈re_val atol=1e-3 # zero control error after 100s

@testset "PI" begin
@named pid_controller = PID(k = 3, Ti = 0.5, Td = false)
@named pid_controller = PID("PI"; k = 3, Ti)
@named model = ODESystem([
connect(ref.output, fb.input1),
connect(plant.output, fb.input2),
connect(fb.output, pid_controller.err_input),
connect(pid_controller.ctr_output, plant.input),
],
t,
systems = [pid_controller, plant, ref, fb])
t, [], [Ti]; systems = [pid_controller, plant, ref, fb])
sys = structural_simplify(model)
prob = ODEProblem(sys, Pair[], (0.0, 100.0))
sol = solve(prob, Rodas4())
Expand All @@ -206,7 +211,7 @@ end
end

@testset "PD" begin
@named pid_controller = PID(k = 10, Ti = false, Td = 1)
@named pid_controller = PID("PD"; k = 10, Td = 1)
@named model = ODESystem([
connect(ref.output, fb.input1),
connect(plant.output, fb.input2),
Expand Down Expand Up @@ -284,7 +289,8 @@ end
@testset "LimPID" begin
re_val = 1
@named ref = Constant(; k = re_val)
@named pid_controller = LimPID(k = 3, Ti = 0.5, Td = 1 / 100, u_max = 1.5, u_min = -1.5,
@named pid_controller = LimPID(; k = 3, Ti = 0.5, Td = 1 / 100, u_max = 1.5,
u_min = -1.5,
Ni = 0.1 / 0.5)
@named plant = Plant()
@named model = ODESystem([
Expand All @@ -305,7 +311,7 @@ end
@test all(-1.5 .<= sol[pid_controller.ctr_output.u] .<= 1.5) # test limit

@testset "PI" begin
@named pid_controller = LimPID(k = 3, Ti = 0.5, Td = false, u_max = 1.5,
@named pid_controller = LimPID(; k = 3, Ti = 0.5, Td = false, u_max = 1.5,
u_min = -1.5, Ni = 0.1 / 0.5)
@named model = ODESystem([
connect(ref.output, pid_controller.reference),
Expand All @@ -325,7 +331,7 @@ end
@test all(-1.5 .<= sol[pid_controller.ctr_output.u] .<= 1.5) # test limit
end
@testset "PD" begin
@named pid_controller = LimPID(k = 10, Ti = false, Td = 1, u_max = 1.5,
@named pid_controller = LimPID(; k = 10, Ti = false, Td = 1, u_max = 1.5,
u_min = -1.5)
@named model = ODESystem([
connect(ref.output, pid_controller.reference),
Expand All @@ -346,7 +352,7 @@ end
end
@testset "set-point weights" begin
@testset "wp" begin
@named pid_controller = LimPID(k = 3, Ti = 0.5, Td = 1 / 100, u_max = 1.5,
@named pid_controller = LimPID(; k = 3, Ti = 0.5, Td = 1 / 100, u_max = 1.5,
u_min = -1.5, Ni = 0.1 / 0.5, wp = 0, wd = 1)
@named model = ODESystem([
connect(ref.output, pid_controller.reference),
Expand All @@ -367,7 +373,7 @@ end
@test all(-1.5 .<= sol[pid_controller.ctr_output.u] .<= 1.5) # test limit
end
@testset "wd" begin
@named pid_controller = LimPID(k = 3, Ti = 0.5, Td = 1 / 100, u_max = 1.5,
@named pid_controller = LimPID(; k = 3, Ti = 0.5, Td = 1 / 100, u_max = 1.5,
u_min = -1.5, Ni = 0.1 / 0.5, wp = 1, wd = 0)
@named model = ODESystem([
connect(ref.output, pid_controller.reference),
Expand All @@ -389,7 +395,7 @@ end
end
end
@testset "PI without AWM" begin
@named pid_controller = LimPID(k = 3, Ti = 0.5, Td = false, u_max = 1.5,
@named pid_controller = LimPID(; k = 3, Ti = 0.5, Td = false, u_max = 1.5,
u_min = -1.5, Ni = Inf)
@named model = ODESystem([
connect(ref.output, pid_controller.reference),
Expand Down
Loading