36 void FeasibilityDrivenDDPSolver::Instantiate(
const FeasibilityDrivenDDPSolverInitializer& init)
39 base_parameters_ = AbstractDDPSolverInitializer(FeasibilityDrivenDDPSolverInitializer(parameters_));
41 clamp_to_control_limits_in_forward_pass_ = base_parameters_.ClampControlsInForwardPass;
42 initial_regularization_rate_ = base_parameters_.RegularizationRate;
43 th_stepinc_ = base_parameters_.ThresholdRegularizationIncrease;
44 th_stepdec_ = base_parameters_.ThresholdRegularizationDecrease;
46 th_stop_ = parameters_.GradientToleranceConvergenceThreshold;
47 th_gradient_tolerance_ = parameters_.GradientTolerance;
48 th_acceptstep_ = parameters_.DescentStepAcceptanceThreshold;
49 th_acceptnegstep_ = parameters_.AscentStepAcceptanceThreshold;
52 void AbstractFeasibilityDrivenDDPSolver::AllocateData()
57 const int T = prob_->get_T() - 1;
71 xs_try_.resize(T + 1);
83 for (
int t = 0; t < T; ++t)
85 Vxx_[t] = Eigen::MatrixXd::Zero(NDX_, NDX_);
86 Vx_[t] = Eigen::VectorXd::Zero(NDX_);
87 Qxx_[t] = Eigen::MatrixXd::Zero(NDX_, NDX_);
88 Qxu_[t] = Eigen::MatrixXd::Zero(NDX_, NU_);
89 Quu_[t] = Eigen::MatrixXd::Zero(NU_, NU_);
90 Qx_[t] = Eigen::VectorXd::Zero(NDX_);
91 Qu_[t] = Eigen::VectorXd::Zero(NU_);
92 K_[t] = Eigen::MatrixXd::Zero(NU_, NDX_);
93 k_[t] = Eigen::VectorXd::Zero(NU_);
94 fs_[t] = Eigen::VectorXd::Zero(NDX_);
98 xs_try_[t] = prob_->get_X(0);
102 xs_try_[t].setZero(NX_);
104 us_try_[t].setZero(NU_);
105 dx_[t] = Eigen::VectorXd::Zero(NDX_);
107 FuTVxx_p_[t] = Eigen::MatrixXd::Zero(NU_, NDX_);
108 Quu_ldlt_[t] = Eigen::LDLT<Eigen::MatrixXd>(NU_);
109 Quuk_[t] = Eigen::VectorXd(NU_);
111 Quu_inv_[t] = Eigen::MatrixXd(NU_, NU_);
112 fx_[t] = Eigen::MatrixXd(NDX_, NDX_);
113 fu_[t] = Eigen::MatrixXd(NDX_, NU_);
115 Vxx_.back() = Eigen::MatrixXd::Zero(NDX_, NDX_);
116 Vx_.back() = Eigen::VectorXd::Zero(NDX_);
117 xs_try_.back().setZero(NX_);
118 fs_.back() = Eigen::VectorXd::Zero(NDX_);
119 dx_.back() = Eigen::VectorXd::Zero(NDX_);
121 FxTVxx_p_ = Eigen::MatrixXd::Zero(NDX_, NDX_);
122 fTVxx_p_ = Eigen::VectorXd::Zero(NDX_);
130 AbstractDDPSolver::SpecifyProblem(pointer);
133 dt_ = dynamics_solver_->get_dt();
135 NU_ = prob_->GetScene()->get_num_controls();
136 NX_ = prob_->GetScene()->get_num_state();
137 NDX_ = prob_->GetScene()->get_num_state_derivative();
142 void AbstractFeasibilityDrivenDDPSolver::Solve(Eigen::MatrixXd& solution)
144 if (!prob_)
ThrowNamed(
"Solver has not been initialized!");
145 Timer planning_timer, backward_pass_timer, line_search_timer;
148 if (T_ != last_T_) AllocateData();
150 dt_ = dynamics_solver_->get_dt();
151 control_limits_ = dynamics_solver_->get_control_limits();
153 const Eigen::MatrixXd& X_warm = prob_->get_X();
154 const Eigen::MatrixXd& U_warm = prob_->get_U();
155 for (
int t = 0; t < T_ - 1; ++t)
157 xs_[t] = X_warm.col(t);
158 us_[t] = U_warm.col(t);
160 xs_[0] = prob_->ApplyStartState();
161 xs_.back() = X_warm.col(T_ - 1);
162 is_feasible_ =
false;
164 prob_->ResetCostEvolution(GetNumberOfMaxIterations() + 1);
165 control_cost_evolution_.assign(GetNumberOfMaxIterations() + 1, std::numeric_limits<double>::quiet_NaN());
166 steplength_evolution_.assign(GetNumberOfMaxIterations() + 1, std::numeric_limits<double>::quiet_NaN());
167 regularization_evolution_.assign(GetNumberOfMaxIterations() + 1, std::numeric_limits<double>::quiet_NaN());
169 solution.resize(T_ - 1, NU_);
174 for (
int t = 0; t < T_ - 1; ++t)
176 prob_->Update(xs_[t], us_[t], t);
177 control_cost_ += dt_ * prob_->GetControlCost(t);
178 cost_ += dt_ * prob_->GetStateCost(t);
181 prob_->set_X(X_warm);
182 cost_ += prob_->GetStateCost(T_ - 1) + control_cost_;
183 prob_->SetCostEvolution(0, cost_);
184 control_cost_evolution_.at(0) = control_cost_;
186 xreg_ = std::max(regmin_, initial_regularization_rate_);
187 ureg_ = std::max(regmin_, initial_regularization_rate_);
188 was_feasible_ =
false;
190 bool diverged =
false;
191 bool converged =
false;
193 bool recalcDiff =
true;
198 for (iter = 1; iter <= GetNumberOfMaxIterations(); ++iter)
201 if (Server::IsRos() && !
ros::ok())
203 if (debug_)
HIGHLIGHT(
"Solving cancelled by user");
204 prob_->termination_criterion = TerminationCriterion::UserDefined;
208 backward_pass_timer.
Reset();
209 while (!ComputeDirection(recalcDiff))
213 IncreaseRegularization();
214 if (xreg_ == regmax_)
216 if (debug_)
WARNING_NAMED(
"FeasibilityDrivenDDPSolver::Solve",
"State regularization exceeds maximum regularization: " << xreg_ <<
" > " << regmax_)
225 time_taken_backward_pass_ = backward_pass_timer.
GetDuration();
229 if (debug_)
WARNING(
"Terminating: Divergence in ComputeDirection / BackwardPass.");
233 UpdateExpectedImprovement();
237 line_search_timer.
Reset();
238 for (
int ai = 0; ai < alpha_space_.size(); ++ai)
240 steplength_ = alpha_space_(ai);
241 dV_ = TryStep(steplength_);
243 ExpectedImprovement();
244 dVexp_ = steplength_ * (d_[0] + 0.5 * steplength_ * d_[1]);
248 if (d_[0] < th_grad_ || dV_ > th_acceptstep_ * dVexp_)
250 was_feasible_ = is_feasible_;
251 SetCandidate(xs_try_, us_try_, (was_feasible_) || (steplength_ == 1.0));
253 control_cost_ = control_cost_try_;
254 prob_->SetCostEvolution(iter, cost_);
255 control_cost_evolution_.at(iter) = control_cost_;
262 if (dV_ > th_acceptnegstep_ * dVexp_)
265 was_feasible_ = is_feasible_;
266 SetCandidate(xs_try_, us_try_, (was_feasible_) || (steplength_ == 1));
268 control_cost_ = control_cost_try_;
269 prob_->SetCostEvolution(iter, cost_);
270 control_cost_evolution_.at(iter) = control_cost_;
279 prob_->SetCostEvolution(iter, cost_);
280 control_cost_evolution_.at(iter) = control_cost_;
282 time_taken_forward_pass_ = line_search_timer.
GetDuration();
284 steplength_evolution_.at(iter) = steplength_;
285 regularization_evolution_.at(iter) = xreg_;
289 if (iter % 10 == 0 || iter == 1)
291 std::cout <<
"iter \t cost \t stop \t grad \t xreg";
292 std::cout <<
" \t ureg \t step \t feas \tdV-exp \t dV\n";
295 std::cout << std::setw(4) << iter <<
" ";
296 std::cout << std::scientific << std::setprecision(5) << cost_ <<
" ";
297 std::cout << stop_ <<
" " << -d_[1] <<
" ";
298 std::cout << xreg_ <<
" " << ureg_ <<
" ";
299 std::cout << std::fixed << std::setprecision(4) << steplength_ <<
" ";
300 std::cout << is_feasible_ <<
" ";
301 std::cout << std::scientific << std::setprecision(5) << dVexp_ <<
" ";
302 std::cout << dV_ <<
'\n';
306 if (steplength_ > th_stepdec_)
308 DecreaseRegularization();
310 if (steplength_ <= th_stepinc_)
312 IncreaseRegularization();
313 if (xreg_ == regmax_)
315 WARNING_NAMED(
"FeasibilityDrivenDDPSolver::Solve",
"State regularization exceeds maximum regularization: " << xreg_ <<
" > " << regmax_)
320 CheckStoppingCriteria();
326 if (was_feasible_ && stop_ < th_stop_ && stop_ != 0.0)
328 if (debug_)
HIGHLIGHT_NAMED(
"FeasibilityDrivenDDPSolver::Solve",
"Convergence: " << stop_ <<
" < " << th_stop_)
335 WARNING_NAMED(
"FeasibilityDrivenDDPSolver::Solve",
"Terminating: Divergence in ForwardPass.");
340 if (was_feasible_ && std::abs(-d_[1]) < th_gradient_tolerance_)
342 if (debug_)
HIGHLIGHT_NAMED(
"FeasibilityDrivenDDPSolver::Solve",
"Gradient tolerance: " << -d_[1] <<
" < " << th_gradient_tolerance_)
343 prob_->termination_criterion = TerminationCriterion::GradientTolerance;
347 prob_->OnSolverIterationEnd();
350 if (diverged) prob_->termination_criterion = TerminationCriterion::Divergence;
351 if (converged) prob_->termination_criterion = TerminationCriterion::Convergence;
352 if (!converged && iter == GetNumberOfMaxIterations() + 1) prob_->termination_criterion = TerminationCriterion::IterationLimit;
355 for (
int t = 0; t < T_ - 1; ++t)
357 solution.row(t) = us_[t].transpose();
366 void AbstractFeasibilityDrivenDDPSolver::IncreaseRegularization()
376 void AbstractFeasibilityDrivenDDPSolver::DecreaseRegularization()
386 double AbstractFeasibilityDrivenDDPSolver::CheckStoppingCriteria()
389 for (
int t = 0; t < T_ - 1; ++t)
391 stop_ += Qu_[t].squaredNorm();
396 const Eigen::Vector2d& AbstractFeasibilityDrivenDDPSolver::ExpectedImprovement()
401 for (
int t = 0; t < T_; ++t)
403 prob_->GetScene()->GetDynamicsSolver()->StateDelta(xs_[t], xs_try_[t], dx_[t]);
404 fTVxx_p_.noalias() = Vxx_[t] * dx_[t];
405 dv_ -= fs_[t].dot(fTVxx_p_);
409 d_[1] = dq_ - 2 * dv_;
413 void AbstractFeasibilityDrivenDDPSolver::UpdateExpectedImprovement()
419 dg_ -= Vx_.back().dot(fs_.back());
420 fTVxx_p_.noalias() = Vxx_.back() * fs_.back();
421 dq_ += fs_.back().dot(fTVxx_p_);
423 for (
int t = 0; t < T_ - 1; ++t)
425 dg_ += Qu_[t].dot(k_[t]);
426 dq_ -= k_[t].dot(Quuk_[t]);
429 dg_ -= Vx_[t].dot(fs_[t]);
430 fTVxx_p_.noalias() = Vxx_[t] * fs_[t];
431 dq_ += fs_[t].dot(fTVxx_p_);
436 void AbstractFeasibilityDrivenDDPSolver::SetCandidate(
const std::vector<Eigen::VectorXd>& xs_in,
const std::vector<Eigen::VectorXd>& us_in,
const bool is_feasible)
438 const std::size_t T =
static_cast<std::size_t
>(prob_->get_T());
440 if (xs_in.size() == 0)
442 for (
int t = 0; t < T_; ++t)
449 if (xs_in.size() != T)
451 ThrowPretty(
"Warm start state has wrong dimension, got " << xs_in.size() <<
" expecting " << (T + 1));
453 std::copy(xs_in.begin(), xs_in.end(), xs_.begin());
456 if (us_in.size() == 0)
458 for (
int t = 0; t < T_ - 1; ++t)
465 if (us_in.size() != T - 1)
467 ThrowPretty(
"Warm start control has wrong dimension, got " << us_in.size() <<
" expecting " << T);
469 std::copy(us_in.begin(), us_in.end(), us_.begin());
471 is_feasible_ = is_feasible;
474 double AbstractFeasibilityDrivenDDPSolver::TryStep(
const double steplength)
476 ForwardPass(steplength);
477 return cost_ - cost_try_;
480 void AbstractFeasibilityDrivenDDPSolver::ForwardPass(
const double steplength)
482 if (steplength > 1. || steplength < 0.)
484 ThrowPretty(
"Invalid argument: invalid step length, value should be between 0. to 1. - got=" << steplength);
487 control_cost_try_ = 0.;
488 xnext_ = prob_->get_X(0);
489 for (
int t = 0; t < T_ - 1; ++t)
492 if ((is_feasible_) || (steplength == 1))
499 prob_->GetScene()->GetDynamicsSolver()->Integrate(xnext_, fs_[t] * (steplength - 1), 1., xs_try_[t]);
501 prob_->GetScene()->GetDynamicsSolver()->StateDelta(xs_try_[t], xs_[t], dx_[t]);
502 us_try_[t].noalias() = us_[t] - k_[t] * steplength - K_[t] * dx_[t];
504 if (clamp_to_control_limits_in_forward_pass_)
506 us_try_[t] = us_try_[t].cwiseMax(control_limits_.col(0)).cwiseMin(control_limits_.col(1));
509 prob_->Update(xs_try_[t], us_try_[t], t);
510 xnext_ = prob_->get_X(t + 1);
512 control_cost_try_ += dt_ * prob_->GetControlCost(t);
513 cost_try_ += dt_ * prob_->GetStateCost(t);
515 if (IsNaN(cost_try_))
517 WARNING_NAMED(
"NaN in ForwardPass",
"forward_error - NaN in cost_try_ at t=" << t);
520 if (IsNaN(xnext_.lpNorm<Eigen::Infinity>()))
522 WARNING_NAMED(
"NaN in ForwardPass",
"forward_error - xnext_ isn't finite at t=" << t <<
": x=" << xs_try_[t].transpose() <<
", u=" << us_try_[t].transpose() <<
", xnext=" << xnext_.transpose());
528 if ((is_feasible_) || (steplength == 1))
530 xs_try_.back() = xnext_;
534 prob_->GetScene()->GetDynamicsSolver()->Integrate(xnext_, fs_.back() * (steplength - 1), 1., xs_try_.back());
536 prob_->UpdateTerminalState(xs_try_.back());
537 cost_try_ += prob_->GetStateCost(T_ - 1) + control_cost_try_;
539 if (IsNaN(cost_try_))
541 WARNING_NAMED(
"NaN in ForwardPass",
"forward_error - cost NaN");
546 bool AbstractFeasibilityDrivenDDPSolver::ComputeDirection(
const bool recalcDiff)
552 return BackwardPassFDDP();
555 double AbstractFeasibilityDrivenDDPSolver::CalcDiff()
560 for (
int t = 0; t < T_ - 1; ++t)
562 control_cost_ += dt_ * prob_->GetControlCost(t);
563 cost_ += dt_ * prob_->GetStateCost(t);
566 cost_ += prob_->GetStateCost(T_ - 1) + control_cost_;
571 for (
int t = 0; t < prob_->get_T(); ++t)
573 prob_->GetScene()->GetDynamicsSolver()->StateDelta(prob_->get_X(t), xs_[t], fs_[t]);
576 else if (!was_feasible_)
578 for (std::vector<Eigen::VectorXd>::iterator it = fs_.begin(); it != fs_.end(); ++it)
586 bool AbstractFeasibilityDrivenDDPSolver::BackwardPassFDDP()
588 Vxx_.back() = prob_->GetStateCostHessian(T_ - 1);
589 Vx_.back() = prob_->GetStateCostJacobian(T_ - 1);
591 if (!std::isnan(xreg_))
593 Vxx_.back().diagonal().array() += xreg_;
598 Vx_.back().noalias() += Vxx_.back() * fs_.back();
601 for (
int t = static_cast<int>(prob_->get_T()) - 2; t >= 0; --t)
603 const Eigen::MatrixXd& Vxx_p = Vxx_[t + 1];
604 const Eigen::VectorXd& Vx_p = Vx_[t + 1];
606 Qxx_[t].noalias() = dt_ * prob_->GetStateCostHessian(t);
607 Qxu_[t].noalias() = dt_ * prob_->GetStateControlCostHessian().transpose();
608 Quu_[t].noalias() = dt_ * prob_->GetControlCostHessian(t);
609 Qx_[t].noalias() = dt_ * prob_->GetStateCostJacobian(t);
610 Qu_[t].noalias() = dt_ * prob_->GetControlCostJacobian(t);
612 dynamics_solver_->ComputeDerivatives(xs_[t], us_[t]);
613 fx_[t].noalias() = dynamics_solver_->get_Fx();
614 fu_[t].noalias() = dynamics_solver_->get_Fu();
616 FxTVxx_p_.noalias() = fx_[t].transpose() * Vxx_p;
617 FuTVxx_p_[t].noalias() = fu_[t].transpose() * Vxx_p;
618 Qxx_[t].noalias() += FxTVxx_p_ * fx_[t];
619 Qxu_[t].noalias() += FxTVxx_p_ * fu_[t];
620 Quu_[t].noalias() += FuTVxx_p_[t] * fu_[t];
621 Qx_[t].noalias() += fx_[t].transpose() * Vx_p;
622 Qu_[t].noalias() += fu_[t].transpose() * Vx_p;
624 if (!std::isnan(ureg_))
626 Quu_[t].diagonal().array() += ureg_;
632 if (std::isnan(ureg_))
634 Vx_[t].noalias() -= K_[t].transpose() * Qu_[t];
638 Quuk_[t].noalias() = Quu_[t] * k_[t];
639 Vx_[t].noalias() += K_[t].transpose() * Quuk_[t];
640 Vx_[t].noalias() -= 2 * (K_[t].transpose() * Qu_[t]);
643 Vxx_[t].noalias() -= Qxu_[t] * K_[t];
644 Vxx_[t] = 0.5 * (Vxx_[t] + Vxx_[t].transpose()).eval();
646 if (!std::isnan(xreg_))
648 Vxx_[t].diagonal().array() += xreg_;
654 Vx_[t].noalias() += Vxx_[t] * fs_[t];
657 if (IsNaN(Vx_[t].lpNorm<Eigen::Infinity>()))
659 HIGHLIGHT(
"Vx_[" << t <<
"] is NaN: " << Vx_[t].transpose());
662 if (IsNaN(Vxx_[t].lpNorm<Eigen::Infinity>()))
671 void AbstractFeasibilityDrivenDDPSolver::ComputeGains(
const int t)
680 Quu_ldlt_[t].compute(Quu_[t]);
681 const Eigen::ComputationInfo& info = Quu_ldlt_[t].info();
682 if (info != Eigen::Success)
684 HIGHLIGHT_NAMED(
"ComputeGains",
"Cholesky failed for reg=" << ureg_ <<
", Quu_[t]=\n" 686 Quu_[t].diagonal().array() -= ureg_;
687 IncreaseRegularization();
688 Quu_[t].diagonal().array() += ureg_;
689 if (ureg_ == regmax_)
ThrowPretty(
"backward_error - error in Cholesky decomposition\n" 699 K_[t] = Qxu_[t].transpose();
700 Quu_ldlt_[t].solveInPlace(K_[t]);
702 Quu_ldlt_[t].solveInPlace(k_[t]);
#define WARNING_NAMED(name, x)
#define REGISTER_MOTIONSOLVER_TYPE(TYPE, DERIV)
#define HIGHLIGHT_NAMED(name, x)
double GetDuration() const
std::shared_ptr< PlanningProblem > PlanningProblemPtr