moveit2
The MoveIt Motion Planning Framework for ROS 2.
lma_kinematics_plugin.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2016, CRI group, NTU, Singapore
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 CRI group 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: Francisco Suarez-Ruiz */
36 
38 #include <kdl/chainfksolverpos_recursive.hpp>
39 #include <kdl/chainiksolverpos_lma.hpp>
40 
41 #include <tf2_kdl/tf2_kdl.hpp>
42 #include <kdl_parser/kdl_parser.hpp>
43 #include <kdl/frames_io.hpp>
44 #include <kdl/kinfam_io.hpp>
45 
46 // register as a KinematicsBase implementation
47 #include <class_loader/class_loader.hpp>
49 
50 namespace lma_kinematics_plugin
51 {
52 static const rclcpp::Logger LOGGER = rclcpp::get_logger("moveit_lma_kinematics_plugin.lma_kinematics_plugin");
53 
55 {
56 }
57 
58 void LMAKinematicsPlugin::getRandomConfiguration(Eigen::VectorXd& jnt_array) const
59 {
60  state_->setToRandomPositions(joint_model_group_);
61  state_->copyJointGroupPositions(joint_model_group_, &jnt_array[0]);
62 }
63 
64 void LMAKinematicsPlugin::getRandomConfiguration(const Eigen::VectorXd& seed_state,
65  const std::vector<double>& consistency_limits,
66  Eigen::VectorXd& jnt_array) const
67 {
68  joint_model_group_->getVariableRandomPositionsNearBy(state_->getRandomNumberGenerator(), &jnt_array[0],
69  &seed_state[0], consistency_limits);
70 }
71 
72 bool LMAKinematicsPlugin::checkConsistency(const Eigen::VectorXd& seed_state,
73  const std::vector<double>& consistency_limits,
74  const Eigen::VectorXd& solution) const
75 {
76  for (std::size_t i = 0; i < dimension_; ++i)
77  if (fabs(seed_state(i) - solution(i)) > consistency_limits[i])
78  return false;
79  return true;
80 }
81 
82 bool LMAKinematicsPlugin::initialize(const rclcpp::Node::SharedPtr& node, const moveit::core::RobotModel& robot_model,
83  const std::string& group_name, const std::string& base_frame,
84  const std::vector<std::string>& tip_frames, double search_discretization)
85 {
86  node_ = node;
87  storeValues(robot_model, group_name, base_frame, tip_frames, search_discretization);
88  joint_model_group_ = robot_model_->getJointModelGroup(group_name);
89  if (!joint_model_group_)
90  return false;
91 
92  if (!joint_model_group_->isChain())
93  {
94  RCLCPP_ERROR(LOGGER, "Group '%s' is not a chain", group_name.c_str());
95  return false;
96  }
97  if (!joint_model_group_->isSingleDOFJoints())
98  {
99  RCLCPP_ERROR(LOGGER, "Group '%s' includes joints that have more than 1 DOF", group_name.c_str());
100  return false;
101  }
102 
103  KDL::Tree kdl_tree;
104 
105  if (!kdl_parser::treeFromUrdfModel(*robot_model.getURDF(), kdl_tree))
106  {
107  RCLCPP_ERROR(LOGGER, "Could not initialize tree object");
108  return false;
109  }
110  if (!kdl_tree.getChain(base_frame_, getTipFrame(), kdl_chain_))
111  {
112  RCLCPP_ERROR(LOGGER, "Could not initialize chain object");
113  return false;
114  }
115 
116  for (const moveit::core::JointModel* jm : joint_model_group_->getJointModels())
117  {
118  if (jm->getType() == moveit::core::JointModel::REVOLUTE || jm->getType() == moveit::core::JointModel::PRISMATIC)
119  {
120  joints_.push_back(jm);
121  joint_names_.push_back(jm->getName());
122  }
123  }
124  dimension_ = joints_.size();
125 
126  // Get Solver Parameters
127  lookupParam(node_, "max_solver_iterations", max_solver_iterations_, 500);
128  lookupParam(node_, "epsilon", epsilon_, 1e-5);
129  lookupParam(node_, "orientation_vs_position", orientation_vs_position_weight_, 0.01);
130 
131  bool position_ik;
132  lookupParam(node_, "position_only_ik", position_ik, false);
133  if (position_ik) // position_only_ik overrules orientation_vs_position
134  orientation_vs_position_weight_ = 0.0;
135  if (orientation_vs_position_weight_ == 0.0)
136  RCLCPP_INFO(LOGGER, "Using position only ik");
137 
138  // Setup the joint state groups that we need
139  state_ = std::make_shared<moveit::core::RobotState>(robot_model_);
140 
141  fk_solver_ = std::make_unique<KDL::ChainFkSolverPos_recursive>(kdl_chain_);
142 
143  initialized_ = true;
144  RCLCPP_DEBUG(LOGGER, "LMA solver initialized");
145  return true;
146 }
147 
148 bool LMAKinematicsPlugin::timedOut(const rclcpp::Time& start_time, double duration) const
149 {
150  return ((node_->now() - start_time).seconds() >= duration);
151 }
152 
153 bool LMAKinematicsPlugin::getPositionIK(const geometry_msgs::msg::Pose& ik_pose,
154  const std::vector<double>& ik_seed_state, std::vector<double>& solution,
155  moveit_msgs::msg::MoveItErrorCodes& error_code,
157 {
158  std::vector<double> consistency_limits;
159 
160  // limit search to a single attempt by setting a timeout of zero
161  return searchPositionIK(ik_pose, ik_seed_state, 0.0, consistency_limits, solution, IKCallbackFn(), error_code,
162  options);
163 }
164 
165 bool LMAKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& ik_pose,
166  const std::vector<double>& ik_seed_state, double timeout,
167  std::vector<double>& solution,
168  moveit_msgs::msg::MoveItErrorCodes& error_code,
170 {
171  std::vector<double> consistency_limits;
172 
173  return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, IKCallbackFn(), error_code,
174  options);
175 }
176 
177 bool LMAKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& ik_pose,
178  const std::vector<double>& ik_seed_state, double timeout,
179  const std::vector<double>& consistency_limits, std::vector<double>& solution,
180  moveit_msgs::msg::MoveItErrorCodes& error_code,
182 {
183  return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, IKCallbackFn(), error_code,
184  options);
185 }
186 
187 bool LMAKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& ik_pose,
188  const std::vector<double>& ik_seed_state, double timeout,
189  std::vector<double>& solution, const IKCallbackFn& solution_callback,
190  moveit_msgs::msg::MoveItErrorCodes& error_code,
192 {
193  std::vector<double> consistency_limits;
194  return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, solution_callback, error_code,
195  options);
196 }
197 
198 void LMAKinematicsPlugin::harmonize(Eigen::VectorXd& values) const
199 {
200  size_t i = 0;
201  for (auto* jm : joints_)
202  jm->harmonizePosition(&values[i++]);
203 }
204 
205 bool LMAKinematicsPlugin::obeysLimits(const Eigen::VectorXd& values) const
206 {
207  size_t i = 0;
208  for (const auto& jm : joints_)
209  if (!jm->satisfiesPositionBounds(&values[i++]))
210  return false;
211  return true;
212 }
213 
214 bool LMAKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& ik_pose,
215  const std::vector<double>& ik_seed_state, double timeout,
216  const std::vector<double>& consistency_limits, std::vector<double>& solution,
217  const IKCallbackFn& solution_callback,
218  moveit_msgs::msg::MoveItErrorCodes& error_code,
220 {
221  rclcpp::Time start_time = node_->now();
222  if (!initialized_)
223  {
224  RCLCPP_ERROR(LOGGER, "kinematics solver not initialized");
225  error_code.val = error_code.NO_IK_SOLUTION;
226  return false;
227  }
228 
229  if (ik_seed_state.size() != dimension_)
230  {
231  RCLCPP_ERROR(LOGGER, "Seed state must have size %d instead of size %zu", dimension_, ik_seed_state.size());
232  error_code.val = error_code.NO_IK_SOLUTION;
233  return false;
234  }
235 
236  if (!consistency_limits.empty() && consistency_limits.size() != dimension_)
237  {
238  RCLCPP_ERROR_STREAM(LOGGER, "Consistency limits be empty or must have size " << dimension_ << " instead of size "
239  << consistency_limits.size());
240  error_code.val = error_code.NO_IK_SOLUTION;
241  return false;
242  }
243 
244  Eigen::Matrix<double, 6, 1> cartesian_weights;
245  cartesian_weights(0) = 1;
246  cartesian_weights(1) = 1;
247  cartesian_weights(2) = 1;
248  cartesian_weights(3) = orientation_vs_position_weight_;
249  cartesian_weights(4) = orientation_vs_position_weight_;
250  cartesian_weights(5) = orientation_vs_position_weight_;
251 
252  KDL::JntArray jnt_seed_state(dimension_);
253  KDL::JntArray jnt_pos_in(dimension_);
254  KDL::JntArray jnt_pos_out(dimension_);
255  jnt_seed_state.data = Eigen::Map<const Eigen::VectorXd>(ik_seed_state.data(), ik_seed_state.size());
256  jnt_pos_in = jnt_seed_state;
257 
258  KDL::ChainIkSolverPos_LMA ik_solver_pos(kdl_chain_, cartesian_weights, epsilon_, max_solver_iterations_);
259  solution.resize(dimension_);
260 
261  KDL::Frame pose_desired;
262  tf2::fromMsg(ik_pose, pose_desired);
263 
264  RCLCPP_DEBUG_STREAM(LOGGER, "searchPositionIK2: Position request pose is "
265  << ik_pose.position.x << " " << ik_pose.position.y << " " << ik_pose.position.z << " "
266  << ik_pose.orientation.x << " " << ik_pose.orientation.y << " "
267  << ik_pose.orientation.z << " " << ik_pose.orientation.w);
268  unsigned int attempt = 0;
269  do
270  {
271  ++attempt;
272  if (attempt > 1) // randomly re-seed after first attempt
273  {
274  if (!consistency_limits.empty())
275  getRandomConfiguration(jnt_seed_state.data, consistency_limits, jnt_pos_in.data);
276  else
277  getRandomConfiguration(jnt_pos_in.data);
278  RCLCPP_DEBUG_STREAM(LOGGER, "New random configuration (" << attempt << "): " << jnt_pos_in);
279  }
280 
281  int ik_valid = ik_solver_pos.CartToJnt(jnt_pos_in, pose_desired, jnt_pos_out);
282  if (ik_valid == 0 || options.return_approximate_solution) // found acceptable solution
283  {
284  harmonize(jnt_pos_out.data);
285  if (!consistency_limits.empty() && !checkConsistency(jnt_seed_state.data, consistency_limits, jnt_pos_out.data))
286  continue;
287  if (!obeysLimits(jnt_pos_out.data))
288  continue;
289 
290  Eigen::Map<Eigen::VectorXd>(solution.data(), solution.size()) = jnt_pos_out.data;
291  if (solution_callback)
292  {
293  solution_callback(ik_pose, solution, error_code);
294  if (error_code.val != error_code.SUCCESS)
295  continue;
296  }
297 
298  // solution passed consistency check and solution callback
299  error_code.val = error_code.SUCCESS;
300  RCLCPP_DEBUG_STREAM(LOGGER, "Solved after " << (node_->now() - start_time).seconds() << " < " << timeout
301  << "s and " << attempt << " attempts");
302  return true;
303  }
304  } while (!timedOut(start_time, timeout));
305 
306  RCLCPP_DEBUG_STREAM(LOGGER, "IK timed out after " << (node_->now() - start_time).seconds() << " > " << timeout
307  << "s and " << attempt << " attempts");
308  error_code.val = error_code.TIMED_OUT;
309  return false;
310 }
311 
312 bool LMAKinematicsPlugin::getPositionFK(const std::vector<std::string>& link_names,
313  const std::vector<double>& joint_angles,
314  std::vector<geometry_msgs::msg::Pose>& poses) const
315 {
316  if (!initialized_)
317  {
318  RCLCPP_ERROR(LOGGER, "kinematics solver not initialized");
319  return false;
320  }
321  poses.resize(link_names.size());
322  if (joint_angles.size() != dimension_)
323  {
324  RCLCPP_ERROR(LOGGER, "Joint angles vector must have size: %d", dimension_);
325  return false;
326  }
327 
328  KDL::Frame p_out;
329  KDL::JntArray jnt_pos_in(dimension_);
330  jnt_pos_in.data = Eigen::Map<const Eigen::VectorXd>(joint_angles.data(), joint_angles.size());
331 
332  bool valid = true;
333  for (unsigned int i = 0; i < poses.size(); ++i)
334  {
335  if (fk_solver_->JntToCart(jnt_pos_in, p_out) >= 0)
336  {
337  poses[i] = tf2::toMsg(p_out);
338  }
339  else
340  {
341  RCLCPP_ERROR(LOGGER, "Could not compute FK for %s", link_names[i].c_str());
342  valid = false;
343  }
344  }
345  return valid;
346 }
347 
348 const std::vector<std::string>& LMAKinematicsPlugin::getJointNames() const
349 {
350  return joint_names_;
351 }
352 
353 const std::vector<std::string>& LMAKinematicsPlugin::getLinkNames() const
354 {
355  return getTipFrames();
356 }
357 
358 } // namespace lma_kinematics_plugin
Provides an interface for kinematics solvers.
void storeValues(const moveit::core::RobotModel &robot_model, const std::string &group_name, const std::string &base_frame, const std::vector< std::string > &tip_frames, double search_discretization)
virtual const std::string & getTipFrame() const
Return the name of the tip frame of the chain on which the solver is operating. This is usually a lin...
static const rclcpp::Logger LOGGER
bool lookupParam(const rclcpp::Node::SharedPtr &node, const std::string &param, T &val, const T &default_val) const
Enables kinematics plugins access to parameters that are defined for the private namespace and inside...
virtual const std::vector< std::string > & getTipFrames() const
Return the names of the tip frames of the kinematic tree on which the solver is operating....
std::function< void(const geometry_msgs::msg::Pose &, const std::vector< double > &, moveit_msgs::msg::MoveItErrorCodes &)> IKCallbackFn
Signature for a callback to validate an IK solution. Typically used for collision checking.
moveit::core::RobotModelConstPtr robot_model_
Implementation of kinematics using Levenberg-Marquardt (LMA) solver from KDL. This version supports a...
const std::vector< std::string > & getJointNames() const override
Return all the joint names in the order they are used internally.
bool initialize(const rclcpp::Node::SharedPtr &node, const moveit::core::RobotModel &robot_model, const std::string &group_name, const std::string &base_frame, const std::vector< std::string > &tip_frames, double search_discretization) override
Initialization function for the kinematics, for use with kinematic chain IK solvers.
bool getPositionIK(const geometry_msgs::msg::Pose &ik_pose, const std::vector< double > &ik_seed_state, std::vector< double > &solution, moveit_msgs::msg::MoveItErrorCodes &error_code, const kinematics::KinematicsQueryOptions &options=kinematics::KinematicsQueryOptions()) const override
Given a desired pose of the end-effector, compute the joint angles to reach it.
bool getPositionFK(const std::vector< std::string > &link_names, const std::vector< double > &joint_angles, std::vector< geometry_msgs::msg::Pose > &poses) const override
Given a set of joint angles and a set of links, compute their pose.
bool searchPositionIK(const geometry_msgs::msg::Pose &ik_pose, const std::vector< double > &ik_seed_state, double timeout, std::vector< double > &solution, moveit_msgs::msg::MoveItErrorCodes &error_code, const kinematics::KinematicsQueryOptions &options=kinematics::KinematicsQueryOptions()) const override
Given a desired pose of the end-effector, search for the joint angles required to reach it....
const std::vector< std::string > & getLinkNames() const override
Return all the link names in the order they are represented internally.
bool isChain() const
Check if this group is a linear chain.
void getVariableRandomPositionsNearBy(random_numbers::RandomNumberGenerator &rng, double *values, const double *near, const double distance) const
Compute random values for the state of the joint group.
const std::vector< const JointModel * > & getJointModels() const
Get all the joints in this group (including fixed and mimic joints).
bool isSingleDOFJoints() const
Return true if the group consists only of joints that are single DOF.
A joint from the robot. Models the transform that this joint applies in the kinematic chain....
Definition: joint_model.h:117
Definition of a kinematic model. This class is not thread safe, however multiple instances can be cre...
Definition: robot_model.h:76
const urdf::ModelInterfaceSharedPtr & getURDF() const
Get the parsed URDF model.
Definition: robot_model.h:106
A set of options for the kinematics solver.
CLASS_LOADER_REGISTER_CLASS(trajopt_interface::TrajOptPlannerManager, planning_interface::PlannerManager)