38 if (pointer->type() !=
"exotica::DynamicTimeIndexedShootingProblem")
40 ThrowNamed(
"This ILQRSolver can't solve problem of type '" << pointer->type() <<
"'!");
42 MotionSolver::SpecifyProblem(pointer);
44 dynamics_solver_ = prob_->
GetScene()->GetDynamicsSolver();
47 void ILQRSolver::BackwardPass()
49 constexpr
double min_clamp_ = -1e10;
50 constexpr
double max_clamp_ = 1e10;
51 const int T = prob_->get_T();
52 const double dt = dynamics_solver_->get_dt();
54 const Eigen::MatrixXd& Qf = prob_->get_Qf();
55 const Eigen::MatrixXd R = dt * prob_->get_R();
56 const Eigen::MatrixXd& X_star = prob_->get_X_star();
59 vk_gains_[T - 1] = Qf * dynamics_solver_->StateDelta(prob_->get_X(T - 1), X_star.col(T - 1));
60 vk_gains_[T - 1] = vk_gains_[T - 1].unaryExpr([min_clamp_, max_clamp_](
double x) ->
double {
61 return std::min(std::max(x, min_clamp_), max_clamp_);
64 Eigen::MatrixXd Sk = Qf;
65 Sk = Sk.unaryExpr([min_clamp_, max_clamp_](
double x) ->
double {
66 return std::min(std::max(x, min_clamp_), max_clamp_);
68 vk_gains_[T - 1] = vk_gains_[T - 1].unaryExpr([min_clamp_, max_clamp_](
double x) ->
double {
69 return std::min(std::max(x, min_clamp_), max_clamp_);
72 for (
int t = T - 2; t >= 0; t--)
74 Eigen::VectorXd x = prob_->get_X(t), u = prob_->get_U(t);
75 dynamics_solver_->ComputeDerivatives(x, u);
76 Eigen::MatrixXd Ak = dynamics_solver_->get_Fx(), Bk = dynamics_solver_->get_Fu(),
77 Q = dt * prob_->get_Q(t);
81 const Eigen::MatrixXd _inv =
82 (Eigen::MatrixXd::Identity(R.rows(), R.cols()) * parameters_.RegularizationRate + R + Bk.transpose() * Sk * Bk).inverse();
84 Kv_gains_[t] = _inv * Bk.transpose();
85 K_gains_[t] = _inv * Bk.transpose() * Sk * Ak;
86 Ku_gains_[t] = _inv * R;
87 Sk = Ak.transpose() * Sk * (Ak - Bk * K_gains_[t]) + Q;
89 vk_gains_[t] = ((Ak - Bk * K_gains_[t]).transpose() * vk_gains_[t + 1]) -
90 (K_gains_[t].transpose() * R * u) + (Q * x);
93 Sk = Sk.unaryExpr([min_clamp_, max_clamp_](
double x) ->
double {
94 return std::min(std::max(x, min_clamp_), max_clamp_);
96 vk_gains_[t] = vk_gains_[t].unaryExpr([min_clamp_, max_clamp_](
double x) ->
double {
97 return std::min(std::max(x, min_clamp_), max_clamp_);
105 const int T = prob_->get_T();
106 const Eigen::MatrixXd control_limits = dynamics_solver_->get_control_limits();
107 const double dt = dynamics_solver_->get_dt();
109 Eigen::VectorXd delta_uk(dynamics_solver_->get_num_controls()), u(dynamics_solver_->get_num_controls());
110 for (
int t = 0; t < T - 1; ++t)
114 delta_uk = -Ku_gains_[t] * u - Kv_gains_[t] * vk_gains_[t + 1] -
115 K_gains_[t] * dynamics_solver_->StateDelta(prob_->get_X(t), ref_x.col(t));
117 u.noalias() += alpha * delta_uk;
119 u = u.cwiseMax(control_limits.col(0)).cwiseMin(control_limits.col(1));
122 cost += dt * (prob_->GetControlCost(t) + prob_->GetStateCost(t));
126 cost += prob_->GetStateCost(T - 1);
130 void ILQRSolver::Solve(Eigen::MatrixXd& solution)
132 if (!prob_)
ThrowNamed(
"Solver has not been initialized!");
133 Timer planning_timer, backward_pass_timer, line_search_timer;
135 const int T = prob_->get_T();
136 const int NU = prob_->GetScene()->get_num_controls();
137 const int NX = prob_->GetScene()->get_num_state();
138 const double dt = dynamics_solver_->get_dt();
139 prob_->ResetCostEvolution(GetNumberOfMaxIterations() + 1);
143 for (
int t = 0; t < T - 1; ++t)
145 prob_->Update(prob_->get_U(t), t);
147 cost += dt * (prob_->GetControlCost(t) + prob_->GetStateCost(t));
151 cost += prob_->GetStateCost(T - 1);
152 prob_->SetCostEvolution(0, cost);
155 K_gains_.assign(T, Eigen::MatrixXd(NU, NX));
156 Ku_gains_.assign(T, Eigen::MatrixXd(NU, NU));
157 Kv_gains_.assign(T, Eigen::MatrixXd(NU, NX));
158 vk_gains_.assign(T, Eigen::MatrixXd(NX, 1));
162 Eigen::MatrixXd new_U = prob_->get_U();
163 solution.resize(T - 1, NU);
165 if (debug_)
HIGHLIGHT_NAMED(
"ILQRSolver",
"Running ILQR solver for max " << GetNumberOfMaxIterations() <<
" iterations");
167 double cost_prev = cost;
168 int last_best_iteration = 0;
170 Eigen::VectorXd alpha_space = Eigen::VectorXd::LinSpaced(11, 0.0, -3.0);
171 for (
int ai = 0; ai < alpha_space.size(); ++ai)
173 alpha_space(ai) = std::pow(10.0, alpha_space(ai));
176 double time_taken_backward_pass = 0.0, time_taken_forward_pass = 0.0;
177 for (
int iteration = 1; iteration <= GetNumberOfMaxIterations(); ++iteration)
180 if (Server::IsRos() && !
ros::ok())
182 if (debug_)
HIGHLIGHT(
"Solving cancelled by user");
183 prob_->termination_criterion = TerminationCriterion::UserDefined;
188 backward_pass_timer.
Reset();
190 time_taken_backward_pass = backward_pass_timer.
GetDuration();
193 line_search_timer.
Reset();
196 Eigen::MatrixXd ref_x = prob_->get_X(),
197 ref_u = prob_->get_U();
200 double best_alpha = 0;
201 for (
int ai = 0; ai < alpha_space.size(); ++ai)
203 const double& alpha = alpha_space(ai);
204 double rollout_cost = ForwardPass(alpha, ref_x, ref_u);
206 if (rollout_cost < cost_prev && !std::isnan(rollout_cost))
209 new_U = prob_->get_U();
216 if (!new_U.allFinite() || !std::isfinite(cost))
218 prob_->termination_criterion = TerminationCriterion::Divergence;
219 WARNING_NAMED(
"ILQRSolver",
"Diverged: Controls or cost are not finite.");
223 time_taken_forward_pass = line_search_timer.
GetDuration();
227 HIGHLIGHT_NAMED(
"ILQRSolver",
"Iteration " << iteration << std::setprecision(3) <<
":\tBackward pass: " << time_taken_backward_pass <<
" s\tForward pass: " << time_taken_forward_pass <<
" s\tCost: " << cost <<
"\talpha: " << best_alpha);
236 if ((cost_prev - cost) < parameters_.FunctionTolerance * std::max(1.0, std::abs(cost)))
239 if (parameters_.FunctionTolerancePatience > 0)
241 if (iteration - last_best_iteration > parameters_.FunctionTolerancePatience)
243 if (debug_)
HIGHLIGHT_NAMED(
"ILQRSolver",
"Early stopping criterion reached (" << cost <<
" < " << cost_prev <<
"). Time: " << planning_timer.
GetDuration());
244 prob_->termination_criterion = TerminationCriterion::FunctionTolerance;
250 if (debug_)
HIGHLIGHT_NAMED(
"ILQRSolver",
"Function tolerance reached (" << cost <<
" < " << cost_prev <<
"). Time: " << planning_timer.
GetDuration());
251 prob_->termination_criterion = TerminationCriterion::FunctionTolerance;
258 last_best_iteration = iteration;
262 if (cost < cost_prev)
284 if (debug_ && iteration == parameters_.MaxIterations)
287 prob_->termination_criterion = TerminationCriterion::IterationLimit;
291 for (
int t = 0; t < T - 1; ++t)
292 prob_->Update(new_U.col(t), t);
295 prob_->SetCostEvolution(iteration, cost);
299 for (
int t = 0; t < T - 1; ++t)
301 solution.row(t) = new_U.col(t).transpose();
302 prob_->Update(new_U.col(t), t);
310 const Eigen::MatrixXd control_limits = dynamics_solver_->get_control_limits();
312 Eigen::VectorXd delta_uk = -Ku_gains_[t] * best_ref_u_.col(t) - Kv_gains_[t] * vk_gains_[t + 1] -
313 K_gains_[t] * dynamics_solver_->StateDelta(x, best_ref_x_.col(t));
315 Eigen::VectorXd u = best_ref_u_.col(t) + delta_uk;
316 return u.cwiseMax(control_limits.col(0)).cwiseMin(control_limits.col(1));
#define WARNING_NAMED(name, x)
#define REGISTER_MOTIONSOLVER_TYPE(TYPE, DERIV)
const Eigen::Ref< const Eigen::MatrixXd > & MatrixXdRefConst
const Eigen::Ref< const Eigen::VectorXd > & VectorXdRefConst
#define HIGHLIGHT_NAMED(name, x)
double GetDuration() const
std::shared_ptr< PlanningProblem > PlanningProblemPtr
ScenePtr GetScene() const