bio_ik
MoveIt kinematics_base plugin based on particle optimization & GA
kinematics_plugin.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 <bio_ik/goal.h>
36 
37 #include "forward_kinematics.h"
38 #include "ik_base.h"
39 #include "ik_parallel.h"
40 #include "problem.h"
41 #include "utils.h"
42 
43 #include <Eigen/Core>
44 #include <Eigen/Dense>
45 #include <Eigen/Geometry>
46 #include <kdl_parser/kdl_parser.hpp>
47 #include <moveit/kinematics_base/kinematics_base.h>
48 #include <moveit/rdf_loader/rdf_loader.h>
49 #include <pluginlib/class_list_macros.h>
50 #include <srdfdom/model.h>
51 #include <urdf/model.h>
52 #include <urdf_model/model.h>
53 
54 #include <eigen_conversions/eigen_msg.h>
55 #include <tf2_eigen/tf2_eigen.h>
56 //#include <moveit/common_planning_interface_objects/common_objects.h>
57 #include <moveit/kinematics_base/kinematics_base.h>
58 #include <moveit/robot_model/robot_model.h>
59 #include <moveit/robot_state/robot_state.h>
60 
61 #include <atomic>
62 #include <mutex>
63 #include <random>
64 #include <tuple>
65 #include <type_traits>
66 
67 #include <bio_ik/goal_types.h>
68 
69 using namespace bio_ik;
70 
71 // implement BioIKKinematicsQueryOptions
72 
73 namespace bio_ik {
74 
75 std::mutex bioIKKinematicsQueryOptionsMutex;
76 std::unordered_set<const void *> bioIKKinematicsQueryOptionsList;
77 
78 BioIKKinematicsQueryOptions::BioIKKinematicsQueryOptions()
79  : replace(false), solution_fitness(0) {
80  std::lock_guard<std::mutex> lock(bioIKKinematicsQueryOptionsMutex);
81  bioIKKinematicsQueryOptionsList.insert(this);
82 }
83 
84 BioIKKinematicsQueryOptions::~BioIKKinematicsQueryOptions() {
85  std::lock_guard<std::mutex> lock(bioIKKinematicsQueryOptionsMutex);
86  bioIKKinematicsQueryOptionsList.erase(this);
87 }
88 
89 bool isBioIKKinematicsQueryOptions(const void *ptr) {
90  std::lock_guard<std::mutex> lock(bioIKKinematicsQueryOptionsMutex);
91  return bioIKKinematicsQueryOptionsList.find(ptr) !=
92  bioIKKinematicsQueryOptionsList.end();
93 }
94 
95 const BioIKKinematicsQueryOptions *
96 toBioIKKinematicsQueryOptions(const void *ptr) {
97  if (isBioIKKinematicsQueryOptions(ptr))
98  return (const BioIKKinematicsQueryOptions *)ptr;
99  else
100  return 0;
101 }
102 
103 } // namespace bio_ik
104 
105 // BioIK Kinematics Plugin
106 
107 namespace bio_ik_kinematics_plugin {
108 
109 // Fallback for older MoveIt versions which don't support lookupParam yet
110 template <class T>
111 static void lookupParam(const std::string &param, T &val,
112  const T &default_val) {
113  ros::NodeHandle nodeHandle("~");
114  val = nodeHandle.param(param, default_val);
115 }
116 
117 struct BioIKKinematicsPlugin : kinematics::KinematicsBase {
118  std::vector<std::string> joint_names, link_names;
119  moveit::core::RobotModelConstPtr robot_model;
120  const moveit::core::JointModelGroup *joint_model_group;
121  mutable std::unique_ptr<IKParallel> ik;
122  mutable std::vector<double> state, temp;
123  mutable std::unique_ptr<moveit::core::RobotState> temp_state;
124  mutable std::vector<Frame> tipFrames;
125  RobotInfo robot_info;
126  bool enable_profiler;
127 
128  BioIKKinematicsPlugin() { enable_profiler = false; }
129 
130  virtual const std::vector<std::string> &getJointNames() const {
131  LOG_FNC();
132  return joint_names;
133  }
134 
135  virtual const std::vector<std::string> &getLinkNames() const {
136  LOG_FNC();
137  return link_names;
138  }
139 
140  virtual bool getPositionFK(const std::vector<std::string> &link_names,
141  const std::vector<double> &joint_angles,
142  std::vector<geometry_msgs::Pose> &poses) const {
143  LOG_FNC();
144  return false;
145  }
146 
147  virtual bool getPositionIK(const geometry_msgs::Pose &ik_pose,
148  const std::vector<double> &ik_seed_state,
149  std::vector<double> &solution,
150  moveit_msgs::MoveItErrorCodes &error_code,
151  const kinematics::KinematicsQueryOptions &options =
152  kinematics::KinematicsQueryOptions()) const {
153  LOG_FNC();
154  return false;
155  }
156 
157  EigenSTL::vector_Isometry3d tip_reference_frames;
158 
159  mutable std::vector<std::unique_ptr<Goal>> default_goals;
160 
161  mutable std::vector<const bio_ik::Goal *> all_goals;
162 
163  IKParams ikparams;
164 
165  mutable Problem problem;
166 
167  static moveit::core::RobotModelConstPtr
168  loadRobotModel(const std::string &robot_description) {
169  static std::map<std::string, moveit::core::RobotModelConstPtr>
170  robot_model_cache;
171  static std::mutex cache_mutex;
172  std::lock_guard<std::mutex> lock(cache_mutex);
173  if (robot_model_cache.find(robot_description) == robot_model_cache.end()) {
174  rdf_loader::RDFLoader rdf_loader(robot_description);
175  auto srdf = rdf_loader.getSRDF();
176  auto urdf_model = rdf_loader.getURDF();
177 
178  if (!urdf_model || !srdf) {
179  LOG("URDF and SRDF must be loaded for kinematics solver to work.");
180  return nullptr;
181  }
182  robot_model_cache[robot_description] = moveit::core::RobotModelConstPtr(
183  new robot_model::RobotModel(urdf_model, srdf));
184  }
185  return robot_model_cache[robot_description];
186 
187  // return
188  // moveit::planning_interface::getSharedRobotModel(robot_description);
189  }
190 
191  bool load(const moveit::core::RobotModelConstPtr &model,
192  std::string robot_description, std::string group_name) {
193  LOG_FNC();
194 
195  // LOG_VAR(robot_description);
196  // LOG_VAR(group_name);
197 
198  LOG("bio ik init", ros::this_node::getName());
199 
200  /*rdf_loader::RDFLoader rdf_loader(robot_description_);
201  auto srdf = rdf_loader.getSRDF();
202  auto urdf_model = rdf_loader.getURDF();
203 
204  if(!urdf_model || !srdf)
205  {
206  LOG("URDF and SRDF must be loaded for kinematics solver to work.");
207  return false;
208  }
209 
210  robot_model.reset(new robot_model::RobotModel(urdf_model, srdf));*/
211 
212  if (model) {
213  this->robot_model = model;
214  } else {
215  this->robot_model = loadRobotModel(robot_description);
216  }
217 
218  joint_model_group = robot_model->getJointModelGroup(group_name);
219  if (!joint_model_group) {
220  LOG("failed to get joint model group");
221  return false;
222  }
223 
224  joint_names.clear();
225 
226  for (auto *joint_model : joint_model_group->getJointModels())
227  if (joint_model->getName() != base_frame_ &&
228  joint_model->getType() != moveit::core::JointModel::UNKNOWN &&
229  joint_model->getType() != moveit::core::JointModel::FIXED)
230  joint_names.push_back(joint_model->getName());
231 
232  auto tips2 = tip_frames_;
233  joint_model_group->getEndEffectorTips(tips2);
234  if (!tips2.empty())
235  tip_frames_ = tips2;
236 
237  link_names = tip_frames_;
238 
239  // for(auto& n : joint_names) LOG("joint", n);
240  // for(auto& n : link_names) LOG("link", n);
241 
242  // bool enable_profiler;
243  lookupParam("profiler", enable_profiler, false);
244  // if(enable_profiler) Profiler::start();
245 
246  robot_info = RobotInfo(robot_model);
247 
248  ikparams.robot_model = robot_model;
249  ikparams.joint_model_group = joint_model_group;
250 
251  // initialize parameters for IKParallel
252  lookupParam("mode", ikparams.solver_class_name,
253  std::string("bio2_memetic"));
254  lookupParam("counter", ikparams.enable_counter, false);
255  lookupParam("threads", ikparams.thread_count, 0);
256 
257  // initialize parameters for Problem
258  lookupParam("dpos", ikparams.dpos, DBL_MAX);
259  lookupParam("drot", ikparams.drot, DBL_MAX);
260  lookupParam("dtwist", ikparams.dtwist, 1e-5);
261 
262  // initialize parameters for ik_evolution_1
263  lookupParam("no_wipeout", ikparams.opt_no_wipeout, false);
264  lookupParam("population_size", ikparams.population_size, 8);
265  lookupParam("elite_count", ikparams.elite_count, 4);
266  lookupParam("linear_fitness", ikparams.linear_fitness, false);
267 
268  temp_state.reset(new moveit::core::RobotState(robot_model));
269 
270  ik.reset(new IKParallel(ikparams));
271 
272  {
273 
274  BLOCKPROFILER("default ik goals");
275 
276  default_goals.clear();
277 
278  for (size_t i = 0; i < tip_frames_.size(); i++) {
279  PoseGoal *goal = new PoseGoal();
280 
281  goal->setLinkName(tip_frames_[i]);
282 
283  // LOG_VAR(goal->link_name);
284 
285  double rotation_scale = 0.5;
286 
287  lookupParam("rotation_scale", rotation_scale, rotation_scale);
288 
289  bool position_only_ik = false;
290  lookupParam("position_only_ik", position_only_ik, position_only_ik);
291  if (position_only_ik)
292  rotation_scale = 0;
293 
294  goal->setRotationScale(rotation_scale);
295 
296  default_goals.emplace_back(goal);
297  }
298 
299  {
300  double weight = 0;
301  lookupParam("center_joints_weight", weight, weight);
302  if (weight > 0.0) {
303  auto *avoid_joint_limits_goal = new bio_ik::CenterJointsGoal();
304  avoid_joint_limits_goal->setWeight(weight);
305  default_goals.emplace_back(avoid_joint_limits_goal);
306  }
307  }
308 
309  {
310  double weight = 0;
311  lookupParam("avoid_joint_limits_weight", weight, weight);
312  if (weight > 0.0) {
313  auto *avoid_joint_limits_goal = new bio_ik::AvoidJointLimitsGoal();
314  avoid_joint_limits_goal->setWeight(weight);
315  default_goals.emplace_back(avoid_joint_limits_goal);
316  }
317  }
318 
319  {
320  double weight = 0;
321  lookupParam("minimal_displacement_weight", weight, weight);
322  if (weight > 0.0) {
323  auto *minimal_displacement_goal =
324  new bio_ik::MinimalDisplacementGoal();
325  minimal_displacement_goal->setWeight(weight);
326  default_goals.emplace_back(minimal_displacement_goal);
327  }
328  }
329  }
330 
331  // LOG("init ready");
332 
333  return true;
334  }
335 
336  virtual bool initialize(const std::string &robot_description,
337  const std::string &group_name,
338  const std::string &base_frame,
339  const std::string &tip_frame,
340  double search_discretization) {
341  LOG_FNC();
342  std::vector<std::string> tip_frames;
343  tip_frames.push_back(tip_frame);
344  initialize(robot_description, group_name, base_frame, tip_frames,
345  search_discretization);
346  return true;
347  }
348 
349  virtual bool initialize(const std::string &robot_description,
350  const std::string &group_name,
351  const std::string &base_frame,
352  const std::vector<std::string> &tip_frames,
353  double search_discretization) {
354  LOG_FNC();
355  setValues(robot_description, group_name, base_frame, tip_frames,
356  search_discretization);
357  load(moveit::core::RobotModelConstPtr(), robot_description, group_name);
358  return true;
359  }
360 
361  virtual bool initialize(const moveit::core::RobotModel &robot_model,
362  const std::string &group_name,
363  const std::string &base_frame,
364  const std::vector<std::string> &tip_frames,
365  double search_discretization) {
366  LOG_FNC();
367  setValues("", group_name, base_frame, tip_frames, search_discretization);
368  load(moveit::core::RobotModelConstPtr(
369  (moveit::core::RobotModel *)&robot_model,
370  [](const moveit::core::RobotModel *robot_model) {}),
371  "", group_name);
372  return true;
373  }
374 
375  virtual bool
376  searchPositionIK(const geometry_msgs::Pose &ik_pose,
377  const std::vector<double> &ik_seed_state, double timeout,
378  std::vector<double> &solution,
379  moveit_msgs::MoveItErrorCodes &error_code,
380  const kinematics::KinematicsQueryOptions &options =
381  kinematics::KinematicsQueryOptions()) const {
382  LOG_FNC();
383  return searchPositionIK(std::vector<geometry_msgs::Pose>{ik_pose},
384  ik_seed_state, timeout, std::vector<double>(),
385  solution, IKCallbackFn(), error_code, options);
386  }
387 
388  virtual bool
389  searchPositionIK(const geometry_msgs::Pose &ik_pose,
390  const std::vector<double> &ik_seed_state, double timeout,
391  const std::vector<double> &consistency_limits,
392  std::vector<double> &solution,
393  moveit_msgs::MoveItErrorCodes &error_code,
394  const kinematics::KinematicsQueryOptions &options =
395  kinematics::KinematicsQueryOptions()) const {
396  LOG_FNC();
397  return searchPositionIK(std::vector<geometry_msgs::Pose>{ik_pose},
398  ik_seed_state, timeout, consistency_limits,
399  solution, IKCallbackFn(), error_code, options);
400  }
401 
402  virtual bool
403  searchPositionIK(const geometry_msgs::Pose &ik_pose,
404  const std::vector<double> &ik_seed_state, double timeout,
405  std::vector<double> &solution,
406  const IKCallbackFn &solution_callback,
407  moveit_msgs::MoveItErrorCodes &error_code,
408  const kinematics::KinematicsQueryOptions &options =
409  kinematics::KinematicsQueryOptions()) const {
410  LOG_FNC();
411  return searchPositionIK(std::vector<geometry_msgs::Pose>{ik_pose},
412  ik_seed_state, timeout, std::vector<double>(),
413  solution, solution_callback, error_code, options);
414  }
415 
416  virtual bool
417  searchPositionIK(const geometry_msgs::Pose &ik_pose,
418  const std::vector<double> &ik_seed_state, double timeout,
419  const std::vector<double> &consistency_limits,
420  std::vector<double> &solution,
421  const IKCallbackFn &solution_callback,
422  moveit_msgs::MoveItErrorCodes &error_code,
423  const kinematics::KinematicsQueryOptions &options =
424  kinematics::KinematicsQueryOptions()) const {
425  LOG_FNC();
426  return searchPositionIK(std::vector<geometry_msgs::Pose>{ik_pose},
427  ik_seed_state, timeout, consistency_limits,
428  solution, solution_callback, error_code, options);
429  }
430 
431  /*struct OptMod : kinematics::KinematicsQueryOptions
432  {
433  int test;
434  };*/
435 
436  virtual bool
437  searchPositionIK(const std::vector<geometry_msgs::Pose> &ik_poses,
438  const std::vector<double> &ik_seed_state, double timeout,
439  const std::vector<double> &consistency_limits,
440  std::vector<double> &solution,
441  const IKCallbackFn &solution_callback,
442  moveit_msgs::MoveItErrorCodes &error_code,
443  const kinematics::KinematicsQueryOptions &options =
444  kinematics::KinematicsQueryOptions(),
445  const moveit::core::RobotState *context_state = NULL) const {
446  double t0 = ros::WallTime::now().toSec();
447 
448  // timeout = 0.1;
449 
450  // LOG("a");
451 
452  if (enable_profiler)
453  Profiler::start();
454 
455  auto *bio_ik_options = toBioIKKinematicsQueryOptions(&options);
456 
457  LOG_FNC();
458 
459  FNPROFILER();
460 
461  // LOG(typeid(options).name());
462  // LOG(((OptMod*)&options)->test);
463 
464  // get variable default positions / context state
465  state.resize(robot_model->getVariableCount());
466  if (context_state)
467  for (size_t i = 0; i < robot_model->getVariableCount(); i++)
468  state[i] = context_state->getVariablePositions()[i];
469  else
470  robot_model->getVariableDefaultPositions(state);
471 
472  // overwrite used variables with seed state
473  solution = ik_seed_state;
474  {
475  int i = 0;
476  for (auto &joint_name : getJointNames()) {
477  auto *joint_model = robot_model->getJointModel(joint_name);
478  if (!joint_model)
479  continue;
480  for (size_t vi = 0; vi < joint_model->getVariableCount(); vi++)
481  state.at(joint_model->getFirstVariableIndex() + vi) =
482  solution.at(i++);
483  }
484  }
485 
486  if (!bio_ik_options || !bio_ik_options->replace) {
487  // transform tips to baseframe
488  tipFrames.clear();
489  for (size_t i = 0; i < ik_poses.size(); i++) {
490  Eigen::Isometry3d p, r;
491  tf::poseMsgToEigen(ik_poses[i], p);
492  if (context_state) {
493  r = context_state->getGlobalLinkTransform(getBaseFrame());
494  } else {
495  if (i == 0)
496  temp_state->setToDefaultValues();
497  r = temp_state->getGlobalLinkTransform(getBaseFrame());
498  }
499  tipFrames.emplace_back(r * p);
500  }
501  }
502 
503  // init ik
504 
505  problem.timeout = t0 + timeout;
506  problem.initial_guess = state;
507 
508  // for(auto& v : state) LOG("var", &v - &state.front(), v);
509 
510  // problem.tip_objectives = tipFrames;
511 
512  /*for(size_t i = 0; i < problem.goals.size(); i++)
513  {
514  problem.goals[i].frame = tipFrames[i];
515  }*/
516 
517  // LOG("---");
518 
519  /*{
520  BLOCKPROFILER("ik goals");
521  std::vector<std::unique_ptr<Goal>> goals;
522  for(size_t i = 0; i < tip_frames_.size(); i++)
523  {
524  //if(rand() % 2) break;
525  PoseGoal* goal = new PoseGoal();
526  goal->link_name = tip_frames_[i];
527  goal->position = tipFrames[i].pos;
528  goal->orientation = tipFrames[i].rot;
529  goals.emplace_back(goal);
530  //if(rand() % 20) break;
531  }
532  //std::random_shuffle(goals.begin(), goals.end());
533  //LOG_VAR(goals.size());
534  setRequestGoals(problem, goals, ikparams);
535  }*/
536 
537  {
538 
539  if (!bio_ik_options || !bio_ik_options->replace) {
540  for (size_t i = 0; i < tip_frames_.size(); i++) {
541  auto *goal = (PoseGoal *)default_goals[i].get();
542  goal->setPosition(tipFrames[i].pos);
543  goal->setOrientation(tipFrames[i].rot);
544  }
545  }
546 
547  all_goals.clear();
548 
549  if (!bio_ik_options || !bio_ik_options->replace)
550  for (auto &goal : default_goals)
551  all_goals.push_back(goal.get());
552 
553  if (bio_ik_options)
554  for (auto &goal : bio_ik_options->goals)
555  all_goals.push_back(goal.get());
556 
557  {
558  BLOCKPROFILER("problem init");
559  problem.initialize(ikparams.robot_model, ikparams.joint_model_group,
560  ikparams, all_goals, bio_ik_options);
561  // problem.setGoals(default_goals, ikparams);
562  }
563  }
564 
565  {
566  BLOCKPROFILER("ik init");
567  ik->initialize(problem);
568  }
569 
570  // run ik solver
571  {
572  BLOCKPROFILER("ik_solve");
573  ik->solve();
574  }
575 
576  // get solution
577  state = ik->getSolution();
578 
579  // wrap angles
580  for (auto ivar : problem.active_variables) {
581  auto v = state[ivar];
582  if (robot_info.isRevolute(ivar) &&
583  robot_model->getMimicJointModels().empty()) {
584  auto r = problem.initial_guess[ivar];
585  auto lo = robot_info.getMin(ivar);
586  auto hi = robot_info.getMax(ivar);
587 
588  // move close to initial guess
589  if (r < v - M_PI || r > v + M_PI) {
590  v -= r;
591  v /= (2 * M_PI);
592  v += 0.5;
593  v -= std::floor(v);
594  v -= 0.5;
595  v *= (2 * M_PI);
596  v += r;
597  }
598 
599  // wrap at joint limits
600  if (v > hi)
601  v -= std::ceil(std::max(0.0, v - hi) / (2 * M_PI)) * (2 * M_PI);
602  if (v < lo)
603  v += std::ceil(std::max(0.0, lo - v) / (2 * M_PI)) * (2 * M_PI);
604 
605  // clamp at edges
606  if (v < lo)
607  v = lo;
608  if (v > hi)
609  v = hi;
610  }
611  state[ivar] = v;
612  }
613 
614  // wrap angles
615  robot_model->enforcePositionBounds(state.data());
616 
617  // map result to jointgroup variables
618  {
619  solution.clear();
620  for (auto &joint_name : getJointNames()) {
621  auto *joint_model = robot_model->getJointModel(joint_name);
622  if (!joint_model)
623  continue;
624  for (size_t vi = 0; vi < joint_model->getVariableCount(); vi++)
625  solution.push_back(
626  state.at(joint_model->getFirstVariableIndex() + vi));
627  }
628  }
629 
630  // set solution fitness
631  if (bio_ik_options) {
632  bio_ik_options->solution_fitness = ik->getSolutionFitness();
633  }
634 
635  // return an error if an accurate solution was requested, but no accurate
636  // solution was found
637  if (!ik->getSuccess() && !options.return_approximate_solution) {
638  error_code.val = error_code.NO_IK_SOLUTION;
639  return false;
640  }
641 
642  // callback?
643  if (!solution_callback.empty()) {
644  // run callback
645  solution_callback(ik_poses.front(), solution, error_code);
646 
647  // return success if callback has accepted the solution
648  return error_code.val == error_code.SUCCESS;
649  } else {
650  // return success
651  error_code.val = error_code.SUCCESS;
652  return true;
653  }
654  }
655 
656  virtual bool supportsGroup(const moveit::core::JointModelGroup *jmg,
657  std::string *error_text_out = 0) const {
658  LOG_FNC();
659  // LOG_VAR(jmg->getName());
660  return true;
661  }
662 };
663 } // namespace bio_ik_kinematics_plugin
664 
665 // register plugin
666 
667 #undef LOG
668 #undef ERROR
670  kinematics::KinematicsBase);