moveit2
The MoveIt Motion Planning Framework for ROS 2.
pr2_arm_kinematics_plugin.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2008, 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: Sachin Chitta */
36 
37 #include <geometry_msgs/msg/pose_stamped.hpp>
38 #include <kdl_parser/kdl_parser.hpp>
39 #include <tf2_kdl/tf2_kdl.hpp>
40 #include <algorithm>
41 #include <cmath>
42 
44 #include <moveit/utils/logger.hpp>
46 
47 using namespace KDL;
48 using namespace std;
49 
50 namespace pr2_arm_kinematics
51 {
52 namespace
53 {
54 rclcpp::Logger getLogger()
55 {
56  return moveit::getLogger("moveit_constraint_samplers.test.pr2_arm_kinematics_plugin");
57 }
58 } // namespace
59 
60 bool PR2ArmIKSolver::getCount(int& count, int max_count, int min_count)
61 {
62  if (count > 0)
63  {
64  if (-count >= min_count)
65  {
66  count = -count;
67  return true;
68  }
69  else if (count + 1 <= max_count)
70  {
71  count = count + 1;
72  return true;
73  }
74  else
75  {
76  return false;
77  }
78  }
79  else
80  {
81  if (1 - count <= max_count)
82  {
83  count = 1 - count;
84  return true;
85  }
86  else if (count - 1 >= min_count)
87  {
88  count = count - 1;
89  return true;
90  }
91  else
92  return false;
93  }
94 }
95 
96 PR2ArmIKSolver::PR2ArmIKSolver(const urdf::ModelInterface& robot_model, const std::string& root_frame_name,
97  const std::string& tip_frame_name, double search_discretization_angle, int free_angle)
98  : ChainIkSolverPos()
99 {
100  search_discretization_angle_ = search_discretization_angle;
101  free_angle_ = free_angle;
102  root_frame_name_ = root_frame_name;
103  active_ = pr2_arm_ik_.init(robot_model, root_frame_name, tip_frame_name);
104 }
105 
107 {
108  // TODO: move (re)allocation of any internal data structures here
109  // to react to changes in chain
110 }
111 
112 int PR2ArmIKSolver::CartToJnt(const KDL::JntArray& q_init, const KDL::Frame& p_in, KDL::JntArray& q_out)
113 {
114  const bool verbose = false;
115  Eigen::Isometry3f b = kdlToEigenMatrix(p_in);
116  std::vector<std::vector<double> > solution_ik;
117  if (free_angle_ == 0)
118  {
119  if (verbose)
120  RCLCPP_WARN(getLogger(), "Solving with %f", q_init(0));
121  pr2_arm_ik_.computeIKShoulderPan(b, q_init(0), solution_ik);
122  }
123  else
124  {
125  pr2_arm_ik_.computeIKShoulderRoll(b, q_init(2), solution_ik);
126  }
127 
128  if (solution_ik.empty())
129  return -1;
130 
131  double min_distance = 1e6;
132  int min_index = -1;
133 
134  for (int i = 0; i < static_cast<int>(solution_ik.size()); ++i)
135  {
136  if (verbose)
137  {
138  RCLCPP_WARN(getLogger(), "Solution : %d", static_cast<int>(solution_ik.size()));
139 
140  for (int j = 0; j < static_cast<int>(solution_ik[i].size()); ++j)
141  {
142  RCLCPP_WARN(getLogger(), "%d: %f", j, solution_ik[i][j]);
143  }
144  }
145  double tmp_distance = computeEuclideanDistance(solution_ik[i], q_init);
146  if (tmp_distance < min_distance)
147  {
148  min_distance = tmp_distance;
149  min_index = i;
150  }
151  }
152 
153  if (min_index > -1)
154  {
155  q_out.resize(static_cast<int>(solution_ik[min_index].size()));
156  for (int i = 0; i < static_cast<int>(solution_ik[min_index].size()); ++i)
157  {
158  q_out(i) = solution_ik[min_index][i];
159  }
160  return 1;
161  }
162  else
163  return -1;
164 }
165 
166 int PR2ArmIKSolver::cartToJntSearch(const KDL::JntArray& q_in, const KDL::Frame& p_in, KDL::JntArray& q_out,
167  double timeout)
168 {
169  const bool verbose = false;
170  KDL::JntArray q_init = q_in;
171  double initial_guess = q_init(free_angle_);
172 
173  rclcpp::Time start_time = rclcpp::Clock(RCL_ROS_TIME).now();
174  double loop_time = 0;
175  int count = 0;
176 
177  int num_positive_increments = static_cast<int>(
178  (pr2_arm_ik_.solver_info_.limits[free_angle_].max_position - initial_guess) / search_discretization_angle_);
179  int num_negative_increments = static_cast<int>(
180  (initial_guess - pr2_arm_ik_.solver_info_.limits[free_angle_].min_position) / search_discretization_angle_);
181  if (verbose)
182  {
183  RCLCPP_WARN(getLogger(), "%f %f %f %d %d \n\n", initial_guess,
184  pr2_arm_ik_.solver_info_.limits[free_angle_].max_position,
185  pr2_arm_ik_.solver_info_.limits[free_angle_].min_position, num_positive_increments,
186  num_negative_increments);
187  }
188  while (loop_time < timeout)
189  {
190  if (CartToJnt(q_init, p_in, q_out) > 0)
191  return 1;
192  if (!getCount(count, num_positive_increments, -num_negative_increments))
193  return -1;
194  q_init(free_angle_) = initial_guess + search_discretization_angle_ * count;
195  if (verbose)
196  RCLCPP_WARN(getLogger(), "%d, %f", count, q_init(free_angle_));
197  loop_time = rclcpp::Clock(RCL_ROS_TIME).now().seconds() - start_time.seconds();
198  }
199  if (loop_time >= timeout)
200  {
201  RCLCPP_WARN(getLogger(), "IK Timed out in %f seconds", timeout);
202  return TIMED_OUT;
203  }
204  else
205  {
206  RCLCPP_WARN(getLogger(), "No IK solution was found");
207  return NO_IK_SOLUTION;
208  }
209  return NO_IK_SOLUTION;
210 }
211 
212 bool getKDLChain(const urdf::ModelInterface& model, const std::string& root_name, const std::string& tip_name,
213  KDL::Chain& kdl_chain)
214 {
215  // create robot chain from root to tip
216  KDL::Tree tree;
217  if (!kdl_parser::treeFromUrdfModel(model, tree))
218  {
219  RCLCPP_ERROR(getLogger(), "Could not initialize tree object");
220  return false;
221  }
222  if (!tree.getChain(root_name, tip_name, kdl_chain))
223  {
224  RCLCPP_ERROR(getLogger(), "Could not initialize chain object for base %s tip %s", root_name.c_str(),
225  tip_name.c_str());
226  return false;
227  }
228  return true;
229 }
230 
231 Eigen::Isometry3f kdlToEigenMatrix(const KDL::Frame& p)
232 {
233  Eigen::Isometry3f b = Eigen::Isometry3f::Identity();
234  for (int i = 0; i < 3; ++i)
235  {
236  for (int j = 0; j < 3; ++j)
237  {
238  b(i, j) = p.M(i, j);
239  }
240  b(i, 3) = p.p(i);
241  }
242  return b;
243 }
244 
245 double computeEuclideanDistance(const std::vector<double>& array_1, const KDL::JntArray& array_2)
246 {
247  double distance = 0.0;
248  for (int i = 0; i < static_cast<int>(array_1.size()); ++i)
249  {
250  distance += (array_1[i] - array_2(i)) * (array_1[i] - array_2(i));
251  }
252  return std::sqrt(distance);
253 }
254 
255 void getKDLChainInfo(const KDL::Chain& chain, moveit_msgs::msg::KinematicSolverInfo& chain_info)
256 {
257  int i = 0; // segment number
258  while (i < static_cast<int>(chain.getNrOfSegments()))
259  {
260  chain_info.link_names.push_back(chain.getSegment(i).getName());
261  i++;
262  }
263 }
264 
266 {
267 }
268 
270 {
271  return active_;
272 }
273 
274 bool PR2ArmKinematicsPlugin::initialize(const rclcpp::Node::SharedPtr& node,
275  const moveit::core::RobotModel& robot_model, const std::string& group_name,
276  const std::string& base_frame, const std::vector<std::string>& tip_frames,
277  double search_discretization)
278 {
279  node_ = node;
280  storeValues(robot_model, group_name, base_frame, tip_frames, search_discretization);
281  const bool verbose = false;
282  std::string xml_string;
283  dimension_ = 7;
284 
285  RCLCPP_WARN(getLogger(), "Loading KDL Tree");
286  if (!getKDLChain(*robot_model.getURDF(), base_frame_, tip_frames_[0], kdl_chain_))
287  {
288  active_ = false;
289  RCLCPP_ERROR(getLogger(), "Could not load kdl tree");
290  }
291  jnt_to_pose_solver_ = std::make_shared<KDL::ChainFkSolverPos_recursive>(kdl_chain_);
292  free_angle_ = 2;
293 
294  pr2_arm_ik_solver_ = std::make_shared<pr2_arm_kinematics::PR2ArmIKSolver>(
295  *robot_model.getURDF(), base_frame_, tip_frames_[0], search_discretization, free_angle_);
296  if (!pr2_arm_ik_solver_->active_)
297  {
298  RCLCPP_ERROR(getLogger(), "Could not load ik");
299  active_ = false;
300  }
301  else
302  {
303  pr2_arm_ik_solver_->getSolverInfo(ik_solver_info_);
305  fk_solver_info_.joint_names = ik_solver_info_.joint_names;
306 
307  if (verbose)
308  {
309  for (const std::string& joint_name : ik_solver_info_.joint_names)
310  {
311  RCLCPP_WARN(getLogger(), "PR2Kinematics:: joint name: %s", joint_name.c_str());
312  }
313  for (const std::string& link_name : ik_solver_info_.link_names)
314  {
315  RCLCPP_WARN(getLogger(), "PR2Kinematics can solve IK for %s", link_name.c_str());
316  }
317  for (const std::string& link_name : fk_solver_info_.link_names)
318  {
319  RCLCPP_WARN(getLogger(), "PR2Kinematics can solve FK for %s", link_name.c_str());
320  }
321  RCLCPP_WARN(getLogger(), "PR2KinematicsPlugin::active for %s", group_name.c_str());
322  }
323  active_ = true;
324  }
325  return active_;
326 }
327 
328 bool PR2ArmKinematicsPlugin::getPositionIK(const geometry_msgs::msg::Pose& /*ik_pose*/,
329  const std::vector<double>& /*ik_seed_state*/,
330  std::vector<double>& /*solution*/,
331  moveit_msgs::msg::MoveItErrorCodes& /*error_code*/,
332  const kinematics::KinematicsQueryOptions& /*options*/) const
333 {
334  return false;
335 }
336 
337 bool PR2ArmKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& ik_pose,
338  const std::vector<double>& ik_seed_state, double timeout,
339  std::vector<double>& solution,
340  moveit_msgs::msg::MoveItErrorCodes& error_code,
341  const kinematics::KinematicsQueryOptions& /*options*/) const
342 {
343  if (!active_)
344  {
345  RCLCPP_ERROR(getLogger(), "kinematics not active");
346  error_code.val = error_code.PLANNING_FAILED;
347  return false;
348  }
349 
350  geometry_msgs::msg::PoseStamped ik_pose_stamped;
351  ik_pose_stamped.pose = ik_pose;
352 
353  tf2::Stamped<KDL::Frame> pose_desired;
354 
355  tf2::fromMsg(ik_pose_stamped, pose_desired);
356 
357  // Do the IK
358  KDL::JntArray jnt_pos_in;
359  KDL::JntArray jnt_pos_out;
360  jnt_pos_in.resize(dimension_);
361  for (int i = 0; i < dimension_; ++i)
362  {
363  jnt_pos_in(i) = ik_seed_state[i];
364  }
365 
366  int ik_valid = pr2_arm_ik_solver_->cartToJntSearch(jnt_pos_in, pose_desired, jnt_pos_out, timeout);
367  if (ik_valid == pr2_arm_kinematics::NO_IK_SOLUTION)
368  {
369  error_code.val = error_code.NO_IK_SOLUTION;
370  return false;
371  }
372 
373  if (ik_valid >= 0)
374  {
375  solution.resize(dimension_);
376  for (int i = 0; i < dimension_; ++i)
377  {
378  solution[i] = jnt_pos_out(i);
379  }
380  error_code.val = error_code.SUCCESS;
381  return true;
382  }
383  else
384  {
385  RCLCPP_WARN(getLogger(), "An IK solution could not be found");
386  error_code.val = error_code.NO_IK_SOLUTION;
387  return false;
388  }
389 }
390 
391 bool PR2ArmKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& /*ik_pose*/,
392  const std::vector<double>& /*ik_seed_state*/, double /*timeout*/,
393  const std::vector<double>& /*consistency_limit*/,
394  std::vector<double>& /*solution*/,
395  moveit_msgs::msg::MoveItErrorCodes& /*error_code*/,
396  const kinematics::KinematicsQueryOptions& /*options*/) const
397 {
398  return false;
399 }
400 
401 bool PR2ArmKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& /*ik_pose*/,
402  const std::vector<double>& /*ik_seed_state*/, double /*timeout*/,
403  std::vector<double>& /*solution*/,
404  const IKCallbackFn& /*solution_callback*/,
405  moveit_msgs::msg::MoveItErrorCodes& /*error_code*/,
406  const kinematics::KinematicsQueryOptions& /*options*/) const
407 {
408  return false;
409 }
410 
411 bool PR2ArmKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& /*ik_pose*/,
412  const std::vector<double>& /*ik_seed_state*/, double /*timeout*/,
413  const std::vector<double>& /*consistency_limit*/,
414  std::vector<double>& /*solution*/,
415  const IKCallbackFn& /*solution_callback*/,
416  moveit_msgs::msg::MoveItErrorCodes& /*error_code*/,
417  const kinematics::KinematicsQueryOptions& /*options*/) const
418 {
419  return false;
420 }
421 
422 bool PR2ArmKinematicsPlugin::getPositionFK(const std::vector<std::string>& /*link_names*/,
423  const std::vector<double>& /*joint_angles*/,
424  std::vector<geometry_msgs::msg::Pose>& /*poses*/) const
425 {
426  return false;
427 }
428 
429 const std::vector<std::string>& PR2ArmKinematicsPlugin::getJointNames() const
430 {
431  if (!active_)
432  {
433  RCLCPP_ERROR(getLogger(), "kinematics not active");
434  }
435  return ik_solver_info_.joint_names;
436 }
437 
438 const std::vector<std::string>& PR2ArmKinematicsPlugin::getLinkNames() const
439 {
440  if (!active_)
441  {
442  RCLCPP_ERROR(getLogger(), "kinematics not active");
443  }
444  return fk_solver_info_.link_names;
445 }
446 
447 } // namespace pr2_arm_kinematics
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)
rclcpp::Node::SharedPtr node_
std::vector< std::string > tip_frames_
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.
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
int cartToJntSearch(const KDL::JntArray &q_in, const KDL::Frame &p_in, KDL::JntArray &q_out, double timeout)
PR2ArmIK pr2_arm_ik_
The PR2 inverse kinematics solver.
int CartToJnt(const KDL::JntArray &q_init, const KDL::Frame &p_in, KDL::JntArray &q_out) override
bool active_
Indicates whether the solver has been successfully initialized.
void computeIKShoulderRoll(const Eigen::Isometry3f &g_in, double shoulder_roll_initial_guess, std::vector< std::vector< double > > &solution) const
compute IK based on an initial guess for the shoulder roll angle. h
Definition: pr2_arm_ik.cpp:475
bool init(const urdf::ModelInterface &robot_model, const std::string &root_name, const std::string &tip_name)
Initialize the solver by providing a urdf::Model and a root and tip name.
Definition: pr2_arm_ik.cpp:65
void computeIKShoulderPan(const Eigen::Isometry3f &g_in, double shoulder_pan_initial_guess, std::vector< std::vector< double > > &solution) const
compute IK based on an initial guess for the shoulder pan angle.
Definition: pr2_arm_ik.cpp:210
moveit_msgs::msg::KinematicSolverInfo solver_info_
get chain information about the arm.
Definition: pr2_arm_ik.h:166
bool isActive()
Specifies if the node is active or not.
moveit_msgs::msg::KinematicSolverInfo ik_solver_info_
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.
const std::vector< std::string > & getLinkNames() const override
Return all the link names in the order they are represented internally.
pr2_arm_kinematics::PR2ArmIKSolverPtr pr2_arm_ik_solver_
moveit_msgs::msg::KinematicSolverInfo fk_solver_info_
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.
std::shared_ptr< KDL::ChainFkSolverPos_recursive > jnt_to_pose_solver_
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....
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79
bool getKDLChain(const urdf::ModelInterface &model, const std::string &root_name, const std::string &tip_name, KDL::Chain &kdl_chain)
double distance(const urdf::Pose &transform)
Definition: pr2_arm_ik.h:55
void getKDLChainInfo(const KDL::Chain &chain, moveit_msgs::msg::KinematicSolverInfo &chain_info)
double computeEuclideanDistance(const std::vector< double > &array_1, const KDL::JntArray &array_2)
Eigen::Isometry3f kdlToEigenMatrix(const KDL::Frame &p)
FilterFn chain(const std::vector< FilterFn > &filter_functions)
A set of options for the kinematics solver.