bio_ik
MoveIt kinematics_base plugin based on particle optimization & GA
ik_evolution_1.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 
41 {
42  struct Individual
43  {
44  std::vector<double> genes;
45  std::vector<double> gradients;
46  double extinction;
47  double fitness;
48  };
49 
51  {
52  size_t variable_count, tip_count;
53  std::vector<double> table;
54  std::vector<double> chain_lengths;
55  std::vector<std::vector<double>> chain_lengths_2;
56 
57  public:
59  HeuristicErrorTree(moveit::core::RobotModelConstPtr robot_model, const std::vector<std::string>& tip_names)
60  {
61  tip_count = tip_names.size();
62  variable_count = robot_model->getVariableCount();
63  table.resize(tip_count * variable_count);
64  for(size_t tip_index = 0; tip_index < tip_count; tip_index++)
65  {
66  auto& tip_name = tip_names[tip_index];
67  for(auto* link_model = robot_model->getLinkModel(tip_name); link_model; link_model = link_model->getParentLinkModel())
68  {
69  auto* joint_model = link_model->getParentJointModel();
70  size_t v1 = joint_model->getFirstVariableIndex();
71  size_t vn = joint_model->getVariableCount();
72  for(size_t variable_index = v1; variable_index < v1 + vn; variable_index++)
73  table[variable_index * tip_count + tip_index] = 1;
74  }
75  }
76  for(size_t variable_index = 0; variable_index < variable_count; variable_index++)
77  {
78  double sum = 0;
79  for(size_t tip_index = 0; tip_index < tip_count; tip_index++)
80  sum += table[variable_index * tip_count + tip_index];
81  if(sum > 0)
82  for(size_t tip_index = 0; tip_index < tip_count; tip_index++)
83  table[variable_index * tip_count + tip_index] /= sum;
84  }
85 
86  chain_lengths.resize(tip_count);
87  for(size_t tip_index = 0; tip_index < tip_count; tip_index++)
88  {
89  auto& tip_name = tip_names[tip_index];
90  double chain_length = 0;
91  for(auto* link_model = robot_model->getLinkModel(tip_name); link_model; link_model = link_model->getParentLinkModel())
92  {
93  chain_length += Frame(link_model->getJointOriginTransform()).pos.length();
94  }
95  chain_lengths[tip_index] = chain_length;
96  }
97 
98  chain_lengths_2.resize(tip_count);
99  for(size_t tip_index = 0; tip_index < tip_count; tip_index++)
100  {
101  auto& tip_name = tip_names[tip_index];
102  double chain_length = 0;
103  chain_lengths_2[tip_index].resize(variable_count, 0.0);
104  for(auto* link_model = robot_model->getLinkModel(tip_name); link_model; link_model = link_model->getParentLinkModel())
105  {
106 
107  auto* joint_model = link_model->getParentJointModel();
108  int vmin = joint_model->getFirstVariableIndex();
109  int vmax = vmin + joint_model->getVariableCount();
110  for(int vi = vmin; vi < vmax; vi++)
111  chain_lengths_2[tip_index][vi] = chain_length;
112  chain_length += Frame(link_model->getJointOriginTransform()).pos.length();
113  }
114  }
115  }
116  inline double getInfluence(size_t variable_index, size_t tip_index) const { return table[variable_index * tip_count + tip_index]; }
117  inline double getChainLength(size_t tip_index) const { return chain_lengths[tip_index]; }
118  inline double getJointVariableChainLength(size_t tip_index, size_t variable_index) const { return chain_lengths_2[tip_index][variable_index]; }
119  };
120 
121  HeuristicErrorTree heuristicErrorTree;
122  std::vector<double> solution;
123  std::vector<Individual> population;
124  int populationSize, eliteCount;
125  std::vector<Individual*> tempPool;
126  std::vector<Individual> tempOffspring;
127  std::vector<double> initialGuess;
128 
129  bool opt_no_wipeout;
130 
131  bool linear_fitness;
132 
133  void setParams(const IKParams& p)
134  {
135  opt_no_wipeout = p.opt_no_wipeout;
136  populationSize = p.population_size;
137  eliteCount = p.elite_count;
138  linear_fitness = p.linear_fitness;
139  }
140 
141  bool in_final_adjustment_loop;
142 
143  template <class t> inline t select(const std::vector<t>& v)
144  {
145  // FNPROFILER();
147  size_t index = d(rng);
148  return v[index];
149  }
150 
151  inline double clip(double v, size_t i) { return modelInfo.clip(v, i); }
152 
153  inline double getMutationStrength(size_t i, const Individual& parentA, const Individual& parentB)
154  {
155  double extinction = 0.5 * (parentA.extinction + parentB.extinction);
156  double span = modelInfo.getSpan(i);
157  return span * extinction;
158  }
159 
160  double computeAngularScale(size_t tip_index, const Frame& tip_frame)
161  {
162  double angular_scale = sqrt(heuristicErrorTree.getChainLength(tip_index) * tip_frame.pos.length()) / M_PI;
163  return angular_scale;
164  // return 1;
165  /*double angular_scale = sqrt(heuristicErrorTree.getChainLength(tip_index) * tip_frame.pos.length()) / M_PI;
166  //double angular_scale = heuristicErrorTree.getChainLength(tip_index) * (1.0 / M_PI);
167  if(opt_angular_scale_full_circle) angular_scale *= 0.5;
168  return angular_scale;*/
169  }
170 
171  double getHeuristicError(size_t variable_index, bool balanced)
172  {
173  // return 1;
174 
175  double heuristic_error = 0;
176  // for(int tip_index = 0; tip_index < tipObjectives.size(); tip_index++)
177  for(int tip_index = 0; tip_index < problem.goals.size(); tip_index++)
178  {
179  double influence = heuristicErrorTree.getInfluence(variable_index, tip_index);
180  if(influence == 0) continue;
181 
182  // const auto& ta = tipObjectives[tip_index];
183  const auto& ta = problem.goals[tip_index].frame;
184  const auto& tb = model.getTipFrame(tip_index);
185 
186  double length = heuristicErrorTree.getJointVariableChainLength(tip_index, variable_index);
187 
188  // LOG_ALWAYS("a", heuristicErrorTree.getJointVariableChainLength(tip_index, variable_index));
189 
190  // double length = model.getJointVariableFrame(variable_index).pos.distance(model.getTipFrame(tip_index).pos); if(length <= 0.000000001) length = 0;
191 
192  // LOG_ALWAYS("b", length);
193 
194  if(modelInfo.isPrismatic(variable_index))
195  {
196  // heuristic_error += ta.pos.distance(tb.pos) * influence;
197  // if(length) heuristic_error += ta.rot.angle(tb.rot) * length * influence;
198 
199  if(length)
200  {
201  heuristic_error += ta.pos.distance(tb.pos) * influence * 0.5;
202  heuristic_error += ta.rot.angle(tb.rot) * length * influence * 0.5;
203  }
204  else
205  {
206  heuristic_error += ta.pos.distance(tb.pos) * influence;
207  }
208  }
209 
210  if(modelInfo.isRevolute(variable_index))
211  {
212  // if(length) heuristic_error += ta.pos.distance(tb.pos) / length * influence;
213  // heuristic_error += ta.rot.angle(tb.rot) * influence;
214 
215  if(length)
216  {
217  heuristic_error += ta.pos.distance(tb.pos) / length * influence * 0.5;
218  heuristic_error += ta.rot.angle(tb.rot) * influence * 0.5;
219  }
220  else
221  {
222  heuristic_error += ta.rot.angle(tb.rot) * influence;
223  }
224 
225  // double d = 0.0;
226  // if(length) d = std::max(d, ta.pos.distance(tb.pos) / length);
227  // d = std::max(d, ta.rot.angle(tb.rot));
228  // heuristic_error += d * influence;
229  }
230  }
231  // heuristic_error *= 0.5;
232  // LOG_ALWAYS(heuristic_error);
233  return heuristic_error;
234  }
235 
236  bool in_adjustment_2, in_get_solution_fitness;
237 
238  void reroll(Individual& offspring)
239  {
240  FNPROFILER();
241  // for(size_t i = 0; i < offspring.genes.size(); i++)
242  for(auto i : problem.active_variables)
243  {
244  offspring.genes[i] = random(modelInfo.getMin(i), modelInfo.getMax(i));
245 
246  offspring.genes[i] = mix(offspring.genes[i], (modelInfo.getMin(i) + modelInfo.getMax(i)) * 0.5, random(0.0, 0.1));
247 
248  offspring.gradients[i] = 0;
249  }
250  offspring.fitness = computeFitness(offspring.genes, false);
251  }
252 
253  double computeFitness(const std::vector<double>& genes, bool balanced)
254  {
255  if(linear_fitness)
256  {
257  model.applyConfiguration(genes);
258  double fitness_sum = 0.0;
259  for(size_t goal_index = 0; goal_index < problem.goals.size(); goal_index++)
260  {
261  const auto& ta = problem.goals[goal_index].frame;
262  const auto& tb = model.getTipFrame(problem.goals[goal_index].tip_index);
263 
264  double tdist = ta.pos.distance(tb.pos) / computeAngularScale(problem.goals[goal_index].tip_index, ta);
265  double rdist = ta.rot.angle(tb.rot);
266 
267  fitness_sum += mix(tdist, rdist, (balanced || in_final_adjustment_loop) ? 0.5 : random());
268  }
269  return fitness_sum;
270  }
271  else
272  {
273  return IKBase::computeFitness(genes);
274  }
275  }
276 
277  bool checkWipeout()
278  {
279  FNPROFILER();
280  auto& genes = population[0].genes;
281  // for(size_t i = 0; i < genes.size(); i++)
282  for(auto i : problem.active_variables)
283  {
284  double v0 = genes[i];
285  double fitness = computeFitness(genes, true);
286  double heuristicError = getHeuristicError(i, true);
287  // double heuristicError = 0.001;
288  genes[i] = modelInfo.clip(v0 + random(0, heuristicError), i);
289  double incFitness = computeFitness(genes, true);
290  genes[i] = modelInfo.clip(v0 - random(0, heuristicError), i);
291  double decFitness = computeFitness(genes, true);
292  genes[i] = v0;
293  if(incFitness < fitness || decFitness < fitness)
294  {
295  // LOG("no wipeout");
296  return false;
297  }
298  }
299  // LOG("wipeout 1");
300  return true;
301  }
302 
303  void computeExtinctions()
304  {
305  double min = population.front().fitness;
306  double max = population.back().fitness;
307  for(size_t i = 0; i < populationSize; i++)
308  {
309  double grading = (double)i / (double)(populationSize - 1);
310  population[i].extinction = (population[i].fitness + min * (grading - 1)) / max;
311  }
312  }
313 
314  bool tryUpdateSolution()
315  {
316  FNPROFILER();
317  double solutionFitness = computeFitness(solution, true);
318  double candidateFitness = computeFitness(population[0].genes, true);
319  // LOG_VAR(solutionFitness);
320  // LOG_VAR(candidateFitness);
321  if(candidateFitness < solutionFitness)
322  {
323  solution = population[0].genes;
324  // solution = initialGuess;
325  // for(auto i : problem.active_variables)
326  // solution[i] = population[0].genes[i];
327  return true;
328  }
329  return false;
330  }
331 
332  double getMutationProbability(const Individual& parentA, const Individual& parentB)
333  {
334  double extinction = 0.5 * (parentA.extinction + parentB.extinction);
335  double inverse = 1.0 / parentA.genes.size();
336  return extinction * (1.0 - inverse) + inverse;
337  }
338 
339  void sortByFitness()
340  {
341  FNPROFILER();
342  sort(population.begin(), population.end(), [](const Individual& a, const Individual& b) { return a.fitness < b.fitness; });
343  }
344 
345  double bounce(double v, int i)
346  {
347  double c = clip(v, i);
348  v = c - (v - c) * 2;
349  // v = c + c - v;
350  v = clip(v, i);
351  return v;
352  }
353 
354  void reproduce(Individual& offspring, const Individual& parentA, const Individual& parentB, const Individual& prototype)
355  {
356  FNPROFILER();
357  for(size_t i = 0; i < offspring.genes.size(); i++)
358  // for(auto i : problem.active_variables)
359  {
360  offspring.genes[i] = mix(parentA.genes[i], parentB.genes[i], random());
361  offspring.genes[i] += parentA.gradients[i] * random();
362  offspring.genes[i] += parentB.gradients[i] * random();
363 
364  double storage = offspring.genes[i];
365 
366  if(random() < getMutationProbability(parentA, parentB)) offspring.genes[i] += random(-1, 1) * getMutationStrength(i, parentA, parentB);
367  // offspring.genes[i] += normal_random() * getMutationStrength(i, parentA, parentB);
368 
369  offspring.genes[i] += mix(random() * (0.5 * (parentA.genes[i] + parentB.genes[i]) - offspring.genes[i]), random() * (prototype.genes[i] - offspring.genes[i]), random());
370 
371  // offspring.genes[i] = clip(offspring.genes[i], i);
372 
373  // offspring.genes[i] += fabs(offspring.genes[i] - storage) * offspring.genes[i] - (modelInfo.getMin(i) + modelInfo.getMax(i)) * 0.5;
374 
375  // offspring.genes[i] = mix(offspring.genes[i], (modelInfo.getMin(i) + modelInfo.getMax(i)) * 0.5, random() * 0.1 * fabs(offspring.genes[i] - storage) / modelInfo.getSpan(i));
376 
377  offspring.genes[i] = clip(offspring.genes[i], i);
378 
379  // offspring.genes[i] = bounce(offspring.genes[i], i);
380 
381  offspring.gradients[i] = random() * offspring.gradients[i] + offspring.genes[i] - storage;
382  }
383 
384  offspring.fitness = computeFitness(offspring.genes, false);
385  }
386 
387  void exploit(Individual& individual)
388  {
389  FNPROFILER();
390 
391  double fitness_sum = 0;
392 
393  // model.incrementalBegin(individual.genes);
394 
395  for(auto i : problem.active_variables)
396  {
397  double fitness = computeFitness(individual.genes, true);
398 
399  double heuristicError = getHeuristicError(i, true);
400  double v_0 = individual.genes[i];
401 
402  double v_inc = clip(v_0 + random(0, heuristicError), i);
403  double v_dec = clip(v_0 - random(0, heuristicError), i);
404 
405  individual.genes[i] = v_inc;
406  double inc_fitness = computeFitness(individual.genes, true);
407  individual.genes[i] = v_dec;
408  double dec_fitness = computeFitness(individual.genes, true);
409 
410  if(inc_fitness < fitness && inc_fitness <= dec_fitness)
411  {
412  individual.genes[i] = v_inc;
413  individual.gradients[i] = v_0 * random() + v_inc - v_0;
414  fitness_sum += inc_fitness;
415  }
416  else if(dec_fitness < fitness && dec_fitness <= inc_fitness)
417  {
418  individual.genes[i] = v_dec;
419  individual.gradients[i] = v_0 * random() + v_dec - v_0;
420  fitness_sum += dec_fitness;
421  }
422  else
423  {
424  individual.genes[i] = v_0;
425  fitness_sum += fitness;
426  }
427  }
428 
429  // model.incrementalEnd();
430 
431  individual.fitness = fitness_sum / individual.genes.size();
432  }
433 
434  IKEvolution1(const IKParams& p)
435  : IKBase(p)
436  , populationSize(12)
437  , eliteCount(4)
438  , in_final_adjustment_loop(false)
439  , in_adjustment_2(false)
440  , in_get_solution_fitness(false)
441  {
442  setParams(p);
443  }
444 
445  void init()
446  {
447  initialGuess = problem.initial_guess;
448  solution = initialGuess;
449 
450  population.resize(populationSize);
451 
452  {
453  auto& p = population[0];
454  p.genes = solution;
455  p.gradients.clear();
456  p.gradients.resize(p.genes.size(), 0);
457  p.fitness = computeFitness(p.genes, false);
458  }
459 
460  for(int i = 1; i < populationSize; i++)
461  {
462  auto& p = population[i];
463  p.genes = solution;
464  p.gradients.clear();
465  p.gradients.resize(p.genes.size(), 0);
466  reroll(p);
467  }
468 
469  sortByFitness();
470  computeExtinctions();
471  }
472 
473  void initialize(const Problem& problem)
474  {
475  IKBase::initialize(problem);
476 
477  std::vector<std::string> tips;
478  for(auto tip_link_index : problem.tip_link_indices)
479  tips.push_back(params.robot_model->getLinkModelNames()[tip_link_index]);
480  heuristicErrorTree = HeuristicErrorTree(params.robot_model, tips);
481 
482  init();
483  }
484 
485  const std::vector<double>& getSolution() const { return solution; }
486 
487  double getSolutionFitness()
488  {
489  in_get_solution_fitness = true;
490  double f = computeFitness(solution, true);
491  in_get_solution_fitness = false;
492  return f;
493  }
494 
495  const std::vector<Frame>& getSolutionTipFrames()
496  {
497  model.applyConfiguration(solution);
498  return model.getTipFrames();
499  }
500 
501  bool evolve()
502  {
503  FNPROFILER();
504 
505  auto& offspring = tempOffspring;
506  offspring = population;
507 
508  for(size_t i = 0; i < eliteCount; i++)
509  {
510  offspring[i] = population[i];
511  exploit(offspring[i]);
512  }
513 
514  auto& pool = tempPool;
515  pool.resize(populationSize);
516  iota(pool.begin(), pool.end(), &population[0]);
517 
518  for(size_t i = eliteCount; i < populationSize; i++)
519  {
520  if(pool.size() > 0)
521  {
522  auto& parentA = *select(pool);
523  auto& parentB = *select(pool);
524  auto& prototype = *select(pool);
525  reproduce(offspring[i], parentA, parentB, prototype);
526  if(offspring[i].fitness < parentA.fitness) pool.erase(remove(pool.begin(), pool.end(), &parentA), pool.end());
527  if(offspring[i].fitness < parentB.fitness) pool.erase(remove(pool.begin(), pool.end(), &parentB), pool.end());
528  }
529  else
530  {
531  reroll(offspring[i]);
532  }
533  }
534 
535  population = offspring;
536 
537  sortByFitness();
538 
539  computeExtinctions();
540 
541  if(tryUpdateSolution()) return true;
542  if(opt_no_wipeout) return false;
543  if(!checkWipeout()) return false;
544 
545  init();
546 
547  return tryUpdateSolution();
548  }
549 
550  void step()
551  {
552  in_adjustment_2 = false;
553  evolve();
554  }
555 
556  virtual size_t concurrency() const { return 4; }
557 };
558 
559 static IKFactory::Class<IKEvolution1> cIKEvolution1("bio1");
560 
561 }