Skip to content

Commit

Permalink
Merge pull request #952 from JuliaControl/fb2dof
Browse files Browse the repository at this point in the history
make `feedback2dof` work for any system type
  • Loading branch information
baggepinnen authored Jan 12, 2025
2 parents 5982cc3 + a7cd0f0 commit 38afacc
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 14 deletions.
40 changes: 33 additions & 7 deletions docs/src/man/creating_systems.md
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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 ├─┬─►
-│ │ │ │ │
│ └─────┘ └─────┘ │
│ │
└─────────────────────┘
Expand Down Expand Up @@ -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

```
Expand Down Expand Up @@ -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]
Expand Down
20 changes: 16 additions & 4 deletions lib/ControlSystemsBase/src/connections.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)`
Expand All @@ -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)[]
Expand All @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions lib/ControlSystemsBase/src/pid_design.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
Expand Down
2 changes: 1 addition & 1 deletion lib/ControlSystemsBase/src/sensitivity_functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions lib/ControlSystemsBase/test/test_connections.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down

0 comments on commit 38afacc

Please sign in to comment.