10 #define CHECK_NAN(VAR, PRINT_PREFIX) \
11 if(VAR.array().isNaN().any() || VAR.array().isInf().any()) \
13 if(print_level >= 3) \
15 std::cout << PRINT_PREFIX << #VAR << " contains NaN or infinity: " << VAR.transpose() << std::endl; \
23 double calcDuration(
const std::chrono::time_point<Clock> & start_time,
const std::chrono::time_point<Clock> & end_time)
25 return 1e3 * std::chrono::duration_cast<std::chrono::duration<double>>(end_time - start_time).count();
31 template<
int StateDim,
int InputDim,
int IneqDim>
41 template<
int StateDim,
int InputDim,
int IneqDim>
48 for(
auto & x : x_list)
52 for(
auto & u : u_list)
56 for(
auto & lambda : lambda_list)
58 lambda.setConstant(_lambda);
60 for(
auto & s : s_list)
64 for(
auto & nu : nu_list)
70 template<
int StateDim,
int InputDim,
int IneqDim>
73 for(
auto & x : x_list)
77 for(
auto & u : u_list)
81 for(
auto & lambda : lambda_list)
85 for(
auto & s : s_list)
89 for(
auto & nu : nu_list)
97 template<
int StateDim,
int InputDim,
int IneqDim>
100 A.resize(state_dim, state_dim);
101 B.resize(state_dim, input_dim);
102 C.resize(ineq_dim, state_dim);
103 D.resize(ineq_dim, input_dim);
105 Lx.resize(state_dim);
106 Lu.resize(input_dim);
107 Lxx.resize(state_dim, state_dim);
108 Luu.resize(input_dim, input_dim);
109 Lxu.resize(state_dim, input_dim);
111 x_bar.resize(state_dim);
112 g_bar.resize(ineq_dim);
113 Lx_bar.resize(state_dim);
114 Lu_bar.resize(input_dim);
117 K.resize(input_dim, state_dim);
119 P.resize(state_dim, state_dim);
122 template<
int StateDim,
int InputDim,
int IneqDim>
125 Lx.resize(state_dim);
126 Lxx.resize(state_dim, state_dim);
127 Lx_bar.resize(state_dim);
130 P.resize(state_dim, state_dim);
133 template<
int StateDim,
int InputDim,
int IneqDim>
147 CHECK_NAN(Lx_bar,
"[FMPC/Coefficient] ");
148 CHECK_NAN(Lu_bar,
"[FMPC/Coefficient] ");
157 template<
int StateDim,
int InputDim,
int IneqDim>
163 auto start_time = std::chrono::system_clock::now();
174 constexpr
double initial_barrier_eps = 1e-4;
175 constexpr
double complementary_variable_margin_rate = 1e-2;
176 constexpr
double complementary_variable_min = 1e-2;
184 .cwiseMax(complementary_variable_min);
201 if constexpr(InputDim == Eigen::Dynamic || IneqDim == Eigen::Dynamic)
228 auto setup_time = std::chrono::system_clock::now();
246 auto end_time = std::chrono::system_clock::now();
252 std::cout <<
"[FMPC] Solve finised. status: " <<
static_cast<int>(status)
259 template<
int StateDim,
int InputDim,
int IneqDim>
262 std::ofstream ofs(file_path);
267 <<
"duration_backward "
268 <<
"duration_forward "
269 <<
"duration_update" << std::endl;
274 ofs << trace_data.iter <<
" "
275 << trace_data.kkt_error <<
" "
276 << trace_data.duration_coeff <<
" "
277 << trace_data.duration_backward <<
" "
278 << trace_data.duration_forward <<
" "
279 << trace_data.duration_update
284 template<
int StateDim,
int InputDim,
int IneqDim>
290 throw std::invalid_argument(
"[FMPC] x_list length should be " + std::to_string(
config_.
horizon_steps + 1) +
" but "
295 throw std::invalid_argument(
"[FMPC] u_list length should be " + std::to_string(
config_.
horizon_steps) +
" but "
300 throw std::invalid_argument(
"[FMPC] lambda_list length should be " + std::to_string(
config_.
horizon_steps + 1)
305 throw std::invalid_argument(
"[FMPC] s_list length should be " + std::to_string(
config_.
horizon_steps) +
" but "
310 throw std::invalid_argument(
"[FMPC] nu_list length should be " + std::to_string(
config_.
horizon_steps) +
" but "
317 if constexpr(InputDim == Eigen::Dynamic)
320 int input_dim =
problem_->inputDim(t);
323 throw std::runtime_error(
"[FMPC] u_list[i] dimension should be " + std::to_string(input_dim) +
" but "
324 + std::to_string(
variable_.
u_list[i].size()) +
". i: " + std::to_string(i)
325 +
", time: " + std::to_string(t));
328 if constexpr(IneqDim == Eigen::Dynamic)
331 int ineq_dim =
problem_->ineqDim(t);
334 throw std::runtime_error(
"[FMPC] s_list[i] dimension should be " + std::to_string(ineq_dim) +
" but "
335 + std::to_string(
variable_.
s_list[i].size()) +
". i: " + std::to_string(i)
336 +
", time: " + std::to_string(t));
340 throw std::runtime_error(
"[FMPC] nu_list[i] dimension should be " + std::to_string(ineq_dim) +
" but "
342 +
", time: " + std::to_string(t));
353 throw std::runtime_error(
"[FMPC] s_list[i] must be non-negative. i: " + std::to_string(i)
354 +
", time: " + std::to_string(t));
358 throw std::runtime_error(
"[FMPC] nu_list[i] must be non-negative. i: " + std::to_string(i)
359 +
", time: " + std::to_string(t));
364 template<
int StateDim,
int InputDim,
int IneqDim>
369 std::cout <<
"[FMPC] Start iteration " << iter << std::endl;
375 trace_data.iter = iter;
381 double s_nu_ave = 0.0;
382 double s_nu_min = std::numeric_limits<double>::max();
383 int total_ineq_dim = 0;
390 s_nu_ave /= total_ineq_dim;
396 constexpr
double barrier_eps_min = 1e-8;
397 constexpr
double barrier_eps_max = 1e6;
398 barrier_eps_ = std::clamp(sigma * s_nu_ave, barrier_eps_min, barrier_eps_max);
403 auto start_time = std::chrono::system_clock::now();
418 problem_->calcStateEqDeriv(t, x, u, coeff.A, coeff.B);
419 problem_->calcIneqConstDeriv(t, x, u, coeff.C, coeff.D);
420 problem_->calcRunningCostDeriv(t, x, u, coeff.Lx, coeff.Lu, coeff.Lxx, coeff.Luu, coeff.Lxu);
422 coeff.x_bar =
problem_->stateEq(t, x, u) - next_x;
423 coeff.g_bar =
problem_->ineqConst(t, x, u) + s;
425 -1 * lambda + dt * coeff.Lx + coeff.A.transpose() * next_lambda + coeff.C.transpose() * nu;
426 coeff.Lu_bar = dt * coeff.Lu + coeff.B.transpose() * next_lambda + coeff.D.transpose() * nu;
433 problem_->calcTerminalCostDeriv(terminal_t, terminal_x, terminal_coeff.Lx, terminal_coeff.Lxx);
434 terminal_coeff.Lx_bar = terminal_coeff.Lx - terminal_lambda;
437 double duration = calcDuration(start_time, std::chrono::system_clock::now());
438 trace_data.duration_coeff = duration;
444 trace_data.kkt_error = kkt_error;
452 auto start_time = std::chrono::system_clock::now();
459 double duration = calcDuration(start_time, std::chrono::system_clock::now());
460 trace_data.duration_backward = duration;
466 auto start_time = std::chrono::system_clock::now();
473 double duration = calcDuration(start_time, std::chrono::system_clock::now());
474 trace_data.duration_forward = duration;
480 auto start_time = std::chrono::system_clock::now();
487 double duration = calcDuration(start_time, std::chrono::system_clock::now());
488 trace_data.duration_update = duration;
495 template<
int StateDim,
int InputDim,
int IneqDim>
498 double kkt_error = 0;
506 kkt_error += coeff.x_bar.squaredNorm();
507 kkt_error += coeff.g_bar.squaredNorm();
508 kkt_error += coeff.Lx_bar.squaredNorm();
509 kkt_error += coeff.Lu_bar.squaredNorm();
515 kkt_error += terminal_coeff.Lx_bar.squaredNorm();
518 kkt_error = std::sqrt(kkt_error);
523 template<
int StateDim,
int InputDim,
int IneqDim>
545 s = -1 * terminal_coeff.Lx_bar;
546 P = terminal_coeff.Lxx;
547 terminal_coeff.s = s;
548 terminal_coeff.P = P;
570 auto start_time_gain_pre = std::chrono::system_clock::now();
575 Qxx_tilde.noalias() = dt * Lxx + C.transpose() * nu_s.asDiagonal() * C;
576 Quu_tilde.noalias() = dt * Luu + D.transpose() * nu_s.asDiagonal() * D;
577 Qxu_tilde.noalias() = dt * Lxu + C.transpose() * nu_s.asDiagonal() * D;
578 Lx_tilde.noalias() = Lx_bar + C.transpose() * tilde_sub;
579 Lu_tilde.noalias() = Lu_bar + D.transpose() * tilde_sub;
581 F.noalias() = Qxx_tilde + A.transpose() * P * A;
582 H.noalias() = Qxu_tilde + A.transpose() * P * B;
583 G.noalias() = Quu_tilde + B.transpose() * P * B;
590 auto start_time_gain_solve = std::chrono::system_clock::now();
592 int input_dim =
static_cast<int>(B.cols());
596 Eigen::LDLT<InputInputDimMatrix> llt_G(G);
597 if(llt_G.info() == Eigen::Success)
599 k.noalias() = -1 * llt_G.solve(B.transpose() * (P * x_bar - s) + Lu_tilde);
600 K.noalias() = -1 * llt_G.solve(H.transpose());
606 std::cout <<
"[FMPC/Backward] G is not positive definite in Cholesky decomposition (LLT)." << std::endl;
614 Eigen::FullPivLU<InputInputDimMatrix> lu_G(G);
615 k.noalias() = -1 * lu_G.solve(B.transpose() * (P * x_bar - s) + Lu_tilde);
616 K.noalias() = -1 * lu_G.solve(H.transpose());
631 auto start_time_gain_post = std::chrono::system_clock::now();
633 s = A.transpose() * (s - P * x_bar) - Lx_tilde - H * k;
634 P.noalias() = F - K.transpose() * G * K;
635 P_symmetric = 0.5 * (P + P.transpose());
653 if(coeff.containsNaN())
657 std::cout <<
"[FMPC/Backward] coeff contains NaN." << std::endl;
667 template<
int StateDim,
int InputDim,
int IneqDim>
702 std::cout <<
"[FMPC/Forward] delta_variable contains NaN." << std::endl;
710 template<
int StateDim,
int InputDim,
int IneqDim>
714 double alpha_s_max = 1.0;
715 double alpha_nu_max = 1.0;
717 auto start_time_fraction = std::chrono::system_clock::now();
719 constexpr
double margin_ratio = 0.995;
726 for(
int ineq_idx = 0; ineq_idx < s.size(); ineq_idx++)
729 if(delta_s[ineq_idx] < 0)
731 alpha_s_max = std::min(alpha_s_max, -1 * margin_ratio * s[ineq_idx] / delta_s[ineq_idx]);
733 if(delta_nu[ineq_idx] < 0)
735 alpha_nu_max = std::min(alpha_nu_max, -1 * margin_ratio * nu[ineq_idx] / delta_nu[ineq_idx]);
739 if(!(alpha_s_max > 0.0 && alpha_s_max <= 1.0 && alpha_nu_max > 0.0 && alpha_nu_max <= 1.0))
743 std::cout <<
"[FMPC/Update] Invalid alpha. barrier_eps: " <<
barrier_eps_ <<
", alpha_s_max: " << alpha_s_max
744 <<
", alpha_nu_max: " << alpha_nu_max << std::endl;
753 double alpha_s = alpha_s_max;
754 double alpha_nu = alpha_nu_max;
759 constexpr
double armijo_scale = 1e-3;
760 constexpr
double alpha_s_update_ratio = 0.5;
761 constexpr
double alpha_s_min = 1e-10;
765 if(alpha_s < alpha_s_min)
769 std::cout <<
"[FMPC/Update] alpha_s is too small in line search backtracking. alpha_s_max: " << alpha_s_max
770 <<
", alpha_s: " << alpha_s << std::endl;
791 alpha_s *= alpha_s_update_ratio;
797 std::cout <<
"[FMPC/update] barrier_eps: " <<
barrier_eps_ <<
", alpha_s_max: " << alpha_s_max
798 <<
", alpha_nu_max: " << alpha_nu_max <<
", alpha_s: " << alpha_s <<
", alpha_nu: " << alpha_nu
813 constexpr
double min_positive_value = std::numeric_limits<double>::lowest();
818 std::cout <<
"[FMPC/Update] Updated s is negative: " <<
variable_.
s_list[i].transpose() << std::endl;
826 std::cout <<
"[FMPC/Update] Updated nu is negative: " <<
variable_.
nu_list[i].transpose() << std::endl;
836 template<
int StateDim,
int InputDim,
int IneqDim>
839 double merit_func_obj = 0.0;
840 double merit_func_const = 0.0;
841 double merit_deriv_obj = 0.0;
842 double merit_deriv_const = 0.0;
847 merit_func_const += const_func.template lpNorm<1>();
866 merit_func_obj +=
problem_->runningCost(t, x, u) * dt;
867 merit_deriv_obj += (coeff.Lx.dot(delta_x) + coeff.Lu.dot(delta_u)) * dt;
871 merit_func_obj += -1 *
barrier_eps_ * s.array().log().sum();
872 merit_deriv_obj += -1 *
barrier_eps_ * s.cwiseInverse().dot(delta_s);
877 merit_func_const += const_func.template lpNorm<1>();
886 merit_func_const += const_func.template lpNorm<1>();
899 merit_func_obj +=
problem_->terminalCost(terminal_t, terminal_x);
900 merit_deriv_obj += terminal_coeff.Lx.dot(terminal_delta_x);
903 constexpr
double merit_const_scale_min = 1e-3;
921 constexpr
double rho = 0.5;
922 merit_const_scale_ = std::max(merit_deriv_obj / ((1.0 - rho) * merit_func_const), merit_const_scale_min);
935 template<
int StateDim,
int InputDim,
int IneqDim>
938 double merit_func_obj = 0.0;
939 double merit_func_const = 0.0;
944 merit_func_const += const_func.template lpNorm<1>();
956 merit_func_obj +=
problem_->runningCost(t, x, u) * dt;
960 merit_func_obj += -1 *
barrier_eps_ * s.array().log().sum();
965 merit_func_const += const_func.template lpNorm<1>();
970 merit_func_const += const_func.template lpNorm<1>();
978 merit_func_obj +=
problem_->terminalCost(terminal_t, terminal_x);