ik_solver.cpp
Go to the documentation of this file.
1 //
2 // Copyright (c) 2018-2020, 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 IKSolver::SpecifyProblem(PlanningProblemPtr pointer)
37 {
38  if (pointer->type() != "exotica::UnconstrainedEndPoseProblem")
39  {
40  ThrowNamed("This IKSolver can't solve problem of type '" << pointer->type() << "'!");
41  }
42  MotionSolver::SpecifyProblem(pointer);
43  prob_ = std::static_pointer_cast<UnconstrainedEndPoseProblem>(pointer);
44 
45  W_inv_ = prob_->W.inverse();
46 
47  // Check dimension of W_ as this is a public member of the problem, and thus, can be edited by error.
48  if (W_inv_.rows() != prob_->N || W_inv_.cols() != prob_->N)
49  ThrowNamed("Size of W incorrect: (" << W_inv_.rows() << ", " << W_inv_.cols() << "), when expected: (" << prob_->N << ", " << prob_->N << ")");
50 
51  // Warn if deprecated MaxStep configuration detected:
52  if (parameters_.MaxStep != 0.0 && GetNumberOfMaxIterations() != 1)
53  WARNING_NAMED("IKSolver", "Deprecated configuration detected: MaxStep (given: " << parameters_.MaxStep << ") only works if MaxIterations == 1 (given: " << GetNumberOfMaxIterations() << ")");
54 
55  // Set up backtracking line-search coefficients
56  alpha_space_ = Eigen::VectorXd::LinSpaced(10, 1.0, 0.1);
57 
58  lambda_ = parameters_.RegularizationRate;
59  th_stepinc_ = parameters_.ThresholdRegularizationIncrease;
60  th_stepdec_ = parameters_.ThresholdRegularizationDecrease;
61  regmax_ = parameters_.MaximumRegularization;
62 
63  th_stop_ = parameters_.GradientToleranceConvergenceThreshold;
64 
65  // Allocate variables
66  q_.resize(prob_->N);
67  qd_.resize(prob_->N);
68  yd_.resize(prob_->cost.length_jacobian);
69  cost_jacobian_.resize(prob_->cost.length_jacobian, prob_->N);
70  J_pseudo_inverse_.resize(prob_->N, prob_->cost.length_jacobian);
71  J_tmp_.resize(prob_->cost.length_jacobian, prob_->cost.length_jacobian);
72 }
73 
74 void IKSolver::Solve(Eigen::MatrixXd& solution)
75 {
76  if (!prob_) ThrowNamed("Solver has not been initialized!");
77 
78  Timer timer;
79 
80  prob_->ResetCostEvolution(GetNumberOfMaxIterations() + 1);
81  lambda_ = parameters_.RegularizationRate;
82  q_ = prob_->ApplyStartState();
83 
84  if (prob_->q_nominal.rows() == prob_->N)
85  {
86  WARNING("Nominal state regularization is no longer supported - please use a JointPose task-map.");
87  }
88 
89  int i;
90  for (i = 0; i < GetNumberOfMaxIterations(); ++i)
91  {
92  prob_->Update(q_);
93  error_ = prob_->GetScalarCost();
94  prob_->SetCostEvolution(i, error_);
95  error_prev_ = error_;
96 
97  // Absolute function tolerance check
98  if (error_ < parameters_.Tolerance)
99  {
100  prob_->termination_criterion = TerminationCriterion::FunctionTolerance;
101  break;
102  }
103 
104  yd_.noalias() = prob_->cost.S * prob_->cost.ydiff;
105  cost_jacobian_.noalias() = prob_->cost.S * prob_->cost.jacobian;
106 
107  // Weighted Regularized Pseudo-Inverse
108  // J_pseudo_inverse_ = W_inv_ * cost_jacobian_.transpose() * ( cost_jacobian_ * W_inv_ * cost_jacobian_.transpose() + lambda_ * I ).inverse();
109 
110  bool decomposition_ok = false;
111  while (!decomposition_ok)
112  {
113  J_tmp_.noalias() = cost_jacobian_ * W_inv_ * cost_jacobian_.transpose();
114  J_tmp_.diagonal().array() += lambda_; // Add regularisation
115  J_decomposition_.compute(J_tmp_);
116  if (J_decomposition_.info() != Eigen::Success)
117  {
118  IncreaseRegularization();
119  if (lambda_ > regmax_)
120  {
121  WARNING("Divergence in Cholesky decomposition :-(");
122  prob_->termination_criterion = TerminationCriterion::Divergence;
123  break;
124  }
125  // ThrowPretty("Error during matrix decomposition of J_tmp_ (lambda=" << lambda_ << "):\n"
126  // << J_tmp_);
127  }
128  else
129  {
130  decomposition_ok = true;
131  }
132  }
133  J_tmp_.noalias() = J_decomposition_.solve(Eigen::MatrixXd::Identity(prob_->cost.length_jacobian, prob_->cost.length_jacobian)); // Inverse
134  J_pseudo_inverse_.noalias() = W_inv_ * cost_jacobian_.transpose() * J_tmp_;
135 
136  qd_.noalias() = J_pseudo_inverse_ * yd_;
137 
138  // Support for a maximum step, e.g., when used as real-time, interactive IK
139  if (GetNumberOfMaxIterations() == 1 && parameters_.MaxStep != 0.0)
140  {
141  ScaleToStepSize(qd_);
142  q_ -= qd_;
143  }
144  // Line search
145  else
146  {
147  for (int ai = 0; ai < alpha_space_.size(); ++ai)
148  {
149  steplength_ = alpha_space_(ai);
150  Eigen::VectorXd q_tmp = q_ - steplength_ * qd_;
151  prob_->Update(q_tmp);
152  error_ = prob_->GetScalarCost();
153 
154  if (error_ < error_prev_)
155  {
156  q_ = q_tmp;
157  qd_ *= steplength_;
158  break;
159  }
160  }
161  }
162 
163  // Step tolerance parameter
164  step_ = qd_.squaredNorm();
165 
166  // Gradient tolerance
167  stop_ = cost_jacobian_.norm();
168 
169  // Debug output
170  if (debug_) PrintDebug(i);
171 
172  // Check step tolerance
173  if (step_ < parameters_.StepToleranceConvergenceThreshold)
174  {
175  prob_->termination_criterion = TerminationCriterion::StepTolerance;
176  break;
177  }
178 
179  // Check gradient tolerance (convergence)
180  if (stop_ < parameters_.GradientToleranceConvergenceThreshold)
181  {
182  prob_->termination_criterion = TerminationCriterion::GradientTolerance;
183  break;
184  }
185 
186  // Adapt regularization based on step-length
187  if (GetNumberOfMaxIterations() == 1 && parameters_.MaxStep == 0.0 && steplength_ > th_stepdec_)
188  {
189  DecreaseRegularization();
190  }
191  if (GetNumberOfMaxIterations() == 1 && parameters_.MaxStep == 0.0 && steplength_ <= th_stepinc_)
192  {
193  IncreaseRegularization();
194  if (lambda_ == regmax_)
195  {
196  prob_->termination_criterion = TerminationCriterion::Divergence;
197  break;
198  }
199  }
200  }
201 
202  // Check if we ran out of iterations
203  if (i == GetNumberOfMaxIterations())
204  {
205  prob_->termination_criterion = TerminationCriterion::IterationLimit;
206  }
207 
208  if (debug_)
209  {
210  switch (prob_->termination_criterion)
211  {
212  case TerminationCriterion::GradientTolerance:
213  HIGHLIGHT_NAMED("IKSolver", "Reached convergence (" << std::scientific << stop_ << " < " << parameters_.GradientToleranceConvergenceThreshold << ")");
214  break;
215  case TerminationCriterion::FunctionTolerance:
216  HIGHLIGHT_NAMED("IKSolver", "Reached absolute function tolerance (" << std::scientific << error_ << " < " << parameters_.Tolerance << ")");
217  break;
218  case TerminationCriterion::StepTolerance:
219  HIGHLIGHT_NAMED("IKSolver", "Reached step tolerance (" << std::scientific << step_ << " < " << parameters_.StepToleranceConvergenceThreshold << ")");
220  break;
221  case TerminationCriterion::Divergence:
222  WARNING_NAMED("IKSolver", "Regularization exceeds maximum regularization: " << lambda_ << " > " << regmax_);
223  break;
224  case TerminationCriterion::IterationLimit:
225  HIGHLIGHT_NAMED("IKSolver", "Reached iteration limit.");
226  break;
227 
228  default:
229  break;
230  }
231  }
232 
233  solution.resize(1, prob_->N);
234  solution.row(0) = q_.transpose();
235  planning_time_ = timer.GetDuration();
236 }
237 
238 void IKSolver::ScaleToStepSize(Eigen::VectorXdRef xd)
239 {
240  const double max_vel = xd.cwiseAbs().maxCoeff();
241  if (max_vel > parameters_.MaxStep)
242  {
243  xd = xd * parameters_.MaxStep / max_vel;
244  }
245 }
246 } // namespace exotica
#define WARNING_NAMED(name, x)
Eigen::Ref< Eigen::VectorXd > VectorXdRef
#define REGISTER_MOTIONSOLVER_TYPE(TYPE, DERIV)
#define ThrowNamed(m)
#define HIGHLIGHT_NAMED(name, x)
Weighted and Regularized Pseudo-Inverse Inverse Kinematics Solver The solver solves a weighted and re...
Definition: ik_solver.h:44
double GetDuration() const
std::shared_ptr< PlanningProblem > PlanningProblemPtr
#define WARNING(x)


exotica_ik_solver
Author(s):
autogenerated on Sat Apr 10 2021 02:35:33