diff --git a/docs/src/man/creating_systems.md b/docs/src/man/creating_systems.md index 80a22c76f..1b66baa50 100644 --- a/docs/src/man/creating_systems.md +++ b/docs/src/man/creating_systems.md @@ -34,7 +34,7 @@ Continuous-time transfer function model ``` The transfer functions created using this method will be of type `TransferFunction{SisoRational}`. -For more general expressions, it is often more convenient to define `s = tf("s")`: +For more general expressions, it is sometimes more convenient to define `s = tf("s")` (only use this approach for low-order systems).: #### Example: ```julia julia> s = tf("s") @@ -295,10 +295,10 @@ The returned closed-loop system will have a state vector comprised of the state --- Closed-loop system from reference to output ``` -r ┌─────┐ ┌─────┐ -───►│ │ u │ │ y - │ C ├────►│ P ├─┬─► - -┌►│ │ │ │ │ + ┌─────┐ ┌─────┐ +r │ │ u │ │ y +──+►│ C ├────►│ P ├─┬─► + -▲ │ │ │ │ │ │ └─────┘ └─────┘ │ │ │ └─────────────────────┘ @@ -396,6 +396,32 @@ Here, we have reversed the order of `P` and `C` to get the correct sign of the c --- +Two degree of freedom control system with feedforward ``F`` and feedback controller ``C`` + +``` + +-------+ + | | + +-----> F +----+ + | | | | + | +-------+ | + | +-------+ | +-------+ +r | - | | | | | y ++--+-----> C +----+----> P +---+--> + | | | | | | + | +-------+ +-------+ | + | | + +--------------------------------+ +``` + +```math +Y = (F+C)\dfrac{P}{I + PC}R +``` + +Code: `feedback(P,C)*(F+C)` or `feedback2dof(P, C, F)` +- [`feedback2dof`](@ref) + +--- + Linear fractional transformation ``` @@ -445,9 +471,9 @@ I & P w_1 \\ w_2 \end{bmatrix} ``` -Code: This function requires the package [RobustAndOptimalControl.jl](https://juliacontrol.github.io/RobustAndOptimalControl.jl/dev/). +Code: ```julia -RobustAndOptimalControl.extended_gangoffour(P, C, pos=true) +extended_gangoffour(P, C, pos=true) # For SISO P S = G[1, 1] PS = G[1, 2] diff --git a/lib/ControlSystemsBase/src/connections.jl b/lib/ControlSystemsBase/src/connections.jl index a95ff86fc..9d3511352 100644 --- a/lib/ControlSystemsBase/src/connections.jl +++ b/lib/ControlSystemsBase/src/connections.jl @@ -331,6 +331,14 @@ function feedback(sys1::AbstractStateSpace, sys2::AbstractStateSpace; if !(isa(Y2, Colon) || allunique(Y2)); @warn "Connecting single output to multiple inputs Y2=$Y2"; end if !(isa(U1, Colon) || allunique(U1)); @warn "Connecting multiple outputs to a single input U1=$U1"; end if !(isa(U2, Colon) || allunique(U2)); @warn "Connecting a single output to multiple inputs U2=$U2"; end + U1 isa Number && (U1 = [U1]) + Y1 isa Number && (Y1 = [Y1]) + U2 isa Number && (U2 = [U2]) + Y2 isa Number && (Y2 = [Y2]) + W1 isa Number && (W1 = [W1]) + Z1 isa Number && (Z1 = [Z1]) + W2 isa Number && (W2 = [W2]) + Z2 isa Number && (Z2 = [Z2]) if (U1 isa Colon ? size(sys1, 2) : length(U1)) != (Y2 isa Colon ? size(sys2, 1) : length(Y2)) error("Lengths of U1 ($U1) and Y2 ($Y2) must be equal") @@ -394,13 +402,13 @@ function feedback(sys1::AbstractStateSpace, sys2::AbstractStateSpace; R1 = try inv(α*I - s2_D22*s1_D22) # slightly faster than α*inv(I - α*s2_D22*s1_D22) catch - error("Ill-posed feedback interconnection, I - α*s2_D22*s1_D22 or I - α*s2_D22*s1_D22 not invertible") + error("Ill-posed feedback interconnection, I - α*s2_D22*s1_D22 not invertible") end R2 = try inv(I - α*s1_D22*s2_D22) catch - error("Ill-posed feedback interconnection, I - α*s2_D22*s1_D22 or I - α*s2_D22*s1_D22 not invertible") + error("Ill-posed feedback interconnection, I - α*s1_D22*s2_D22 not invertible") end s2_B2R2 = s2_B2*R2 @@ -442,7 +450,7 @@ end feedback2dof(B,A,R,S,T) = tf(conv(B,T),zpconv(A,R,B,S)) """ - feedback2dof(P::TransferFunction, C::TransferFunction, F::TransferFunction) + feedback2dof(P, C, F) Return the transfer function `P(F+C)/(1+PC)` @@ -463,7 +471,7 @@ r | - | | | | | y ``` """ function feedback2dof(P::TransferFunction{TE}, C::TransferFunction{TE}, F::TransferFunction{TE}) where TE - !issiso(P) && error("Feedback not implemented for MIMO systems") + issiso(P) || return tf(feedback2dof(ss(P), ss(C), ss(F))) timeevol = common_timeevol(P, C, F) Pn,Pd = numpoly(P)[], denpoly(P)[] @@ -473,6 +481,10 @@ function feedback2dof(P::TransferFunction{TE}, C::TransferFunction{TE}, F::Trans tf(Cd*Pn*Fn + Pn*Cn*Fd, den, timeevol) end +function feedback2dof(P,C,F) + feedback(P,C)*(F+C) +end + """ lft(G, Δ, type=:l) diff --git a/lib/ControlSystemsBase/src/pid_design.jl b/lib/ControlSystemsBase/src/pid_design.jl index e47bdb002..822899024 100644 --- a/lib/ControlSystemsBase/src/pid_design.jl +++ b/lib/ControlSystemsBase/src/pid_design.jl @@ -131,8 +131,9 @@ The `form` can be chosen as one of the following (determines how the arguments ` This controller has negative feedback built in, and the closed-loop system from `r` to `y` is thus formed as ``` Cr, Cy = C[1, 1], C[1, 2] -feedback(P, Cy, pos_feedback=true)*Cr # Alternative 1 -feedback(P, -Cy)*Cr # Alternative 2 +feedback(P, Cy, pos_feedback=true)*Cr # Alternative 1 +feedback(P, -Cy)*Cr # Alternative 2 +feedback(P, C, U2=2, W2=1, W1=[], pos_feedback=true) # Alternative 3, less pretty but more efficient, returns smaller realization ``` """ function pid_2dof(args...; state_space = true, Ts = nothing, disc = :tustin, kwargs...) diff --git a/lib/ControlSystemsBase/src/sensitivity_functions.jl b/lib/ControlSystemsBase/src/sensitivity_functions.jl index e84d7b0c5..2789301e4 100644 --- a/lib/ControlSystemsBase/src/sensitivity_functions.jl +++ b/lib/ControlSystemsBase/src/sensitivity_functions.jl @@ -156,7 +156,7 @@ T = G[P.ny+1:end, P.ny+1:end] # Input complimentary sensitivity function The gang of four can be plotted like so ```julia Gcl = extended_gangoffour(G, C) # Form closed-loop system -bodeplot(Gcl, lab=["S" "CS" "PS" "T"], plotphase=false) |> display # Plot gang of four +bodeplot(Gcl, lab=["S" "PS" "CS" "T"], plotphase=false) |> display # Plot gang of four ``` Note, the last input of Gcl is the negative of the `PS` and `T` transfer functions from `gangoffour2`. To get a transfer matrix with the same sign as [`G_PS`](@ref) and [`input_comp_sensitivity`](@ref), call `extended_gangoffour(P, C, pos=false)`. See `glover_mcfarlane` from RobustAndOptimalControl.jl for an extended example. See also `ncfmargin` and `feedback_control` from RobustAndOptimalControl.jl. diff --git a/lib/ControlSystemsBase/test/test_connections.jl b/lib/ControlSystemsBase/test/test_connections.jl index e84b88e9f..c58c9265f 100644 --- a/lib/ControlSystemsBase/test/test_connections.jl +++ b/lib/ControlSystemsBase/test/test_connections.jl @@ -294,6 +294,8 @@ F = tf(1.0, [1,1]) @test feedback2dof(P0, C, 0*F) == feedback(P0*C) @test_nowarn feedback2dof(P0, C, F) +C = pid(1, 0, 1, form=:parallel) +hinfnorm(minreal(feedback2dof(ss(P0), ss(C*F), ss(F)) - feedback2dof(P0, C*F, F)))[1] < 1e-8 G1 = tf([1, 0],[1, 2, 2])