akreon-control
Optimal and robust control and state estimation in C++26.
Loading...
Searching...
No Matches
hinf_state_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"
11#include "akreon/convex/composed/weighted_sum.hpp"
12#include "akreon/convex/convex_problem.hpp"
13#include "akreon/convex/intersection.hpp"
14#include "akreon/convex/sets/lmi.hpp"
15#include "akreon/convex/variable.hpp"
16#include "akreon/linalg/operator/matrix_inverse.hpp"
17#include "akreon/linalg/operator/matrix_pencil.hpp"
18
19namespace control {
20
79template <ContinuousPlantModelLike ObsModel>
82 using Model = linalg::remove_cvref_t<ObsModel>;
83
85
86 static constexpr std::size_t StateDim = plant_traits<Model>::StateDim;
87 static constexpr std::size_t ExogenousDim = plant_traits<Model>::ExogenousDim;
88 static constexpr std::size_t MeasurementDim =
90 static constexpr std::size_t PerformanceDim =
92
93 using QMat = linalg::Matrix<T, StateDim, StateDim>;
94 using WMat = linalg::Matrix<T, StateDim, MeasurementDim>;
95 using LMat = linalg::Matrix<T, StateDim, MeasurementDim>;
96
97 static constexpr std::size_t QDim = StateDim * (StateDim + 1) / 2;
98 static constexpr std::size_t WDim = StateDim * MeasurementDim;
99 static constexpr std::size_t GammaDim = 1;
100 static constexpr std::size_t DecVarDim = QDim + WDim + GammaDim;
101
102 using DecVarSpace = linalg::RowVector<T, DecVarDim>;
103
113 template <convex::AtomLike DecVar>
114 constexpr auto operator()(const ObsModel& model, const DecVar& Z_hat,
115 T margin = 1e-6) const {
116 return apply(model, Z_hat, margin);
117 }
118
128 template <convex::AtomLike DecVar>
129 constexpr auto apply(const ObsModel& model, const DecVar& Z_hat,
130 T margin = 1e-6) const {
131 static_assert(convex::is_var_type_v<DecVar>, "Input atom is not a var");
132 using DecVarDom = convex::meta_dom_t<convex::atom_meta_t<DecVar>>;
133 static_assert(convex::dom_vartype_v<DecVarDom> == convex::VarType::FREE,
134 "Merged decision var must be free");
135 static_assert(std::same_as<DecVarSpace, convex::dom_space_t<DecVarDom>>,
136 "Expected merged decision size [svec(Q); vec(W); gamma]");
137
138 const auto q_lmi = q_constraint(Z_hat, margin);
139 const auto brl_lmi = brl_constraint(model, Z_hat, margin);
140 return convex::Intersection(q_lmi, brl_lmi);
141 }
142
154 template <convex::AtomLike DecVar>
155 constexpr auto q_constraint(const DecVar& Z_hat, T margin = 1e-6) const {
156 using FMat = QMat;
157 using Fs = linalg::Vector<FMat, DecVarDim>;
158
159 const auto F_q =
160 linalg::MatrixPencil<FMat, QDim>{}.template apply_sym_mat<QMat>(
161 [](const QMat& E) -> FMat { return E; });
162
163 Fs F_blks{};
164 for (std::size_t i = 0; i < QDim; ++i) F_blks(i) = F_q(i);
165
166 const auto I = linalg::make_identity<T, StateDim>();
167 return convex::LMI(F_blks, T(-1) * margin * I, Z_hat);
168 }
169
184 template <convex::AtomLike DecVar>
185 constexpr auto brl_constraint(const ObsModel& model, const DecVar& Z_hat,
186 T margin = 1e-6) const {
187 constexpr std::size_t StateEnd = StateDim;
188 constexpr std::size_t DisturbanceEnd = StateDim + ExogenousDim;
189 constexpr std::size_t LmiDim = StateDim + ExogenousDim + PerformanceDim;
190
191 using FMat = linalg::Matrix<T, LmiDim, LmiDim>;
192 using Fs = linalg::Vector<FMat, DecVarDim>;
193
194 auto M_q = [&](const QMat& E) -> FMat {
195 FMat F{};
196 const auto A = model.dynamics_matrix();
197 const auto B_w = model.exogenous_matrix();
198 F = linalg::set_block<0, StateEnd, 0, StateEnd>(
199 F, T(-1) * (linalg::transpose(A) * E + E * A));
200 F = linalg::set_block<0, StateEnd, StateEnd, DisturbanceEnd>(
201 F, T(-1) * E * B_w);
202 F = linalg::set_block<StateEnd, DisturbanceEnd, 0, StateEnd>(
203 F, T(-1) * linalg::transpose(B_w) * E);
204 return F;
205 };
206
207 auto M_w = [&](const WMat& E) -> FMat {
208 FMat F{};
209 const auto C_y = model.measurement_matrix();
210 const auto D_yw = model.template feedthrough<PortRole::MEASUREMENT,
212 F = linalg::set_block<0, StateEnd, 0, StateEnd>(
213 F, linalg::transpose(C_y) * linalg::transpose(E) + E * C_y);
214 F = linalg::set_block<0, StateEnd, StateEnd, DisturbanceEnd>(F, E * D_yw);
215 F = linalg::set_block<StateEnd, DisturbanceEnd, 0, StateEnd>(
216 F, linalg::transpose(D_yw) * linalg::transpose(E));
217 return F;
218 };
219
220 auto M_gamma = [](const linalg::Matrix<T, 1, 1>& E) -> FMat {
221 FMat F{};
222 const auto gamma = E(0, 0);
223 const auto I_w = linalg::make_identity<T, ExogenousDim>();
224 const auto I_z = linalg::make_identity<T, PerformanceDim>();
225 F = linalg::set_block<StateEnd, DisturbanceEnd, StateEnd, DisturbanceEnd>(
226 F, gamma * I_w);
227 F = linalg::set_block<DisturbanceEnd, LmiDim, DisturbanceEnd, LmiDim>(
228 F, gamma * I_z);
229 return F;
230 };
231
232 const auto F_q =
233 linalg::MatrixPencil<FMat, QDim>{}.template apply_sym_mat<QMat>(M_q);
234 const auto F_w =
235 linalg::MatrixPencil<FMat, WDim>{}.template apply_full<WMat>(M_w);
236 const auto F_gamma =
237 linalg::MatrixPencil<FMat, GammaDim>{}
239
240 Fs F_blks{};
241 for (std::size_t i = 0; i < QDim; ++i) F_blks(i) = F_q(i);
242 for (std::size_t i = 0; i < WDim; ++i) F_blks(QDim + i) = F_w(i);
243 F_blks(QDim + WDim) = F_gamma(0);
244
245 FMat F_0{};
246 const auto C_e = model.performance_matrix();
247 const auto D_ew =
248 model
250 F_0 = linalg::set_block<0, StateEnd, DisturbanceEnd, LmiDim>(
251 F_0, T(-1) * linalg::transpose(C_e));
252 F_0 = linalg::set_block<DisturbanceEnd, LmiDim, 0, StateEnd>(F_0,
253 T(-1) * C_e);
254 F_0 = linalg::set_block<StateEnd, DisturbanceEnd, DisturbanceEnd, LmiDim>(
255 F_0, T(-1) * linalg::transpose(D_ew));
256 F_0 = linalg::set_block<DisturbanceEnd, LmiDim, StateEnd, DisturbanceEnd>(
257 F_0, T(-1) * D_ew);
258
259 const auto I = linalg::make_identity<T, LmiDim>();
260 return convex::LMI(F_blks, F_0 + T(-1) * margin * I, Z_hat);
261 }
262
272 template <convex::AtomLike DecVar>
273 constexpr auto objective(const DecVar& Z_hat) const {
274 linalg::Vector<T, DecVarDim> weights{};
275 weights(QDim + WDim) = T(1);
276 return convex::make_weighted_sum(weights, Z_hat);
277 }
278
289 constexpr auto L(const QMat& Q, const WMat& W) const {
291 }
292};
293
303template <ContinuousPlantModelLike ObsModel, convex::AtomLike DecVar>
305 const ObsModel& model, const DecVar& Z_hat,
308 const auto lmis = hinf(model, Z_hat, margin);
309 const auto prob = convex::make_problem(Z_hat).in_set(lmis);
310 const auto obj = hinf.objective(Z_hat);
311 return prob.minimize(obj);
312}
313
314} // namespace control
constexpr bool is_continuous_v
check if continuous or not
@ EXOGENOUS
w: disturbance, reference, noise, or other external input
@ MEASUREMENT
y: measured output available for feedback/estimation
constexpr auto recover_state_observer_gain(const QMat &Q, const WMat &W)
recover state-observer gain from a lifted gain variable
typename plant_traits< linalg::remove_cvref_t< Plant > >::T plant_type_t
extract scalar type from plant
constexpr auto make_hinf_state_observer_problem(const ObsModel &model, const DecVar &Z_hat, plant_type_t< ObsModel > margin=1e-6)
make hinf state observer problem
linalg::Matrix< T, StateDim, MeasurementDim > LMat
static constexpr std::size_t DecVarDim
constexpr auto L(const QMat &Q, const WMat &W) const
L.
constexpr auto q_constraint(const DecVar &Z_hat, T margin=1e-6) const
q_constraint
static constexpr std::size_t WDim
static constexpr std::size_t QDim
linalg::Matrix< T, StateDim, StateDim > QMat
static constexpr std::size_t StateDim
constexpr auto apply(const ObsModel &model, const DecVar &Z_hat, T margin=1e-6) const
apply
linalg::Matrix< T, StateDim, MeasurementDim > WMat
static constexpr std::size_t ExogenousDim
static constexpr std::size_t GammaDim
constexpr auto objective(const DecVar &Z_hat) const
objective
constexpr auto brl_constraint(const ObsModel &model, const DecVar &Z_hat, T margin=1e-6) const
brl_constraint
static constexpr std::size_t MeasurementDim
linalg::remove_cvref_t< ObsModel > Model
extracting alias and types
linalg::RowVector< T, DecVarDim > DecVarSpace
constexpr auto operator()(const ObsModel &model, const DecVar &Z_hat, T margin=1e-6) const
operator overload to form Hinf state observer
static constexpr std::size_t PerformanceDim
base type traits for plant model