feasibility_driven_ddp_solver.cpp
Go to the documentation of this file.
1 //
2 // Copyright (c) 2019-2020, LAAS-CNRS, University of Edinburgh, University of Oxford
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of nor the names of its contributors may be used to
14 // endorse or promote products derived from this software without specific
15 // prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 //
29 
31 
33 
34 namespace exotica
35 {
36 void FeasibilityDrivenDDPSolver::Instantiate(const FeasibilityDrivenDDPSolverInitializer& init)
37 {
38  parameters_ = init;
39  base_parameters_ = AbstractDDPSolverInitializer(FeasibilityDrivenDDPSolverInitializer(parameters_));
40 
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;
45 
46  th_stop_ = parameters_.GradientToleranceConvergenceThreshold;
47  th_gradient_tolerance_ = parameters_.GradientTolerance;
48  th_acceptstep_ = parameters_.DescentStepAcceptanceThreshold;
49  th_acceptnegstep_ = parameters_.AscentStepAcceptanceThreshold;
50 }
51 
52 void AbstractFeasibilityDrivenDDPSolver::AllocateData()
53 {
54  // TODO: -1 here because of copy-paste and different conventions...
55  // Crocoddyl: T running models + 1 terminal model
56  // Exotica: T models ("T-1 running models")
57  const int T = prob_->get_T() - 1;
58  Vxx_.resize(T + 1);
59  Vx_.resize(T + 1);
60  Qxx_.resize(T);
61  Qxu_.resize(T);
62  Quu_.resize(T);
63  Qx_.resize(T);
64  Qu_.resize(T);
65  K_.resize(T);
66  k_.resize(T);
67  fs_.resize(T + 1);
68 
69  xs_.resize(T + 1);
70  us_.resize(T);
71  xs_try_.resize(T + 1);
72  us_try_.resize(T);
73  dx_.resize(T + 1);
74 
75  FuTVxx_p_.resize(T);
76  Quu_ldlt_.resize(T);
77  Quuk_.resize(T);
78 
79  Quu_inv_.resize(T);
80  fx_.resize(T);
81  fu_.resize(T);
82 
83  for (int t = 0; t < T; ++t)
84  {
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_);
95 
96  if (t == 0)
97  {
98  xs_try_[t] = prob_->get_X(0);
99  }
100  else
101  {
102  xs_try_[t].setZero(NX_);
103  }
104  us_try_[t].setZero(NU_);
105  dx_[t] = Eigen::VectorXd::Zero(NDX_);
106 
107  FuTVxx_p_[t] = Eigen::MatrixXd::Zero(NU_, NDX_);
108  Quu_ldlt_[t] = Eigen::LDLT<Eigen::MatrixXd>(NU_);
109  Quuk_[t] = Eigen::VectorXd(NU_);
110 
111  Quu_inv_[t] = Eigen::MatrixXd(NU_, NU_);
112  fx_[t] = Eigen::MatrixXd(NDX_, NDX_);
113  fu_[t] = Eigen::MatrixXd(NDX_, NU_);
114  }
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_);
120 
121  FxTVxx_p_ = Eigen::MatrixXd::Zero(NDX_, NDX_);
122  fTVxx_p_ = Eigen::VectorXd::Zero(NDX_);
123 
124  // If T changed, we need to re-allocate.
125  last_T_ = T_;
126 }
127 
128 void AbstractFeasibilityDrivenDDPSolver::SpecifyProblem(PlanningProblemPtr pointer)
129 {
130  AbstractDDPSolver::SpecifyProblem(pointer);
131 
132  T_ = prob_->get_T();
133  dt_ = dynamics_solver_->get_dt();
134 
135  NU_ = prob_->GetScene()->get_num_controls();
136  NX_ = prob_->GetScene()->get_num_state(); // State vector size
137  NDX_ = prob_->GetScene()->get_num_state_derivative(); // Tangent vector size
138 
139  AllocateData();
140 }
141 
142 void AbstractFeasibilityDrivenDDPSolver::Solve(Eigen::MatrixXd& solution)
143 {
144  if (!prob_) ThrowNamed("Solver has not been initialized!");
145  Timer planning_timer, backward_pass_timer, line_search_timer;
146 
147  T_ = prob_->get_T();
148  if (T_ != last_T_) AllocateData();
149 
150  dt_ = dynamics_solver_->get_dt();
151  control_limits_ = dynamics_solver_->get_control_limits();
152 
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)
156  {
157  xs_[t] = X_warm.col(t);
158  us_[t] = U_warm.col(t);
159  }
160  xs_[0] = prob_->ApplyStartState(); // Apply start state
161  xs_.back() = X_warm.col(T_ - 1);
162  is_feasible_ = false; // We assume the first iteration is always infeasible. TODO: Make this configurable
163 
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());
168  prob_->PreUpdate();
169  solution.resize(T_ - 1, NU_);
170 
171  // Initial roll-out to get initial cost
172  cost_ = 0.0;
173  control_cost_ = 0.0;
174  for (int t = 0; t < T_ - 1; ++t)
175  {
176  prob_->Update(xs_[t], us_[t], t);
177  control_cost_ += dt_ * prob_->GetControlCost(t);
178  cost_ += dt_ * prob_->GetStateCost(t);
179  }
180  // Reset shooting nodes so we can warm-start from state trajectory
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_;
185 
186  xreg_ = std::max(regmin_, initial_regularization_rate_);
187  ureg_ = std::max(regmin_, initial_regularization_rate_);
188  was_feasible_ = false;
189 
190  bool diverged = false;
191  bool converged = false;
192 
193  bool recalcDiff = true;
194  int iter;
195 
196  // double time_taken_setup_ = planning_timer.GetDuration();
197 
198  for (iter = 1; iter <= GetNumberOfMaxIterations(); ++iter)
199  {
200  // Check whether user interrupted (Ctrl+C)
201  if (Server::IsRos() && !ros::ok())
202  {
203  if (debug_) HIGHLIGHT("Solving cancelled by user");
204  prob_->termination_criterion = TerminationCriterion::UserDefined;
205  break;
206  }
207 
208  backward_pass_timer.Reset();
209  while (!ComputeDirection(recalcDiff))
210  {
211  // HIGHLIGHT("Increase regularization in ComputeDirection and try again.")
212  recalcDiff = false;
213  IncreaseRegularization();
214  if (xreg_ == regmax_)
215  {
216  if (debug_) WARNING_NAMED("FeasibilityDrivenDDPSolver::Solve", "State regularization exceeds maximum regularization: " << xreg_ << " > " << regmax_)
217  diverged = true;
218  break;
219  }
220  else
221  {
222  continue;
223  }
224  }
225  time_taken_backward_pass_ = backward_pass_timer.GetDuration();
226 
227  if (diverged)
228  {
229  if (debug_) WARNING("Terminating: Divergence in ComputeDirection / BackwardPass.");
230  break;
231  }
232 
233  UpdateExpectedImprovement();
234 
235  // We need to recalculate the derivatives when the step length passes
236  recalcDiff = false;
237  line_search_timer.Reset();
238  for (int ai = 0; ai < alpha_space_.size(); ++ai)
239  {
240  steplength_ = alpha_space_(ai);
241  dV_ = TryStep(steplength_);
242 
243  ExpectedImprovement();
244  dVexp_ = steplength_ * (d_[0] + 0.5 * steplength_ * d_[1]);
245 
246  if (dVexp_ >= 0)
247  { // descent direction
248  if (d_[0] < th_grad_ || dV_ > th_acceptstep_ * dVexp_)
249  {
250  was_feasible_ = is_feasible_;
251  SetCandidate(xs_try_, us_try_, (was_feasible_) || (steplength_ == 1.0));
252  cost_ = cost_try_;
253  control_cost_ = control_cost_try_;
254  prob_->SetCostEvolution(iter, cost_);
255  control_cost_evolution_.at(iter) = control_cost_;
256  recalcDiff = true;
257  break;
258  }
259  }
260  else
261  { // reducing the gaps by allowing a small increment in the cost value
262  if (dV_ > th_acceptnegstep_ * dVexp_)
263  {
264  // if (debug_) INFO_NAMED("FDDP", "Ascent direction: " << dV_ << " > " << th_acceptnegstep_ * dVexp_)
265  was_feasible_ = is_feasible_;
266  SetCandidate(xs_try_, us_try_, (was_feasible_) || (steplength_ == 1));
267  cost_ = cost_try_;
268  control_cost_ = control_cost_try_;
269  prob_->SetCostEvolution(iter, cost_);
270  control_cost_evolution_.at(iter) = control_cost_;
271  break;
272  }
273  // else
274  // {
275  // if (debug_) INFO_NAMED("FDDP", "Ascent direction, but not accepted: " << dV_ << " < " << th_acceptnegstep_ * dVexp_)
276  // }
277  }
278 
279  prob_->SetCostEvolution(iter, cost_);
280  control_cost_evolution_.at(iter) = control_cost_;
281  }
282  time_taken_forward_pass_ = line_search_timer.GetDuration();
283 
284  steplength_evolution_.at(iter) = steplength_;
285  regularization_evolution_.at(iter) = xreg_;
286 
287  if (debug_)
288  {
289  if (iter % 10 == 0 || iter == 1)
290  {
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";
293  }
294 
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';
303  }
304 
305  // Adapt regularization based on step-length
306  if (steplength_ > th_stepdec_)
307  {
308  DecreaseRegularization();
309  }
310  if (steplength_ <= th_stepinc_)
311  {
312  IncreaseRegularization();
313  if (xreg_ == regmax_)
314  {
315  WARNING_NAMED("FeasibilityDrivenDDPSolver::Solve", "State regularization exceeds maximum regularization: " << xreg_ << " > " << regmax_)
316  diverged = true;
317  break;
318  }
319  }
320  CheckStoppingCriteria();
321 
322  // Stop is only exactly zero if all dimensions of Qu_[t] have been
323  // artificially set to 0. This is e.g. the case for a Control-Limited
324  // variant (BoxFDDP). However, we do not want to stop at the first
325  // saturation of all dimensions.
326  if (was_feasible_ && stop_ < th_stop_ && stop_ != 0.0)
327  {
328  if (debug_) HIGHLIGHT_NAMED("FeasibilityDrivenDDPSolver::Solve", "Convergence: " << stop_ << " < " << th_stop_)
329  converged = true;
330  break;
331  }
332 
333  if (diverged)
334  {
335  WARNING_NAMED("FeasibilityDrivenDDPSolver::Solve", "Terminating: Divergence in ForwardPass.");
336  break;
337  }
338 
339  // Check gradient tolerance
340  if (was_feasible_ && std::abs(-d_[1]) < th_gradient_tolerance_)
341  {
342  if (debug_) HIGHLIGHT_NAMED("FeasibilityDrivenDDPSolver::Solve", "Gradient tolerance: " << -d_[1] << " < " << th_gradient_tolerance_)
343  prob_->termination_criterion = TerminationCriterion::GradientTolerance;
344  break;
345  }
346 
347  prob_->OnSolverIterationEnd();
348  }
349 
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;
353 
354  // Store the best solution found over all iterations
355  for (int t = 0; t < T_ - 1; ++t)
356  {
357  solution.row(t) = us_[t].transpose();
358  // prob_->Update(us_[t], t);
359  }
360 
361  planning_time_ = planning_timer.GetDuration();
362 
363  // HIGHLIGHT(std::setprecision(4) << "Setup: " << time_taken_setup_ * 1e3 << "\tBwd: " << time_taken_backward_pass_ * 1e3 << "\tFwd: " << time_taken_forward_pass_ * 1e3 << "\tSolve = " << (planning_time_ - time_taken_setup_)*1e3 << "\talpha=" << steplength_)
364 }
365 
366 void AbstractFeasibilityDrivenDDPSolver::IncreaseRegularization()
367 {
368  xreg_ *= regfactor_;
369  if (xreg_ > regmax_)
370  {
371  xreg_ = regmax_;
372  }
373  ureg_ = xreg_;
374 }
375 
376 void AbstractFeasibilityDrivenDDPSolver::DecreaseRegularization()
377 {
378  xreg_ /= regfactor_;
379  if (xreg_ < regmin_)
380  {
381  xreg_ = regmin_;
382  }
383  ureg_ = xreg_;
384 }
385 
386 double AbstractFeasibilityDrivenDDPSolver::CheckStoppingCriteria()
387 {
388  stop_ = 0.;
389  for (int t = 0; t < T_ - 1; ++t)
390  {
391  stop_ += Qu_[t].squaredNorm();
392  }
393  return stop_;
394 }
395 
396 const Eigen::Vector2d& AbstractFeasibilityDrivenDDPSolver::ExpectedImprovement()
397 {
398  dv_ = 0;
399  if (!is_feasible_)
400  {
401  for (int t = 0; t < T_; ++t)
402  {
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_);
406  }
407  }
408  d_[0] = dg_ + dv_;
409  d_[1] = dq_ - 2 * dv_;
410  return d_;
411 }
412 
413 void AbstractFeasibilityDrivenDDPSolver::UpdateExpectedImprovement()
414 {
415  dg_ = 0;
416  dq_ = 0;
417  if (!is_feasible_)
418  {
419  dg_ -= Vx_.back().dot(fs_.back());
420  fTVxx_p_.noalias() = Vxx_.back() * fs_.back();
421  dq_ += fs_.back().dot(fTVxx_p_);
422  }
423  for (int t = 0; t < T_ - 1; ++t)
424  {
425  dg_ += Qu_[t].dot(k_[t]);
426  dq_ -= k_[t].dot(Quuk_[t]);
427  if (!is_feasible_)
428  {
429  dg_ -= Vx_[t].dot(fs_[t]);
430  fTVxx_p_.noalias() = Vxx_[t] * fs_[t];
431  dq_ += fs_[t].dot(fTVxx_p_);
432  }
433  }
434 }
435 
436 void AbstractFeasibilityDrivenDDPSolver::SetCandidate(const std::vector<Eigen::VectorXd>& xs_in, const std::vector<Eigen::VectorXd>& us_in, const bool is_feasible)
437 {
438  const std::size_t T = static_cast<std::size_t>(prob_->get_T());
439 
440  if (xs_in.size() == 0)
441  {
442  for (int t = 0; t < T_; ++t)
443  {
444  xs_[t].setZero(NX_);
445  }
446  }
447  else
448  {
449  if (xs_in.size() != T)
450  {
451  ThrowPretty("Warm start state has wrong dimension, got " << xs_in.size() << " expecting " << (T + 1));
452  }
453  std::copy(xs_in.begin(), xs_in.end(), xs_.begin());
454  }
455 
456  if (us_in.size() == 0)
457  {
458  for (int t = 0; t < T_ - 1; ++t)
459  {
460  us_[t].setZero(NU_);
461  }
462  }
463  else
464  {
465  if (us_in.size() != T - 1)
466  {
467  ThrowPretty("Warm start control has wrong dimension, got " << us_in.size() << " expecting " << T);
468  }
469  std::copy(us_in.begin(), us_in.end(), us_.begin());
470  }
471  is_feasible_ = is_feasible;
472 }
473 
474 double AbstractFeasibilityDrivenDDPSolver::TryStep(const double steplength)
475 {
476  ForwardPass(steplength);
477  return cost_ - cost_try_;
478 }
479 
480 void AbstractFeasibilityDrivenDDPSolver::ForwardPass(const double steplength)
481 {
482  if (steplength > 1. || steplength < 0.)
483  {
484  ThrowPretty("Invalid argument: invalid step length, value should be between 0. to 1. - got=" << steplength);
485  }
486  cost_try_ = 0.;
487  control_cost_try_ = 0.;
488  xnext_ = prob_->get_X(0);
489  for (int t = 0; t < T_ - 1; ++t)
490  {
491  // If feasible or the gaps are closed, start the shooting from the previous step.
492  if ((is_feasible_) || (steplength == 1))
493  {
494  xs_try_[t] = xnext_;
495  }
496  else
497  {
498  // We need to obtain a state based on the expected reduction of the gap given the step length (dt=unit time)
499  prob_->GetScene()->GetDynamicsSolver()->Integrate(xnext_, fs_[t] * (steplength - 1), 1., xs_try_[t]);
500  }
501  prob_->GetScene()->GetDynamicsSolver()->StateDelta(xs_try_[t], xs_[t], dx_[t]); // NB: StateDelta in Exotica is the other way round than in Pinocchio
502  us_try_[t].noalias() = us_[t] - k_[t] * steplength - K_[t] * dx_[t]; // This is weird. It works better WITHOUT the feedback ?!
503 
504  if (clamp_to_control_limits_in_forward_pass_)
505  {
506  us_try_[t] = us_try_[t].cwiseMax(control_limits_.col(0)).cwiseMin(control_limits_.col(1));
507  }
508 
509  prob_->Update(xs_try_[t], us_try_[t], t); // Performs integration and update of cost
510  xnext_ = prob_->get_X(t + 1);
511 
512  control_cost_try_ += dt_ * prob_->GetControlCost(t);
513  cost_try_ += dt_ * prob_->GetStateCost(t);
514 
515  if (IsNaN(cost_try_))
516  {
517  WARNING_NAMED("NaN in ForwardPass", "forward_error - NaN in cost_try_ at t=" << t);
518  return;
519  }
520  if (IsNaN(xnext_.lpNorm<Eigen::Infinity>()))
521  {
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());
523  return;
524  }
525  }
526 
527  // Terminal model
528  if ((is_feasible_) || (steplength == 1))
529  {
530  xs_try_.back() = xnext_;
531  }
532  else
533  {
534  prob_->GetScene()->GetDynamicsSolver()->Integrate(xnext_, fs_.back() * (steplength - 1), 1., xs_try_.back());
535  }
536  prob_->UpdateTerminalState(xs_try_.back());
537  cost_try_ += prob_->GetStateCost(T_ - 1) + control_cost_try_;
538 
539  if (IsNaN(cost_try_))
540  {
541  WARNING_NAMED("NaN in ForwardPass", "forward_error - cost NaN");
542  return;
543  }
544 }
545 
546 bool AbstractFeasibilityDrivenDDPSolver::ComputeDirection(const bool recalcDiff)
547 {
548  if (recalcDiff)
549  {
550  CalcDiff();
551  }
552  return BackwardPassFDDP();
553 }
554 
555 double AbstractFeasibilityDrivenDDPSolver::CalcDiff()
556 {
557  cost_ = 0;
558  control_cost_ = 0;
559  // Running cost
560  for (int t = 0; t < T_ - 1; ++t)
561  {
562  control_cost_ += dt_ * prob_->GetControlCost(t);
563  cost_ += dt_ * prob_->GetStateCost(t);
564  }
565  // Terminal cost
566  cost_ += prob_->GetStateCost(T_ - 1) + control_cost_;
567 
568  if (!is_feasible_)
569  {
570  // Defects for t=0..T
571  for (int t = 0; t < prob_->get_T(); ++t)
572  {
573  prob_->GetScene()->GetDynamicsSolver()->StateDelta(prob_->get_X(t), xs_[t], fs_[t]); // Exotica's convention differs...
574  }
575  }
576  else if (!was_feasible_)
577  { // closing the gaps
578  for (std::vector<Eigen::VectorXd>::iterator it = fs_.begin(); it != fs_.end(); ++it)
579  {
580  it->setZero();
581  }
582  }
583  return cost_;
584 }
585 
586 bool AbstractFeasibilityDrivenDDPSolver::BackwardPassFDDP()
587 {
588  Vxx_.back() = prob_->GetStateCostHessian(T_ - 1);
589  Vx_.back() = prob_->GetStateCostJacobian(T_ - 1);
590 
591  if (!std::isnan(xreg_))
592  {
593  Vxx_.back().diagonal().array() += xreg_;
594  }
595 
596  if (!is_feasible_)
597  {
598  Vx_.back().noalias() += Vxx_.back() * fs_.back();
599  }
600 
601  for (int t = static_cast<int>(prob_->get_T()) - 2; t >= 0; --t)
602  {
603  const Eigen::MatrixXd& Vxx_p = Vxx_[t + 1];
604  const Eigen::VectorXd& Vx_p = Vx_[t + 1];
605 
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);
611 
612  dynamics_solver_->ComputeDerivatives(xs_[t], us_[t]);
613  fx_[t].noalias() = dynamics_solver_->get_Fx();
614  fu_[t].noalias() = dynamics_solver_->get_Fu();
615 
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;
623 
624  if (!std::isnan(ureg_))
625  {
626  Quu_[t].diagonal().array() += ureg_;
627  }
628 
629  ComputeGains(t);
630 
631  Vx_[t] = Qx_[t];
632  if (std::isnan(ureg_))
633  {
634  Vx_[t].noalias() -= K_[t].transpose() * Qu_[t];
635  }
636  else
637  {
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]);
641  }
642  Vxx_[t] = Qxx_[t];
643  Vxx_[t].noalias() -= Qxu_[t] * K_[t];
644  Vxx_[t] = 0.5 * (Vxx_[t] + Vxx_[t].transpose()).eval();
645 
646  if (!std::isnan(xreg_))
647  {
648  Vxx_[t].diagonal().array() += xreg_;
649  }
650 
651  // Compute and store the Vx gradient at end of the interval (rollout state)
652  if (!is_feasible_)
653  {
654  Vx_[t].noalias() += Vxx_[t] * fs_[t];
655  }
656 
657  if (IsNaN(Vx_[t].lpNorm<Eigen::Infinity>()))
658  {
659  HIGHLIGHT("Vx_[" << t << "] is NaN: " << Vx_[t].transpose());
660  return false;
661  }
662  if (IsNaN(Vxx_[t].lpNorm<Eigen::Infinity>()))
663  {
664  HIGHLIGHT("Vxx_[" << t << "] is NaN");
665  return false;
666  }
667  }
668  return true;
669 }
670 
671 void AbstractFeasibilityDrivenDDPSolver::ComputeGains(const int t)
672 {
673  // Quu_inv_[t].noalias() = Quu_[t].inverse();
674  // K_[t].noalias() = Quu_inv_[t] * Qxu_[t].transpose();
675  // k_[t].noalias() = Quu_inv_[t] * Qu_[t];
676  // return;
677 
678  while (true)
679  {
680  Quu_ldlt_[t].compute(Quu_[t]);
681  const Eigen::ComputationInfo& info = Quu_ldlt_[t].info();
682  if (info != Eigen::Success)
683  {
684  HIGHLIGHT_NAMED("ComputeGains", "Cholesky failed for reg=" << ureg_ << ", Quu_[t]=\n"
685  << Quu_[t])
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"
690  << Quu_[t]);
691  // ThrowPretty("backward_error - error in Cholesky decomposition\n"
692  // << Quu_[t]);
693  }
694  else
695  {
696  break;
697  }
698  }
699  K_[t] = Qxu_[t].transpose();
700  Quu_ldlt_[t].solveInPlace(K_[t]);
701  k_[t] = Qu_[t];
702  Quu_ldlt_[t].solveInPlace(k_[t]);
703 }
704 
705 } // namespace exotica
#define HIGHLIGHT(x)
#define WARNING_NAMED(name, x)
#define ThrowPretty(m)
#define REGISTER_MOTIONSOLVER_TYPE(TYPE, DERIV)
#define ThrowNamed(m)
ROSCPP_DECL bool ok()
#define HIGHLIGHT_NAMED(name, x)
double GetDuration() const
std::shared_ptr< PlanningProblem > PlanningProblemPtr
#define WARNING(x)


exotica_ddp_solver
Author(s): Traiko Dinev
autogenerated on Sat Apr 10 2021 02:36:22