[CONTROL SYSTEMS] - MIMO system through array of SISO


I would like to know if it’s possible, in ControlSystems.jl, to reach an MIMO system through an array of TransferFunctions.

Let’s suppose that we have a matrix H, which is related to a Y = H*U, where Y is a vector of outputs, and U is a vector of inputs. If Y{2,1} and U{2,1},H is H{2,2} (assuming that curly brackets refers to dimension).

Therefore, H can be stated as 4 transfer functions, each one related to H11, H12, H21 and H22. In this way, my intent is to create a matrix that can be stated as a “transfer function matrix”, and later, be converted to a StateSpace system.

H11 = tf([1,0],[1,2,3,4])
H12 = tf([2,0],[1,2,3,4])
H21 = tf([3,0],[1,2,3,4])
H22 = tf([4,0],[1,2,3,4])

H = Matrix{TransferFunction}(undef,2,2)

H[1,1] = H11
H[1,2] = H12
H[2,1] = H21
H[2,2] = H22

And then, i would like to do the following:


And, then, finding the poles of the system. But dampreport dont understand H as a MIMO transfer function, and instead, as a matrix (which really is, but i expected that it would be automatic to be a MIMO).

I know the fact that documentation provides the reference that MIMO systems should be implemented as an array of vectors, but this didn’t worked for me. But idk if i understand what i should understand as an “array of vectors”. (Constructors · ControlSystems.jl) An example of MIMO TF would be very welcomed.

I should use Transfer Functions because i only have the coefficients for them.

However, i would like to complement this question: if it’s possible to use a z^-1 format for discrete transform, instead of the standard z.

This might be a noob question, and idk if i should post in “New in Julia”.

Hi, welcome to Julia community.

julia> using ControlSystems

julia> N = [[1,2],[3,4]]
2-element Vector{Vector{Int64}}:
 [1, 2]
 [3, 4]

julia> D = [[5,6,7],[8,9,10]]
2-element Vector{Vector{Int64}}:
 [5, 6, 7]
 [8, 9, 10]

julia> G = tf(N,D)
TransferFunction{Continuous, ControlSystemsBase.SisoRational{Int64}}
Input 1 to output 1
    s + 2
5s^2 + 6s + 7

Input 1 to output 2
    3s + 4
8s^2 + 9s + 10

Continuous-time transfer function model

Hello and welcome!

Have a look at this page in the docs
in particular, the section on Converting an array of systems to a MIMO system

Your example would be

H_mimo = array2mimo(H)

or by construction directly

julia> [H11 H12; H21 H22]
TransferFunction{Continuous, ControlSystemsBase.SisoRational{Int64}}
Input 1 to output 1
s^3 + 2s^2 + 3s + 4

Input 1 to output 2
s^3 + 2s^2 + 3s + 4

Input 2 to output 1
s^3 + 2s^2 + 3s + 4

Input 2 to output 2
s^3 + 2s^2 + 3s + 4

Continuous-time transfer function model

If you convert this to a statespace system you will not get a minimal realization directly, use the function minreal if you want to obtain such a minimal realization:

julia> ss([H11 H12; H21 H22]).nx

julia> minreal(ss([H11 H12; H21 H22])).nx

This is unfortunately not supported at the moment.

I took a read at this section, and i noticed that in the example given, the matrix is created through a fill, so the type of data is already defined by P. In my problem, i let it more generic, (i should have stated it here).

Due to the fact that i get the coefficients from a polynomial and put it in a TF. But in the way that i organized the problem, this procedural is made in a loop. Therefore, the first idea that you have to storage, is to initialize an array. But i didn’t knew how exactly this array. So i did as following:

H = Matrix{TransferFunction}(undef,ny,nu)

Without specifying the type of TF. So, i wasn’t able to storage the TF’s to the array H.

Then, reading the answer that you provided, together with mr. @zdenek_hurak answer, things went more clear.

G = Matrix{TransferFunction{Discrete{Float64}, ControlSystemsBase.SisoRational{Float64}}}(undef,ny,nu);
G = ss(array2mimo(G))

And then, returning what i desired.

In this way, i would like to thank both, for providing an example and a path to a answer for a problem, which i should had stated it more clearly.

In many situations in which you are initializing an array before a loop, and then using the loop to populate it, you can make use of the map function instead. This saves you from having to figure out the type of the resulting array. Here’s an example

using ControlSystemsBase
w = 1:3
z = 0.1:0.2:0.7
H = map(Iterators.product(w, z)) do (ω, ζ)
    tf(ω^2, [1, 2ζ*ω, ω^2])

You may also find the following functions useful when dealing with types

julia> typeof(tf(1.0, [1, 1])) # Get the type of something
TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}

julia> eltype(H) # Get the element type of an array
TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}

julia> typeof(H)
Matrix{TransferFunction{Continuous, SisoRational{Float64}}} (alias for Array{TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}, 2})

julia> Matrix{typeof(tf(1.0, [1, 1]))}(undef, 2,2) # Create an array with element type given by the transfer function
2×2 Matrix{TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}}:
 #undef  #undef
 #undef  #undef

julia> similar(H, 2, 1) # create a similar array of the same type as another array, but modify its size
2×1 Matrix{TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}}:
1 Like