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