akreon-control
Optimal and robust control and state estimation in C++26.
Loading...
Searching...
No Matches
polytopic_observer.hpp
Go to the documentation of this file.
1/*
2 * Copyright © 2025-2026 Akreon LLC
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6#pragma once
7
8#include "akreon/linalg/assert.hpp"
10#include "akreon/convex/composed/quad_form.hpp"
11#include "akreon/convex/convex_problem.hpp"
12#include "akreon/convex/intersection.hpp"
13#include "akreon/convex/sets/lmi.hpp"
14#include "akreon/convex/variable.hpp"
15#include "akreon/linalg/operator/matrix_inverse.hpp"
16#include "akreon/linalg/operator/matrix_pencil.hpp"
17
18namespace control {
19
40template <ContinuousPlantModelLike Plant, std::size_t N>
42 using Model = linalg::remove_cvref_t<Plant>;
43
46
47 static constexpr std::size_t StateDim = plant_traits<Model>::StateDim;
48 static constexpr std::size_t OutputDim = plant_traits<Model>::MeasurementDim;
49
50 using QMat = linalg::Matrix<T, StateDim, StateDim>;
51 using WMat = linalg::Matrix<T, StateDim, OutputDim>;
52 using LMat = linalg::Matrix<T, StateDim, OutputDim>;
53 using FMat = QMat;
54
55 static constexpr std::size_t QDim = StateDim * (StateDim + 1) / 2;
56 static constexpr std::size_t WDim = OutputDim * StateDim;
57 static constexpr std::size_t ZDim = QDim + WDim;
58
59 using ZSpace = linalg::RowVector<T, ZDim>;
60 using Fs = linalg::Vector<FMat, ZDim>;
61 using Polytope = linalg::Vector<Plant, N>;
62
71 template <convex::AtomLike Var>
72 constexpr auto operator()(const Polytope& poly, const Var& Z,
73 T margin = 1e-6) const {
74 return apply(poly, Z, margin);
75 }
76
85 template <convex::AtomLike Var>
86 constexpr auto apply(const Polytope& poly, const Var& Z,
87 T margin = 1e-6) const {
88 static_assert(convex::is_var_type_v<Var>, "Input atom is not a var");
89 using ZDom = convex::meta_dom_t<convex::atom_meta_t<Var>>;
90 static_assert(convex::dom_vartype_v<ZDom> == convex::VarType::FREE,
91 "Merged decision var must be free");
92 using ZVarSpace = convex::dom_space_t<ZDom>;
93 static_assert(std::same_as<ZVarSpace, ZSpace>,
94 "Expected merged decision size [svec(Q); vec(W)]");
95
96 const auto q_lmi = q_constraint(Z, margin);
97
98 auto make_vertex_lmi = [&]<std::size_t idx>() {
99 return vertex_constraint(poly(idx), Z);
100 };
101
102 auto form_intersection = [&]<std::size_t... Is>(
103 std::index_sequence<Is...>) {
104 return convex::Intersection(q_lmi,
105 make_vertex_lmi.template operator()<Is>()...);
106 };
107
108 return form_intersection(std::make_index_sequence<N>{});
109 }
110
119 template <convex::AtomLike ZVar>
120 constexpr auto q_constraint(const ZVar& Z, T margin = 1e-6) const {
121 const auto I = linalg::make_identity<T, StateDim>();
122 const auto Fq =
123 linalg::MatrixPencil<QMat, QDim>{}.template apply_sym_mat<QMat>(
124 [](const QMat& E) -> QMat { return E; });
125
126 Fs F_blks{};
127 for (std::size_t i = 0; i < QDim; ++i) F_blks(i) = Fq(i);
128 for (std::size_t i = 0; i < WDim; ++i) F_blks(QDim + i) = QMat{};
129
130 return convex::LMI(F_blks, T(-1) * margin * I, Z);
131 }
132
141 template <convex::AtomLike ZVar>
142 constexpr auto vertex_constraint(const Plant& plant, const ZVar& Z) const {
143 auto M_q = [&](const QMat& E) -> FMat {
144 const auto A = plant.dynamics_matrix();
145 return T(-1) * (linalg::transpose(A) * E + E * A);
146 };
147
148 auto M_w = [&](const WMat& E) -> FMat {
149 const auto C_y = plant.measurement_matrix();
150 return E * C_y + linalg::transpose(C_y) * linalg::transpose(E);
151 };
152
153 const auto Fq_local =
154 linalg::MatrixPencil<FMat, QDim>{}.template apply_sym_mat<QMat>(M_q);
155 const auto Fw_local =
156 linalg::MatrixPencil<FMat, WDim>{}.template apply_full<WMat>(M_w);
157
158 Fs F_blks{};
159 for (std::size_t i = 0; i < QDim; ++i) F_blks(i) = Fq_local(i);
160 for (std::size_t i = 0; i < WDim; ++i) F_blks(QDim + i) = Fw_local(i);
161
162 return convex::LMI(F_blks, FMat{}, Z);
163 }
164
175 constexpr auto L(const QMat& Q, const WMat& W) const {
176 const auto [Q_inv, event] = linalg::SymmetricMatrixInverse<QMat>()(Q);
177 if (event != linalg::NumericalEvent::NONE) return std::tuple(LMat{}, event);
178 const auto L = Q_inv * W;
179 return std::tuple(L, linalg::NumericalEvent::NONE);
180 }
181};
182
194template <ContinuousPlantModelLike Plant, std::size_t N, convex::AtomLike ZVar>
198 const linalg::Vector<plant_type_t<Plant>,
200 const ZVar& Z) {
201 return convex::make_quad_form(P, q, Z);
202}
203
214template <ContinuousPlantModelLike Plant, std::size_t N, convex::AtomLike ZVar>
216 const linalg::Vector<Plant, N>& poly, const ZVar& Z, const auto& obj,
218 const auto pldi = PolytopicObserver<Plant, N>()(poly, Z, margin);
219 return convex::make_problem(Z).in_set(pldi).minimize(obj);
220}
221
231template <ContinuousPlantModelLike Plant, std::size_t N, convex::AtomLike ZVar>
233 const linalg::Vector<Plant, N>& poly, const ZVar& Z,
235 const auto pldi = PolytopicObserver<Plant, N>()(poly, Z, margin);
236 return convex::make_problem(Z).in_set(pldi).feasibility();
237}
238
239} // namespace control
constexpr bool is_continuous_v
check if continuous or not
typename plant_traits< linalg::remove_cvref_t< Plant > >::State plant_state_vec_t
extract state vector from plant
constexpr auto make_polytopic_observer_quadratic_objective(const linalg::Matrix< plant_type_t< Plant >, PolytopicObserver< Plant, N >::ZDim, PolytopicObserver< Plant, N >::ZDim > &P, const linalg::Vector< plant_type_t< Plant >, PolytopicObserver< Plant, N >::ZDim > &q, const ZVar &Z)
make quadratic objective over merged PLDI variable
typename plant_traits< linalg::remove_cvref_t< Plant > >::T plant_type_t
extract scalar type from plant
constexpr auto make_polytopic_observer_problem(const linalg::Vector< Plant, N > &poly, const ZVar &Z, const auto &obj, plant_type_t< Plant > margin=1e-6)
make PLDI problem with objective
Polytopic Luenberger Observer.
constexpr auto q_constraint(const ZVar &Z, T margin=1e-6) const
q_constraint
linalg::Vector< FMat, ZDim > Fs
static constexpr std::size_t OutputDim
static constexpr std::size_t StateDim
constexpr auto L(const QMat &Q, const WMat &W) const
L.
linalg::RowVector< T, ZDim > ZSpace
constexpr auto apply(const Polytope &poly, const Var &Z, T margin=1e-6) const
apply
linalg::Vector< Plant, N > Polytope
static constexpr std::size_t WDim
static constexpr std::size_t QDim
linalg::remove_cvref_t< Plant > Model
linalg::Matrix< T, StateDim, OutputDim > WMat
constexpr auto vertex_constraint(const Plant &plant, const ZVar &Z) const
vertex constraint
plant_state_vec_t< Model > State
linalg::Matrix< T, StateDim, StateDim > QMat
static constexpr std::size_t ZDim
constexpr auto operator()(const Polytope &poly, const Var &Z, T margin=1e-6) const
operator overload (polytopic observer)
linalg::Matrix< T, StateDim, OutputDim > LMat
base type traits for plant model