From b546d960ad268f5c79293810ce49626b58714b7a Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Fri, 24 Jun 2022 08:21:08 +0200 Subject: [PATCH 1/3] add check on fundamental limitations --- src/analysis.jl | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/analysis.jl b/src/analysis.jl index 6d1eba2ce..5d9ce4922 100644 --- a/src/analysis.jl +++ b/src/analysis.jl @@ -598,3 +598,72 @@ function gangofseven(P::LTISystem, C::LTISystem, F::LTISystem) RE = S*F return S, PS, CS, T, RY, RU, RE end + +""" + lower, upper = fundamental_limitations(P::LTISystem{Continuous},[ C]; verbose=true) + +Check the following list of rules-of-thumb for fundamental limitations on the gain-crossover frequency ωgc imposed by RHP poles and zeros and time delays. + +- A RHP zero z: gives an upper bound to bandwidth: ωgc / z < 0.5 +- A double RHP zero: ωgc / z < 0.25 +- A time delay L gives an upper bound to bandwidth: ωgc * L < 1 +- A RHP pole p gives a lower bound to bandwidth: ωgc / p > 2 +- A double RHP pole: ωgc / p > 4 +- A RHP pole zero pair requires: z / p > 4 + +These rules, give sensitivities Mₛ and Mₜ around 2 and phase lags of the nonminimum phase factor around 90°. + +If a controller `C` is provided as the second argument, a check of the gain-crossover frequency of `P*C` is performed. + +Ref: "Loop Shaping", Bo Bernhardsson and Karl Johan Åström 2016 +""" +function fundamental_limitations(P::LTISystem{Continuous}, C=nothing; verbose=true) + + ωgc_l = 0.0 + ωgc_u = Inf + + if P isa DelayLtiSystem + τ = maximum(P.Tau) + verbose && @info "ωgc upper bounded by a time delay to 1/τ = $(1/τ) rad/s" + ωgc_u = min(ωgc_u, 1/τ) + return (; ωgc_l, ωgc_u) + end + + ps = poles(P) + zs = tzeros(P) + rhpp = filter(p->real(p) > 0, ps) + rhpz = filter(z->real(z) > 0, zs) + + p = maximum(abs, rhpp) + if length(rhpp) >= 2 + verbose && @info "ωgc lower bounded by double RHP pole to 4*p = $(4p) rad/s" + ωgc_l = 4p + + elseif length(rhpp) == 1 + verbose && @info "ωgc lower bounded by RHP pole to 2*p = $(2p) rad/s" + ωgc_l = 2p + end + + z = maximum(abs, rhpz) + if length(rhpz) >= 2 + verbose && @info "ωgc upper bounded by double RHP zero to z/4 = $(z/4) rad/s" + ωgc_u = z/4 + elseif length(rhpz) == 1 + verbose && @info "ωgc upper bounded by RHP zero to z/2 = $(z/2) rad/s" + ωgc_u = z/2 + end + + if length(rhpp) > 1 && length(rhpz) > 1 + verbose && @info "RHP pole and zero requires z > 4p which is $(z > 4p)" + end + + if C !== nothing + wgm, gm, wpm, pm = margin(P*C) + # ωgc = wpm + if isfinite(pm[]) # No gain crossover if phase margin is not finite + (ωgc_l ≤ maximum(wpm) ≤ ωgc_u) || error("Failed to meet rules-of-thumb with the given controller, ωgc = $wpm") + end + end + + (; ωgc_l, ωgc_u) +end \ No newline at end of file From 8fb54e6710418092b80ba1b2a7a9a8332e7c6844 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Fri, 14 Jul 2023 10:43:31 +0200 Subject: [PATCH 2/3] add some checks --- src/analysis.jl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/analysis.jl b/src/analysis.jl index 5d9ce4922..8721b8290 100644 --- a/src/analysis.jl +++ b/src/analysis.jl @@ -617,7 +617,7 @@ If a controller `C` is provided as the second argument, a check of the gain-cros Ref: "Loop Shaping", Bo Bernhardsson and Karl Johan Åström 2016 """ -function fundamental_limitations(P::LTISystem{Continuous}, C=nothing; verbose=true) +function fundamental_limitations(P::LTISystem{Continuous}, C=nothing; verbose=true, Ms = nothing) ωgc_l = 0.0 ωgc_u = Inf @@ -642,6 +642,11 @@ function fundamental_limitations(P::LTISystem{Continuous}, C=nothing; verbose=tr elseif length(rhpp) == 1 verbose && @info "ωgc lower bounded by RHP pole to 2*p = $(2p) rad/s" ωgc_l = 2p + if Ms isa Number + ωgc_l2 = p*√((Ms+1)/(Ms-1)) # https://www.control.lth.se/fileadmin/control/staff/KJ/200113FeedbackFundamentals.pdf slide 51 + verbose && @info "ωgc lower bounded by RHP pole and Ms to p*√((Ms+1)/(Ms-1)) = $(p*√((Ms+1)/(Ms-1))) rad/s" + ωgc_l = max(ωgc_l, ωgc_l2) + end end z = maximum(abs, rhpz) @@ -651,10 +656,18 @@ function fundamental_limitations(P::LTISystem{Continuous}, C=nothing; verbose=tr elseif length(rhpz) == 1 verbose && @info "ωgc upper bounded by RHP zero to z/2 = $(z/2) rad/s" ωgc_u = z/2 + + if Ms isa Number + ωgc_u2 = z*√((Ms-1)/(Ms+1)) # https://www.control.lth.se/fileadmin/control/staff/KJ/200113FeedbackFundamentals.pdf slide 51 + verbose && @info "ωgc lower bounded by RHP pole and Ms to z*√((Ms-1)/(Ms+1)) = $(z*√((Ms-1)/(Ms+1))) rad/s" + ωgc_u = min(ωgc_u, ωgc_u2) + end + end if length(rhpp) > 1 && length(rhpz) > 1 verbose && @info "RHP pole and zero requires z > 4p which is $(z > 4p)" + verbose && @info "Ms and Mt are always larger than |(p+z) / (p-z)| = $(abs((p+z) / (p-z))) due to RHP pole and zero pair" end if C !== nothing From dc045d4fae72f5a6e1c92b9a900977d7c69197c3 Mon Sep 17 00:00:00 2001 From: Fredrik Bagge Carlson Date: Fri, 10 Jan 2025 16:00:49 +0100 Subject: [PATCH 3/3] Update analysis.jl --- src/analysis.jl | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/analysis.jl b/src/analysis.jl index 8721b8290..345e91e84 100644 --- a/src/analysis.jl +++ b/src/analysis.jl @@ -601,20 +601,15 @@ end """ lower, upper = fundamental_limitations(P::LTISystem{Continuous},[ C]; verbose=true) - Check the following list of rules-of-thumb for fundamental limitations on the gain-crossover frequency ωgc imposed by RHP poles and zeros and time delays. - - A RHP zero z: gives an upper bound to bandwidth: ωgc / z < 0.5 - A double RHP zero: ωgc / z < 0.25 - A time delay L gives an upper bound to bandwidth: ωgc * L < 1 - A RHP pole p gives a lower bound to bandwidth: ωgc / p > 2 - A double RHP pole: ωgc / p > 4 - A RHP pole zero pair requires: z / p > 4 - These rules, give sensitivities Mₛ and Mₜ around 2 and phase lags of the nonminimum phase factor around 90°. - If a controller `C` is provided as the second argument, a check of the gain-crossover frequency of `P*C` is performed. - Ref: "Loop Shaping", Bo Bernhardsson and Karl Johan Åström 2016 """ function fundamental_limitations(P::LTISystem{Continuous}, C=nothing; verbose=true, Ms = nothing) @@ -624,7 +619,7 @@ function fundamental_limitations(P::LTISystem{Continuous}, C=nothing; verbose=tr if P isa DelayLtiSystem τ = maximum(P.Tau) - verbose && @info "ωgc upper bounded by a time delay to 1/τ = $(1/τ) rad/s" + verbose && @info "ωgc upper bounded by a time delay to ωgc < 1/τ = $(1/τ) rad/s" ωgc_u = min(ωgc_u, 1/τ) return (; ωgc_l, ωgc_u) end @@ -636,30 +631,30 @@ function fundamental_limitations(P::LTISystem{Continuous}, C=nothing; verbose=tr p = maximum(abs, rhpp) if length(rhpp) >= 2 - verbose && @info "ωgc lower bounded by double RHP pole to 4*p = $(4p) rad/s" + verbose && @info "ωgc lower bounded by double RHP pole to ωgc > 4*p = $(4p) rad/s" ωgc_l = 4p elseif length(rhpp) == 1 - verbose && @info "ωgc lower bounded by RHP pole to 2*p = $(2p) rad/s" + verbose && @info "ωgc lower bounded by RHP pole to ωgc > 2*p = $(2p) rad/s" ωgc_l = 2p if Ms isa Number ωgc_l2 = p*√((Ms+1)/(Ms-1)) # https://www.control.lth.se/fileadmin/control/staff/KJ/200113FeedbackFundamentals.pdf slide 51 - verbose && @info "ωgc lower bounded by RHP pole and Ms to p*√((Ms+1)/(Ms-1)) = $(p*√((Ms+1)/(Ms-1))) rad/s" + verbose && @info "ωgc lower bounded by RHP pole and Ms to ωgc > p*√((Ms+1)/(Ms-1)) = $(p*√((Ms+1)/(Ms-1))) rad/s" ωgc_l = max(ωgc_l, ωgc_l2) end end z = maximum(abs, rhpz) if length(rhpz) >= 2 - verbose && @info "ωgc upper bounded by double RHP zero to z/4 = $(z/4) rad/s" + verbose && @info "ωgc upper bounded by double RHP zero to ωgc < z/4 = $(z/4) rad/s" ωgc_u = z/4 elseif length(rhpz) == 1 - verbose && @info "ωgc upper bounded by RHP zero to z/2 = $(z/2) rad/s" + verbose && @info "ωgc upper bounded by RHP zero to ωgc < z/2 = $(z/2) rad/s" ωgc_u = z/2 if Ms isa Number ωgc_u2 = z*√((Ms-1)/(Ms+1)) # https://www.control.lth.se/fileadmin/control/staff/KJ/200113FeedbackFundamentals.pdf slide 51 - verbose && @info "ωgc lower bounded by RHP pole and Ms to z*√((Ms-1)/(Ms+1)) = $(z*√((Ms-1)/(Ms+1))) rad/s" + verbose && @info "ωgc upper bounded by RHP zero and Ms to ωgc < z*√((Ms-1)/(Ms+1)) = $(z*√((Ms-1)/(Ms+1))) rad/s" ωgc_u = min(ωgc_u, ωgc_u2) end @@ -674,9 +669,13 @@ function fundamental_limitations(P::LTISystem{Continuous}, C=nothing; verbose=tr wgm, gm, wpm, pm = margin(P*C) # ωgc = wpm if isfinite(pm[]) # No gain crossover if phase margin is not finite - (ωgc_l ≤ maximum(wpm) ≤ ωgc_u) || error("Failed to meet rules-of-thumb with the given controller, ωgc = $wpm") + if (ωgc_l ≤ maximum(wpm) ≤ ωgc_u) + verbose && @info("Controller satisifes rules of thumb, ωgc = $wpm") + else + @error("Failed to meet rules-of-thumb with the given controller, ωgc = $wpm") + end end end (; ωgc_l, ωgc_u) -end \ No newline at end of file +end