akreon-control
Optimal and robust control and state estimation in C++26.
Loading...
Searching...
No Matches
h2_state_feedback.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/trace.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
76template <ContinuousPlantModelLike SFModel>
79 using Model = linalg::remove_cvref_t<SFModel>;
80
82
83 static constexpr std::size_t StateDim = plant_traits<Model>::StateDim;
84 static constexpr std::size_t InputDim = plant_traits<Model>::ControlDim;
85 static constexpr std::size_t OutputDim = plant_traits<Model>::PerformanceDim;
86
87 using QMat = linalg::Matrix<T, StateDim, StateDim>;
88 using YMat = linalg::Matrix<T, InputDim, StateDim>;
89 using KMat = linalg::Matrix<T, InputDim, StateDim>;
90 using ZMat = linalg::Matrix<T, OutputDim, OutputDim>;
91
92 static constexpr std::size_t QDim = StateDim * (StateDim + 1) / 2;
93 static constexpr std::size_t YDim = InputDim * StateDim;
94 static constexpr std::size_t ZDim = OutputDim * (OutputDim + 1) / 2;
95 static constexpr std::size_t DecVarDim = QDim + YDim + ZDim;
96
97 using DecVarSpace = linalg::RowVector<T, DecVarDim>;
98
107 template <convex::AtomLike DecVar>
108 constexpr auto operator()(const SFModel& model, const DecVar& Z_hat,
109 T margin = 1e-6) const {
110 return apply(model, Z_hat, margin);
111 }
112
121 template <convex::AtomLike DecVar>
122 constexpr auto apply(const SFModel& model, const DecVar& Z_hat,
123 T margin = 1e-6) const {
124 static_assert(convex::is_var_type_v<DecVar>, "Input atom is not a var");
125 using DecVarDom = convex::meta_dom_t<convex::atom_meta_t<DecVar>>;
126 static_assert(convex::dom_vartype_v<DecVarDom> == convex::VarType::FREE,
127 "Merged decision var must be free");
128 static_assert(std::same_as<DecVarSpace, convex::dom_space_t<DecVarDom>>,
129 "Expected merged decision size [svec(Q); vec(Y); svec(Z)]");
130
132 const auto z_lmi = z_constraint(model, Z_hat, margin);
133 return convex::Intersection(z_lmi, stability_lmi);
134 }
135
147 template <convex::AtomLike DecVar>
148 constexpr auto stability_constraint(const SFModel& model,
149 const DecVar& Z_hat) const {
150 using FMat = QMat;
151 using Fs = linalg::Vector<FMat, DecVarDim>;
152
153 auto M_q = [&](const QMat& E) -> FMat {
154 const auto& A = model.dynamics_matrix();
155 return T(-1) * (A * E + E * linalg::transpose(A));
156 };
157
158 auto M_y = [&](const YMat& E) -> FMat {
159 const auto& B_u = model.control_matrix();
160 return T(-1) * (B_u * E + linalg::transpose(E) * linalg::transpose(B_u));
161 };
162
163 const auto B_w = model.exogenous_matrix();
164 const auto F_0 = T(-1) * (B_w * linalg::transpose(B_w));
165
166 const auto F_q_local =
167 linalg::MatrixPencil<FMat, QDim>{}.template apply_sym_mat<QMat>(M_q);
168 const auto F_y_local =
169 linalg::MatrixPencil<FMat, YDim>{}.template apply_full<YMat>(M_y);
170
171 Fs F_blks{};
172 for (std::size_t i = 0; i < QDim; ++i) F_blks(i) = F_q_local(i);
173 for (std::size_t i = 0; i < YDim; ++i) F_blks(QDim + i) = F_y_local(i);
174
175 return convex::LMI(F_blks, F_0, Z_hat);
176 }
177
195 template <convex::AtomLike DecVar>
196 constexpr auto z_constraint(const SFModel& model, const DecVar& Z_hat,
197 T margin = 1e-6) const {
198 constexpr std::size_t LmiDim = OutputDim + StateDim;
199 using FMat = linalg::Matrix<T, LmiDim, LmiDim>;
200 using Fs = linalg::Vector<FMat, DecVarDim>;
201 auto M_z = [](const ZMat& E) -> FMat {
202 FMat F{};
203 return linalg::set_block<0, OutputDim, 0, OutputDim>(F, E);
204 };
205 auto M_q = [&](const QMat& E) -> FMat {
206 FMat F{};
207 const auto C = model.performance_matrix();
208 F = linalg::set_block<OutputDim, LmiDim, OutputDim, LmiDim>(F, E);
209 F = linalg::set_block<0, OutputDim, OutputDim, LmiDim>(F, C * E);
210 F = linalg::set_block<OutputDim, LmiDim, 0, OutputDim>(
211 F, E * linalg::transpose(C));
212 return F;
213 };
214 auto M_y = [&](const YMat& E) -> FMat {
215 FMat F{};
216 const auto D_u =
217 model
219 F = linalg::set_block<0, OutputDim, OutputDim, LmiDim>(F, D_u * E);
220 F = linalg::set_block<OutputDim, LmiDim, 0, OutputDim>(
221 F, linalg::transpose(E) * linalg::transpose(D_u));
222 return F;
223 };
224
225 const auto F_z_local =
226 linalg::MatrixPencil<FMat, ZDim>{}.template apply_sym_mat<ZMat>(M_z);
227 const auto F_q_local =
228 linalg::MatrixPencil<FMat, QDim>{}.template apply_sym_mat<QMat>(M_q);
229 const auto F_y_local =
230 linalg::MatrixPencil<FMat, YDim>{}.template apply_full<YMat>(M_y);
231
232 Fs F_blks{};
233 for (std::size_t i = 0; i < QDim; ++i) F_blks(i) = F_q_local(i);
234 for (std::size_t i = 0; i < YDim; ++i) F_blks(QDim + i) = F_y_local(i);
235 for (std::size_t i = 0; i < ZDim; ++i)
236 F_blks(QDim + YDim + i) = F_z_local(i);
237
238 const auto I = linalg::make_identity<T, LmiDim>();
239 return convex::LMI(F_blks, T(-1) * margin * I, Z_hat);
240 }
241
251 template <convex::AtomLike DecVar>
252 constexpr auto objective(const DecVar& Z_hat) const {
253 return convex::make_trace_block<QDim + YDim, linalg::SymmetricMatrix<ZMat>>(
254 Z_hat);
255 }
256
267 constexpr auto K(const QMat& Q, const YMat& Y) const {
269 }
270};
271
281template <ContinuousPlantModelLike SFModel, convex::AtomLike DecVar>
283 const SFModel& model, const DecVar& Z_hat,
286 const auto lmis = h2(model, Z_hat, margin);
287 const auto prob = convex::make_problem(Z_hat).in_set(lmis);
288 const auto obj = h2.objective(Z_hat);
289 return prob.minimize(obj);
290}
291
292} // namespace control
constexpr auto make_h2_state_feedback_problem(const SFModel &model, const DecVar &Z_hat, plant_type_t< SFModel > margin=1e-6)
make h2 state feedback problem
constexpr bool is_continuous_v
check if continuous or not
constexpr auto recover_state_feedback_gain(const PMat &P, const YMat &Y)
recover state-feedback 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 stability_constraint(const SFModel &model, const DecVar &Z_hat) const
stability_constraint
linalg::RowVector< T, DecVarDim > DecVarSpace
linalg::Matrix< T, InputDim, StateDim > YMat
static constexpr std::size_t InputDim
linalg::Matrix< T, StateDim, StateDim > QMat
static constexpr std::size_t StateDim
linalg::Matrix< T, OutputDim, OutputDim > ZMat
constexpr auto objective(const DecVar &Z_hat) const
objective
constexpr auto operator()(const SFModel &model, const DecVar &Z_hat, T margin=1e-6) const
operator overload to form H2 state feedback
static constexpr std::size_t OutputDim
static constexpr std::size_t QDim
static constexpr std::size_t ZDim
plant_type_t< Model > T
linalg::remove_cvref_t< SFModel > Model
extracting alias and types
constexpr auto K(const QMat &Q, const YMat &Y) const
K.
static constexpr std::size_t DecVarDim
linalg::Matrix< T, InputDim, StateDim > KMat
static constexpr std::size_t YDim
constexpr auto z_constraint(const SFModel &model, const DecVar &Z_hat, T margin=1e-6) const
z_constraint
constexpr auto apply(const SFModel &model, const DecVar &Z_hat, T margin=1e-6) const
apply
base type traits for plant model