bio_ik
MoveIt kinematics_base plugin based on particle optimization & GA
ik_parallel.h
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 #include <boost/thread/barrier.hpp>
38 
39 namespace bio_ik
40 {
41 
42 // executes a function in parallel on pre-allocated threads
44 {
45  std::function<void(size_t)> fun;
46  std::vector<std::thread> threads;
47  boost::barrier barrier;
48  volatile bool exit;
49  double best_fitness;
50 
51 public:
52  template <class FUN>
53  ParallelExecutor(size_t thread_count, const FUN& f)
54  : exit(false)
55  , threads(thread_count)
56  , fun(f)
57  , barrier(thread_count)
58  {
59  for(size_t i = 1; i < thread_count; i++)
60  {
61  std::thread t([this, i]() {
62  while(true)
63  {
64  barrier.wait();
65  if(exit) break;
66  fun(i);
67  barrier.wait();
68  if(exit) break;
69  }
70  });
71  std::swap(t, threads[i]);
72  }
73  }
75  {
76  exit = true;
77  barrier.wait();
78  for(auto& t : threads)
79  if(t.joinable()) t.join();
80  }
81  void run()
82  {
83  barrier.wait();
84  fun(0);
85  barrier.wait();
86  }
87 };
88 
89 // runs ik on multiple threads until a stop criterion is met
90 struct IKParallel
91 {
92  IKParams params;
93  std::vector<std::unique_ptr<IKSolver>> solvers;
94  std::vector<std::vector<double>> solver_solutions;
95  std::vector<std::vector<double>> solver_temps;
96  std::vector<int> solver_success;
97  std::vector<double> solver_fitness;
98  int thread_count;
99  // std::vector<RobotFK_Fast> fk; // TODO: remove
100  double timeout;
101  bool success;
102  std::atomic<int> finished;
103  std::atomic<uint32_t> iteration_count;
104  std::vector<double> result;
105  std::unique_ptr<ParallelExecutor> par;
106  Problem problem;
107  bool enable_counter;
108  double best_fitness;
109 
110  IKParallel(const IKParams& params)
111  : params(params)
112  {
113  // solver class name
114  std::string name = params.solver_class_name;
115 
116  enable_counter = params.enable_counter;
117 
118  // create solvers
119  solvers.emplace_back(IKFactory::create(name, params));
120  thread_count = solvers.front()->concurrency();
121  if(params.thread_count) {
122  thread_count = params.thread_count;
123  }
124  while(solvers.size() < thread_count)
125  solvers.emplace_back(IKFactory::clone(solvers.front().get()));
126  for(size_t i = 0; i < thread_count; i++)
127  solvers[i]->thread_index = i;
128 
129  // while(fk.size() < thread_count) fk.emplace_back(params.robot_model);
130 
131  // init buffers
132  solver_solutions.resize(thread_count);
133  solver_temps.resize(thread_count);
134  solver_success.resize(thread_count);
135  solver_fitness.resize(thread_count);
136 
137  // create parallel executor
138  par.reset(new ParallelExecutor(thread_count, [this](size_t i) { solverthread(i); }));
139  }
140 
141  void initialize(const Problem& problem)
142  {
143  this->problem = problem;
144  // for(auto& f : fk) f.initialize(problem.tip_link_indices);
145  }
146 
147 private:
148  void solverthread(size_t i)
149  {
150  THREADPROFILER("thread", i);
151  COUNTERPROFILER("solver threads");
152 
153  // initialize ik solvers
154  {
155  BLOCKPROFILER("ik solver init");
156  solvers[i]->initialize(problem);
157  }
158 
159  // run solver iterations until solution found or timeout
160  for(size_t iteration = 0; (ros::WallTime::now().toSec() < timeout && finished == 0) || (iteration == 0 && i == 0); iteration++)
161  {
162  if(finished) break;
163 
164  // run solver for a few steps
165  solvers[i]->step();
166  iteration_count++;
167  for(int it2 = 1; it2 < 4; it2++)
168  if(ros::WallTime::now().toSec() < timeout && finished == 0) solvers[i]->step();
169 
170  if(finished) break;
171 
172  // get solution and check stop criterion
173  auto& result = solver_temps[i];
174  result = solvers[i]->getSolution();
175  auto& fk = solvers[i]->model;
176  fk.applyConfiguration(result);
177  bool success = solvers[i]->checkSolution(result, fk.getTipFrames());
178  if(success) finished = 1;
179  solver_success[i] = success;
180  solver_solutions[i] = result;
181  solver_fitness[i] = solvers[i]->computeFitness(result, fk.getTipFrames());
182 
183  if(success) break;
184  }
185 
186  finished = 1;
187 
188  for(auto& s : solvers)
189  s->canceled = true;
190  }
191 
192 public:
193  void solve()
194  {
195  BLOCKPROFILER("solve mt");
196 
197  // prepare
198  iteration_count = 0;
199  result = problem.initial_guess;
200  timeout = problem.timeout;
201  success = false;
202  finished = 0;
203  for(auto& s : solver_solutions)
204  s = problem.initial_guess;
205  for(auto& s : solver_temps)
206  s = problem.initial_guess;
207  for(auto& s : solver_success)
208  s = 0;
209  for(auto& f : solver_fitness)
210  f = DBL_MAX;
211  for(auto& s : solvers)
212  s->canceled = false;
213 
214  // run solvers
215  {
216  BLOCKPROFILER("solve mt 2");
217  par->run();
218  }
219 
220  size_t best_index = 0;
221  best_fitness = DBL_MAX;
222 
223  // if exact primary goal matches have been found ...
224  for(size_t i = 0; i < thread_count; i++)
225  {
226  if(solver_success[i])
227  {
228  double fitness;
229  if(solvers[0]->problem.secondary_goals.empty())
230  {
231  // ... and if no secondary goals have been specified,
232  // select the best result according to primary goals
233  fitness = solver_fitness[i];
234  }
235  else
236  {
237  // ... and if secondary goals have been specified,
238  // select the result that best satisfies primary and secondary goals
239  fitness = solver_fitness[i] + solvers[0]->computeSecondaryFitnessAllVariables(solver_solutions[i]);
240  }
241  if(fitness < best_fitness)
242  {
243  best_fitness = fitness;
244  best_index = i;
245  }
246  }
247  }
248 
249  // if no exact primary goal matches have been found,
250  // select best primary goal approximation
251  if(best_fitness == DBL_MAX)
252  {
253  for(size_t i = 0; i < thread_count; i++)
254  {
255  if(solver_fitness[i] < best_fitness)
256  {
257  best_fitness = solver_fitness[i];
258  best_index = i;
259  }
260  }
261  }
262 
263  if(enable_counter)
264  {
265  LOG("iterations", iteration_count);
266  }
267 
268  result = solver_solutions[best_index];
269  success = solver_success[best_index];
270  }
271 
272  double getSolutionFitness() const { return best_fitness; }
273 
274  bool getSuccess() const { return success; }
275 
276  const std::vector<double>& getSolution() const { return result; }
277 };
278 }