Skip to content

Commit 35b15b1

Browse files
authored
test on v1.6 (#31)
* test on v1.6 * 1.6 compat
1 parent 830bed7 commit 35b15b1

6 files changed

Lines changed: 53 additions & 27 deletions

File tree

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
version:
13-
- '1.7'
13+
- '1.6'
1414
os:
1515
- ubuntu-latest
1616
arch:

Project.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "RobustAndOptimalControl"
22
uuid = "21fd56a4-db03-40ee-82ee-a87907bee541"
33
authors = ["Fredrik Bagge Carlson", "Marcus Greiff"]
4-
version = "0.2.1"
4+
version = "0.2.2"
55

66
[deps]
77
ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66"
@@ -21,7 +21,7 @@ UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
2121

2222
[compat]
2323
ComponentArrays = "0.9, 0.10"
24-
ControlSystems = "0.11.2"
24+
ControlSystems = "0.11.6"
2525
Distributions = "0.25"
2626
IntervalArithmetic = "0.20"
2727
MatrixEquations = "2"
@@ -30,7 +30,7 @@ MonteCarloMeasurements = "1.0"
3030
Optim = "1.5"
3131
RecipesBase = "1"
3232
UnPack = "1.0"
33-
julia = "1.7"
33+
julia = "1.6"
3434

3535
[extras]
3636
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"

docs/src/uncertainty.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,26 @@ P_{32} & P_{33}\\
235235
```
236236
into $P_{22}$ for the purposes of uncertainty analysis (use `ss` to convert it to a standard statespace object), and later use [`partition`](@ref) to recover the internal block structure.
237237

238-
Given an [`UncertainSS`](@ref) $P$, we can close the loop around $\Delta$ by calling `starprod(Δ, P)` or `lft(P, Δ, :u)` (note the different order of the arguments), and given an [`ExtendedStateSpace`](@ref), we can close the loop around `K` by calling `starprod(P, K)` or `lft(P, K)` (using positive feedback). This works even if `P` is a regular statespace object, in which case the convention is that the inputs and outputs are ordered as in the block diagrams above. The number of signals that will be connected by [`lft`](@ref) is determined by the input-output arity of $K$ and $\Delta$ respectively.
238+
Given an [`UncertainSS`](@ref) $P$, we can close the loop around $\Delta$ by calling `lft(P, Δ, :u)`, and given an [`ExtendedStateSpace`](@ref), we can close the loop around `K` by calling `starprod(P, K)` or `lft(P, K)` (using positive feedback). This works even if `P` is a regular statespace object, in which case the convention is that the inputs and outputs are ordered as in the block diagrams above. The number of signals that will be connected by [`lft`](@ref) is determined by the input-output arity of $K$ and $\Delta$ respectively.
239239

240240
We have the following methods for `lft` (in addition to the standard ones in ControlSystems.jl)
241241
- `lft(G::UncertainSS, K::LTISystem)` forms the lower LFT closing the loop around $K$.
242242
- `lft(G::UncertainSS, Δ::AbstractArray=G.Δ)` forms the upper LFT closing the loop around $\Delta$.
243243
- `lft(G::ExtendedStateSpace, K)` forms the lower LFT closing the loop around $K$.
244244

245+
### Robust stability and performance
246+
To check robust stability of the system in the last block diagram (with or without $z$ and $w$), we can use the functions [`structured_singular_value`](@ref), [`robstab`](@ref) and [`diskmargin`](@ref).
247+
248+
Currently, [`structured_singular_value`](@ref) is rather limited and supports diagonal complex blocks only. If $\Delta$ is a single full complex block, `opnorm(P.M) < 1` is the condition for stability.
249+
250+
Robust performance can be verified by introducing an additional fictitious "performance perturbation" $\Delta_p$ which is a full complex block, around which we close the loop from $z$ to $w$ and check the [`structured_singular_value`](@ref) with the augmented perturbation block
251+
```math
252+
\Delta_a = \begin{bmatrix}
253+
\Delta & 0\\
254+
0 & \Delta_p\\
255+
\end{bmatrix}
256+
```
257+
245258

246259

247260
### Examples

src/uncertainty_interface.jl

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,18 @@ function Base.promote_rule(::Type{U}, ::Type{L}) where {U <: UncertainSS, L <: L
148148
UncertainSS
149149
end
150150

151+
function Base.promote_rule(::Type{U}, ::Type{L}) where {U <: UncertainSS, L <: AbstractArray}
152+
UncertainSS
153+
end
154+
151155
function Base.convert(::Type{U}, d:) where {U <: UncertainSS}
152156
uss(d)
153157
end
154158

159+
function Base.convert(::Type{U}, d::AbstractArray) where {U <: UncertainSS}
160+
uss(d)
161+
end
162+
155163
function Base.convert(::Type{U}, s::LTISystem) where {U <: UncertainSS}
156164
s isa UncertainSS && return s
157165
sys = ss(s)
@@ -165,8 +173,8 @@ Base.:+(n::δ, sys::UncertainSS{TE}) where TE <: ControlSystems.TimeEvolution =
165173
Base.:+(sys::UncertainSS{TE}, n::Number) where TE <: ControlSystems.TimeEvolution = +(sys, uss(n))
166174
Base.:+(n::Number, sys::UncertainSS{TE}) where TE <: ControlSystems.TimeEvolution = +(uss(n), sys)
167175

168-
Base.:*(G::LTISystem, d::UncertainElement) = uss(G) * uss(d)
169-
Base.:*(d::UncertainElement, G::LTISystem) = uss(d) * uss(G)
176+
Base.:*(G::LTISystem, d::UncertainElement) = uss(ss(G)) * uss(d)
177+
Base.:*(d::UncertainElement, G::LTISystem) = uss(d) * uss(ss(G))
170178

171179
"""
172180
uss(D11, D12, D21, D22, Δ, Ts = nothing)
@@ -207,11 +215,16 @@ uss(n::Number, Ts=nothing) = uss(zeros(0,0), zeros(0,1), zeros(1,0), 1, [], Ts)
207215
"""
208216
uss(D::AbstractArray, Δ, Ts = nothing)
209217
210-
If only a single `D` matrix is provided, it's treated as `D11`.
218+
If only a single `D` matrix is provided, it's treated as `D11` if Δ is given, and as `D22` if no Δ is provided.
211219
"""
212-
function uss(D::AbstractArray, Δ, Ts=nothing)
213-
length(Δ) == size(D,1) || throw(DimensionMismatch("length(Δ) != size(D,1)"))
214-
uss(D, zeros(size(D,1),0), zeros(0,size(D,2)), zeros(0,0), Δ, Ts)
220+
function uss(D::AbstractArray, Δ=[], Ts=nothing)
221+
if length(Δ) == size(D,1)
222+
uss(D, zeros(size(D,1),0), zeros(0,size(D,2)), zeros(0,0), Δ, Ts)
223+
elseif isempty(Δ)
224+
uss(zeros(0,0), zeros(0,size(D,2)), zeros(size(D,1),0), D, Δ, Ts)
225+
else
226+
throw(DimensionMismatch("length(Δ) != size(D,1)"))
227+
end
215228
end
216229

217230
uss(s::UncertainSS) = s
@@ -474,7 +487,7 @@ function ss2particles(G::Vector{<:AbstractStateSpace})
474487
B = reduce(hcat, vec.(getproperty.(G, :B))) |> pdp
475488
C = reduce(hcat, vec.(getproperty.(G, :C))) |> pdp
476489
D = reduce(hcat, vec.(getproperty.(G, :D))) |> pdp
477-
(; nx,ny,nu) = G[1]
490+
@unpack nx,ny,nu = G[1]
478491
A = reshape(A, nx, nx)
479492
B = reshape(B, nx, nu)
480493
C = reshape(C, ny, nx)

test/test_diskmargin.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ plot(dms)
5555
## Loop at a time
5656
a = 10
5757
P = ss([0 a; -a 0], I(2), [1 a; -a 1], 0)
58-
K = ss(1.0I(2))
58+
K = ss(I(2))
5959

6060
Li = K*P
6161
Lo = P*K
@@ -86,7 +86,7 @@ w = 2π .* exp10.(LinRange(-2, 2, 300))
8686
dm = loop_diskmargin(L3, 0, 4.05)
8787
@test dm[1].α 0.794418036911981 rtol=1e-3
8888

89-
dm = diskmargin(L3, ss(1.0I(3), L3.Ts), 0, w)
89+
dm = diskmargin(L3, ss(I(3), L3.Ts), 0, w)
9090
plot(dm.simultaneous)
9191
plot!(dm.simultaneous_input)
9292
plot!(dm.simultaneous_output)
@@ -98,7 +98,7 @@ plot!(dm.output)
9898

9999
a = 10
100100
P = ss([0 a; -a 0], I(2), [1 a; -a 1], 0)
101-
K = ss(1.0I(2))
101+
K = ss(I(2))
102102

103103
w = 2π .* exp10.(LinRange(-2, 2, 300))
104104
# @time bisect_a(P, K, w)
@@ -146,8 +146,8 @@ L3 = let
146146
end
147147

148148

149-
a = bisect_a(L3, ss(1.0I(3), L3.Ts), w; tol=2e-3)
150-
au = bisect_a(L3, ss(1.0I(3), L3.Ts), w; tol=2e-3, upper=true, N=256)
149+
a = bisect_a(L3, ss(I(3), L3.Ts), w; tol=2e-3)
150+
au = bisect_a(L3, ss(I(3), L3.Ts), w; tol=2e-3, upper=true, N=256)
151151
plot(w, a, xscale=:log10, xlabel="Frequency", ylims=(0,Inf))
152152
plot!(w, au, xscale=:log10, xlabel="Frequency", ylims=(0,Inf))
153153

test/test_uncertainty.jl

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,13 @@ end
117117

118118

119119
##
120-
@test uss(ss(I(2))).M.D == I
120+
@test uss(I(2)).M.D == I
121121
@test sum(δss(2, 2).D) == 4
122122
@test sum(δss(4, 4).D) == 8
123123

124124
##
125125
H = δss(2, 3)
126-
W = ss(tf([1, .1],[.1, 1]))*I(2)
126+
W = tf([1, .1],[.1, 1]) .* I(2)
127127
WH = W*H
128128
@test WH.D == [zeros(3,2) I(3); 10I(2) zeros(2,3)]
129129

@@ -155,14 +155,14 @@ temp = (W*d)
155155
@test temp.nu == temp.ny == 1
156156
@test temp.nz == temp.nw == 1
157157

158-
temp = (ss(I(1)) + W*d)
158+
temp = I(1) + W*d
159159
@test temp.nu == temp.ny == 1
160160
@test temp.nz == temp.nw == 1
161161

162162
@test length(d.Δ) == 1
163163

164164

165-
Pd = Pn*(ss(I(1)) + W*d)
165+
Pd = Pn*(I(1) + W*d)
166166

167167

168168
usyss = sminreal(system_mapping(Pd))
@@ -182,7 +182,7 @@ end
182182

183183
Pn = ssrand(3,4,5)
184184
@test δss(4,4, bound=0.2).D == [0I(4) sqrt(0.2)*I(4); sqrt(0.2)*I(4) 0I(4)]
185-
mu = ss(I(4)) + δss(4,4, bound=0.2)
185+
mu = I(4) + δss(4,4, bound=0.2)
186186

187187

188188
@test mu.D == [0I(4) sqrt(0.2)*I(4); sqrt(0.2)*I(4) I(4)]
@@ -199,8 +199,8 @@ Pn2 = system_mapping(P)
199199
## this time mimo real
200200
delta = uss([δr(), δr()])
201201
a = 1
202-
P = ss([0 a; -a -1], I(2), [1 a; 0 1], 0)* (ss(1.0*I(2)) + delta)
203-
K = ss(1.0I(2))
202+
P = ss([0 a; -a -1], I(2), [1 a; 0 1], 0)* (I(2) + delta)
203+
K = ss(I(2))
204204

205205
G = lft(P, -K)
206206
hn = norm(G, Inf)
@@ -260,7 +260,7 @@ blocks, M = RobustAndOptimalControl.blocksort(P)
260260

261261
w = exp10.(LinRange(-2, 2, 500))
262262
delta = δss(1,1)
263-
P = ss(tf(1,[1, .2, 1])) * (1+0.2*delta)
263+
P = (tf(1,[1, .2, 1])) * (1+0.2*delta)
264264
s = tf("s")
265265
K = ss(1 + 2/s + 0.9s/(0.1s+1))
266266
Gcl = lft(P, -K)
@@ -278,7 +278,7 @@ mu = structured_singular_value(Gcl, w)
278278
## same as above but with scalar instead of 1×1 system
279279
w = exp10.(LinRange(-2, 2, 500))
280280
delta = δc()
281-
P = ss(tf(1,[1, .2, 1])) * (1+0.2*delta)
281+
P = (tf(1,[1, .2, 1])) * (1+0.2*delta)
282282
s = tf("s")
283283
K = ss(1 + 2/s + 0.9s/(0.1s+1))
284284
Gcl = lft(P, -K)
@@ -295,7 +295,7 @@ mu = structured_singular_value(Gcl, w)
295295

296296
## this time mimo complex
297297
delta = uss([δc(), δc()])
298-
P = ss([0 1; 0 0], I(2), [1 0], 0) * (ss(1.0I(2)) + delta)
298+
P = ss([0 1; 0 0], I(2), [1 0], 0) * (I(2) + delta)
299299
# diagonal input uncertainty
300300

301301
K = ss([1;1])

0 commit comments

Comments
 (0)