dynamic_time_indexed_shooting_problem.cpp
Go to the documentation of this file.
1 //
2 // Copyright (c) 2019, Wolfgang Merkt
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 #include <exotica_core/setup.h>
34 #include <cmath>
35 
36 REGISTER_PROBLEM_TYPE("DynamicTimeIndexedShootingProblem", exotica::DynamicTimeIndexedShootingProblem)
37 
39 {
40 DynamicTimeIndexedShootingProblem::DynamicTimeIndexedShootingProblem()
41 {
42  this->flags_ = KIN_FK | KIN_J;
43 }
44 DynamicTimeIndexedShootingProblem::~DynamicTimeIndexedShootingProblem() = default;
45 
46 void DynamicTimeIndexedShootingProblem::InstantiateCostTerms(const DynamicTimeIndexedShootingProblemInitializer& init)
47 {
49 
50  // L2
51  if (parameters_.LossType == "L2") loss_type_ = ControlCostLossTermType::L2;
52 
53  // L1
54  if (parameters_.LossType == "SmoothL1" || parameters_.LossType == "AdaptiveSmoothL1") loss_type_ = ControlCostLossTermType::SmoothL1;
55 
56  if (parameters_.LossType == "AdaptiveSmoothL1")
57  {
58  smooth_l1_mean_ = Eigen::VectorXd::Zero(scene_->get_num_controls());
59  smooth_l1_std_ = Eigen::VectorXd::Zero(scene_->get_num_controls());
60  }
61 
62  // Huber
63  if (parameters_.LossType == "Huber") loss_type_ = ControlCostLossTermType::Huber;
64 
65  if (parameters_.LossType == "PseudoHuber") loss_type_ = ControlCostLossTermType::PseudoHuber;
66 
67  // If still undefined, throw.
68  if (loss_type_ == ControlCostLossTermType::Undefined) ThrowPretty("Unknown loss type: " << parameters_.LossType);
69 
70  // L1 Rate
71  if (parameters_.L1Rate.size() == 1)
72  {
73  l1_rate_.setConstant(scene_->get_num_controls(), parameters_.L1Rate(0));
74  }
75  else if (parameters_.L1Rate.size() == scene_->get_num_controls())
76  {
77  l1_rate_ = parameters_.L1Rate;
78  }
79  else if (parameters_.L1Rate.size() != 0)
80  {
81  ThrowPretty("L1Rate has wrong size: expected " << scene_->get_num_controls() << ", 1, or 0 (default), got " << parameters_.L1Rate.size());
82  }
83  // Default
84  else
85  {
86  l1_rate_.setConstant(scene_->get_num_controls(), 1);
87  }
88 
89  // Huber Rate
90  if (parameters_.HuberRate.size() == 1)
91  {
92  huber_rate_.setConstant(scene_->get_num_controls(), parameters_.HuberRate(0));
93  }
94  else if (parameters_.HuberRate.size() == scene_->get_num_controls())
95  {
96  huber_rate_ = parameters_.HuberRate;
97  }
98  else if (parameters_.HuberRate.size() != 0)
99  {
100  ThrowPretty("HuberRate has wrong size: expected " << scene_->get_num_controls() << ", 1, or 0, got " << parameters_.HuberRate.size());
101  }
102  else
103  {
104  huber_rate_.setConstant(scene_->get_num_controls(), 1);
105  }
106 
107  control_cost_weight_ = parameters_.ControlCostWeight;
108 }
109 
110 void DynamicTimeIndexedShootingProblem::Instantiate(const DynamicTimeIndexedShootingProblemInitializer& init)
111 {
112  this->parameters_ = init;
113 
114  if (!scene_->GetDynamicsSolver()) ThrowPretty("DynamicsSolver is not initialised!");
115 
116  const int NX = scene_->get_num_positions() + scene_->get_num_velocities(),
117  NDX = 2 * scene_->get_num_velocities(),
118  NU = scene_->get_num_controls();
119  Qf_ = Eigen::MatrixXd::Identity(NDX, NDX);
120  if (this->parameters_.Qf.rows() > 0)
121  {
122  if (this->parameters_.Qf.rows() == NDX)
123  {
124  Qf_.diagonal() = this->parameters_.Qf;
125  }
126  else
127  {
128  ThrowNamed("Qf dimension mismatch! Expected " << NDX << ", got " << this->parameters_.Qf.rows());
129  }
130  }
131  Qf_ *= this->parameters_.Qf_rate;
132 
133  R_ = Eigen::MatrixXd::Identity(scene_->get_num_controls(), scene_->get_num_controls());
134  if (this->parameters_.R.rows() > 0)
135  {
136  if (this->parameters_.R.rows() == scene_->get_num_controls())
137  {
138  R_.diagonal() = this->parameters_.R;
139  }
140  else
141  {
142  ThrowNamed("R dimension mismatch! Expected " << scene_->get_num_controls() << ", got " << this->parameters_.R.rows());
143  }
144  }
145  R_ *= this->parameters_.R_rate;
146 
147  // Set up stochastic terms
148  // see https://homes.cs.washington.edu/~todorov/papers/TodorovNeuralComp05.pdf
149  // eq. 3.1 and the text before (search for 'column') to see why this makes sense
150  //
151  // We specify a matrix of size NX x NU from which the C matrices
152  // are extracted
153  //
154  // E.g. NU = 2, NX = 4
155  //
156  // The matrix is
157  // a b
158  // c d
159  // e f
160  // g h
161  //
162  // From which the Ci matrices become
163  // C0 = a b C1 = 0 0 C2 = 0 0 C3 = 0 0
164  // 0 0 c d 0 0 0 0
165  // 0 0 0 0 e f 0 0
166  // 0 0 0 0 0 0 g h
167  //
168  // If you specify C_rate, then this is equivalent to:
169  //
170  // C = 0 0
171  // 0 0
172  // c 0
173  // 0 c
174  //
175  // The velocities then take the noise terms in. For an
176  // underactuated system these are somewhat ill-defined. E.g.
177  // if above NU = 1 and you specify c:
178  //
179  // C = 0
180  // 0
181  // c
182  // 0
183  bool full_noise_set = false;
184  Ci_.assign(NX, Eigen::MatrixXd::Zero(NX, NU));
185  if (parameters_.C_rate != 0.0)
186  {
187  if (NU <= NX)
188  {
189  for (int i = 0; i < NU; ++i)
190  Ci_.at(NX - NU + i)(NX - NU + i, i) = parameters_.C_rate;
191  }
192  else
193  {
194  ThrowPretty("Noise does not work for systems that have NU > NX. This should be fixed in the future.");
195  }
196  }
197 
198  if (this->parameters_.C.rows() > 0)
199  {
200  if (parameters_.C.rows() * parameters_.C.cols() == NX * NU)
201  {
202  Eigen::Map<Eigen::MatrixXd> C_map(parameters_.C.data(), NU, NX);
203 
204  for (int i = 0; i < NX; ++i)
205  Ci_[i].row(i) = C_map.col(i).transpose(); // row over vs. col order
206  full_noise_set = true;
207  }
208  else
209  {
210  ThrowNamed("C dimension mismatch! Expected " << NX << "x" << NU << ", got " << parameters_.C.rows() << "x" << parameters_.C.cols());
211  }
212  }
213 
214  CW_ = this->parameters_.CW_rate * Eigen::MatrixXd::Identity(NX, NX);
215  if (parameters_.CW.rows() > 0)
216  {
217  if (parameters_.CW.rows() == NX)
218  {
219  CW_.diagonal() = parameters_.CW;
220  full_noise_set = true;
221  }
222  else
223  {
224  ThrowNamed("CW dimension mismatch! Expected " << NX << ", got " << parameters_.R.rows());
225  }
226  }
227 
228  if (parameters_.C_rate > 0 || parameters_.CW_rate > 0 || full_noise_set)
229  {
230  stochastic_matrices_specified_ = true;
231  stochastic_updates_enabled_ = true;
232  }
233 
234  T_ = this->parameters_.T;
235  tau_ = this->parameters_.tau;
236 
237  // For now, without inter-/extra-polation for integrators, assure that tau is a multiple of dt
238  const long double fmod_tau_dt = std::fmod(static_cast<long double>(1000. * tau_), static_cast<long double>(1000. * scene_->GetDynamicsSolver()->get_dt()));
239  if (fmod_tau_dt > 1e-5) ThrowPretty("tau is not a multiple of dt: tau=" << tau_ << ", dt=" << scene_->GetDynamicsSolver()->get_dt() << ", mod(" << fmod_tau_dt << ")");
240 
241  // Initialize general costs
242  cost.Initialize(this->parameters_.Cost, shared_from_this(), cost_Phi);
243 
244  ApplyStartState(false);
245  InstantiateCostTerms(init);
246  ReinitializeVariables();
247 }
248 
249 void DynamicTimeIndexedShootingProblem::ReinitializeVariables()
250 {
251  if (debug_) HIGHLIGHT_NAMED("DynamicTimeIndexedShootingProblem", "Initialize problem with T=" << T_);
252 
253  const int NX = scene_->get_num_positions() + scene_->get_num_velocities(), NDX = 2 * scene_->get_num_velocities(), NU = scene_->get_num_controls();
254 
255  X_ = Eigen::MatrixXd::Zero(NX, T_);
256  X_star_ = Eigen::MatrixXd::Zero(NX, T_);
257  X_diff_ = Eigen::MatrixXd::Zero(NDX, T_);
258  U_ = Eigen::MatrixXd::Zero(scene_->get_num_controls(), T_ - 1);
259 
260  // Set w component of quaternion by default
261  if (scene_->get_has_quaternion_floating_base())
262  {
263  for (int t = 0; t < T_; ++t)
264  {
267  }
268  }
269 
270  // Set GoalState
271  if (this->parameters_.GoalState.rows() > 0)
272  {
273  Eigen::MatrixXd goal_state(X_star_);
274 
275  if (this->parameters_.GoalState.rows() == NX)
276  {
277  goal_state.col(T_ - 1) = this->parameters_.GoalState;
278  }
279  else if (this->parameters_.GoalState.rows() == scene_->get_num_positions())
280  {
281  goal_state.col(T_ - 1).head(scene_->get_num_positions()) = this->parameters_.GoalState;
282  }
283  else if (this->parameters_.GoalState.rows() == NX * T_)
284  {
285  for (int t = 0; t < T_; ++t)
286  {
287  goal_state.col(t) = this->parameters_.GoalState.segment(t * NX, NX);
288  }
289  }
290  else
291  {
292  ThrowPretty("GoalState has " << this->parameters_.GoalState.rows() << " rows, but expected either NX=" << NX << " or NQ=" << scene_->get_num_positions() << ", or NX*T=" << NX * T_);
293  }
294  set_X_star(goal_state);
295  }
296 
297  // Set StartState
298  if (this->parameters_.StartState.rows() > 0)
299  {
300  Eigen::MatrixXd start_state(X_);
301  if (this->parameters_.StartState.rows() == NX)
302  {
303  start_state = this->parameters_.StartState.replicate(1, T_);
304  }
305  else if (this->parameters_.StartState.rows() == scene_->get_num_positions())
306  {
307  for (int t = 0; t < T_; ++t)
308  {
309  start_state.col(t).head(scene_->get_num_positions()) = this->parameters_.StartState;
310  }
311  }
312  else if (this->parameters_.StartState.rows() == NX * T_)
313  {
314  for (int t = 0; t < T_; ++t)
315  {
316  start_state.col(t) = this->parameters_.StartState.segment(t * NX, NX);
317  }
318  }
319  else
320  {
321  ThrowPretty("StartState has " << this->parameters_.StartState.rows() << " rows, but expected either NX=" << NX << " or NQ=" << scene_->get_num_positions() << ", or NX*T=" << NX * T_);
322  }
323  set_X(start_state);
324  }
325 
326  Eigen::MatrixXd Q = Eigen::MatrixXd::Identity(NDX, NDX);
327  if (this->parameters_.Q.rows() > 0)
328  {
329  if (this->parameters_.Q.rows() == NDX)
330  {
331  Q.diagonal() = this->parameters_.Q;
332  }
333  else
334  {
335  ThrowNamed("Q dimension mismatch! Expected " << NDX << ", got " << this->parameters_.Q.rows());
336  }
337  }
338  Q *= this->parameters_.Q_rate;
339  Q_.assign(T_, Q);
340 
341  // Set final Q (Qf) -- the Qf variable has been populated in Instantiate
342  set_Qf(Qf_);
343 
344  // Reinitialize general cost (via taskmaps)
345  num_tasks = tasks_.size();
346  length_Phi = 0;
347  length_jacobian = 0;
348  TaskSpaceVector y_ref_;
349  for (int i = 0; i < num_tasks; ++i)
350  {
351  AppendVector(y_ref_.map, tasks_[i]->GetLieGroupIndices());
352  length_Phi += tasks_[i]->length;
353  length_jacobian += tasks_[i]->length_jacobian;
354  }
355 
356  // Initialize the TaskSpaceVector and its derivatives
357  y_ref_.SetZero(length_Phi);
358  Phi.assign(T_, y_ref_);
359 
360  if (flags_ & KIN_J)
361  {
362  dPhi_dx.assign(T_, Eigen::MatrixXd(length_jacobian, scene_->get_num_state_derivative()));
363  dPhi_du.assign(T_, Eigen::MatrixXd(length_jacobian, scene_->get_num_controls()));
364  }
365 
366  if (flags_ & KIN_H)
367  {
368  ddPhi_ddx.assign(T_, Hessian::Constant(length_jacobian, Eigen::MatrixXd::Zero(scene_->get_num_state_derivative(), scene_->get_num_state_derivative())));
369  ddPhi_ddu.assign(T_, Hessian::Constant(length_jacobian, Eigen::MatrixXd::Zero(scene_->get_num_controls(), scene_->get_num_controls())));
370  ddPhi_dxdu.assign(T_, Hessian::Constant(length_jacobian, Eigen::MatrixXd::Zero(scene_->get_num_state_derivative(), scene_->get_num_controls())));
371  }
372  cost.ReinitializeVariables(T_, shared_from_this(), cost_Phi);
373 
374  // Initialise variables for state and control cost
375  // NB: To do this, we had to remove the "const" qualifier of the Hessian/Jacobian methods.
376  dxdiff_.assign(T_, Eigen::MatrixXd::Zero(NDX, NDX));
377  state_cost_jacobian_.assign(T_, Eigen::VectorXd::Zero(NDX));
378  state_cost_hessian_.assign(T_, Eigen::MatrixXd::Zero(NDX, NDX));
379  general_cost_jacobian_.assign(T_, Eigen::VectorXd::Zero(NDX));
380  general_cost_hessian_.assign(T_, Eigen::MatrixXd::Zero(NDX, NDX));
381  control_cost_jacobian_.assign(T_ - 1, Eigen::VectorXd::Zero(NU));
382  control_cost_hessian_.assign(T_ - 1, Eigen::MatrixXd::Zero(NU, NU));
383 
384  PreUpdate();
385 }
386 
387 const int& DynamicTimeIndexedShootingProblem::get_T() const
388 {
389  return T_;
390 }
391 
392 void DynamicTimeIndexedShootingProblem::set_T(const int& T_in)
393 {
394  if (T_in <= 2)
395  {
396  ThrowNamed("Invalid number of timesteps: " << T_in);
397  }
398  T_ = T_in;
399  ReinitializeVariables();
400 }
401 
402 const double& DynamicTimeIndexedShootingProblem::get_tau() const
403 {
404  return tau_;
405 }
406 
407 void DynamicTimeIndexedShootingProblem::PreUpdate()
408 {
409  PlanningProblem::PreUpdate();
410  for (int i = 0; i < tasks_.size(); ++i) tasks_[i]->is_used = false;
411  cost.UpdateS();
412 
413  // Create a new set of kinematic solutions with the size of the trajectory
414  // based on the lastest KinematicResponse in order to reflect model state
415  // updates etc.
416  kinematic_solutions_.clear();
417  kinematic_solutions_.resize(T_);
418  for (int i = 0; i < T_; ++i) kinematic_solutions_[i] = std::make_shared<KinematicResponse>(*scene_->GetKinematicTree().GetKinematicResponse());
419 
420  if (this->parameters_.WarmStartWithInverseDynamics)
421  {
422  for (int t = 0; t < T_ - 1; ++t)
423  {
424  U_.col(t) = scene_->GetDynamicsSolver()->InverseDynamics(X_.col(t));
425  X_.col(t + 1) = scene_->GetDynamicsSolver()->Simulate(X_.col(t), U_.col(t), tau_);
426  }
427  }
428 }
429 
430 Eigen::VectorXd DynamicTimeIndexedShootingProblem::ApplyStartState(bool update_traj)
431 {
432  PlanningProblem::ApplyStartState(update_traj);
433  return start_state_;
434 }
435 
436 const Eigen::MatrixXd& DynamicTimeIndexedShootingProblem::get_X() const
437 {
438  return X_;
439 }
440 
441 Eigen::VectorXd DynamicTimeIndexedShootingProblem::get_X(int t) const
442 {
443  ValidateTimeIndex(t);
444  return X_.col(t);
445 }
446 
447 void DynamicTimeIndexedShootingProblem::set_X(Eigen::MatrixXdRefConst X_in)
448 {
449  if (X_in.rows() != X_.rows() || X_in.cols() != X_.cols()) ThrowPretty("Sizes don't match! " << X_.rows() << "x" << X_.cols() << " vs " << X_in.rows() << "x" << X_in.cols());
450  X_ = X_in;
451 
452  // Normalize quaternion, if required.
453  if (scene_->get_has_quaternion_floating_base())
454  {
455  for (int t = 0; t < T_; ++t)
456  {
458  }
459  }
460 }
461 
462 const Eigen::MatrixXd& DynamicTimeIndexedShootingProblem::get_U() const
463 {
464  return U_;
465 }
466 
467 Eigen::VectorXd DynamicTimeIndexedShootingProblem::get_U(int t) const
468 {
469  ValidateTimeIndex(t);
470  return U_.col(t);
471 }
472 
473 void DynamicTimeIndexedShootingProblem::set_U(Eigen::MatrixXdRefConst U_in)
474 {
475  if (U_in.rows() != U_.rows() || U_in.cols() != U_.cols()) ThrowPretty("Sizes don't match! " << U_.rows() << "x" << U_.cols() << " vs " << U_in.rows() << "x" << U_in.cols());
476  U_ = U_in;
477 }
478 
479 const Eigen::MatrixXd& DynamicTimeIndexedShootingProblem::get_X_star() const
480 {
481  return X_star_;
482 }
483 
484 void DynamicTimeIndexedShootingProblem::set_X_star(Eigen::MatrixXdRefConst X_star_in)
485 {
486  if (X_star_in.rows() != X_star_.rows() || X_star_in.cols() != X_star_.cols()) ThrowPretty("Sizes don't match! " << X_star_.rows() << "x" << X_star_.cols() << " vs " << X_star_in.rows() << "x" << X_star_in.cols());
487  X_star_ = X_star_in;
488 
489  // Normalize quaternion, if required.
490  if (scene_->get_has_quaternion_floating_base())
491  {
492  for (int t = 0; t < T_; ++t)
493  {
495  }
496  }
497 }
498 
499 const Eigen::MatrixXd& DynamicTimeIndexedShootingProblem::get_Q(int t) const
500 {
501  ValidateTimeIndex(t);
502  return Q_[t];
503 }
504 
505 const Eigen::MatrixXd& DynamicTimeIndexedShootingProblem::get_Qf() const
506 {
507  return Q_[T_ - 1];
508 }
509 
510 const Eigen::MatrixXd& DynamicTimeIndexedShootingProblem::get_R() const
511 {
512  return R_;
513 }
514 
515 void DynamicTimeIndexedShootingProblem::set_Q(Eigen::MatrixXdRefConst Q_in, int t)
516 {
517  ValidateTimeIndex(t);
518  if (Q_in.rows() != Q_[t].rows() || Q_in.cols() != Q_[t].cols()) ThrowPretty("Dimension mismatch!");
519  Q_[t] = Q_in;
520 }
521 
522 void DynamicTimeIndexedShootingProblem::set_Qf(Eigen::MatrixXdRefConst Q_in)
523 {
524  set_Q(Q_in, T_ - 1);
525 }
526 
527 void DynamicTimeIndexedShootingProblem::Update(Eigen::VectorXdRefConst x_in, Eigen::VectorXdRefConst u_in, int t)
528 {
529  // We can only update t=0, ..., T-1 - the last state will be created from integrating u_{T-1} to get x_T
530  if (t >= (T_ - 1) || t < -1)
531  {
532  ThrowPretty("Requested t=" << t << " out of range, needs to be 0 =< t < " << T_ - 1);
533  }
534  else if (t == -1)
535  {
536  t = T_ - 2;
537  }
538 
539  if (u_in.rows() != scene_->get_num_controls())
540  {
541  ThrowPretty("Mismatching in size of control vector: " << u_in.rows() << " given, expected: " << scene_->get_num_controls());
542  }
543 
544  if (x_in.rows() != scene_->get_num_positions() + scene_->get_num_velocities())
545  {
546  ThrowPretty("Mismatching in size of state vector vector: " << x_in.rows() << " given, expected: " << scene_->get_num_positions() + scene_->get_num_velocities());
547  }
548 
549  X_.col(t) = x_in;
550  U_.col(t) = u_in;
551 
552  // Update xdiff
553  scene_->GetDynamicsSolver()->StateDelta(X_.col(t), X_star_.col(t), X_diff_.col(t));
554 
555  // Update current state kinematics and costs
556  if (num_tasks > 0) UpdateTaskMaps(X_.col(t), U_.col(t), t);
557 
558  // Set the corresponding KinematicResponse for KinematicTree in order to
559  // have Kinematics elements updated based in x_in.
560  scene_->GetKinematicTree().SetKinematicResponse(kinematic_solutions_[t]);
561 
562  // Pass the corresponding number of relevant task kinematics to the TaskMaps
563  // via the PlanningProblem::UpdateMultipleTaskKinematics method. For now we
564  // support passing _two_ timesteps - this can be easily changed later on.
565  std::vector<std::shared_ptr<KinematicResponse>> kinematics_solutions{kinematic_solutions_[t]};
566 
567  // If the current timestep is 0, pass the 0th timestep's response twice.
568  // Otherwise pass the (t-1)th response.
569  kinematics_solutions.emplace_back((t == 0) ? kinematic_solutions_[t] : kinematic_solutions_[t - 1]);
570 
571  // Actually update the tasks' kinematics mappings.
572  PlanningProblem::UpdateMultipleTaskKinematics(kinematics_solutions);
573 
574  // Simulate for tau
575  X_.col(t + 1) = scene_->GetDynamicsSolver()->Simulate(X_.col(t), U_.col(t), tau_);
576 
577  // Clamp!
578  if (scene_->GetDynamicsSolver()->get_has_state_limits())
579  {
580  scene_->GetDynamicsSolver()->ClampToStateLimits(X_.col(t + 1));
581  }
582 
583  // Update xdiff
584  scene_->GetDynamicsSolver()->StateDelta(X_.col(t + 1), X_star_.col(t + 1), X_diff_.col(t + 1));
585 
586  // Stochastic noise, if enabled
587  if (stochastic_matrices_specified_ && stochastic_updates_enabled_)
588  {
589  Eigen::VectorXd noise(scene_->get_num_positions() + scene_->get_num_velocities());
590  for (int i = 0; i < scene_->get_num_positions() + scene_->get_num_velocities(); ++i)
591  noise(i) = standard_normal_noise_(generator_);
592 
593  Eigen::VectorXd control_dependent_noise = std::sqrt(scene_->GetDynamicsSolver()->get_dt()) * get_F(t) * noise;
594 
595  for (int i = 0; i < scene_->get_num_positions() + scene_->get_num_velocities(); ++i)
596  noise(i) = standard_normal_noise_(generator_);
597  Eigen::VectorXd white_noise = std::sqrt(scene_->GetDynamicsSolver()->get_dt()) * CW_ * noise;
598 
599  X_.col(t + 1) = X_.col(t + 1) + white_noise + control_dependent_noise;
600  }
601 
602  // Twice would not be necessary if "UpdateTerminalState" is used by the solver.
603  // However, as this is a recent addition, this check and update is required for
604  // backwards compatibility.
605  if (num_tasks > 0 && t == T_ - 2)
606  {
607  UpdateTaskMaps(X_.col(t + 1), Eigen::VectorXd::Zero(scene_->get_num_controls()), t + 1);
608  }
609 
610  ++number_of_problem_updates_;
611 }
612 
613 void DynamicTimeIndexedShootingProblem::Update(Eigen::VectorXdRefConst u, int t)
614 {
615  // We can only update t=0, ..., T-1 - the last state will be created from integrating u_{T-1} to get x_T
616  if (t >= (T_ - 1) || t < -1)
617  {
618  ThrowPretty("Requested t=" << t << " out of range, needs to be 0 =< t < " << T_ - 1);
619  }
620  else if (t == -1)
621  {
622  t = T_ - 2;
623  }
624 
625  return Update(X_.col(t), u, t);
626 }
627 
628 void DynamicTimeIndexedShootingProblem::UpdateTerminalState(Eigen::VectorXdRefConst x_in)
629 {
630  int t = T_ - 1;
631 
632  if (x_in.rows() != scene_->get_num_positions() + scene_->get_num_velocities())
633  {
634  ThrowPretty("Mismatching in size of state vector vector: " << x_in.rows() << " given, expected: " << scene_->get_num_positions() + scene_->get_num_velocities());
635  }
636 
637  X_.col(t) = x_in;
638  scene_->GetDynamicsSolver()->StateDelta(X_.col(t), X_star_.col(t), X_diff_.col(t));
639 
640  // Set the corresponding KinematicResponse for KinematicTree in order to
641  // have Kinematics elements updated based in x_in.
642  scene_->GetKinematicTree().SetKinematicResponse(kinematic_solutions_[t]);
643 
644  // Pass the corresponding number of relevant task kinematics to the TaskMaps
645  // via the PlanningProblem::UpdateMultipleTaskKinematics method. For now we
646  // support passing _two_ timesteps - this can be easily changed later on.
647  std::vector<std::shared_ptr<KinematicResponse>> kinematics_solutions{kinematic_solutions_[t]};
648 
649  // If the current timestep is 0, pass the 0th timestep's response twice.
650  // Otherwise pass the (t-1)th response.
651  kinematics_solutions.emplace_back((t == 0) ? kinematic_solutions_[t] : kinematic_solutions_[t - 1]);
652 
653  // Actually update the tasks' kinematics mappings.
654  PlanningProblem::UpdateMultipleTaskKinematics(kinematics_solutions);
655 
656  if (num_tasks > 0) UpdateTaskMaps(X_.col(t), Eigen::VectorXd::Zero(scene_->get_num_controls()), t);
657 
658  ++number_of_problem_updates_;
659 }
660 
661 void DynamicTimeIndexedShootingProblem::UpdateTaskMaps(Eigen::VectorXdRefConst x, Eigen::VectorXdRefConst u, int t)
662 {
663  ValidateTimeIndex(t);
664 
665  // Update the kinematic scene based on the configuration.
666  // NB: The KinematicTree only understands a certain format for the configuration (RPY)
667  // => As a result, we need to use GetPosition to potentially convert.
668  const Eigen::VectorXd q = scene_->GetDynamicsSolver()->GetPosition(x);
669  scene_->Update(q, static_cast<double>(t) * tau_);
670 
671  // Reset the task space vector and its derivatives for the current timestep
672  Phi[t].SetZero(length_Phi);
673 
674  if (flags_ & KIN_J)
675  {
676  dPhi_dx[t].setZero();
677  dPhi_du[t].setZero();
678  }
679 
680  if (flags_ & KIN_H)
681  {
682  for (int i = 0; i < length_jacobian; ++i)
683  {
684  ddPhi_ddx[t](i).setZero();
685  ddPhi_ddu[t](i).setZero();
686  ddPhi_dxdu[t](i).setZero();
687  }
688  }
689 
690  // Update all task-maps
691  for (int i = 0; i < num_tasks; ++i)
692  {
693  // Only update TaskMap if rho is not 0
694  if (tasks_[i]->is_used)
695  {
696  if (flags_ & KIN_H)
697  {
698  tasks_[i]->Update(x, u,
699  Phi[t].data.segment(tasks_[i]->start, tasks_[i]->length),
700  dPhi_dx[t].middleRows(tasks_[i]->start_jacobian, tasks_[i]->length_jacobian),
701  dPhi_du[t].middleRows(tasks_[i]->start_jacobian, tasks_[i]->length_jacobian),
702  ddPhi_ddx[t].segment(tasks_[i]->start_jacobian, tasks_[i]->length_jacobian),
703  ddPhi_ddu[t].segment(tasks_[i]->start_jacobian, tasks_[i]->length_jacobian),
704  ddPhi_dxdu[t].segment(tasks_[i]->start_jacobian, tasks_[i]->length_jacobian));
705  }
706  else if (flags_ & KIN_J)
707  {
708  tasks_[i]->Update(x, u,
709  Phi[t].data.segment(tasks_[i]->start, tasks_[i]->length),
710  dPhi_dx[t].middleRows(tasks_[i]->start_jacobian, tasks_[i]->length_jacobian),
711  dPhi_du[t].middleRows(tasks_[i]->start_jacobian, tasks_[i]->length_jacobian));
712  }
713  else
714  {
715  tasks_[i]->Update(x, u, Phi[t].data.segment(tasks_[i]->start, tasks_[i]->length));
716  }
717  }
718  }
719 
720  // Update costs (TimeIndexedTask)
721  if (flags_ & KIN_H)
722  {
723  cost.Update(Phi[t], dPhi_dx[t], dPhi_du[t], ddPhi_ddx[t], ddPhi_ddu[t], ddPhi_dxdu[t], t);
724  }
725  else if (flags_ & KIN_J)
726  {
727  cost.Update(Phi[t], dPhi_dx[t], dPhi_du[t], t);
728  }
729  else
730  {
731  cost.Update(Phi[t], t);
732  }
733 }
734 
735 double DynamicTimeIndexedShootingProblem::GetStateCost(int t) const
736 {
737  ValidateTimeIndex(t);
738  const double state_cost = X_diff_.col(t).transpose() * Q_[t] * X_diff_.col(t);
739  const double general_cost = cost.ydiff[t].transpose() * cost.S[t] * cost.ydiff[t];
740  return state_cost + general_cost; // TODO: ct scaling
741 }
742 
743 Eigen::VectorXd DynamicTimeIndexedShootingProblem::GetStateCostJacobian(int t)
744 {
745  ValidateTimeIndex(t);
746 
747  // (NDX,NDX)^T * (NDX,NDX) * (NDX,1) * (1,1) => (NDX,1), TODO: We should change this to RowVectorXd format
748  dxdiff_[t] = scene_->GetDynamicsSolver()->dStateDelta(X_.col(t), X_star_.col(t), ArgumentPosition::ARG0);
749  state_cost_jacobian_[t].noalias() = dxdiff_[t].transpose() * Q_[t] * X_diff_.col(t) * 2.0;
750 
751  // m => dimension of task maps, "length_jacobian"
752  // (m,NQ)^T * (m,m) * (m,1) * (1,1) => (NQ,1), TODO: We should change this to RowVectorXd format
753  general_cost_jacobian_[t].noalias() = cost.dPhi_dx[t].transpose() * cost.S[t] * cost.ydiff[t] * 2.0;
754 
755  return state_cost_jacobian_[t] + general_cost_jacobian_[t];
756 }
757 
758 Eigen::MatrixXd DynamicTimeIndexedShootingProblem::GetStateCostHessian(int t)
759 {
760  ValidateTimeIndex(t);
761 
762  // State Cost
763  dxdiff_[t] = scene_->GetDynamicsSolver()->dStateDelta(X_.col(t), X_star_.col(t), ArgumentPosition::ARG0);
764  state_cost_hessian_[t].noalias() = dxdiff_[t].transpose() * Q_[t] * dxdiff_[t];
765 
766  // For non-Euclidean spaces (i.e. on manifolds), there exists a second derivative of the state delta
767  if (scene_->get_has_quaternion_floating_base())
768  {
769  Eigen::RowVectorXd xdiffTQ = X_diff_.col(t).transpose() * Q_[t]; // (1*ndx)
770  Hessian ddxdiff = scene_->GetDynamicsSolver()->ddStateDelta(X_.col(t), X_star_.col(t), ArgumentPosition::ARG0);
771  for (int i = 0; i < ddxdiff.size(); ++i)
772  {
773  state_cost_hessian_[t].noalias() += xdiffTQ(i) * ddxdiff(i);
774  }
775  }
776 
777  // General Cost
778  general_cost_hessian_[t].noalias() = cost.dPhi_dx[t].transpose() * cost.S[t] * cost.dPhi_dx[t];
779 
780  // Contract task-map Hessians
781  if (flags_ & KIN_H)
782  {
783  Eigen::RowVectorXd ydiffTS = cost.ydiff[t].transpose() * cost.S[t]; // (1*m)
784  for (int i = 0; i < cost.length_jacobian; ++i) // length m
785  {
786  general_cost_hessian_[t].noalias() += ydiffTS(i) * cost.ddPhi_ddx[t](i);
787  }
788  }
789 
790  return 2.0 * state_cost_hessian_[t] + 2.0 * general_cost_hessian_[t];
791 }
792 
793 Eigen::MatrixXd DynamicTimeIndexedShootingProblem::GetControlCostHessian(int t)
794 {
795  if (t >= T_ - 1 || t < -1)
796  {
797  ThrowPretty("Requested t=" << t << " out of range, needs to be 0 =< t < " << T_ - 1);
798  }
799  else if (t == -1)
800  {
801  t = T_ - 2;
802  }
803 
804  // This allows composition of multiple functions
805  // useful when you want to apply different cost functions to different controls
806  // if (parameters_.LossType == "L2")
807  control_cost_hessian_[t] = R_ + R_.transpose();
808 
809  // Sparsity-related control Hessian
810  for (int iu = 0; iu < scene_->get_num_controls(); ++iu)
811  {
812  if (loss_type_ == ControlCostLossTermType::SmoothL1)
813  control_cost_hessian_[t](iu, iu) += smooth_l1_hessian(U_.col(t)[iu], l1_rate_(iu));
814 
815  // if huber_rate is 0, huber is undefined
816  // this is a shortcut for disabling the loss
817  else if (loss_type_ == ControlCostLossTermType::Huber && huber_rate_(iu) != 0)
818  control_cost_hessian_[t](iu, iu) += huber_hessian(U_.col(t)[iu], huber_rate_(iu));
819 
820  else if (loss_type_ == ControlCostLossTermType::PseudoHuber && huber_rate_(iu) != 0)
821  control_cost_hessian_[t](iu, iu) += pseudo_huber_hessian(U_.col(t)[iu], huber_rate_(iu));
822  }
823  return control_cost_weight_ * control_cost_hessian_[t];
824 }
825 
826 double DynamicTimeIndexedShootingProblem::GetControlCost(int t) const
827 {
828  if (t >= T_ - 1 || t < -1)
829  {
830  ThrowPretty("Requested t=" << t << " out of range, needs to be 0 =< t < " << T_ - 1);
831  }
832  else if (t == -1)
833  {
834  t = T_ - 2;
835  }
836 
837  double cost = 0;
838 
839  // This allows composition of multiple functions
840  // useful when you want to apply different cost functions to different controls
841  // if (parameters_.LossType == "L2")
842  cost += U_.col(t).cwiseAbs2().cwiseProduct(R_.diagonal()).sum();
843 
844  // Sparsity-related control cost
845  for (int iu = 0; iu < scene_->get_num_controls(); ++iu)
846  {
847  // if (U_.col(t)[iu] >= control_limits.col(1)[iu])
848  // continue;
849  if (loss_type_ == ControlCostLossTermType::SmoothL1)
850  cost += smooth_l1_cost(U_.col(t)[iu], l1_rate_(iu));
851 
852  // if huber_rate is 0, huber is undefined
853  // this is a shortcut for disabling the loss
854  else if (loss_type_ == ControlCostLossTermType::Huber && huber_rate_(iu) != 0)
855  cost += huber_cost(U_.col(t)[iu], huber_rate_(iu));
856 
857  else if (loss_type_ == ControlCostLossTermType::PseudoHuber && huber_rate_(iu) != 0)
858  cost += pseudo_huber_cost(U_.col(t)[iu], huber_rate_(iu));
859  }
860  if (!std::isfinite(cost))
861  {
862  cost = 0.0; // Likely "inf" as u is too small.
863  }
864  return control_cost_weight_ * cost;
865 }
866 
867 Eigen::VectorXd DynamicTimeIndexedShootingProblem::GetControlCostJacobian(int t)
868 {
869  if (t >= T_ - 1 || t < -1)
870  {
871  ThrowPretty("Requested t=" << t << " out of range, needs to be 0 =< t < " << T_ - 1);
872  }
873  else if (t == -1)
874  {
875  t = T_ - 2;
876  }
877 
878  // This allows composition of multiple functions
879  // useful when you want to apply different cost functions to different controls
880  // if (parameters_.LossType == "L2")
881  // control_cost_jacobian_[t] += 2.0 * U_.col(t).transpose() * R_; // Assumes R is diagonal
882  control_cost_jacobian_[t].noalias() = R_ * U_.col(t) + R_.transpose() * U_.col(t);
883 
884  // Sparsity-related control cost Jacobian
885  for (int iu = 0; iu < scene_->get_num_controls(); ++iu)
886  {
887  // if (U_.col(t)[iu] >= control_limits.col(1)[iu])
888  // continue;
889  if (loss_type_ == ControlCostLossTermType::SmoothL1)
890  control_cost_jacobian_[t](iu) += smooth_l1_jacobian(U_.col(t)[iu], l1_rate_(iu));
891 
892  // if huber_rate is 0, huber is undefined
893  // this is a shortcut for disabling the loss
894  else if (loss_type_ == ControlCostLossTermType::Huber && huber_rate_(iu) != 0)
895  control_cost_jacobian_[t](iu) += huber_jacobian(U_.col(t)[iu], huber_rate_(iu));
896 
897  else if (loss_type_ == ControlCostLossTermType::PseudoHuber && huber_rate_(iu) != 0)
898  control_cost_jacobian_[t](iu) += pseudo_huber_jacobian(U_.col(t)[iu], huber_rate_(iu));
899  }
900  return control_cost_weight_ * control_cost_jacobian_[t];
901 }
902 
903 Eigen::MatrixXd DynamicTimeIndexedShootingProblem::get_F(int t) const
904 {
905  if (t >= T_ - 1 || t < -1)
906  {
907  ThrowPretty("Requested t=" << t << " out of range, needs to be 0 =< t < " << T_ - 1);
908  }
909 
910  const int NX = scene_->get_num_positions() + scene_->get_num_velocities();
911  Eigen::MatrixXd F(NX, NX);
912 
913  for (int i = 0; i < NX; ++i)
914  F.col(i) = Ci_[i] * U_.col(t);
915 
916  return F;
917 }
918 
919 // F[i]_u
920 const Eigen::MatrixXd& DynamicTimeIndexedShootingProblem::GetControlNoiseJacobian(int column_idx) const
921 {
922  if (column_idx < 0 || column_idx >= scene_->get_num_velocities())
923  ThrowPretty("Requested column_idx=" << column_idx << " out of range; needs to be 0 <= column_idx < " << scene_->get_num_velocities() - 1);
924  return Ci_[column_idx];
925 }
926 
927 void DynamicTimeIndexedShootingProblem::EnableStochasticUpdates()
928 {
929  stochastic_updates_enabled_ = true;
930 }
931 
932 void DynamicTimeIndexedShootingProblem::DisableStochasticUpdates()
933 {
934  stochastic_updates_enabled_ = false;
935 }
936 
937 } // namespace exotica
std::vector< TaskVectorEntry > map
double huber_jacobian(double x, double beta)
Definition: sparse_costs.h:47
void NormalizeQuaternionInConfigurationVector(Eigen::Ref< Eigen::VectorXd > q)
Definition: conversions.h:141
double smooth_l1_cost(double x, double beta)
Definition: sparse_costs.h:63
double pseudo_huber_jacobian(double x, double beta)
Definition: sparse_costs.h:91
double smooth_l1_hessian(double x, double beta)
Definition: sparse_costs.h:79
#define ThrowPretty(m)
Definition: exception.h:36
double smooth_l1_jacobian(double x, double beta)
Definition: sparse_costs.h:70
void setZero()
const Eigen::Ref< const Eigen::MatrixXd > & MatrixXdRefConst
Definition: conversions.h:53
#define ThrowNamed(m)
Definition: exception.h:42
Eigen::Array< Eigen::MatrixXd, Eigen::Dynamic, 1 > Hessian
Definition: conversions.h:154
void SetDefaultQuaternionInConfigurationVector(Eigen::Ref< Eigen::VectorXd > q)
Definition: conversions.h:146
const Eigen::Ref< const Eigen::VectorXd > & VectorXdRefConst
Convenience wrapper for storing references to sub-matrices/vectors.
Definition: conversions.h:52
double huber_cost(double x, double beta)
Definition: sparse_costs.h:39
#define HIGHLIGHT_NAMED(name, x)
Definition: printable.h:62
double pseudo_huber_cost(double x, double beta)
Definition: sparse_costs.h:86
double pseudo_huber_hessian(double x, double beta)
Definition: sparse_costs.h:96
void AppendVector(std::vector< Val > &orig, const std::vector< Val > &extra)
Definition: conversions.h:223
double huber_hessian(double x, double beta)
Definition: sparse_costs.h:56
#define REGISTER_PROBLEM_TYPE(TYPE, DERIV)


exotica_core
Author(s): Yiming Yang, Michael Camilleri
autogenerated on Sat Apr 10 2021 02:34:49