Skip to content

Commit e09543a

Browse files
committed
add example file
1 parent d006909 commit e09543a

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
lines changed

docs/src/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,6 @@ using Plots
314314
plot(sim)
315315
```
316316

317+
See [Example: `lqg_mpc_disturbance.jl`](https://github.com/JuliaControl/RobustAndOptimalControl.jl/blob/master/examples/lqg_mpc_disturbance.jl) for a more detailed example.
318+
317319
See the [docs for LinearMPC.jl](https://darnstrom.github.io/LinearMPC.jl/stable/) for more details on how to use the MPC controller and modify its settings.

examples/lqg_mpc_disturbance.jl

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#=
2+
This file demonstrates
3+
- Adding integral action to an LQG controller by means of state augmentation
4+
- Conversion of the LQG controller into an MPC contorller with added constraints
5+
=#
6+
using RobustAndOptimalControl, ControlSystemsBase, Plots, LinearAlgebra
7+
Ts = 1 # Sample time
8+
G = c2d(ss(tf(1, [10, 1])), Ts) # Process model
9+
10+
nx = G.nx
11+
nu = G.nu
12+
ny = G.ny
13+
x0 = zeros(G.nx) # Initial condition
14+
15+
Q1 = 100diagm(ones(G.nx)) # state cost matrix
16+
Q2 = 0.01diagm(ones(nu)) # control cost matrix
17+
18+
R1 = 0.001I(nx) # State noise covariance
19+
R2 = I(ny) # measurement noise covariance
20+
prob = LQGProblem(G, Q1, Q2, R1, R2)
21+
22+
disturbance = (x, t) -> t * Ts 10 # This is our load disturbance, a step at ``t = 10``
23+
Gcl = G_PS(prob) # This forms the transfer function from load disturbance to output
24+
res = lsim(Gcl, disturbance, 100)
25+
plot(res)
26+
27+
Gd1 = add_low_frequency_disturbance(G, ϵ = 1e-6, measurement=false)
28+
Gd2 = add_low_frequency_disturbance(G, ϵ = 1e-6, measurement=true) # The ϵ moves the integrator pole slightly into the stable region
29+
plots = map([Gd1, Gd2]) do Gd
30+
nx = Gd.nx
31+
32+
C = Gd.C
33+
Q1 = 100C'diagm(ones(G.nx)) * C # state cost matrix
34+
x0 = zeros(nx)
35+
36+
R1 = diagm([0.001, 1])
37+
R2 = I(ny)
38+
prob = LQGProblem(Gd, Q1, Q2, R1, R2)
39+
Gcl = [G_PS(prob); -comp_sensitivity(prob)] # -comp_sensitivity(prob) is the same as the transfer function from load disturbance to control signal
40+
res = lsim(Gcl, disturbance, 100)
41+
f1 = plot(res, ylabel=["y" "u"]); ylims!((-0.05, 0.3), sp = 1)
42+
43+
w = exp10.(LinRange(-3, log10(pi / Ts), 200))
44+
f2 = gangoffourplot(prob, w, lab = "", legend = :bottomright)
45+
46+
R1 = diagm([0.001, 0.2]) # Reduce the noise on the integrator state from 1 to 0.2
47+
R2 = I(ny)
48+
prob = LQGProblem(Gd, Q1, Q2, R1, R2)
49+
50+
Gcl = [G_PS(prob); -comp_sensitivity(prob)]
51+
res = lsim(Gcl, disturbance, 100)
52+
f3 = plot(res, ylabel=["y" "u"]); ylims!((-0.05, 0.3), sp = 1)
53+
f4 = gangoffourplot(prob, w, lab = "", legend = :bottomright)
54+
55+
plot(f1, f2, f3, f4, titlefontsize=10)
56+
end
57+
58+
plot(plots..., size=(1200,1000))
59+
60+
61+
62+
# ==============================================================================
63+
## LinearMPC
64+
# The example below illustrate how we can convert the LQGProblem into an MPC contorller by loading LinearMPC.jl
65+
# We then perform a rather low-level simulation with a manual loop, where we form the observer `obs` and step the plant
66+
# ==============================================================================
67+
68+
using LinearMPC
69+
70+
Gd = add_low_frequency_disturbance(G, ϵ = 1e-6, measurement=false)
71+
72+
C = Gd.C
73+
Q1 = 100diagm([1.0]) # output cost matrix
74+
R1 = diagm([0.001, 1]) # Dynamics noise covariance
75+
R2 = I(ny) # Measurement noise covariance
76+
Gde = ExtendedStateSpace(Gd, B1=I) # Since B1=I, R1 has size determined by state dimension, but C1=C, so Q1 has size determined by the output dimension
77+
prob = LQGProblem(Gde, Q1, Q2, R1, R2)
78+
obs = observer_predictor(prob, direct=false) # If a predictor is used, the observer update should be carried out in the end of the loop, if we use the filter below, we should instead perform the observer update in the beginning of the loop directly after obtaining the new measurement but before computing a new control signal.
79+
obs = observer_filter(prob)
80+
81+
mpc = LinearMPC.MPC(prob; N=20, umin=[-3], umax=[3])
82+
x = zeros(G.nx) # True plant state
83+
xh = zeros(Gd.nx) # Observer state
84+
X = [x[]] # For storage
85+
U = Float64[]
86+
u_mpc = [0.0]
87+
for i = 1:50
88+
y = G.C*x # Compute the true measurement output
89+
xh = obs.A * xh + obs.B*[u_mpc; y] # Predict one step with the observer, u here is the control signal from the previous iteration, if using the predictor, use u from the current iteration and perform the observer update in the end of the loop instead
90+
u_disturbance = i * Ts 10 ? 1.0 : 0.0
91+
r = [1.0] # output reference
92+
u_mpc = LinearMPC.compute_control(mpc, xh; r) # Call MPC optimizer with estimated state
93+
u_tot = u_mpc .+ u_disturbance # Total input is control signal + disturbance
94+
x = G.A*x + G.B*u_tot # Advance the true plant state
95+
push!(X, x[]) # Store data for plotting
96+
push!(U, u_mpc[])
97+
end
98+
99+
100+
plot(X*G.C', layout=2, sp=1, label="\$y\$")
101+
plot!(U, sp=2, label="\$u\$")
102+
hline!([1.0 3.0], linestyle=:dash, color=:black, label=["Reference" "\$u_{max}\$"], sp=[1 2])
103+
104+
105+
using Test
106+
@test (G.C*X[end])[] 1 rtol=1e-4

src/lqg.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,18 @@ function ControlSystemsBase.observer_controller(l::LQGProblem, L::AbstractMatrix
354354
ss(Ac, Bc, Cc, Dc, l.timeevol)
355355
end
356356

357+
function ControlSystemsBase.observer_predictor(l::LQGProblem, K::Union{AbstractMatrix, Nothing} = nothing; direct = false, kwargs...)
358+
P = system_mapping(l, identity)
359+
if K === nothing
360+
K = kalman(l; direct)
361+
end
362+
observer_predictor(P, K; kwargs...)
363+
end
364+
365+
function ControlSystemsBase.observer_filter(l::LQGProblem, K = kalman(l); kwargs...)
366+
P = system_mapping(l, identity)
367+
observer_filter(P, K; kwargs...)
368+
end
357369

358370
"""
359371
ff_controller(sys, L, K; comp_dc = true)

test/runtests.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ using Test
126126
include("../examples/uncertain.jl")
127127
end
128128

129+
@testset "lqg_mpc_disturbance" begin
130+
@info "Testing LQG MPC example"
131+
include("../examples/lqg_mpc_disturbance.jl")
132+
end
133+
129134
@testset "mcm_nugap" begin
130135
@info "Testing mcm_nugap"
131136
include("test_mcm_nugap.jl")

0 commit comments

Comments
 (0)