diff --git a/README.md b/README.md index 8c9af65..1e91595 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,17 @@ plot(t, [Ym Um], layout=(2,1), ylabel = ["y" "u"], legend=false) ``` Once again, the output looks identical and is therefore omitted here. +## Parameter conversion +The form of the PID controller used in this package is often referred to as ["standard form"](https://en.wikipedia.org/wiki/Proportional%E2%80%93integral%E2%80%93derivative_controller#Standard_form). If you have PID parameters on "parallel form" +```math +K_p (br-y) + K_i (r-y)/s - K_d s y/(Tf s + 1) +``` +you may convert these to the standard form +```math +K (b r - y + 1/T_i (r - y) - s T_d y/(1 + s T_d / N)) +``` +using the function `K, Ti, Td = parallel2standard(kp, ki, kd)` or, if a filter parameter is included, `K, Ti, Td, N = parallel2standard(kp, ki, kd, Tf)`. This function also accepts a vector of parameters in the same order, in which case a vector is returned. + ## Details - The derivative term only acts on the (filtered) measurement and not the command signal. It is thus safe to pass step changes in the reference to the controller. The parameter $b$ can further be set to zero to avoid step changes in the control signal in response to step changes in the reference. - Bumpless transfer when updating `K` is realized by updating the state `I`. See the docs for `set_K!` for more details. diff --git a/examples/juliac/juliac_pid.jl b/examples/juliac/juliac_pid.jl index c8adaa9..477232c 100644 --- a/examples/juliac/juliac_pid.jl +++ b/examples/juliac/juliac_pid.jl @@ -46,5 +46,5 @@ end # compile using something like this, modified to suit your local paths # cd(@__DIR__) -# run(`/home/fredrikb/repos/julia/julia --project --experimental /home/fredrikb/repos/julia/contrib/juliac.jl --output-lib juliac_pid --experimental --trim=unsafe-warn --compile-ccallable juliac_pid.jl`) +# run(`/home/fredrikb/repos/julia/julia --project --experimental /home/fredrikb/repos/julia/contrib/juliac.jl --output-lib juliac_pid --trim=unsafe-warn --experimental --compile-ccallable juliac_pid.jl`) # run(`ls -ltrh`) diff --git a/src/DiscretePIDs.jl b/src/DiscretePIDs.jl index 14bf60f..c6fb18f 100644 --- a/src/DiscretePIDs.jl +++ b/src/DiscretePIDs.jl @@ -1,6 +1,6 @@ module DiscretePIDs -export DiscretePID, calculate_control!, set_K!, set_Td!, set_Ti!, reset_state! +export DiscretePID, calculate_control!, set_K!, set_Td!, set_Ti!, reset_state!, parallel2standard using Printf @@ -201,4 +201,41 @@ function reset_state!(pid::DiscretePID) nothing end +""" + K, Ti, Td = parallel2standard(Kp, Ki, Kd) + +Convert parameters from form "parallel" form +``K_p + K_i/s + K_d s`` + +to "standard" form used in DiscretePID: +``K(1 + 1/(T_i s) + T_d s)`` + +You may provide either three arguments or an array with three elements in the same order. +""" +function parallel2standard(Kp, Ki, Kd) + Kp == 0 && throw(DomainError("Cannot convert to standard form when Kp=0")) + return (Kp, Kp / Ki, Kd / Kp) +end + +""" + K, Ti, Td, N = parallel2standard(Kp, Ki, Kd, Tf) + +Convert parameters from form "parallel" form with first-order filter +``K_p (br-y) + K_i (r-y)/s - K_d s y/(Tf s + 1)`` + +to "standard" form used in DiscretePID: +``K (br-y + (r-y)/(T_i s) - T_d s y/(T_d / N s + 1))`` + +You may provide either four arguments or an array with four elements in the same order. +""" +function parallel2standard(Kp, Ki, Kd, Tf) + Kp, Ti, Td = parallel2standard(Kp, Ki, Kd) + N = Td / Tf + return (Kp, Ti, Td, N) +end + +function parallel2standard(p) + return [parallel2standard(p...)...] +end + end diff --git a/test/runtests.jl b/test/runtests.jl index 9f0c5d5..4159e47 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -183,4 +183,12 @@ res2 = lsim(P, ctrl, 3) @test DiscretePID(Ts=1f0) isa DiscretePID{Float32} @test DiscretePID(Ts=1.0) isa DiscretePID{Float64} +kpkikdTf = rand(4) +kp, ki, kd, Tf = kpkikdTf +ps = parallel2standard(kpkikdTf) +K,Ti,Td,N = ps + +@test ControlSystemsBase.pid(kp, ki, kd; Tf, form = :parallel, filter_order=1) ≈ ControlSystemsBase.pid(K, Ti, Td; Tf=Td/N, form = :standard, filter_order=1) + + end