|
| 1 | +# Linear Analysis |
| 2 | + |
| 3 | +!!! danger "Experimental" |
| 4 | + The interface described here is currently experimental and at any time subject to breaking changes not respecting semantic versioning. |
| 5 | + |
| 6 | +Linear analysis refers to the process of linearizing a nonlinear model and analysing the resulting linear dynamical system. To facilitate linear analysis, ModelingToolkitStandardLibrary provides the concept of an [`AnalysisPoint`](@ref), which can be inserted in-between two causal blocks (such as those from the `Blocks` sub module). Once a model containing analysis points is built, several operations are available: |
| 7 | + |
| 8 | +- [`get_sensitivity`](@ref) get the [sensitivity function (wiki)](https://en.wikipedia.org/wiki/Sensitivity_(control_systems)), $S(s)$, as defined in the field of control theory. |
| 9 | +- [`get_comp_sensitivity`](@ref) get the complementary sensitivity function $T(s) : S(s)+T(s)=1$. |
| 10 | +- [`get_looptransfer`](@ref) get the (open) loop-transfer function where the loop starts and ends in the analysis point. |
| 11 | +- [`linearize`](@ref) can be called with two analysis points denoting the input and output of the linearized system. Parts of the model not appearing between the input and output will be removed. |
| 12 | +- [`open_loop`](@ref) return a new (nonlinear) system where the loop has been broken in the analysis point, i.e., the connection the analysis point usually implies has been removed. |
| 13 | + |
| 14 | +An analysis point can be created explicitly using the constructor [`AnalysisPoint`](@ref), or automatically when connecting two causal components using `connect`: |
| 15 | +```julia |
| 16 | +connect(comp1.output, :analysis_point_name, comp2.input) |
| 17 | +``` |
| 18 | + |
| 19 | +Of the above mentioned functions, all except for [`open_loop`](@ref) return the output of [`ModelingToolkit.linearize`](@ref), which is |
| 20 | +```julia |
| 21 | +matrices, simplified_sys = linearize(...) |
| 22 | +# matrices = (; A, B, C, D) |
| 23 | +``` |
| 24 | +i.e., `matrices` is a named tuple containing the matrices of a linear state-space system on the form |
| 25 | +```math |
| 26 | +\begin{aligned} |
| 27 | +\dot x &= Ax + Bu\\ |
| 28 | +y &= Cx + Du |
| 29 | +\end{aligned} |
| 30 | +``` |
| 31 | + |
| 32 | +## Example |
| 33 | +The following example builds a simple closed-loop system with a plant $P$ and a controller $C$. Two analysis points are inserted, one before and one after $P$. We then derive a number of sensitivity functions and show the corresponding code using the package ControlSystemBase.jl |
| 34 | + |
| 35 | +```@example LINEAR_ANALYSIS |
| 36 | +using ModelingToolkitStandardLibrary.Blocks, ModelingToolkit |
| 37 | +@named P = FirstOrder(k=1, T=1) # A first-order system with pole in -1 |
| 38 | +@named C = Gain(-1) # A P controller |
| 39 | +t = ModelingToolkit.get_iv(P) |
| 40 | +eqs = [ |
| 41 | + connect(P.output, :plant_output, C.input) # Connect with an automatically created analysis point called :plant_output |
| 42 | + connect(C.output, :plant_input, P.input) # Connect with an automatically created analysis point called :plant_input |
| 43 | +] |
| 44 | +sys = ODESystem(eqs, t, systems=[P,C], name=:feedback_system) |
| 45 | +
|
| 46 | +matrices_S = get_sensitivity(sys, :plant_input)[1] # Compute the matrices of a state-space representation of the (input)sensitivity function. |
| 47 | +matrices_T = get_comp_sensitivity(sys, :plant_input)[1] |
| 48 | +``` |
| 49 | +Continued linear analysis and design can be performed using ControlSystemsBase.jl. |
| 50 | +We create `ControlSystemsBase.StateSpace` objects using |
| 51 | +```@example LINEAR_ANALYSIS |
| 52 | +using ControlSystemsBase, Plots |
| 53 | +S = ss(matrices_S...) |
| 54 | +T = ss(matrices_T...) |
| 55 | +bodeplot([S, T], lab=["S" "" "T" ""]) |
| 56 | +``` |
| 57 | + |
| 58 | +The sensitivity functions obtained this way should be equivalent to the ones obtained with the code below |
| 59 | + |
| 60 | +```@example LINEAR_ANALYSIS_CS |
| 61 | +using ControlSystemsBase |
| 62 | +P = tf(1.0, [1, 1]) |> ss |
| 63 | +C = 1 # Negative feedback assumed in ControlSystems |
| 64 | +S = sensitivity(P, C) # or feedback(1, P*C) |
| 65 | +T = comp_sensitivity(P, C) # or feedback(P*C) |
| 66 | +``` |
| 67 | + |
| 68 | +We may also derive the loop-transfer function $L(s) = P(s)C(s)$ using |
| 69 | + |
| 70 | +```@example LINEAR_ANALYSIS |
| 71 | +matrices_L = get_looptransfer(sys, :plant_output)[1] |
| 72 | +L = ss(matrices_L...) |
| 73 | +``` |
| 74 | +which is equivalent to the following with ControlSystems |
| 75 | +```@example LINEAR_ANALYSIS_CS |
| 76 | +L = P*(-C) # Add the minus sign to build the negative feedback into the controller |
| 77 | +``` |
| 78 | + |
| 79 | + |
| 80 | +To obtain the transfer function between two analysis points, we call `linearize` |
| 81 | +```@example LINEAR_ANALYSIS |
| 82 | +matrices_P = linearize(sys, :plant_input, :plant_output)[1] |
| 83 | +``` |
| 84 | +this particular transfer function should be equivalent to the linear system `P`, i.e., equivalent to this call |
| 85 | +```@example LINEAR_ANALYSIS |
| 86 | +@unpack input, output = P # To get the correct namespace |
| 87 | +linearize(P, [input.u], [output.u])[1] |
| 88 | +``` |
| 89 | + |
| 90 | +## Index |
| 91 | +```@index |
| 92 | +Pages = ["linear_analysis.md"] |
| 93 | +``` |
| 94 | + |
| 95 | +```@autodocs |
| 96 | +Modules = [ModelingToolkitStandardLibrary.Blocks] |
| 97 | +Pages = ["Blocks/analysis_points.jl"] |
| 98 | +Order = [:function, :type] |
| 99 | +Private = false |
| 100 | +``` |
0 commit comments