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 return false;
93 }
94}
95
96PR2ArmIKSolver::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
112int 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
166int 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
212bool 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
231Eigen::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
245double 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
255void 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
268
270{
271 return active_;
272}
273
274bool 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
328bool 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
337bool 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
391bool 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
401bool 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
411bool 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
422bool 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
429const 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
438const 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.
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.
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)
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
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.
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.
moveit_msgs::msg::KinematicSolverInfo solver_info_
get chain information about the arm.
Definition pr2_arm_ik.h:170
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:59
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.