Skip to content

Commit bcfa7a2

Browse files
Dev ebci hybrid (#12)
2 parents e50f713 + 546e695 commit bcfa7a2

25 files changed

+9076
-31
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/flightstack)
5757
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/flightstack/config)
5858
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/flightstack/control)
5959
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/flightstack/control/funnel_two_layer_mrac)
60+
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/flightstack/control/hybrid_two_layer_mrac)
6061
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/flightstack/control/mrac)
6162
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/flightstack/control/pid)
6263
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/flightstack/control/two_layer_mrac)
@@ -75,6 +76,8 @@ set(SOURCES
7576
src/control/outer_loop_safety_mechanism.cpp
7677
src/control/funnel_two_layer_mrac/funnel_two_layer_mrac.cpp
7778
src/control/funnel_two_layer_mrac/logging_funnel_two_layer_mrac.cpp
79+
src/control/hybrid_two_layer_mrac/hybrid_two_layer_mrac.cpp
80+
src/control/hybrid_two_layer_mrac/logging_hybrid_two_layer_mrac.cpp
7881
src/control/mrac/logging_mrac.cpp
7982
src/control/mrac/mrac.cpp
8083
src/control/pid/logging_pid.cpp

include/flightstack/config/config.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ class MultiThreadedNode;
5353
#define __MRAC__ 2
5454
#define __TWO_LAYER_MRAC__ 3
5555
#define __FUNNEL_TWO_LAYER_MRAC__ 4
56+
#define __HYBRID_TWO_LAYER_MRAC__ 5
5657

5758
// SELECT here the CONTROLLER you want to run -----------------------------------------------------------------------
58-
#define SELECTED_CONTROLLER __FUNNEL_TWO_LAYER_MRAC__
59+
#define SELECTED_CONTROLLER __HYBRID_TWO_LAYER_MRAC__
5960
// ------------------------------------------------------------------------------------------------------------------
6061

6162
// Define ControlType based on SELECTED_CONTROLLER using type aliasing
@@ -83,6 +84,12 @@ using ControlType = TwoLayerMRAC;
8384
#include "logging_funnel_two_layer_mrac.hpp"
8485
using ControlType = FunnelTwoLayerMRAC;
8586

87+
#elif SELECTED_CONTROLLER == __HYBRID_TWO_LAYER_MRAC__
88+
89+
#include "hybrid_two_layer_mrac.hpp"
90+
#include "logging_hybrid_two_layer_mrac.hpp"
91+
using ControlType = HybridTwoLayerMRAC;
92+
8693
#else
8794
#error "ERROR: Unsupported controller type selected."
8895
#endif

include/flightstack/control/base_mrac.hpp

Lines changed: 274 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,280 @@ inline std::pair<Eigen::Matrix<double, 1, 1>, int> computeEtaDotFunnel(
255255
return std::make_pair(Eigen::Matrix<double, 1, 1>(eta_dot), case_eta_dot);
256256
}
257257

258-
259258
} // namespace funnel
260259

260+
261+
namespace hybrid {
262+
263+
/*
264+
Store the previous iteration trajectory tracking error.
265+
*/
266+
template <typename VectorType>
267+
inline VectorType computePreviousTrajectoryTrackingError(const VectorType& e)
268+
{
269+
static_assert(
270+
static_cast<int>(VectorType::ColsAtCompileTime) == 1,
271+
"e must be a column vector"
272+
);
273+
VectorType e_previous = e;
274+
275+
return e_previous;
276+
}
277+
278+
/*
279+
Evaluate the s-th element of the series used for reference resetting events.
280+
*/
281+
inline double evaluateSeriesElement(double s, double alpha)
282+
{
283+
return 1.0 / std::pow(s, alpha);
284+
}
285+
286+
/*
287+
Compute the next index s in the convergent series.
288+
weighted_e_squared = eᵀ(t_j) P e(t_j)
289+
*/
290+
inline int findSseries(int s_previous, double weighted_e_squared, double alpha)
291+
{
292+
// Compute ceil(weighted_e_squared^(-1/alpha))
293+
double exponent = -1.0 / alpha;
294+
int candidate = static_cast<int>(
295+
std::ceil(std::pow(weighted_e_squared, exponent))
296+
);
297+
int s = std::max(candidate, s_previous + 1);
298+
return s;
299+
}
300+
301+
/*
302+
Compute the quadratic form: vectorᵀ * matrix * vector
303+
*/
304+
template <typename VectorType, typename MatrixType>
305+
inline double computeQuadraticForm(const VectorType& vector, const MatrixType& matrix)
306+
{
307+
static_assert(static_cast<int>(VectorType::ColsAtCompileTime) == 1, "vector must be a column vector");
308+
309+
static_assert(static_cast<int>(MatrixType::RowsAtCompileTime) == static_cast<int>(MatrixType::ColsAtCompileTime),
310+
"matrix must be square"
311+
);
312+
313+
static_assert(static_cast<int>(VectorType::RowsAtCompileTime) == static_cast<int>(MatrixType::RowsAtCompileTime),
314+
"Dimension mismatch between vector and matrix"
315+
);
316+
317+
double result = (vector.transpose() * matrix * vector).value();
318+
319+
return result;
320+
}
321+
322+
/*
323+
Update the hybrid summation term.
324+
*/
325+
inline double updateSummationHybridP(double ePe, double ePe_previous, double summation)
326+
{
327+
if (ePe > ePe_previous)
328+
{
329+
summation += (ePe - ePe_previous);
330+
}
331+
return summation;
332+
}
333+
334+
/*
335+
Reset the hybrid series index when either:
336+
- it grows beyond a prescribed threshold, or
337+
- sufficient time has elapsed since the last trajectory reset.
338+
*/
339+
inline int resetSeriesIfNeeded(
340+
int s_hybrid,
341+
double time_now,
342+
double time_of_last_trajectory_reset,
343+
double tolerance_time_reset_series
344+
)
345+
{
346+
constexpr int S_MAX_THRESHOLD = 1000000000;
347+
const double time_since_last_trajectory_reset = time_now - time_of_last_trajectory_reset;
348+
349+
if (s_hybrid > S_MAX_THRESHOLD || (s_hybrid > 0 && time_since_last_trajectory_reset > tolerance_time_reset_series))
350+
{
351+
s_hybrid = 0;
352+
}
353+
return s_hybrid;
354+
}
355+
356+
/*
357+
Check the hybrid reset condition and, if satisfied, update:
358+
- reset time,
359+
- series index s,
360+
- reference trajectory,
361+
- integral term storage.
362+
*/
363+
template <typename VectorType, typename MatrixType, typename DerivedRef>
364+
inline std::pair<int, double> checkAndTriggerTrajectoryReset(
365+
double summation_hybrid_P,
366+
double time_now,
367+
double time_of_last_trajectory_reset,
368+
double ePe,
369+
double alpha_hybrid_series,
370+
int s_hybrid,
371+
const VectorType& e,
372+
const VectorType& e_previous,
373+
const MatrixType& Q,
374+
Eigen::MatrixBase<DerivedRef>& reference_model_state_map,
375+
Eigen::Ref<Eigen::Matrix<double,1,1>> integral_eQe_map
376+
)
377+
{
378+
static_assert(VectorType::ColsAtCompileTime == 1, "Vectors must be column vectors");
379+
static_assert(MatrixType::RowsAtCompileTime == MatrixType::ColsAtCompileTime, "Q must be square");
380+
381+
double integral_eQe = integral_eQe_map(0);
382+
383+
// std::cout << "integral_eQe: " << integral_eQe << std::endl;
384+
// std::cout << "summation_hybrid_P: " << summation_hybrid_P << std::endl;
385+
386+
if (integral_eQe >= summation_hybrid_P)
387+
{
388+
// Update reset time
389+
time_of_last_trajectory_reset = time_now;
390+
391+
// Update series index
392+
s_hybrid = findSseries(s_hybrid, ePe, alpha_hybrid_series);
393+
394+
// Update reference trajectory (jump)
395+
const double series_element = evaluateSeriesElement(static_cast<double>(s_hybrid), alpha_hybrid_series);
396+
const double delta_series = ePe - series_element;
397+
const double jump_factor = 1.0 - std::sqrt(delta_series / ePe);
398+
const VectorType jump_reference_trajectory = jump_factor * e;
399+
reference_model_state_map += jump_reference_trajectory;
400+
401+
// Update integral storage term related to (e^T * Q * e)
402+
const double eQe = computeQuadraticForm(e, Q);
403+
const double eQe_previous = computeQuadraticForm(e_previous, Q);
404+
const double delta_integral_eQe = eQe - eQe_previous;
405+
integral_eQe_map(0) += delta_integral_eQe;
406+
407+
std::cout << "checkAndTriggerTrajectoryReset() cond. verified [integral_eQe >= summation_hybrid_P]" << std::endl;
408+
}
409+
410+
return {s_hybrid, time_of_last_trajectory_reset};
411+
}
412+
413+
/*
414+
Execute one hybrid MRAC step:
415+
- Update quadratic forms and summation term
416+
- Handle series reset logic
417+
- Trigger trajectory reset if condition is met
418+
*/
419+
template <typename VectorType, typename MatrixType, typename DerivedRef>
420+
inline std::tuple<Eigen::Matrix<double,1,1>, double, int, double> runHybridStep(
421+
const VectorType& e,
422+
const VectorType& e_previous,
423+
const MatrixType& P,
424+
const MatrixType& Q,
425+
double summation_hybrid_P,
426+
int s_hybrid,
427+
double time_of_last_trajectory_reset,
428+
double alpha_hybrid_series,
429+
double time_now,
430+
double tolerance_time_reset_series_hybrid,
431+
Eigen::MatrixBase<DerivedRef>& reference_model_state_map,
432+
Eigen::Ref<Eigen::Matrix<double,1,1>> integral_eQe_map,
433+
bool& first_controller_loop
434+
)
435+
{
436+
static_assert(VectorType::ColsAtCompileTime == 1, "Vectors must be column vectors");
437+
static_assert(MatrixType::RowsAtCompileTime == MatrixType::ColsAtCompileTime, "Matrices must be square");
438+
439+
// Compute quadratic terms
440+
Eigen::Matrix<double,1,1> eQe;
441+
eQe(0,0) = computeQuadraticForm(e, Q);
442+
const double ePe = computeQuadraticForm(e, P);
443+
const double ePe_previous = computeQuadraticForm(e_previous, P);
444+
// Update hybrid summation term
445+
summation_hybrid_P = updateSummationHybridP(ePe, ePe_previous, summation_hybrid_P);
446+
447+
if (!first_controller_loop)
448+
{
449+
// Reset series index if required
450+
s_hybrid = resetSeriesIfNeeded(
451+
s_hybrid,
452+
time_now,
453+
time_of_last_trajectory_reset,
454+
tolerance_time_reset_series_hybrid
455+
);
456+
457+
// Check reset condition
458+
std::tie(s_hybrid, time_of_last_trajectory_reset) = checkAndTriggerTrajectoryReset(
459+
summation_hybrid_P,
460+
time_now,
461+
time_of_last_trajectory_reset,
462+
ePe,
463+
alpha_hybrid_series,
464+
s_hybrid,
465+
e,
466+
e_previous,
467+
Q,
468+
reference_model_state_map,
469+
integral_eQe_map
470+
);
471+
}
472+
473+
first_controller_loop = false;
474+
475+
return {eQe, summation_hybrid_P, s_hybrid, time_of_last_trajectory_reset};
476+
}
477+
478+
479+
} // namespace hybrid
480+
481+
482+
namespace ebci {
483+
484+
/*
485+
Compute the error bounding control input (non-adaptive EBCI).
486+
*/
487+
template <typename MatrixBType, typename MatrixPType, typename VectorType>
488+
inline Eigen::Matrix<typename MatrixBType::Scalar, MatrixBType::ColsAtCompileTime, 1> computeErrorBoundingControlInput(
489+
double xi_bar_d,
490+
double lambda_bar,
491+
double delta_ebci,
492+
const MatrixBType& B,
493+
const MatrixPType& P,
494+
const VectorType& e,
495+
bool use_ebci
496+
)
497+
{
498+
using Scalar = typename MatrixBType::Scalar;
499+
500+
constexpr int n = MatrixBType::RowsAtCompileTime;
501+
constexpr int m = MatrixBType::ColsAtCompileTime;
502+
503+
static_assert(VectorType::ColsAtCompileTime == 1, "e must be a column vector");
504+
static_assert(MatrixPType::RowsAtCompileTime == MatrixPType::ColsAtCompileTime, "P must be square");
505+
static_assert(MatrixPType::RowsAtCompileTime == n, "Dimension mismatch: P and B must have same row size");
506+
static_assert(VectorType::RowsAtCompileTime == n, "Dimension mismatch: e and B must have same row size");
507+
508+
using InputVector = Eigen::Matrix<Scalar, m, 1>;
509+
510+
if (!use_ebci)
511+
{
512+
return InputVector::Zero();
513+
}
514+
515+
InputVector BPe = B.transpose() * P * e;
516+
Scalar BPe_norm = BPe.norm();
517+
518+
// Dead-zone
519+
if (BPe_norm < delta_ebci)
520+
{
521+
return InputVector::Zero();
522+
}
523+
524+
Eigen::Matrix<Scalar, n, 1> Pe = P * e;
525+
Scalar sum_Pe = Pe.cwiseAbs().sum();
526+
InputVector control_input = - (xi_bar_d / lambda_bar) * (BPe / BPe_norm) * sum_Pe;
527+
528+
return control_input;
529+
}
530+
531+
} // namespace ebci
532+
533+
261534
} // namespace base_mrac

include/flightstack/control/control.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ class Control
242242
return K_hat_state_dot;
243243
}
244244

245+
inline static constexpr bool has_post_integration_algorithm_ = false;
246+
245247
protected:
246248

247249
VehicleInfo vehicle_info;

include/flightstack/control/funnel_two_layer_mrac/logging_funnel_two_layer_mrac.hpp

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -86,35 +86,6 @@ class MultiThreadedNode;
8686
// Forward declaration of FunnelTwoLayerMRAC class
8787
class FunnelTwoLayerMRAC;
8888

89-
// Struct that contains info related to the logger that can be accessed via a getter from the logger class.
90-
struct LoggingInfo
91-
{
92-
std::string timestamp; // YYYYMMDD_HHMMSS
93-
std::string date; // YYYYMMDD
94-
95-
std::string base_dir; // ./src/flightstack/log/<date>/<controller>
96-
97-
std::string logs_dir;
98-
std::string log_filename;
99-
100-
std::string gains_dir;
101-
std::string gains_target_filename;
102-
103-
std::string info_dir;
104-
std::string safe_mech_dir;
105-
std::string safe_mech_target_filename;
106-
std::string git_info_dir;
107-
std::string git_info_filename;
108-
std::string low_pass_filter_dir;
109-
std::string low_pass_filter_filename;
110-
111-
std::string der_gains_dir;
112-
std::string der_gains_filename;
113-
114-
std::string logs_debug_dir;
115-
std::string log_debug_filename;
116-
};
117-
11889
class LogData_FunnelTwoLayerMRAC
11990
{
12091
public:

0 commit comments

Comments
 (0)