38 if (pointer->type() !=
"exotica::DynamicTimeIndexedShootingProblem")
40 ThrowNamed(
"This ILQGSolver can't solve problem of type '" << pointer->type() <<
"'!");
43 MotionSolver::SpecifyProblem(pointer);
45 dynamics_solver_ = prob_->
GetScene()->GetDynamicsSolver();
49 void ILQGSolver::BackwardPass()
51 constexpr
double min_clamp_ = -1e10;
52 constexpr
double max_clamp_ = 1e10;
53 const int T = prob_->get_T();
54 const double dt = dynamics_solver_->get_dt();
55 const int NU = prob_->GetScene()->get_num_controls();
58 Eigen::VectorXd big_C_times_little_c = Eigen::VectorXd::Zero(NU, 1);
59 Eigen::MatrixXd big_C_times_big_C = Eigen::MatrixXd::Zero(NU, NU);
60 Eigen::MatrixXd little_c_times_little_c = Eigen::MatrixXd::Zero(1, 1);
63 double s0 = prob_->GetStateCost(T - 1);
64 Eigen::MatrixXd
s = prob_->GetStateCostJacobian(T - 1);
65 Eigen::MatrixXd S = prob_->GetStateCostHessian(T - 1);
67 for (
int t = T - 2; t > 0; --t)
70 Eigen::VectorXd
x = prob_->get_X(t), u = prob_->get_U(t);
71 dynamics_solver_->ComputeDerivatives(x, u);
72 Eigen::MatrixXd
A = dynamics_solver_->get_Fx();
73 Eigen::MatrixXd B = dynamics_solver_->get_Fu();
75 double q0 = dt * (prob_->GetStateCost(t) + prob_->GetControlCost(t));
77 Eigen::MatrixXd q = dt * prob_->GetStateCostJacobian(t);
78 Eigen::MatrixXd Q = dt * prob_->GetStateCostHessian(t);
79 Eigen::MatrixXd
r = dt * prob_->GetControlCostJacobian(t);
80 Eigen::MatrixXd R = dt * prob_->GetControlCostHessian(t);
81 Eigen::MatrixXd P = dt * prob_->GetStateControlCostHessian();
83 Eigen::MatrixXd g = r + B.transpose() * s;
84 Eigen::MatrixXd G = P + B.transpose() * S * A;
85 Eigen::MatrixXd H = R + B.transpose() * S * B;
87 if (parameters_.IncludeNoiseTerms)
89 Eigen::MatrixXd F = prob_->get_F(t);
90 for (
int i = 0; i < NU; ++i)
92 Eigen::MatrixXd C = std::sqrt(dt) * prob_->GetControlNoiseJacobian(i);
93 Eigen::VectorXd c = std::sqrt(dt) * F.col(i);
95 big_C_times_little_c = big_C_times_little_c + C.transpose() * S * c;
96 big_C_times_big_C = big_C_times_big_C + C.transpose() * S * C;
97 little_c_times_little_c = little_c_times_little_c + c.transpose() * S * c;
100 g = g + big_C_times_little_c;
101 H = H + big_C_times_big_C;
105 Eigen::EigenSolver<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>> eig_solver(H);
106 auto d = eig_solver.eigenvalues();
107 auto V = eig_solver.eigenvectors();
108 Eigen::MatrixXcd D = Eigen::MatrixXcd::Zero(
d.size(),
d.size());
110 for (
int i = 0; i <
d.size(); ++i)
114 d[i] = 1. / (
d[i] + parameters_.RegularizationRate);
118 Eigen::MatrixXd H1 = (V * D * V.transpose()).
real();
120 l_gains_[t] = -H1 * g;
121 L_gains_[t] = -H1 * G;
124 S = Q + A.transpose() * S * A + L_gains_[t].transpose() * H * L_gains_[t] + L_gains_[t].transpose() * G + G.transpose() * L_gains_[t];
125 s = q + A.transpose() * s + L_gains_[t].transpose() * H * l_gains_[t] +
126 L_gains_[t].transpose() * g + G.transpose() * l_gains_[t];
127 s0 = q0 + s0 + (l_gains_[t].transpose() * H * l_gains_[t] / 2.0 + l_gains_[t].transpose() * g)(0);
129 if (parameters_.IncludeNoiseTerms)
131 s0 = s0 + 0.5 * little_c_times_little_c(0);
135 S = S.unaryExpr([min_clamp_, max_clamp_](
double x) ->
double {
136 return std::min(std::max(x, min_clamp_), max_clamp_);
138 s = s.unaryExpr([min_clamp_, max_clamp_](
double x) ->
double {
139 return std::min(std::max(x, min_clamp_), max_clamp_);
141 s0 = std::min(std::max(s0, min_clamp_), max_clamp_);
148 const int T = prob_->get_T();
149 const Eigen::MatrixXd control_limits = dynamics_solver_->get_control_limits();
150 const double dt = dynamics_solver_->get_dt();
154 for (
int t = 0; t < T - 1; ++t)
156 Eigen::VectorXd u = ref_u.col(t);
158 Eigen::VectorXd delta_uk = l_gains_[t] +
159 L_gains_[t] * dynamics_solver_->StateDelta(prob_->get_X(t), ref_x.col(t));
161 u.noalias() += alpha * delta_uk;
163 u = u.cwiseMax(control_limits.col(0)).cwiseMin(control_limits.col(1));
166 cost += dt * (prob_->GetControlCost(t) + prob_->GetStateCost(t));
170 cost += prob_->GetStateCost(T - 1);
174 void ILQGSolver::Solve(Eigen::MatrixXd& solution)
176 if (!prob_)
ThrowNamed(
"Solver has not been initialized!");
177 Timer planning_timer, backward_pass_timer, line_search_timer;
179 prob_->DisableStochasticUpdates();
181 const int T = prob_->get_T();
182 const int NU = prob_->GetScene()->get_num_controls();
183 const int NX = prob_->GetScene()->get_num_state();
184 const double dt = dynamics_solver_->get_dt();
185 prob_->ResetCostEvolution(GetNumberOfMaxIterations() + 1);
188 double initial_cost = 0;
189 for (
int t = 0; t < T - 1; ++t)
190 initial_cost += dt * (prob_->GetControlCost(t) + prob_->GetStateCost(t));
193 initial_cost += prob_->GetStateCost(T - 1);
194 prob_->SetCostEvolution(0, initial_cost);
197 l_gains_.assign(T, Eigen::MatrixXd::Zero(NU, 1));
198 L_gains_.assign(T, Eigen::MatrixXd::Zero(NU, NX));
202 Eigen::MatrixXd new_U, global_best_U = prob_->get_U();
203 solution.resize(T - 1, NU);
205 if (debug_)
HIGHLIGHT_NAMED(
"ILQGSolver",
"Running ILQG solver for max " << parameters_.MaxIterations <<
" iterations");
207 double last_cost = initial_cost, global_best_cost = initial_cost;
208 int last_best_iteration = 0;
210 for (
int iteration = 1; iteration <= GetNumberOfMaxIterations(); ++iteration)
213 if (Server::IsRos() && !
ros::ok())
215 if (debug_)
HIGHLIGHT(
"Solving cancelled by user");
216 prob_->termination_criterion = TerminationCriterion::UserDefined;
221 backward_pass_timer.
Reset();
226 line_search_timer.
Reset();
231 const Eigen::VectorXd alpha_space = Eigen::VectorXd::LinSpaced(10, 0.1, 1.0);
232 double current_cost = 0, best_alpha = 0;
234 Eigen::MatrixXd ref_x = prob_->get_X(),
235 ref_u = prob_->get_U();
237 for (
int ai = 0; ai < alpha_space.rows(); ++ai)
239 double alpha = alpha_space(ai);
240 double cost = ForwardPass(alpha, ref_x, ref_u);
242 if (ai == 0 || (cost < current_cost && !std::isnan(cost)))
245 new_U = prob_->get_U();
253 if (!new_U.allFinite() || !std::isfinite(current_cost))
255 prob_->termination_criterion = TerminationCriterion::Divergence;
262 HIGHLIGHT_NAMED(
"ILQGSolver",
"Forward pass complete in " << line_search_timer.
GetDuration() <<
" with cost: " << current_cost <<
" and alpha " << best_alpha);
263 HIGHLIGHT_NAMED(
"ILQGSolver",
"Final state: " << prob_->get_X(T - 1).transpose());
267 if (global_best_cost > current_cost)
269 global_best_cost = current_cost;
270 last_best_iteration = iteration;
271 global_best_U = new_U;
276 if (iteration - last_best_iteration > parameters_.FunctionTolerancePatience)
279 prob_->termination_criterion = TerminationCriterion::FunctionTolerance;
283 if (last_cost - current_cost < parameters_.FunctionTolerance && last_cost - current_cost > 0)
286 prob_->termination_criterion = TerminationCriterion::Divergence;
290 if (debug_ && iteration == parameters_.MaxIterations)
293 prob_->termination_criterion = TerminationCriterion::IterationLimit;
296 last_cost = current_cost;
297 for (
int t = 0; t < T - 1; ++t)
298 prob_->Update(new_U.col(t), t);
299 prob_->SetCostEvolution(iteration, current_cost);
303 for (
int t = 0; t < T - 1; ++t)
305 solution.row(t) = global_best_U.col(t).transpose();
306 prob_->Update(global_best_U.col(t), t);
312 prob_->EnableStochasticUpdates();
317 const Eigen::MatrixXd control_limits = dynamics_solver_->get_control_limits();
319 Eigen::VectorXd delta_uk = l_gains_[t] +
320 L_gains_[t] * dynamics_solver_->StateDelta(x, best_ref_x_.col(t));
322 Eigen::VectorXd u = best_ref_u_.col(t) + delta_uk;
323 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
const AutoDiffScalar< DerType > & real(const AutoDiffScalar< DerType > &x)