moveit2
The MoveIt Motion Planning Framework for ROS 2.
srv_kinematics_plugin.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2014, JSK, The University of Tokyo.
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 JSK, The University of Tokyo 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: Dave Coleman, Masaki Murooka */
36 
37 #include <moveit_msgs/srv/get_position_ik.hpp>
39 #include <class_loader/class_loader.hpp>
41 #include <iterator>
42 #include <moveit/utils/logger.hpp>
43 
44 // Eigen
45 #include <Eigen/Core>
46 #include <Eigen/Geometry>
47 
48 // register SRVKinematics as a KinematicsBase implementation
50 
51 namespace srv_kinematics_plugin
52 {
53 namespace
54 {
55 rclcpp::Logger getLogger()
56 {
57  return moveit::getLogger("srv_kinematics_plugin");
58 }
59 } // namespace
60 
62 {
63 }
64 
65 bool SrvKinematicsPlugin::initialize(const rclcpp::Node::SharedPtr& node, const moveit::core::RobotModel& robot_model,
66  const std::string& group_name, const std::string& base_frame,
67  const std::vector<std::string>& tip_frames, double search_discretization)
68 {
69  node_ = node;
70  bool debug = false;
71 
72  RCLCPP_INFO(getLogger(), "SrvKinematicsPlugin initializing");
73 
74  // Get Solver Parameters
75  std::string kinematics_param_prefix = "robot_description_kinematics." + group_name;
76  param_listener_ = std::make_shared<srv_kinematics::ParamListener>(node, kinematics_param_prefix);
77  params_ = param_listener_->get_params();
78 
79  storeValues(robot_model, group_name, base_frame, tip_frames, search_discretization);
80  joint_model_group_ = robot_model_->getJointModelGroup(group_name);
81  if (!joint_model_group_)
82  return false;
83 
84  if (debug)
85  {
86  std::cout << "Joint Model Variable Names: ------------------------------------------- \n ";
87  const std::vector<std::string> jm_names = joint_model_group_->getVariableNames();
88  std::copy(jm_names.begin(), jm_names.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
89  std::cout << '\n';
90  }
91 
92  // Get the dimension of the planning group
93  dimension_ = joint_model_group_->getVariableCount();
94  RCLCPP_INFO_STREAM(getLogger(), "Dimension planning group '"
95  << group_name << "': " << dimension_
96  << ". Active Joints Models: " << joint_model_group_->getActiveJointModels().size()
97  << ". Mimic Joint Models: " << joint_model_group_->getMimicJointModels().size());
98 
99  // Copy joint names
100  for (std::size_t i = 0; i < joint_model_group_->getJointModels().size(); ++i)
101  {
102  ik_group_info_.joint_names.push_back(joint_model_group_->getJointModelNames()[i]);
103  }
104 
105  if (debug)
106  {
107  RCLCPP_ERROR(getLogger(), "tip links available:");
108  std::copy(tip_frames_.begin(), tip_frames_.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
109  }
110 
111  // Make sure all the tip links are in the link_names vector
112  for (const std::string& tip_frame : tip_frames_)
113  {
114  if (!joint_model_group_->hasLinkModel(tip_frame))
115  {
116  RCLCPP_ERROR(getLogger(), "Could not find tip name '%s' in joint group '%s'", tip_frame.c_str(),
117  group_name.c_str());
118  return false;
119  }
120  ik_group_info_.link_names.push_back(tip_frame);
121  }
122 
123  // Setup the joint state groups that we need
124  robot_state_ = std::make_shared<moveit::core::RobotState>(robot_model_);
125  robot_state_->setToDefaultValues();
126 
127  // Create the ROS2 service client
128  RCLCPP_DEBUG(getLogger(), "IK Service client topic : %s", params_.kinematics_solver_service_name.c_str());
129  ik_service_client_ = node_->create_client<moveit_msgs::srv::GetPositionIK>(params_.kinematics_solver_service_name);
130 
131  if (!ik_service_client_->wait_for_service(std::chrono::seconds(1)))
132  { // wait 0.1 seconds, blocking
133  RCLCPP_WARN_STREAM(getLogger(),
134  "Unable to connect to ROS service client with name: " << ik_service_client_->get_service_name());
135  }
136  else
137  {
138  RCLCPP_INFO_STREAM(getLogger(),
139  "Service client started with ROS service name: " << ik_service_client_->get_service_name());
140  }
141 
142  active_ = true;
143  RCLCPP_DEBUG(getLogger(), "ROS service-based kinematics solver initialized");
144  return true;
145 }
146 
147 bool SrvKinematicsPlugin::setRedundantJoints(const std::vector<unsigned int>& redundant_joints)
148 {
149  if (num_possible_redundant_joints_ < 0)
150  {
151  RCLCPP_ERROR(getLogger(), "This group cannot have redundant joints");
152  return false;
153  }
154  if (int(redundant_joints.size()) > num_possible_redundant_joints_)
155  {
156  RCLCPP_ERROR(getLogger(), "This group can only have %d redundant joints", num_possible_redundant_joints_);
157  return false;
158  }
159 
160  return true;
161 }
162 
163 bool SrvKinematicsPlugin::isRedundantJoint(unsigned int index) const
164 {
165  for (unsigned int redundant_joint_indice : redundant_joint_indices_)
166  {
167  if (redundant_joint_indice == index)
168  return true;
169  }
170  return false;
171 }
172 
173 int SrvKinematicsPlugin::getJointIndex(const std::string& name) const
174 {
175  for (unsigned int i = 0; i < ik_group_info_.joint_names.size(); ++i)
176  {
177  if (ik_group_info_.joint_names[i] == name)
178  return i;
179  }
180  return -1;
181 }
182 
183 bool SrvKinematicsPlugin::timedOut(const rclcpp::Time& start_time, double duration) const
184 {
185  return ((node_->now() - start_time).seconds() >= duration);
186 }
187 
188 bool SrvKinematicsPlugin::getPositionIK(const geometry_msgs::msg::Pose& ik_pose,
189  const std::vector<double>& ik_seed_state, std::vector<double>& solution,
190  moveit_msgs::msg::MoveItErrorCodes& error_code,
192 {
193  std::vector<double> consistency_limits;
194 
195  return searchPositionIK(ik_pose, ik_seed_state, default_timeout_, consistency_limits, solution, IKCallbackFn(),
196  error_code, options);
197 }
198 
199 bool SrvKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& ik_pose,
200  const std::vector<double>& ik_seed_state, double timeout,
201  std::vector<double>& solution,
202  moveit_msgs::msg::MoveItErrorCodes& error_code,
204 {
205  std::vector<double> consistency_limits;
206 
207  return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, IKCallbackFn(), error_code,
208  options);
209 }
210 
211 bool SrvKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& ik_pose,
212  const std::vector<double>& ik_seed_state, double timeout,
213  const std::vector<double>& consistency_limits, std::vector<double>& solution,
214  moveit_msgs::msg::MoveItErrorCodes& error_code,
216 {
217  return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, IKCallbackFn(), error_code,
218  options);
219 }
220 
221 bool SrvKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& ik_pose,
222  const std::vector<double>& ik_seed_state, double timeout,
223  std::vector<double>& solution, const IKCallbackFn& solution_callback,
224  moveit_msgs::msg::MoveItErrorCodes& error_code,
226 {
227  std::vector<double> consistency_limits;
228  return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, solution_callback, error_code,
229  options);
230 }
231 
232 bool SrvKinematicsPlugin::searchPositionIK(const geometry_msgs::msg::Pose& ik_pose,
233  const std::vector<double>& ik_seed_state, double timeout,
234  const std::vector<double>& consistency_limits, std::vector<double>& solution,
235  const IKCallbackFn& solution_callback,
236  moveit_msgs::msg::MoveItErrorCodes& error_code,
238 {
239  // Convert single pose into a vector of one pose
240  std::vector<geometry_msgs::msg::Pose> ik_poses;
241  ik_poses.push_back(ik_pose);
242 
243  return searchPositionIK(ik_poses, ik_seed_state, timeout, consistency_limits, solution, solution_callback, error_code,
244  options);
245 }
246 
247 bool SrvKinematicsPlugin::searchPositionIK(const std::vector<geometry_msgs::msg::Pose>& ik_poses,
248  const std::vector<double>& ik_seed_state, double /*timeout*/,
249  const std::vector<double>& /*consistency_limits*/,
250  std::vector<double>& solution, const IKCallbackFn& solution_callback,
251  moveit_msgs::msg::MoveItErrorCodes& error_code,
252  const kinematics::KinematicsQueryOptions& /*options*/,
253  const moveit::core::RobotState* /*context_state*/) const
254 {
255  // Check if active
256  if (!active_)
257  {
258  RCLCPP_ERROR(getLogger(), "kinematics not active");
259  error_code.val = error_code.NO_IK_SOLUTION;
260  return false;
261  }
262 
263  // Check if seed state correct
264  if (ik_seed_state.size() != dimension_)
265  {
266  RCLCPP_ERROR_STREAM(getLogger(),
267  "Seed state must have size " << dimension_ << " instead of size " << ik_seed_state.size());
268  error_code.val = error_code.NO_IK_SOLUTION;
269  return false;
270  }
271 
272  // Check that we have the same number of poses as tips
273  if (tip_frames_.size() != ik_poses.size())
274  {
275  RCLCPP_ERROR_STREAM(getLogger(), "Mismatched number of pose requests (" << ik_poses.size() << ") to tip frames ("
276  << tip_frames_.size()
277  << ") in searchPositionIK");
278  error_code.val = error_code.NO_IK_SOLUTION;
279  return false;
280  }
281 
282  // Create the service message
283  auto ik_srv = std::make_shared<moveit_msgs::srv::GetPositionIK::Request>();
284  ik_srv->ik_request.avoid_collisions = true;
285  ik_srv->ik_request.group_name = getGroupName();
286 
287  // Copy seed state into virtual robot state and convert into moveit_msg
288  robot_state_->setJointGroupPositions(joint_model_group_, ik_seed_state);
289  moveit::core::robotStateToRobotStateMsg(*robot_state_, ik_srv->ik_request.robot_state);
290 
291  // Load the poses into the request in difference places depending if there is more than one or not
292  geometry_msgs::msg::PoseStamped ik_pose_st;
293  ik_pose_st.header.frame_id = base_frame_;
294  if (tip_frames_.size() > 1)
295  {
296  // Load into vector of poses
297  for (std::size_t i = 0; i < tip_frames_.size(); ++i)
298  {
299  ik_pose_st.pose = ik_poses[i];
300  ik_srv->ik_request.pose_stamped_vector.push_back(ik_pose_st);
301  ik_srv->ik_request.ik_link_names.push_back(tip_frames_[i]);
302  }
303  }
304  else
305  {
306  ik_pose_st.pose = ik_poses[0];
307 
308  // Load into single pose value
309  ik_srv->ik_request.pose_stamped = ik_pose_st;
310  ik_srv->ik_request.ik_link_name = getTipFrames()[0];
311  }
312 
313  RCLCPP_DEBUG(getLogger(), "Calling service: %s", ik_service_client_->get_service_name());
314  auto result_future = ik_service_client_->async_send_request(ik_srv);
315  const auto& response = result_future.get();
316  if (rclcpp::spin_until_future_complete(node_, result_future) == rclcpp::FutureReturnCode::SUCCESS)
317  {
318  // Check error code
319  error_code.val = response->error_code.val;
320  if (error_code.val != error_code.SUCCESS)
321  {
322  // TODO (JafarAbdi) Print the entire message for ROS2?
323  // RCLCPP_DEBUG("srv", "An IK that satisfies the constraints and is collision free could not be found."
324  // << "\nRequest was: \n"
325  // << ik_srv.request.ik_request << "\nResponse was: \n"
326  // << ik_srv.response.solution);
327  switch (error_code.val)
328  {
329  case moveit_msgs::msg::MoveItErrorCodes::FAILURE:
330  RCLCPP_ERROR(getLogger(), "Service failed with with error code: FAILURE");
331  break;
332  case moveit_msgs::msg::MoveItErrorCodes::NO_IK_SOLUTION:
333  RCLCPP_DEBUG(getLogger(), "Service failed with with error code: NO IK SOLUTION");
334  break;
335  default:
336  RCLCPP_DEBUG_STREAM(getLogger(), "Service failed with with error code: " << error_code.val);
337  }
338  return false;
339  }
340  }
341  else
342  {
343  RCLCPP_DEBUG_STREAM(getLogger(),
344  "Service call failed to connect to service: " << ik_service_client_->get_service_name());
345  error_code.val = error_code.FAILURE;
346  return false;
347  }
348  // Convert the robot state message to our robot_state representation
349  if (!moveit::core::robotStateMsgToRobotState(response->solution, *robot_state_))
350  {
351  RCLCPP_ERROR(getLogger(), "An error occurred converting received robot state message into internal robot state.");
352  error_code.val = error_code.FAILURE;
353  return false;
354  }
355 
356  // Get just the joints we are concerned about in our planning group
357  robot_state_->copyJointGroupPositions(joint_model_group_, solution);
358 
359  // Run the solution callback (i.e. collision checker) if available
360  if (solution_callback)
361  {
362  RCLCPP_DEBUG(getLogger(), "Calling solution callback on IK solution");
363 
364  // hack: should use all poses, not just the 0th
365  solution_callback(ik_poses[0], solution, error_code);
366 
367  if (error_code.val != error_code.SUCCESS)
368  {
369  switch (error_code.val)
370  {
371  case moveit_msgs::msg::MoveItErrorCodes::FAILURE:
372  RCLCPP_ERROR(getLogger(), "IK solution callback failed with with error code: FAILURE");
373  break;
374  case moveit_msgs::msg::MoveItErrorCodes::NO_IK_SOLUTION:
375  RCLCPP_ERROR(getLogger(), "IK solution callback failed with with error code: "
376  "NO IK SOLUTION");
377  break;
378  default:
379  RCLCPP_ERROR_STREAM(getLogger(), "IK solution callback failed with with error code: " << error_code.val);
380  }
381  return false;
382  }
383  }
384 
385  RCLCPP_INFO(getLogger(), "IK Solver Succeeded!");
386  return true;
387 }
388 
389 bool SrvKinematicsPlugin::getPositionFK(const std::vector<std::string>& link_names,
390  const std::vector<double>& joint_angles,
391  std::vector<geometry_msgs::msg::Pose>& poses) const
392 {
393  if (!active_)
394  {
395  RCLCPP_ERROR(getLogger(), "kinematics not active");
396  return false;
397  }
398  poses.resize(link_names.size());
399  if (joint_angles.size() != dimension_)
400  {
401  RCLCPP_ERROR(getLogger(), "Joint angles vector must have size: %d", dimension_);
402  return false;
403  }
404 
405  RCLCPP_ERROR(getLogger(), "Forward kinematics not implemented");
406 
407  return false;
408 }
409 
410 const std::vector<std::string>& SrvKinematicsPlugin::getJointNames() const
411 {
412  return ik_group_info_.joint_names;
413 }
414 
415 const std::vector<std::string>& SrvKinematicsPlugin::getLinkNames() const
416 {
417  return ik_group_info_.link_names;
418 }
419 
420 const std::vector<std::string>& SrvKinematicsPlugin::getVariableNames() const
421 {
422  return joint_model_group_->getVariableNames();
423 }
424 
425 } // namespace srv_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)
std::vector< unsigned int > redundant_joint_indices_
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::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.
moveit::core::RobotModelConstPtr robot_model_
virtual const std::string & getGroupName() const
Return the name of the group that the solver is operating on.
const std::vector< const JointModel * > & getActiveJointModels() const
Get the active joints in this group (that have controllable DOF). This does not include mimic joints.
const std::vector< std::string > & getJointModelNames() const
Get the names of the joints in this group. These are the names of the joints returned by getJointMode...
const std::vector< const JointModel * > & getJointModels() const
Get all the joints in this group (including fixed and mimic joints).
bool hasLinkModel(const std::string &link) const
Check if a link is part of this group.
unsigned int getVariableCount() const
Get the number of variables that describe this joint group. This includes variables necessary for mim...
const std::vector< std::string > & getVariableNames() const
Get the names of the variables that make up the joints included in this group. The number of returned...
const std::vector< const JointModel * > & getMimicJointModels() const
Get the mimic joints that are part of this group.
Definition of a kinematic model. This class is not thread safe, however multiple instances can be cre...
Definition: robot_model.h:76
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition: robot_state.h:90
Specific implementation of kinematics using ROS service calls to communicate with external IK solvers...
bool setRedundantJoints(const std::vector< unsigned int > &redundant_joint_indices) override
Set a set of redundant joints for the kinematics solver to use. This can fail, depending on the IK so...
const std::vector< std::string > & getJointNames() const override
Return all the joint names in the order they are used internally.
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....
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.
const std::vector< std::string > & getVariableNames() const
Return all the variable names in the order they are represented internally.
const std::vector< std::string > & getLinkNames() const override
Return all the link names in the order they are represented internally.
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 initialize(const rclcpp::Node::SharedPtr &node, const moveit::core::RobotModel &robot_model, const std::string &group_name, const std::string &base_name, const std::vector< std::string > &tip_frames, double search_discretization) override
Initialization function for the kinematics, for use with kinematic chain IK solvers.
void robotStateToRobotStateMsg(const RobotState &state, moveit_msgs::msg::RobotState &robot_state, bool copy_attached_bodies=true)
Convert a MoveIt robot state to a robot state message.
bool robotStateMsgToRobotState(const Transforms &tf, const moveit_msgs::msg::RobotState &robot_state, RobotState &state, bool copy_attached_bodies=true)
Convert a robot state msg (with accompanying extra transforms) to a MoveIt robot state.
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79
name
Definition: setup.py:7
CLASS_LOADER_REGISTER_CLASS(default_planning_request_adapters::ResolveConstraintFrames, planning_interface::PlanningRequestAdapter)
A set of options for the kinematics solver.