akreon-control
Optimal and robust control and state estimation in C++26.
Loading...
Searching...
No Matches
hinf_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/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
95template <ContinuousPlantModelLike SFModel>
98 using Model = linalg::remove_cvref_t<SFModel>;
99
101
102 static constexpr std::size_t StateDim = plant_traits<Model>::StateDim;
104 static constexpr std::size_t InputDim = plant_traits<Model>::ControlDim;
105 static constexpr std::size_t ExogenousDim = plant_traits<Model>::ExogenousDim;
106 static constexpr std::size_t OutputDim = plant_traits<Model>::PerformanceDim;
107
108 using QMat = linalg::Matrix<T, StateDim, StateDim>;
109 using YMat = linalg::Matrix<T, InputDim, StateDim>;
110 using KMat = linalg::Matrix<T, InputDim, StateDim>;
111
112 static constexpr std::size_t QDim = StateDim * (StateDim + 1) / 2;
113 static constexpr std::size_t YDim = InputDim * StateDim;
114 static constexpr std::size_t GammaDim = 1;
115 static constexpr std::size_t DecVarDim = QDim + YDim + GammaDim;
116
117 using DecVarSpace = linalg::RowVector<T, DecVarDim>;
118
128 template <convex::AtomLike DecVar>
129 constexpr auto operator()(const SFModel& model, const DecVar& Z_hat,
130 T margin = 1e-6) const {
131 return apply(model, Z_hat, margin);
132 }
133
143 template <convex::AtomLike DecVar>
144 constexpr auto apply(const SFModel& model, const DecVar& Z_hat,
145 T margin = 1e-6) const {
146 static_assert(convex::is_var_type_v<DecVar>, "Input atom is not a var");
147 using DecVarDom = convex::meta_dom_t<convex::atom_meta_t<DecVar>>;
148 static_assert(convex::dom_vartype_v<DecVarDom> == convex::VarType::FREE,
149 "Merged decision var must be free");
150 static_assert(std::same_as<DecVarSpace, convex::dom_space_t<DecVarDom>>,
151 "Expected merged decision size [svec(Q); vec(Y); gamma]");
152
153 const auto q_lmi = q_constraint(Z_hat, margin);
154 const auto brl_lmi = brl_constraint(model, Z_hat, margin);
155 return convex::Intersection(q_lmi, brl_lmi);
156 }
157
169 template <convex::AtomLike DecVar>
170 constexpr auto q_constraint(const DecVar& Z_hat, T margin = 1e-6) const {
171 using FMat = QMat;
172 using Fs = linalg::Vector<FMat, DecVarDim>;
173
174 const auto F_q =
175 linalg::MatrixPencil<FMat, QDim>{}.template apply_sym_mat<QMat>(
176 [](const QMat& E) -> FMat { return E; });
177
178 Fs F_blks{};
179 for (std::size_t i = 0; i < QDim; ++i) F_blks(i) = F_q(i);
180
181 const auto I = linalg::make_identity<T, StateDim>();
182 return convex::LMI(F_blks, T(-1) * margin * I, Z_hat);
183 }
184
199 template <convex::AtomLike DecVar>
200 constexpr auto brl_constraint(const SFModel& model, const DecVar& Z_hat,
201 T margin = 1e-6) const {
202 constexpr std::size_t StateEnd = StateDim;
203 constexpr std::size_t DisturbanceEnd = StateDim + ExogenousDim;
204 constexpr std::size_t LmiDim = StateDim + ExogenousDim + OutputDim;
205
206 using FMat = linalg::Matrix<T, LmiDim, LmiDim>;
207 using Fs = linalg::Vector<FMat, DecVarDim>;
208
209 auto M_q = [&](const QMat& E) -> FMat {
210 FMat F{};
211 const auto A = model.dynamics_matrix();
212 const auto C_z = model.performance_matrix();
213 F = linalg::set_block<0, StateEnd, 0, StateEnd>(
214 F, T(-1) * (A * E + E * linalg::transpose(A)));
215 F = linalg::set_block<0, StateEnd, DisturbanceEnd, LmiDim>(
216 F, T(-1) * linalg::transpose(C_z * E));
217 F = linalg::set_block<DisturbanceEnd, LmiDim, 0, StateEnd>(
218 F, T(-1) * C_z * E);
219 return F;
220 };
221
222 auto M_y = [&](const YMat& E) -> FMat {
223 FMat F{};
224 const auto B_u = model.control_matrix();
225 const auto D_u =
226 model
228 F = linalg::set_block<0, StateEnd, 0, StateEnd>(
229 F, T(-1) * (B_u * E + linalg::transpose(E) * linalg::transpose(B_u)));
230 F = linalg::set_block<0, StateEnd, DisturbanceEnd, LmiDim>(
231 F, T(-1) * linalg::transpose(D_u * E));
232 F = linalg::set_block<DisturbanceEnd, LmiDim, 0, StateEnd>(
233 F, T(-1) * D_u * E);
234 return F;
235 };
236
237 auto M_gamma = [](const linalg::Matrix<T, 1, 1>& E) -> FMat {
238 FMat F{};
239 const auto gamma = E(0, 0);
240 const auto I_w = linalg::make_identity<T, ExogenousDim>();
241 const auto I_z = linalg::make_identity<T, OutputDim>();
242 F = linalg::set_block<StateEnd, DisturbanceEnd, StateEnd, DisturbanceEnd>(
243 F, gamma * I_w);
244 F = linalg::set_block<DisturbanceEnd, LmiDim, DisturbanceEnd, LmiDim>(
245 F, gamma * I_z);
246 return F;
247 };
248
249 const auto F_q =
250 linalg::MatrixPencil<FMat, QDim>{}.template apply_sym_mat<QMat>(M_q);
251 const auto F_y =
252 linalg::MatrixPencil<FMat, YDim>{}.template apply_full<YMat>(M_y);
253 const auto F_gamma =
254 linalg::MatrixPencil<FMat, GammaDim>{}
256
257 Fs F_blks{};
258 for (std::size_t i = 0; i < QDim; ++i) F_blks(i) = F_q(i);
259 for (std::size_t i = 0; i < YDim; ++i) F_blks(QDim + i) = F_y(i);
260 F_blks(QDim + YDim) = F_gamma(0);
261
262 FMat F_0{};
263 const auto B_w = model.exogenous_matrix();
264 const auto D_w =
265 model
267 F_0 = linalg::set_block<0, StateEnd, StateEnd, DisturbanceEnd>(F_0,
268 T(-1) * B_w);
269 F_0 = linalg::set_block<StateEnd, DisturbanceEnd, 0, StateEnd>(
270 F_0, T(-1) * linalg::transpose(B_w));
271 F_0 = linalg::set_block<StateEnd, DisturbanceEnd, DisturbanceEnd, LmiDim>(
272 F_0, T(-1) * linalg::transpose(D_w));
273 F_0 = linalg::set_block<DisturbanceEnd, LmiDim, StateEnd, DisturbanceEnd>(
274 F_0, T(-1) * D_w);
275
276 const auto I = linalg::make_identity<T, LmiDim>();
277 return convex::LMI(F_blks, F_0 + T(-1) * margin * I, Z_hat);
278 }
279
289 template <convex::AtomLike DecVar>
290 constexpr auto objective(const DecVar& Z_hat) const {
291 linalg::Vector<T, DecVarDim> weights{};
292 weights(QDim + YDim) = T(1);
293 return convex::make_weighted_sum(weights, Z_hat);
294 }
295
306 constexpr auto K(const QMat& Q, const YMat& Y) const {
308 }
309};
310
320template <ContinuousPlantModelLike SFModel, convex::AtomLike DecVar>
322 const SFModel& model, const DecVar& Z_hat,
325 const auto lmis = hinf(model, Z_hat, margin);
326 const auto prob = convex::make_problem(Z_hat).in_set(lmis);
327 const auto obj = hinf.objective(Z_hat);
328 return prob.minimize(obj);
329}
330
331} // namespace control
constexpr bool is_continuous_v
check if continuous or not
constexpr auto make_hinf_state_feedback_problem(const SFModel &model, const DecVar &Z_hat, plant_type_t< SFModel > margin=1e-6)
make hinf state feedback problem
PortRole
Plant port role.
@ EXOGENOUS
w: disturbance, reference, noise, or other external input
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
static constexpr std::size_t DecVarDim
static constexpr std::size_t StateDim
static constexpr std::size_t YDim
linalg::RowVector< T, DecVarDim > DecVarSpace
static constexpr std::size_t InputDim
static constexpr std::size_t OutputDim
static constexpr std::size_t GammaDim
constexpr auto objective(const DecVar &Z_hat) const
objective
static constexpr std::size_t QDim
constexpr auto q_constraint(const DecVar &Z_hat, T margin=1e-6) const
q_constraint
static constexpr std::size_t ExogenousDim
constexpr auto operator()(const SFModel &model, const DecVar &Z_hat, T margin=1e-6) const
operator overload to form Hinf state feedback
constexpr auto apply(const SFModel &model, const DecVar &Z_hat, T margin=1e-6) const
apply
constexpr auto K(const QMat &Q, const YMat &Y) const
K.
static constexpr PortRole ExogenousPortRole
constexpr auto brl_constraint(const SFModel &model, const DecVar &Z_hat, T margin=1e-6) const
brl_constraint
linalg::Matrix< T, StateDim, StateDim > QMat
linalg::remove_cvref_t< SFModel > Model
extracting alias and types
linalg::Matrix< T, InputDim, StateDim > KMat
linalg::Matrix< T, InputDim, StateDim > YMat
base type traits for plant model