bio_ik
MoveIt kinematics_base plugin based on particle optimization & GA
ik_gradient.cpp
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2016-2017, Philipp Sebastian Ruppel
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 #include "ik_base.h"
36 
37 namespace bio_ik
38 {
39 
40 // pseudoinverse jacobian solver
41 // (mainly for testing RobotFK_Jacobian::computeJacobian)
42 template <class BASE> struct IKJacobianBase : BASE
43 {
44  using BASE::modelInfo;
45  using BASE::model;
46  using BASE::params;
47  using BASE::computeFitness;
48  using BASE::problem;
49 
50  std::vector<Frame> tipObjectives;
51 
52  Eigen::VectorXd tip_diffs;
53  Eigen::VectorXd joint_diffs;
54  Eigen::MatrixXd jacobian;
55  std::vector<Frame> tip_frames_temp;
56 
57  IKJacobianBase(const IKParams& p)
58  : BASE(p)
59  {
60  }
61 
62  void initialize(const Problem& problem)
63  {
64  BASE::initialize(problem);
65  tipObjectives.resize(problem.tip_link_indices.size());
66  for(auto& goal : problem.goals)
67  tipObjectives[goal.tip_index] = goal.frame;
68  }
69 
70  void optimizeJacobian(std::vector<double>& solution)
71  {
72  FNPROFILER();
73 
74  int tip_count = problem.tip_link_indices.size();
75  tip_diffs.resize(tip_count * 6);
76  joint_diffs.resize(problem.active_variables.size());
77 
78  // compute fk
79  model.applyConfiguration(solution);
80 
81  double translational_scale = 1;
82  double rotational_scale = 1;
83 
84  // compute goal diffs
85  tip_frames_temp = model.getTipFrames();
86  for(int itip = 0; itip < tip_count; itip++)
87  {
88  auto twist = frameTwist(tip_frames_temp[itip], tipObjectives[itip]);
89  tip_diffs(itip * 6 + 0) = twist.vel.x() * translational_scale;
90  tip_diffs(itip * 6 + 1) = twist.vel.y() * translational_scale;
91  tip_diffs(itip * 6 + 2) = twist.vel.z() * translational_scale;
92  tip_diffs(itip * 6 + 3) = twist.rot.x() * rotational_scale;
93  tip_diffs(itip * 6 + 4) = twist.rot.y() * rotational_scale;
94  tip_diffs(itip * 6 + 5) = twist.rot.z() * rotational_scale;
95  }
96 
97  // compute jacobian
98  {
99  model.computeJacobian(problem.active_variables, jacobian);
100  int icol = 0;
101  for(auto ivar : problem.active_variables)
102  {
103  for(size_t itip = 0; itip < tip_count; itip++)
104  {
105  jacobian(itip * 6 + 0, icol) *= translational_scale;
106  jacobian(itip * 6 + 1, icol) *= translational_scale;
107  jacobian(itip * 6 + 2, icol) *= translational_scale;
108  jacobian(itip * 6 + 3, icol) *= rotational_scale;
109  jacobian(itip * 6 + 4, icol) *= rotational_scale;
110  jacobian(itip * 6 + 5, icol) *= rotational_scale;
111  }
112  icol++;
113  }
114  }
115 
116  // get pseudoinverse and multiply
117  joint_diffs = jacobian.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(tip_diffs);
118  // joint_diffs = (jacobian.transpose() * jacobian).ldlt().solve(jacobian.transpose() * tip_diffs);
119 
120  // apply joint deltas and clip
121  {
122  int icol = 0;
123  for(auto ivar : problem.active_variables)
124  {
125  auto v = solution[ivar] + joint_diffs(icol);
126  if(!std::isfinite(v)) continue;
127  v = modelInfo.clip(v, ivar);
128  solution[ivar] = v;
129  icol++;
130  }
131  }
132  }
133 };
134 
135 // simple gradient descent
136 template <int if_stuck, size_t threads> struct IKGradientDescent : IKBase
137 {
138  std::vector<double> solution, best_solution, gradient, temp;
139  bool reset;
140 
141  IKGradientDescent(const IKParams& p)
142  : IKBase(p)
143  {
144  }
145 
146  void initialize(const Problem& problem)
147  {
148  IKBase::initialize(problem);
149  solution = problem.initial_guess;
150  if(thread_index > 0)
151  for(auto& vi : problem.active_variables)
152  solution[vi] = random(modelInfo.getMin(vi), modelInfo.getMax(vi));
153  best_solution = solution;
154  reset = false;
155  }
156 
157  const std::vector<double>& getSolution() const { return best_solution; }
158 
159  void step()
160  {
161  // random reset if stuck
162  if(reset)
163  {
164  reset = false;
165  for(auto& vi : problem.active_variables)
166  solution[vi] = random(modelInfo.getMin(vi), modelInfo.getMax(vi));
167  }
168 
169  // compute gradient direction
170  temp = solution;
171  double jd = 0.0001;
172  gradient.resize(solution.size(), 0);
173  for(auto ivar : problem.active_variables)
174  {
175  temp[ivar] = solution[ivar] - jd;
176  double p1 = computeFitness(temp);
177 
178  temp[ivar] = solution[ivar] + jd;
179  double p3 = computeFitness(temp);
180 
181  temp[ivar] = solution[ivar];
182 
183  gradient[ivar] = p3 - p1;
184  }
185 
186  // normalize gradient direction
187  double sum = 0.0001;
188  for(auto ivar : problem.active_variables)
189  sum += fabs(gradient[ivar]);
190  double f = 1.0 / sum * jd;
191  for(auto ivar : problem.active_variables)
192  gradient[ivar] *= f;
193 
194  // initialize line search
195  temp = solution;
196 
197  for(auto ivar : problem.active_variables)
198  temp[ivar] = solution[ivar] - gradient[ivar];
199  double p1 = computeFitness(temp);
200 
201  // for(auto ivar : problem.active_variables) temp[ivar] = solution[ivar];
202  // double p2 = computeFitness(temp);
203 
204  for(auto ivar : problem.active_variables)
205  temp[ivar] = solution[ivar] + gradient[ivar];
206  double p3 = computeFitness(temp);
207 
208  double p2 = (p1 + p3) * 0.5;
209 
210  // linear step size estimation
211  double cost_diff = (p3 - p1) * 0.5;
212  double joint_diff = p2 / cost_diff;
213 
214  // apply optimization step
215  // (move along gradient direction by estimated step size)
216  for(auto ivar : problem.active_variables)
217  temp[ivar] = modelInfo.clip(solution[ivar] - gradient[ivar] * joint_diff, ivar);
218 
219  if(if_stuck == 'c')
220  {
221  // always accept solution and continue
222  solution = temp;
223  }
224  else
225  {
226  // has solution improved?
227  if(computeFitness(temp) < computeFitness(solution))
228  {
229  // solution improved -> accept solution
230  solution = temp;
231  }
232  else
233  {
234  if(if_stuck == 'r')
235  {
236  // reset if stuck
237  reset = true;
238  }
239  }
240  }
241 
242  // update best solution
243  if(computeFitness(solution) < computeFitness(best_solution)) best_solution = solution;
244  }
245 
246  size_t concurrency() const { return threads; }
247 };
248 
250 static IKFactory::Class<IKGradientDescent<' ', 2>> gd_2("gd_2");
251 static IKFactory::Class<IKGradientDescent<' ', 4>> gd_4("gd_4");
252 static IKFactory::Class<IKGradientDescent<' ', 8>> gd_8("gd_8");
253 
254 static IKFactory::Class<IKGradientDescent<'r', 1>> gd_r("gd_r");
255 static IKFactory::Class<IKGradientDescent<'r', 2>> gd_2_r("gd_r_2");
256 static IKFactory::Class<IKGradientDescent<'r', 4>> gd_4_r("gd_r_4");
257 static IKFactory::Class<IKGradientDescent<'r', 8>> gd_8_r("gd_r_8");
258 
259 static IKFactory::Class<IKGradientDescent<'c', 1>> gd_c("gd_c");
260 static IKFactory::Class<IKGradientDescent<'c', 2>> gd_2_c("gd_c_2");
261 static IKFactory::Class<IKGradientDescent<'c', 4>> gd_4_c("gd_c_4");
262 static IKFactory::Class<IKGradientDescent<'c', 8>> gd_8_c("gd_c_8");
263 
264 // pseudoinverse jacobian only
265 template <size_t threads> struct IKJacobian : IKJacobianBase<IKBase>
266 {
267  using IKBase::initialize;
268  std::vector<double> solution;
269  IKJacobian(const IKParams& p)
271  {
272  }
273  void initialize(const Problem& problem)
274  {
276  solution = problem.initial_guess;
277  if(thread_index > 0)
278  for(auto& vi : problem.active_variables)
279  solution[vi] = random(modelInfo.getMin(vi), modelInfo.getMax(vi));
280  }
281  const std::vector<double>& getSolution() const { return solution; }
282  void step() { optimizeJacobian(solution); }
283  size_t concurrency() const { return threads; }
284 };
285 static IKFactory::Class<IKJacobian<1>> jac("jac");
286 static IKFactory::Class<IKJacobian<2>> jac_2("jac_2");
287 static IKFactory::Class<IKJacobian<4>> jac_4("jac_4");
288 static IKFactory::Class<IKJacobian<8>> jac_8("jac_8");
289 }