Skip to content

Commit 94f4422

Browse files
authored
Add polar approach to quadrotor tutorial (#275)
1 parent 0d3785e commit 94f4422

File tree

1 file changed

+89
-3
lines changed

1 file changed

+89
-3
lines changed

docs/src/tutorials/Systems and Control/quadrotor.jl

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,13 @@ function γ_step(solver, V, γ_min, degree_k, degree_s3; γ_tol = 1e-1, max_iter
157157
@info("Iteration $num_iters/$max_iters : Solving with $(solver_name(model)) for `γ = `")
158158
optimize!(model)
159159
@info("After $(solve_time(model)) seconds, terminated with $(termination_status(model)) ($(raw_status(model)))")
160-
if primal_status(model) == MOI.FEASIBLE_POINT
161-
@info("Feasible solution found")
160+
if primal_status(model) == MOI.FEASIBLE_POINT || primal_status(model) == MOI.NEARLY_FEASIBLE_POINT
161+
@info("Feasible solution found : primal is $(primal_status(model))")
162162
γ_min = γ
163163
k_best = value.(k)
164164
s3_best = value(s3)
165165
elseif dual_status(model) == MOI.INFEASIBILITY_CERTIFICATE
166-
@info("Infeasibility certificate found")
166+
@info("Infeasibility certificate found : dual is $(dual_status(model))")
167167
if γ == γ0_min # This corresponds to the case above where we reached the tol or max iteration and we just did a last run at the value of `γ_min` provided by the user
168168
error("The value `$γ0_min` of `γ_min` provided is not feasible")
169169
end
@@ -256,3 +256,89 @@ solution_summary(model)
256256

257257
push!(Vs, V2 - γ2)
258258
plot_lyapunovs(Vs, [1, 2])
259+
260+
# ## Different starting point
261+
262+
# The LQR regulator we computed as starting point does not take the
263+
# the constraints `X` into account.
264+
# To take the constraint into account,
265+
# we compute an ellipsoidal control invariant set using [LJ21, Corollary 9]
266+
# For this, we first compute the descriptor system described in [LJ21, Proposition 5].
267+
#
268+
# [LJ21] Legat, Benoît, and Jungers, Raphaël M.
269+
# *Geometric control of algebraic systems.*
270+
# IFAC-PapersOnLine 54.5 (2021): 79-84.
271+
272+
nD = n_x + n_u
273+
E = sparse(1:n_x, 1:n_x, ones(n_x), n_x, nD)
274+
C = [A B]
275+
276+
# We know solve [LJ21, (13)]
277+
278+
using LinearAlgebra
279+
model = Model(solver)
280+
@variable(model, Q[1:nD, 1:nD] in PSDCone())
281+
cref = @constraint(model, Symmetric(-C * Q * E' - E * Q * C') in PSDCone())
282+
@constraint(model, rect_ref[i in 1:nD], Q[i, i] <= rectangle[i]^2)
283+
@variable(model, volume)
284+
q = [Q[i, j] for j in 1:nD for i in 1:j]
285+
@constraint(model, [volume; q] in MOI.RootDetConeTriangle(nD))
286+
@objective(model, Max, volume)
287+
optimize!(model)
288+
solution_summary(model)
289+
290+
# We now have the control-Lyapunov function `V(x, u) = [x, u]' inv(Q) [x, u]`.
291+
# In other words, The 1-sublevel set of `V(x, u)` is an invariant subset of `rectangle`
292+
# with any state-feedback `κ(x)` such that `V(x, κ(x)) ≤ 1` for any `x` such that
293+
# `min_u V(x, u) ≤ 1`.
294+
# Such candidate `κ(x)` can therefore be chosen as `argmin_u V(x, u)`.
295+
# Let `inv(Q) = U' * U` where `U = [Ux Uu]`. We have `V(x, u) = ||Ux * x + Uu * u||_2`.
296+
# `κ(x)` is therefore the least-square solution of `Uu * κ(x) = -Ux * x`.
297+
# This we find the linear state-feedback `κ(x) = K * x` where `K = -Uu \ Ux`.
298+
299+
P = inv(Symmetric(value.(Q)))
300+
using LinearAlgebra
301+
F = cholesky(P)
302+
K = -F.U[:, (n_x + 1):(nD)] \ F.U[:, 1:n_x] # That gives the following state feedback in polynomial form:
303+
304+
# The corresponding polynomial form is given by:
305+
306+
κ0 = K * x
307+
308+
# We now have two equivalent ways to obtain the Lyapunov function.
309+
# Because `{V(x) ≤ 1} = {min_u V(x, u) ≤ 1}`,
310+
# see the left-hand side as the projection of the ellipsoid on `x, u`.
311+
# As the projection on the polar becomes simply cutting with the hyperplane `u = 0`,
312+
# the polar of the projection is simply `Q[1:6, 1:6]` ! So
313+
314+
Px = inv(Symmetric(value.(Q[1:n_x, 1:n_x])))
315+
Px_inv = Px #src
316+
317+
# An alternative way is to use our linear state feedback.
318+
# We know that `min_u V(x, u) = V(x, Kx)` so
319+
Px = [I; K]' * P * [I; K]
320+
@test Px Px_inv #src
321+
322+
# We can double check that this matrix is negative definite:
323+
324+
eigen(Symmetric(Px * (A + B * K) + (A + B * K)' * Px)).values
325+
@test all(v -> v < 0, eigen(Symmetric(Px * (A + B * K) + (A + B * K)' * Px)).values) #src
326+
327+
# Let's use the `support_` prefix to differentiate since we obtained it
328+
# with the support function.
329+
# The corresponding Lyapunov is:
330+
331+
support_V0 = x' * Px * x
332+
333+
# `support_V0 - 1` is a controlled invariant set for the linearized system
334+
# but not for the nonlinear one. We can plot it to visualize but
335+
# it is not comparable to the ones found before which were
336+
# controlled invariant to the nonlinear system.
337+
338+
support_Vs = [support_V0 - 1]
339+
plot_lyapunovs(support_Vs, [1, 2])
340+
341+
support_γ1, support_κ1, support_s3_1 = γ_step(solver, support_V0, 0.0, [2, 2], 2)
342+
@test support_γ1 0.0 atol = 1.e-1 #src
343+
344+
# We do not find it for any positive `γ` so the controlled invariant set is an empty set.

0 commit comments

Comments
 (0)