moveit2
The MoveIt Motion Planning Framework for ROS 2.
kinematics_plugin_loader.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2012, Willow Garage, Inc.
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 Willow Garage 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 /* Author: Ioan Sucan, Dave Coleman */
36 
39 #include <pluginlib/class_loader.hpp>
40 #include <rclcpp/logger.hpp>
41 #include <rclcpp/logging.hpp>
42 #include <rclcpp/parameter.hpp>
43 #include <rclcpp/parameter_value.hpp>
44 #include <sstream>
45 #include <vector>
46 #include <map>
47 #include <memory>
48 #include <mutex>
49 
51 {
52 rclcpp::Logger LOGGER = rclcpp::get_logger("kinematics_plugin_loader");
53 
54 template <rclcpp::ParameterType ParamType>
55 rclcpp::Parameter declare_parameter(const rclcpp::Node::SharedPtr& node, const std::string& parameter_name)
56 {
57  if (!node->has_parameter(parameter_name))
58  node->declare_parameter(parameter_name, ParamType);
59  rclcpp::Parameter parameter;
60  if (!node->get_parameter(parameter_name, parameter))
61  RCLCPP_DEBUG_STREAM(LOGGER, "Parameter `" << parameter_name << "` doesn't exists");
62  return parameter;
63 }
65 {
66 public:
74  KinematicsLoaderImpl(const rclcpp::Node::SharedPtr& node, const std::string& robot_description,
75  const std::map<std::string, std::vector<std::string>>& possible_kinematics_solvers,
76  const std::map<std::string, std::vector<double>>& search_res,
77  const std::map<std::string, std::vector<std::string>>& iksolver_to_tip_links)
78  : node_(node)
79  , robot_description_(robot_description)
80  , possible_kinematics_solvers_(possible_kinematics_solvers)
81  , search_res_(search_res)
82  , iksolver_to_tip_links_(iksolver_to_tip_links)
83  {
84  try
85  {
86  kinematics_loader_ =
87  std::make_shared<pluginlib::ClassLoader<kinematics::KinematicsBase>>("moveit_core", "kinematics::"
88  "KinematicsBase");
89  }
90  catch (pluginlib::PluginlibException& e)
91  {
92  RCLCPP_ERROR(LOGGER, "Unable to construct kinematics loader. Error: %s", e.what());
93  }
94  }
95 
101  std::vector<std::string> chooseTipFrames(const moveit::core::JointModelGroup* jmg)
102  {
103  std::vector<std::string> tips;
104  std::map<std::string, std::vector<std::string>>::const_iterator ik_it = iksolver_to_tip_links_.find(jmg->getName());
105 
106  // Check if tips were loaded onto the rosparam server previously
107  if (ik_it != iksolver_to_tip_links_.end())
108  {
109  // the tip is being chosen based on a corresponding rosparam ik link
110  RCLCPP_DEBUG(LOGGER,
111  "Choosing tip frame of kinematic solver for group %s"
112  "based on values in rosparam server.",
113  jmg->getName().c_str());
114  tips = ik_it->second;
115  }
116  else
117  {
118  // get the last link in the chain
119  RCLCPP_DEBUG(LOGGER,
120  "Choosing tip frame of kinematic solver for group %s"
121  "based on last link in chain",
122  jmg->getName().c_str());
123 
124  tips.push_back(jmg->getLinkModels().back()->getName());
125  }
126 
127  // Error check
128  if (tips.empty())
129  {
130  RCLCPP_ERROR(LOGGER, "Error choosing kinematic solver tip frame(s).");
131  }
132 
133  // Debug tip choices
134  std::stringstream tip_debug;
135  tip_debug << "Planning group '" << jmg->getName() << "' has tip(s): ";
136  for (const auto& tip : tips)
137  tip_debug << tip << ", ";
138  RCLCPP_DEBUG_STREAM(LOGGER, tip_debug.str());
139 
140  return tips;
141  }
142 
143  kinematics::KinematicsBasePtr allocKinematicsSolver(const moveit::core::JointModelGroup* jmg)
144  {
145  kinematics::KinematicsBasePtr result;
146  if (!kinematics_loader_)
147  {
148  RCLCPP_ERROR(LOGGER, "Invalid kinematics loader.");
149  return result;
150  }
151  if (!jmg)
152  {
153  RCLCPP_ERROR(LOGGER, "Specified group is nullptr. Cannot allocate kinematics solver.");
154  return result;
155  }
156  const std::vector<const moveit::core::LinkModel*>& links = jmg->getLinkModels();
157  if (links.empty())
158  {
159  RCLCPP_ERROR(LOGGER, "No links specified for group '%s'. Cannot allocate kinematics solver.",
160  jmg->getName().c_str());
161  return result;
162  }
163 
164  RCLCPP_DEBUG(LOGGER, "Trying to allocate kinematics solver for group '%s'", jmg->getName().c_str());
165 
166  std::map<std::string, std::vector<std::string>>::const_iterator it =
167  possible_kinematics_solvers_.find(jmg->getName());
168  if (it == possible_kinematics_solvers_.end())
169  {
170  RCLCPP_DEBUG(LOGGER, "No kinematics solver available for this group");
171  return result;
172  }
173 
174  const std::string& base = links.front()->getParentJointModel()->getParentLinkModel() ?
175  links.front()->getParentJointModel()->getParentLinkModel()->getName() :
176  jmg->getParentModel().getModelFrame();
177 
178  // just to be sure, do not call the same pluginlib instance allocation function in parallel
179  std::scoped_lock slock(lock_);
180  for (std::size_t i = 0; !result && i < it->second.size(); ++i)
181  {
182  try
183  {
184  result = kinematics_loader_->createUniqueInstance(it->second[i]);
185  if (result)
186  {
187  // choose the tip of the IK solver
188  const std::vector<std::string> tips = chooseTipFrames(jmg);
189 
190  // choose search resolution
191  double search_res = search_res_.find(jmg->getName())->second[i]; // we know this exists, by construction
192 
193  if (!result->initialize(node_, jmg->getParentModel(), jmg->getName(),
194  (base.empty() || base[0] != '/') ? base : base.substr(1), tips, search_res))
195  {
196  RCLCPP_ERROR(LOGGER, "Kinematics solver of type '%s' could not be initialized for group '%s'",
197  it->second[i].c_str(), jmg->getName().c_str());
198  result.reset();
199  continue;
200  }
201 
202  result->setDefaultTimeout(jmg->getDefaultIKTimeout());
203  RCLCPP_DEBUG(LOGGER,
204  "Successfully allocated and initialized a kinematics solver of type '%s' with search "
205  "resolution %lf for group '%s' at address %p",
206  it->second[i].c_str(), search_res, jmg->getName().c_str(), result.get());
207  break;
208  }
209  }
210  catch (pluginlib::PluginlibException& e)
211  {
212  RCLCPP_ERROR(LOGGER, "The kinematics plugin (%s) failed to load. Error: %s", it->first.c_str(), e.what());
213  }
214  }
215 
216  if (!result)
217  {
218  RCLCPP_DEBUG(LOGGER, "No usable kinematics solver was found for this group.\n"
219  "Did you load kinematics.yaml into your node's namespace?");
220  }
221  return result;
222  }
223 
224  // cache solver between two consecutive calls
225  // first call in RobotModelLoader::loadKinematicsSolvers() is just to check suitability for jmg
226  // second call in JointModelGroup::setSolverAllocators() is to actually retrieve the instance for use
227  kinematics::KinematicsBasePtr allocKinematicsSolverWithCache(const moveit::core::JointModelGroup* jmg)
228  {
229  std::scoped_lock slock(cache_lock_);
230  kinematics::KinematicsBasePtr& cached = instances_[jmg];
231  if (cached.unique())
232  return std::move(cached); // pass on unique instance
233 
234  // create a new instance and store in instances_
235  cached = allocKinematicsSolver(jmg);
236  return cached;
237  }
238 
239  void status() const
240  {
241  for (std::map<std::string, std::vector<std::string>>::const_iterator it = possible_kinematics_solvers_.begin();
242  it != possible_kinematics_solvers_.end(); ++it)
243  {
244  for (std::size_t i = 0; i < it->second.size(); ++i)
245  {
246  RCLCPP_INFO(LOGGER, "Solver for group '%s': '%s' (search resolution = %lf)", it->first.c_str(),
247  it->second[i].c_str(), search_res_.at(it->first)[i]);
248  }
249  }
250  }
251 
252 private:
253  const rclcpp::Node::SharedPtr node_;
254  std::string robot_description_;
255  std::map<std::string, std::vector<std::string>> possible_kinematics_solvers_;
256  std::map<std::string, std::vector<double>> search_res_;
257  std::map<std::string, std::vector<std::string>> iksolver_to_tip_links_; // a map between each ik solver and a vector
258  // of custom-specified tip link(s)
259  std::shared_ptr<pluginlib::ClassLoader<kinematics::KinematicsBase>> kinematics_loader_;
260  std::map<const moveit::core::JointModelGroup*, kinematics::KinematicsBasePtr> instances_;
261  std::mutex lock_;
262  std::mutex cache_lock_;
263 };
264 
266 {
267  if (loader_)
268  loader_->status();
269  else
270  RCLCPP_INFO(LOGGER, "Loader function was never required");
271 }
272 
274 {
275  if (loader_)
276  return [&loader = *loader_](const moveit::core::JointModelGroup* jmg) {
277  return loader.allocKinematicsSolverWithCache(jmg);
278  };
279 
280  rdf_loader::RDFLoader rml(node_, robot_description_);
281  robot_description_ = rml.getRobotDescription();
282  return getLoaderFunction(rml.getSRDF());
283 }
284 
286 {
287  if (!loader_)
288  {
289  RCLCPP_DEBUG(LOGGER, "Configuring kinematics solvers");
290  groups_.clear();
291 
292  std::map<std::string, std::vector<std::string>> possible_kinematics_solvers;
293  std::map<std::string, std::vector<double>> search_res;
294  std::map<std::string, std::vector<std::string>> iksolver_to_tip_links;
295 
296  if (srdf_model)
297  {
298  const std::vector<srdf::Model::Group>& known_groups = srdf_model->getGroups();
299  if (default_search_resolution_ <= std::numeric_limits<double>::epsilon())
301 
302  if (default_solver_plugin_.empty())
303  {
304  RCLCPP_DEBUG(LOGGER, "Loading settings for kinematics solvers from the ROS param server ...");
305  // read the list of plugin names for possible kinematics solvers
306  for (const srdf::Model::Group& known_group : known_groups)
307  {
308  std::string base_param_name = known_group.name_;
309  std::string ksolver_param_name = base_param_name + ".kinematics_solver";
310  RCLCPP_DEBUG(LOGGER, "Looking for param %s ", ksolver_param_name.c_str());
311  rclcpp::Parameter ksolver_param =
312  declare_parameter<rclcpp::ParameterType::PARAMETER_STRING>(node_, ksolver_param_name);
313  if (ksolver_param.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET)
314  {
315  base_param_name = robot_description_ + "_kinematics." + known_group.name_;
316  ksolver_param_name = base_param_name + ".kinematics_solver";
317  RCLCPP_DEBUG(LOGGER, "Looking for param %s ", ksolver_param_name.c_str());
318  ksolver_param = declare_parameter<rclcpp::ParameterType::PARAMETER_STRING>(node_, ksolver_param_name);
319  }
320  if (ksolver_param.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET)
321  {
322  RCLCPP_DEBUG(LOGGER, "Found param %s", ksolver_param_name.c_str());
323 
324  const auto& ksolver = ksolver_param.as_string();
325  std::stringstream ss(ksolver);
326  bool first = true;
327  while (ss.good() && !ss.eof())
328  {
329  if (first)
330  {
331  first = false;
332  groups_.push_back(known_group.name_);
333  }
334  std::string solver;
335  ss >> solver >> std::ws;
336  possible_kinematics_solvers[known_group.name_].push_back(solver);
337  RCLCPP_DEBUG(LOGGER, "Using kinematics solver '%s' for group '%s'.", solver.c_str(),
338  known_group.name_.c_str());
339  }
340  }
341 
342  std::string ksolver_timeout_param_name = base_param_name + ".kinematics_solver_timeout";
343  rclcpp::Parameter ksolver_timeout_param =
344  declare_parameter<rclcpp::ParameterType::PARAMETER_DOUBLE>(node_, ksolver_timeout_param_name);
345  if (ksolver_timeout_param.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET)
346  {
347  if (ksolver_timeout_param.get_type() == rclcpp::ParameterType::PARAMETER_DOUBLE)
348  {
349  ik_timeout_[known_group.name_] = ksolver_timeout_param.as_double();
350  }
351  else if (ksolver_timeout_param.get_type() == rclcpp::ParameterType::PARAMETER_INTEGER)
352  { // just in case this is an int
353  ik_timeout_[known_group.name_] = ksolver_timeout_param.as_int();
354  }
355  }
356 
357  std::string ksolver_res_param_name = base_param_name + ".kinematics_solver_search_resolution";
358  rclcpp::Parameter ksolver_res_param =
359  declare_parameter<rclcpp::ParameterType::PARAMETER_DOUBLE>(node_, ksolver_res_param_name);
360  if (ksolver_res_param.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET)
361  {
362  if (ksolver_res_param.get_type() == rclcpp::ParameterType::PARAMETER_STRING)
363  {
364  const auto& ksolver_res = ksolver_res_param.as_string();
365  std::stringstream ss(ksolver_res);
366  while (ss.good() && !ss.eof())
367  {
368  double res;
369  ss >> res >> std::ws;
370  search_res[known_group.name_].push_back(res);
371  }
372  }
373  // handle the case this param is just one value and parsed as a double
374  else if (ksolver_res_param.get_type() == rclcpp::ParameterType::PARAMETER_DOUBLE)
375  {
376  search_res[known_group.name_].push_back(ksolver_res_param.as_double());
377  }
378  else if (ksolver_res_param.get_type() == rclcpp::ParameterType::PARAMETER_INTEGER)
379  {
380  search_res[known_group.name_].push_back(ksolver_res_param.as_int());
381  }
382  }
383 
384  // Allow a kinematic solver's tip links to be specified on the rosparam server as an array
385  std::string ksolver_ik_links_param_name = base_param_name + ".kinematics_solver_ik_links";
386  rclcpp::Parameter ksolver_ik_links_param =
387  declare_parameter<rclcpp::ParameterType::PARAMETER_STRING_ARRAY>(node_, ksolver_ik_links_param_name);
388  if (ksolver_ik_links_param.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET)
389  {
390  if (ksolver_ik_links_param.get_type() == rclcpp::ParameterType::PARAMETER_STRING_ARRAY)
391  {
392  const auto& ksolver_ik_links = ksolver_ik_links_param.as_string_array();
393  for (auto& ksolver_ik_link : ksolver_ik_links)
394  {
395  RCLCPP_DEBUG(LOGGER, "found tip %s for group %s", ksolver_ik_link.c_str(), known_group.name_.c_str());
396  iksolver_to_tip_links[known_group.name_].push_back(ksolver_ik_link);
397  }
398  }
399  else
400  {
401  RCLCPP_WARN(LOGGER, "the parameter '%s' needs to be of type 'STRING_ARRAY'",
402  ksolver_ik_links_param_name.c_str());
403  }
404  }
405 
406  // make sure there is a default resolution at least specified for every solver (in case it was not specified
407  // on the param server)
408  while (search_res[known_group.name_].size() < possible_kinematics_solvers[known_group.name_].size())
409  search_res[known_group.name_].push_back(default_search_resolution_);
410  }
411  }
412  else
413  {
414  RCLCPP_DEBUG(LOGGER, "Using specified default settings for kinematics solvers ...");
415  for (const srdf::Model::Group& known_group : known_groups)
416  {
417  possible_kinematics_solvers[known_group.name_].resize(1, default_solver_plugin_);
418  search_res[known_group.name_].resize(1, default_search_resolution_);
419  ik_timeout_[known_group.name_] = default_solver_timeout_;
420  groups_.push_back(known_group.name_);
421  }
422  }
423  }
424 
425  loader_ = std::make_shared<KinematicsLoaderImpl>(node_, robot_description_, possible_kinematics_solvers, search_res,
426  iksolver_to_tip_links);
427  }
428 
429  return [&loader = *loader_](const moveit::core::JointModelGroup* jmg) {
430  return loader.allocKinematicsSolverWithCache(jmg);
431  };
432 }
433 } // namespace kinematics_plugin_loader
static const double DEFAULT_SEARCH_DISCRETIZATION
std::vector< std::string > chooseTipFrames(const moveit::core::JointModelGroup *jmg)
Helper function to decide which, and how many, tip frames a planning group has.
kinematics::KinematicsBasePtr allocKinematicsSolverWithCache(const moveit::core::JointModelGroup *jmg)
KinematicsLoaderImpl(const rclcpp::Node::SharedPtr &node, const std::string &robot_description, const std::map< std::string, std::vector< std::string >> &possible_kinematics_solvers, const std::map< std::string, std::vector< double >> &search_res, const std::map< std::string, std::vector< std::string >> &iksolver_to_tip_links)
Pimpl Implementation of KinematicsLoader.
kinematics::KinematicsBasePtr allocKinematicsSolver(const moveit::core::JointModelGroup *jmg)
moveit::core::SolverAllocatorFn getLoaderFunction()
Get a function pointer that allocates and initializes a kinematics solver. If not previously called,...
double getDefaultIKTimeout() const
Get the default IK timeout.
const std::string & getName() const
Get the name of the joint group.
const std::vector< const LinkModel * > & getLinkModels() const
Get the links that are part of this joint group.
const RobotModel & getParentModel() const
Get the kinematic model this group is part of.
const std::string & getModelFrame() const
Get the frame in which the transforms for this model are computed (when using a RobotState)....
Definition: robot_model.h:94
const srdf::ModelSharedPtr & getSRDF() const
Get the parsed SRDF model.
Definition: rdf_loader.h:91
const std::string & getRobotDescription() const
Get the resolved parameter name for the robot description.
Definition: rdf_loader.h:79
rclcpp::Parameter declare_parameter(const rclcpp::Node::SharedPtr &node, const std::string &parameter_name)
std::function< kinematics::KinematicsBasePtr(const JointModelGroup *)> SolverAllocatorFn
Function type that allocates a kinematics solver for a particular group.