Skip to content

Commit 9de96de

Browse files
authored
Merge pull request #1923 from SciML/fb/mimo_disturbance
make `add_input_disturbance` handle MIMO systems
2 parents 1f045f5 + 72d17ee commit 9de96de

File tree

2 files changed

+53
-4
lines changed

2 files changed

+53
-4
lines changed

src/inputoutput.jl

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,14 +342,16 @@ function get_disturbance_system(dist::DisturbanceModel{<:ODESystem})
342342
end
343343

344344
"""
345-
(f_oop, f_ip), augmented_sys, dvs, p = add_input_disturbance(sys, dist::DisturbanceModel)
345+
(f_oop, f_ip), augmented_sys, dvs, p = add_input_disturbance(sys, dist::DisturbanceModel, inputs = nothing)
346346
347347
Add a model of an unmeasured disturbance to `sys`. The disturbance model is an instance of [`DisturbanceModel`](@ref).
348348
349349
The generated dynamics functions `(f_oop, f_ip)` will preserve any state and dynamics associated with disturbance inputs, but the disturbance inputs themselves will not be included as inputs to the generated function. The use case for this is to generate dynamics for state observers that estimate the influence of unmeasured disturbances, and thus require state variables for the disturbance model, but without disturbance inputs since the disturbances are not available for measurement.
350350
351351
`dvs` will be the states of the simplified augmented system, consisting of the states of `sys` as well as the states of the disturbance model.
352352
353+
For MIMO systems, all inputs to the system has to be specified in the argument `inputs`
354+
353355
# Example
354356
The example below builds a double-mass model and adds an integrating disturbance to the input
355357
```julia
@@ -390,18 +392,29 @@ dist = ModelingToolkit.DisturbanceModel(model.torque.tau.u, dmodel)
390392
```
391393
`f_oop` will have an extra state corresponding to the integrator in the disturbance model. This state will not be affected by any input, but will affect the dynamics from where it enters, in this case it will affect additively from `model.torque.tau.u`.
392394
"""
393-
function add_input_disturbance(sys, dist::DisturbanceModel)
395+
function add_input_disturbance(sys, dist::DisturbanceModel, inputs = nothing)
394396
t = get_iv(sys)
395397
@variables d(t)=0 [disturbance = true]
396-
@variables u(t)=0 [input = true]
398+
@variables u(t)=0 [input = true] # New system input
397399
dsys = get_disturbance_system(dist)
398400

401+
if inputs === nothing
402+
all_inputs = [u]
403+
else
404+
i = findfirst(isequal(dist.input), inputs)
405+
if i === nothing
406+
throw(ArgumentError("Input $(dist.input) indicated in the disturbance model was not found among inputs specified to add_input_disturbance"))
407+
end
408+
all_inputs = copy(inputs)
409+
all_inputs[i] = u # The input where the disturbance acts is no longer an input, the new input is u
410+
end
411+
399412
eqs = [dsys.input.u[1] ~ d
400413
dist.input ~ u + dsys.output.u[1]]
401414

402415
augmented_sys = ODESystem(eqs, t, systems = [sys, dsys], name = gensym(:outer))
403416

404-
(f_oop, f_ip), dvs, p = generate_control_function(augmented_sys, [u],
417+
(f_oop, f_ip), dvs, p = generate_control_function(augmented_sys, all_inputs,
405418
[d])
406419
(f_oop, f_ip), augmented_sys, dvs, p
407420
end

test/input_output_handling.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,39 @@ sys_simp, input_idxs = structural_simplify(sys, (; inputs = m_inputs, outputs =
318318
systems = [int, gain, c, fb])
319319
sys = structural_simplify(model)
320320
@test length(states(sys)) == length(equations(sys)) == 1
321+
322+
## Disturbance models when plant has multiple inputs
323+
using ModelingToolkit, LinearAlgebra
324+
using ModelingToolkit: DisturbanceModel, io_preprocessing, get_iv, get_disturbance_system
325+
using ModelingToolkitStandardLibrary.Blocks
326+
A, C = [randn(2, 2) for i in 1:2]
327+
B = [1.0 0; 0 1.0]
328+
@named model = Blocks.StateSpace(A, B, C)
329+
@named integrator = Blocks.StateSpace([-0.001;;], [1.0;;], [1.0;;], [0.0;;])
330+
331+
ins = collect(model.input.u)
332+
outs = collect(model.output.u)
333+
334+
disturbed_input = ins[1]
335+
@named dist_integ = DisturbanceModel(disturbed_input, integrator)
336+
337+
(f_oop, f_ip), augmented_sys, dvs, p = ModelingToolkit.add_input_disturbance(model,
338+
dist_integ,
339+
ins)
340+
341+
augmented_sys = complete(augmented_sys)
342+
matrices, ssys = linearize(augmented_sys,
343+
[
344+
augmented_sys.u,
345+
augmented_sys.model.input.u[2],
346+
augmented_sys.d,
347+
], outs)
348+
@test matrices.A [A [1; 0]; zeros(1, 2) -0.001]
349+
@test matrices.B == I
350+
@test matrices.C == [C zeros(2)]
351+
@test matrices.D == zeros(2, 3)
352+
353+
# Verify using ControlSystemsBase
354+
# P = ss(A,B,C,0)
355+
# G = ss(matrices...)
356+
# @test sminreal(G[1, 3]) ≈ sminreal(P[1,1])*dist

0 commit comments

Comments
 (0)