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.h>
43 #include <kdl/frames_io.hpp>
44 #include <kdl/kinfam_io.hpp>
45 
46 // register as a KinematicsBase implementation
49 
50 namespace lma_kinematics_plugin
51 {
52 LMAKinematicsPlugin::LMAKinematicsPlugin() : initialized_(false)
53 {
54 }
55 
56 void LMAKinematicsPlugin::getRandomConfiguration(Eigen::VectorXd& jnt_array) const
57 {
58  state_->setToRandomPositions(joint_model_group_);
59  state_->copyJointGroupPositions(joint_model_group_, &jnt_array[0]);
60 }
61 
62 void LMAKinematicsPlugin::getRandomConfiguration(const Eigen::VectorXd& seed_state,
63  const std::vector<double>& consistency_limits,
64  Eigen::VectorXd& jnt_array) const
65 {
66  joint_model_group_->getVariableRandomPositionsNearBy(state_->getRandomNumberGenerator(), &jnt_array[0],
67  &seed_state[0], consistency_limits);
68 }
69 
70 bool LMAKinematicsPlugin::checkConsistency(const Eigen::VectorXd& seed_state,
71  const std::vector<double>& consistency_limits,
72  const Eigen::VectorXd& solution) const
73 {
74  for (std::size_t i = 0; i < dimension_; ++i)
75  if (fabs(seed_state(i) - solution(i)) > consistency_limits[i])
76  return false;
77  return true;
78 }
79 
80 bool LMAKinematicsPlugin::initialize(const moveit::core::RobotModel& robot_model, const std::string& group_name,
81  const std::string& base_frame, const std::vector<std::string>& tip_frames,
82  double search_discretization)
83 {
84  storeValues(robot_model, group_name, base_frame, tip_frames, search_discretization);
85  joint_model_group_ = robot_model_->getJointModelGroup(group_name);
86  if (!joint_model_group_)
87  return false;
88 
89  if (!joint_model_group_->isChain())
90  {
91  ROS_ERROR_NAMED("lma", "Group '%s' is not a chain", group_name.c_str());
92  return false;
93  }
94  if (!joint_model_group_->isSingleDOFJoints())
95  {
96  ROS_ERROR_NAMED("lma", "Group '%s' includes joints that have more than 1 DOF", group_name.c_str());
97  return false;
98  }
99 
100  KDL::Tree kdl_tree;
101 
102  if (!kdl_parser::treeFromUrdfModel(*robot_model.getURDF(), kdl_tree))
103  {
104  ROS_ERROR_NAMED("lma", "Could not initialize tree object");
105  return false;
106  }
107  if (!kdl_tree.getChain(base_frame_, getTipFrame(), kdl_chain_))
108  {
109  ROS_ERROR_NAMED("lma", "Could not initialize chain object");
110  return false;
111  }
112 
113  for (const moveit::core::JointModel* jm : joint_model_group_->getJointModels())
114  {
115  if (jm->getType() == moveit::core::JointModel::REVOLUTE || jm->getType() == moveit::core::JointModel::PRISMATIC)
116  {
117  joints_.push_back(jm);
118  joint_names_.push_back(jm->getName());
119  }
120  }
121  dimension_ = joints_.size();
122 
123  // Get Solver Parameters
124  lookupParam("max_solver_iterations", max_solver_iterations_, 500);
125  lookupParam("epsilon", epsilon_, 1e-5);
126  lookupParam("orientation_vs_position", orientation_vs_position_weight_, 0.01);
127 
128  bool position_ik;
129  lookupParam("position_only_ik", position_ik, false);
130  if (position_ik) // position_only_ik overrules orientation_vs_position
131  orientation_vs_position_weight_ = 0.0;
132  if (orientation_vs_position_weight_ == 0.0)
133  ROS_INFO_NAMED("lma", "Using position only ik");
134 
135  // Setup the joint state groups that we need
136  state_ = std::make_shared<moveit::core::RobotState>(robot_model_);
137 
138  fk_solver_ = std::make_unique<KDL::ChainFkSolverPos_recursive>(kdl_chain_);
139 
140  initialized_ = true;
141  ROS_DEBUG_NAMED("lma", "LMA solver initialized");
142  return true;
143 }
144 
145 bool LMAKinematicsPlugin::timedOut(const ros::WallTime& start_time, double duration) const
146 {
147  return ((ros::WallTime::now() - start_time).toSec() >= duration);
148 }
149 
150 bool LMAKinematicsPlugin::getPositionIK(const geometry_msgs::Pose& ik_pose, const std::vector<double>& ik_seed_state,
151  std::vector<double>& solution, moveit_msgs::MoveItErrorCodes& error_code,
152  const kinematics::KinematicsQueryOptions& options) const
153 {
154  std::vector<double> consistency_limits;
155 
156  // limit search to a single attempt by setting a timeout of zero
157  return searchPositionIK(ik_pose, ik_seed_state, 0.0, consistency_limits, solution, IKCallbackFn(), error_code,
158  options);
159 }
160 
161 bool LMAKinematicsPlugin::searchPositionIK(const geometry_msgs::Pose& ik_pose, const std::vector<double>& ik_seed_state,
162  double timeout, std::vector<double>& solution,
163  moveit_msgs::MoveItErrorCodes& error_code,
164  const kinematics::KinematicsQueryOptions& options) const
165 {
166  std::vector<double> consistency_limits;
167 
168  return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, IKCallbackFn(), error_code,
169  options);
170 }
171 
172 bool LMAKinematicsPlugin::searchPositionIK(const geometry_msgs::Pose& ik_pose, const std::vector<double>& ik_seed_state,
173  double timeout, const std::vector<double>& consistency_limits,
174  std::vector<double>& solution, moveit_msgs::MoveItErrorCodes& error_code,
175  const kinematics::KinematicsQueryOptions& options) const
176 {
177  return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, IKCallbackFn(), error_code,
178  options);
179 }
180 
181 bool LMAKinematicsPlugin::searchPositionIK(const geometry_msgs::Pose& ik_pose, const std::vector<double>& ik_seed_state,
182  double timeout, std::vector<double>& solution,
183  const IKCallbackFn& solution_callback,
184  moveit_msgs::MoveItErrorCodes& error_code,
185  const kinematics::KinematicsQueryOptions& options) const
186 {
187  std::vector<double> consistency_limits;
188  return searchPositionIK(ik_pose, ik_seed_state, timeout, consistency_limits, solution, solution_callback, error_code,
189  options);
190 }
191 
192 void LMAKinematicsPlugin::harmonize(Eigen::VectorXd& values) const
193 {
194  size_t i = 0;
195  for (auto* jm : joints_)
196  jm->harmonizePosition(&values[i++]);
197 }
198 
199 bool LMAKinematicsPlugin::obeysLimits(const Eigen::VectorXd& values) const
200 {
201  size_t i = 0;
202  for (const auto& jm : joints_)
203  if (!jm->satisfiesPositionBounds(&values[i++]))
204  return false;
205  return true;
206 }
207 
208 bool LMAKinematicsPlugin::searchPositionIK(const geometry_msgs::Pose& ik_pose, const std::vector<double>& ik_seed_state,
209  double timeout, const std::vector<double>& consistency_limits,
210  std::vector<double>& solution, const IKCallbackFn& solution_callback,
211  moveit_msgs::MoveItErrorCodes& error_code,
212  const kinematics::KinematicsQueryOptions& options) const
213 {
214  ros::WallTime start_time = ros::WallTime::now();
215  if (!initialized_)
216  {
217  ROS_ERROR_NAMED("lma", "kinematics solver not initialized");
218  error_code.val = error_code.NO_IK_SOLUTION;
219  return false;
220  }
221 
222  if (ik_seed_state.size() != dimension_)
223  {
225  "Seed state must have size " << dimension_ << " instead of size " << ik_seed_state.size());
226  error_code.val = error_code.NO_IK_SOLUTION;
227  return false;
228  }
229 
230  if (!consistency_limits.empty() && consistency_limits.size() != dimension_)
231  {
232  ROS_ERROR_STREAM_NAMED("lma", "Consistency limits be empty or must have size " << dimension_ << " instead of size "
233  << consistency_limits.size());
234  error_code.val = error_code.NO_IK_SOLUTION;
235  return false;
236  }
237 
238  Eigen::Matrix<double, 6, 1> cartesian_weights;
239  cartesian_weights(0) = 1;
240  cartesian_weights(1) = 1;
241  cartesian_weights(2) = 1;
242  cartesian_weights(3) = orientation_vs_position_weight_;
243  cartesian_weights(4) = orientation_vs_position_weight_;
244  cartesian_weights(5) = orientation_vs_position_weight_;
245 
246  KDL::JntArray jnt_seed_state(dimension_);
247  KDL::JntArray jnt_pos_in(dimension_);
248  KDL::JntArray jnt_pos_out(dimension_);
249  jnt_seed_state.data = Eigen::Map<const Eigen::VectorXd>(ik_seed_state.data(), ik_seed_state.size());
250  jnt_pos_in = jnt_seed_state;
251 
252  KDL::ChainIkSolverPos_LMA ik_solver_pos(kdl_chain_, cartesian_weights, epsilon_, max_solver_iterations_);
253  solution.resize(dimension_);
254 
255  KDL::Frame pose_desired;
256  tf2::fromMsg(ik_pose, pose_desired);
257 
258  ROS_DEBUG_STREAM_NAMED("lma", "searchPositionIK2: Position request pose is "
259  << ik_pose.position.x << " " << ik_pose.position.y << " " << ik_pose.position.z
260  << " " << ik_pose.orientation.x << " " << ik_pose.orientation.y << " "
261  << ik_pose.orientation.z << " " << ik_pose.orientation.w);
262  unsigned int attempt = 0;
263  do
264  {
265  ++attempt;
266  if (attempt > 1) // randomly re-seed after first attempt
267  {
268  if (!consistency_limits.empty())
269  getRandomConfiguration(jnt_seed_state.data, consistency_limits, jnt_pos_in.data);
270  else
271  getRandomConfiguration(jnt_pos_in.data);
272  ROS_DEBUG_STREAM_NAMED("lma", "New random configuration (" << attempt << "): " << jnt_pos_in);
273  }
274 
275  int ik_valid = ik_solver_pos.CartToJnt(jnt_pos_in, pose_desired, jnt_pos_out);
276  if (ik_valid == 0 || options.return_approximate_solution) // found acceptable solution
277  {
278  harmonize(jnt_pos_out.data);
279  if (!consistency_limits.empty() && !checkConsistency(jnt_seed_state.data, consistency_limits, jnt_pos_out.data))
280  continue;
281  if (!obeysLimits(jnt_pos_out.data))
282  continue;
283 
284  Eigen::Map<Eigen::VectorXd>(solution.data(), solution.size()) = jnt_pos_out.data;
285  if (!solution_callback.empty())
286  {
287  solution_callback(ik_pose, solution, error_code);
288  if (error_code.val != error_code.SUCCESS)
289  continue;
290  }
291 
292  // solution passed consistency check and solution callback
293  error_code.val = error_code.SUCCESS;
294  ROS_DEBUG_STREAM_NAMED("lma", "Solved after " << (ros::WallTime::now() - start_time).toSec() << " < " << timeout
295  << "s and " << attempt << " attempts");
296  return true;
297  }
298  } while (!timedOut(start_time, timeout));
299 
300  ROS_DEBUG_STREAM_NAMED("lma", "IK timed out after " << (ros::WallTime::now() - start_time).toSec() << " > " << timeout
301  << "s and " << attempt << " attempts");
302  error_code.val = error_code.TIMED_OUT;
303  return false;
304 }
305 
306 bool LMAKinematicsPlugin::getPositionFK(const std::vector<std::string>& link_names,
307  const std::vector<double>& joint_angles,
308  std::vector<geometry_msgs::Pose>& poses) const
309 {
310  if (!initialized_)
311  {
312  ROS_ERROR_NAMED("lma", "kinematics solver not initialized");
313  return false;
314  }
315  poses.resize(link_names.size());
316  if (joint_angles.size() != dimension_)
317  {
318  ROS_ERROR_NAMED("lma", "Joint angles vector must have size: %d", dimension_);
319  return false;
320  }
321 
322  KDL::Frame p_out;
323  KDL::JntArray jnt_pos_in(dimension_);
324  jnt_pos_in.data = Eigen::Map<const Eigen::VectorXd>(joint_angles.data(), joint_angles.size());
325 
326  bool valid = true;
327  for (unsigned int i = 0; i < poses.size(); i++)
328  {
329  if (fk_solver_->JntToCart(jnt_pos_in, p_out) >= 0)
330  {
331  poses[i] = tf2::toMsg(p_out);
332  }
333  else
334  {
335  ROS_ERROR_NAMED("lma", "Could not compute FK for %s", link_names[i].c_str());
336  valid = false;
337  }
338  }
339  return valid;
340 }
341 
342 const std::vector<std::string>& LMAKinematicsPlugin::getJointNames() const
343 {
344  return joint_names_;
345 }
346 
347 const std::vector<std::string>& LMAKinematicsPlugin::getLinkNames() const
348 {
349  return getTipFrames();
350 }
351 
352 } // namespace lma_kinematics_plugin
CLASS_LOADER_REGISTER_CLASS
#define CLASS_LOADER_REGISTER_CLASS(Derived, Base)
kinematics::KinematicsQueryOptions::return_approximate_solution
bool return_approximate_solution
duration
std::chrono::system_clock::duration duration
moveit::core::RobotModel::getURDF
const urdf::ModelInterfaceSharedPtr & getURDF() const
tf2::fromMsg
void fromMsg(const A &, B &b)
ROS_DEBUG_STREAM_NAMED
#define ROS_DEBUG_STREAM_NAMED(name, args)
lma_kinematics_plugin.h
moveit::core::RobotModel
ROS_ERROR_NAMED
#define ROS_ERROR_NAMED(name,...)
moveit::core::JointModel::PRISMATIC
PRISMATIC
kinematics::KinematicsBase::IKCallbackFn
boost::function< void(const geometry_msgs::Pose &, const std::vector< double > &, moveit_msgs::MoveItErrorCodes &)> IKCallbackFn
kdl_parser::treeFromUrdfModel
KDL_PARSER_PUBLIC bool treeFromUrdfModel(const urdf::ModelInterface &robot_model, KDL::Tree &tree)
moveit::core::JointModel::REVOLUTE
REVOLUTE
lma_kinematics_plugin::LMAKinematicsPlugin
Implementation of kinematics using Levenberg-Marquardt (LMA) solver from KDL. This version supports a...
Definition: lma_kinematics_plugin.h:98
ROS_INFO_NAMED
#define ROS_INFO_NAMED(name,...)
kinematics::KinematicsBase
ROS_DEBUG_NAMED
#define ROS_DEBUG_NAMED(name,...)
ros::WallTime::now
static WallTime now()
ROS_ERROR_STREAM_NAMED
#define ROS_ERROR_STREAM_NAMED(name, args)
tf2_kdl.h
class_loader.hpp
ros::WallTime
kdl_parser.hpp
values
std::vector< double > values
kinematics::KinematicsQueryOptions
tf2::toMsg
B toMsg(const A &a)
lma_kinematics_plugin::LMAKinematicsPlugin::LMAKinematicsPlugin
LMAKinematicsPlugin()
Default constructor.
Definition: lma_kinematics_plugin.cpp:84
lma_kinematics_plugin
Definition: lma_kinematics_plugin.h:60
moveit::core::JointModel


moveit_kinematics
Author(s): Dave Coleman , Ioan Sucan , Sachin Chitta
autogenerated on Fri May 3 2024 02:29:33