@@ -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
0 commit comments