test_kinematics_plugin.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2015, Southwest Research Institute
5  * 2018, Bielefeld University
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of Willow Garage nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
36 /* Author: Jorge Nicho, Robert Haschke */
37 
38 #include <gtest/gtest.h>
39 #include <memory>
40 #include <functional>
42 #include <ros/ros.h>
43 #include <tf2_eigen/tf2_eigen.h>
44 #include <xmlrpcpp/XmlRpcValue.h>
45 
46 // MoveIt
48 #include <moveit/rdf_loader/rdf_loader.h>
51 
53 #include <moveit_msgs/DisplayTrajectory.h>
55 
56 const std::string ROBOT_DESCRIPTION_PARAM = "robot_description";
57 const double DEFAULT_SEARCH_DISCRETIZATION = 0.01f;
58 const double EXPECTED_SUCCESS_RATE = 0.8;
59 const double DEFAULT_TOLERANCE = 1e-5;
60 
61 template <typename T>
62 inline bool getParam(const std::string& param, T& val)
63 {
64  // first look within private namespace
65  ros::NodeHandle pnh("~");
66  if (pnh.getParam(param, val))
67  return true;
68 
69  // then in local namespace
70  ros::NodeHandle nh;
71  return nh.getParam(param, val);
72 }
73 
74 // As loading of parameters is quite slow, we share them across all tests
76 {
77  friend class KinematicsTest;
79 
80  moveit::core::RobotModelPtr robot_model_;
81  std::unique_ptr<KinematicsLoader> kinematics_loader_;
82  std::string root_link_;
83  std::string tip_link_;
84  std::string group_name_;
85  std::vector<std::string> joints_;
86  std::vector<double> seed_;
87  std::vector<double> consistency_limits_;
88  double timeout_;
89  double tolerance_;
97 
98  SharedData(SharedData const&) = delete; // this is a singleton
100  {
101  initialize();
102  }
103 
104  void initialize()
105  {
106  ROS_INFO_STREAM("Loading robot model from " << ros::this_node::getNamespace() << "/" << ROBOT_DESCRIPTION_PARAM);
107  // load robot model
108  rdf_loader::RDFLoader rdf_loader(ROBOT_DESCRIPTION_PARAM);
109  robot_model_ = std::make_shared<moveit::core::RobotModel>(rdf_loader.getURDF(), rdf_loader.getSRDF());
110  ASSERT_TRUE(bool(robot_model_)) << "Failed to load robot model";
111 
112  // init ClassLoader
113  kinematics_loader_ = std::make_unique<KinematicsLoader>("moveit_core", "kinematics::KinematicsBase");
114  ASSERT_TRUE(bool(kinematics_loader_)) << "Failed to instantiate ClassLoader";
115 
116  // load parameters
117  ASSERT_TRUE(getParam("group", group_name_));
118  ASSERT_TRUE(getParam("tip_link", tip_link_));
119  ASSERT_TRUE(getParam("root_link", root_link_));
120  ASSERT_TRUE(getParam("joint_names", joints_));
121  getParam("seed", seed_);
122  ASSERT_TRUE(seed_.empty() || seed_.size() == joints_.size());
123  getParam("consistency_limits", consistency_limits_);
124  if (!getParam("ik_timeout", timeout_) || timeout_ < 0.0)
125  timeout_ = 1.0;
126  if (!getParam("tolerance", tolerance_) || tolerance_ < 0.0)
128  ASSERT_TRUE(consistency_limits_.empty() || consistency_limits_.size() == joints_.size());
129  ASSERT_TRUE(getParam("num_fk_tests", num_fk_tests_));
130  ASSERT_TRUE(getParam("num_ik_cb_tests", num_ik_cb_tests_));
131  ASSERT_TRUE(getParam("num_ik_tests", num_ik_tests_));
132  ASSERT_TRUE(getParam("num_ik_multiple_tests", num_ik_multiple_tests_));
133  ASSERT_TRUE(getParam("num_nearest_ik_tests", num_nearest_ik_tests_));
134 
135  ASSERT_TRUE(robot_model_->hasJointModelGroup(group_name_));
136  ASSERT_TRUE(robot_model_->hasLinkModel(root_link_));
137  ASSERT_TRUE(robot_model_->hasLinkModel(tip_link_));
138 
139  if (!getParam("plugin_fk_support", plugin_fk_support_))
140  plugin_fk_support_ = true;
141 
142  if (!getParam("position_only_check", position_only_check_))
143  position_only_check_ = false;
144  }
145 
146 public:
147  auto createUniqueInstance(const std::string& name) const
148  {
149  return kinematics_loader_->createUniqueInstance(name);
150  }
151 
152  static const SharedData& instance()
153  {
154  static SharedData instance;
155  return instance;
156  }
157  static void release()
158  {
159  SharedData& shared = const_cast<SharedData&>(instance());
160  shared.kinematics_loader_.reset();
161  }
162 };
163 
164 class KinematicsTest : public ::testing::Test
165 {
166 protected:
167  void operator=(const SharedData& data)
168  {
169  robot_model_ = data.robot_model_;
170  jmg_ = robot_model_->getJointModelGroup(data.group_name_);
171  root_link_ = data.root_link_;
172  tip_link_ = data.tip_link_;
173  group_name_ = data.group_name_;
174  joints_ = data.joints_;
175  seed_ = data.seed_;
177  timeout_ = data.timeout_;
178  tolerance_ = data.tolerance_;
186  }
187 
188  void SetUp() override
189  {
190  *this = SharedData::instance();
191 
192  std::string plugin_name;
193  ASSERT_TRUE(getParam("ik_plugin_name", plugin_name));
194  ROS_INFO_STREAM("Loading " << plugin_name);
196  ASSERT_TRUE(bool(kinematics_solver_)) << "Failed to load plugin: " << plugin_name;
197 
198  // initializing plugin
199  ASSERT_TRUE(kinematics_solver_->initialize(*robot_model_, group_name_, root_link_, { tip_link_ },
203  << "Solver failed to initialize";
204 
205  jmg_ = robot_model_->getJointModelGroup(kinematics_solver_->getGroupName());
206  ASSERT_TRUE(jmg_);
207 
208  // Validate chain information
209  ASSERT_EQ(root_link_, kinematics_solver_->getBaseFrame());
210  ASSERT_FALSE(kinematics_solver_->getTipFrames().empty());
211  ASSERT_EQ(tip_link_, kinematics_solver_->getTipFrame());
212  ASSERT_EQ(joints_, kinematics_solver_->getJointNames());
213  }
214 
215 public:
216  testing::AssertionResult isNear(const char* expr1, const char* expr2, const char* /*abs_error_expr*/,
217  const geometry_msgs::Point& val1, const geometry_msgs::Point& val2, double abs_error)
218  {
219  // clang-format off
220  if (std::fabs(val1.x - val2.x) <= abs_error &&
221  std::fabs(val1.y - val2.y) <= abs_error &&
222  std::fabs(val1.z - val2.z) <= abs_error)
223  return testing::AssertionSuccess();
224 
225  return testing::AssertionFailure()
226  << std::setprecision(std::numeric_limits<double>::digits10 + 2)
227  << "Expected: " << expr1 << " [" << val1.x << ", " << val1.y << ", " << val1.z << "]\n"
228  << "Actual: " << expr2 << " [" << val2.x << ", " << val2.y << ", " << val2.z << "]";
229  // clang-format on
230  }
231  testing::AssertionResult isNear(const char* expr1, const char* expr2, const char* /*abs_error_expr*/,
232  const geometry_msgs::Quaternion& val1, const geometry_msgs::Quaternion& val2,
233  double abs_error)
234  {
235  if ((std::fabs(val1.x - val2.x) <= abs_error && std::fabs(val1.y - val2.y) <= abs_error &&
236  std::fabs(val1.z - val2.z) <= abs_error && std::fabs(val1.w - val2.w) <= abs_error) ||
237  (std::fabs(val1.x + val2.x) <= abs_error && std::fabs(val1.y + val2.y) <= abs_error &&
238  std::fabs(val1.z + val2.z) <= abs_error && std::fabs(val1.w + val2.w) <= abs_error))
239  return testing::AssertionSuccess();
240 
241  // clang-format off
242  return testing::AssertionFailure()
243  << std::setprecision(std::numeric_limits<double>::digits10 + 2)
244  << "Expected: " << expr1 << " [" << val1.w << ", " << val1.x << ", " << val1.y << ", " << val1.z << "]\n"
245  << "Actual: " << expr2 << " [" << val2.w << ", " << val2.x << ", " << val2.y << ", " << val2.z << "]";
246  // clang-format on
247  }
248  testing::AssertionResult expectNearHelper(const char* expr1, const char* expr2, const char* abs_error_expr,
249  const std::vector<geometry_msgs::Pose>& val1,
250  const std::vector<geometry_msgs::Pose>& val2, double abs_error)
251  {
252  if (val1.size() != val2.size())
253  return testing::AssertionFailure() << "Different vector sizes"
254  << "\nExpected: " << expr1 << " (" << val1.size() << ")"
255  << "\nActual: " << expr2 << " (" << val2.size() << ")";
256 
257  for (size_t i = 0; i < val1.size(); ++i)
258  {
259  ::std::stringstream ss;
260  ss << "[" << i << "].position";
261  GTEST_ASSERT_(isNear((expr1 + ss.str()).c_str(), (expr2 + ss.str()).c_str(), abs_error_expr, val1[i].position,
262  val2[i].position, abs_error),
263  GTEST_NONFATAL_FAILURE_);
264 
266  {
267  ss.str("");
268  ss << "[" << i << "].orientation";
269  GTEST_ASSERT_(isNear((expr1 + ss.str()).c_str(), (expr2 + ss.str()).c_str(), abs_error_expr,
270  val1[i].orientation, val2[i].orientation, abs_error),
271  GTEST_NONFATAL_FAILURE_);
272  }
273  }
274  return testing::AssertionSuccess();
275  }
276 
277  void searchIKCallback(const std::vector<double>& joint_state, moveit_msgs::MoveItErrorCodes& error_code,
278  moveit::core::RobotState& robot_state)
279  {
280  std::vector<std::string> link_names = { tip_link_ };
281  std::vector<geometry_msgs::Pose> poses;
282  if (!getPositionFK(link_names, joint_state, poses, robot_state))
283  {
284  error_code.val = error_code.PLANNING_FAILED;
285  return;
286  }
287 
288  EXPECT_GT(poses[0].position.z, 0.0f);
289  if (poses[0].position.z > 0.0)
290  error_code.val = error_code.SUCCESS;
291  else
292  error_code.val = error_code.PLANNING_FAILED;
293  }
294 
295  bool getPositionFK(const std::vector<std::string>& link_names, const std::vector<double>& joint_state,
296  std::vector<geometry_msgs::Pose>& poses, moveit::core::RobotState& robot_state)
297  {
298  // There are some cases, e.g. testing a IKFast plugin targeting a robot of dof < 6, where
299  // kinematics_solver_->getPositionFK() will always return false, and that will render the entire test process
300  // useless as every test is doomed to fail due to calls to getPositionFK().
301  //
302  // When plugin_fk_support_ is set to false, the FK pass will be done by updating robot_state with the given joints
303  // and calling moveit::core::RobotState::getGlobalLinkTransform(); therefore the lack of support in
304  // kinematics_solver_->getPositionFK() is circumvented, and tests for IK functionality can still be run to perform
305  // meaningful checks
306 
307  if (plugin_fk_support_)
308  return kinematics_solver_->getPositionFK(link_names, joint_state, poses);
309  else
310  {
311  std::vector<double> joint_state_backup;
312  robot_state.copyJointGroupPositions(jmg_, joint_state_backup);
313  robot_state.setJointGroupPositions(jmg_, joint_state);
314  robot_state.updateLinkTransforms();
315 
316  poses.clear();
317  poses.reserve(link_names.size());
318  for (const std::string& link_name : link_names)
319  poses.emplace_back(tf2::toMsg(robot_state.getGlobalLinkTransform(link_name)));
320 
321  robot_state.setJointGroupPositions(jmg_, joint_state_backup);
322  robot_state.updateLinkTransforms();
323  return true;
324  }
325  }
326 
327 public:
328  moveit::core::RobotModelPtr robot_model_;
330  kinematics::KinematicsBasePtr kinematics_solver_;
332  std::string root_link_;
333  std::string tip_link_;
334  std::string group_name_;
335  std::vector<std::string> joints_;
336  std::vector<double> seed_;
337  std::vector<double> consistency_limits_;
338  double timeout_;
339  double tolerance_;
340  unsigned int num_fk_tests_;
341  unsigned int num_ik_cb_tests_;
342  unsigned int num_ik_tests_;
344  unsigned int num_nearest_ik_tests_;
347 };
348 
349 #define EXPECT_NEAR_POSES(lhs, rhs, near) \
350  SCOPED_TRACE("EXPECT_NEAR_POSES(" #lhs ", " #rhs ")"); \
351  GTEST_ASSERT_(expectNearHelper(#lhs, #rhs, #near, lhs, rhs, near), GTEST_NONFATAL_FAILURE_);
352 
354 {
355  std::vector<double> joints(kinematics_solver_->getJointNames().size(), 0.0);
356  const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
357  moveit::core::RobotState robot_state(robot_model_);
358  robot_state.setToDefaultValues();
359 
360  for (unsigned int i = 0; i < num_fk_tests_; ++i)
361  {
362  robot_state.setToRandomPositions(jmg_, this->rng_);
363  robot_state.copyJointGroupPositions(jmg_, joints);
364  std::vector<geometry_msgs::Pose> fk_poses;
365  EXPECT_TRUE(getPositionFK(tip_frames, joints, fk_poses, robot_state));
366 
367  robot_state.updateLinkTransforms();
368  std::vector<geometry_msgs::Pose> model_poses;
369  model_poses.reserve(tip_frames.size());
370  for (const auto& tip : tip_frames)
371  model_poses.emplace_back(tf2::toMsg(robot_state.getGlobalLinkTransform(tip)));
372  EXPECT_NEAR_POSES(model_poses, fk_poses, tolerance_);
373  }
374 }
375 
376 // perform random walk in joint-space, reaching poses via IK
377 TEST_F(KinematicsTest, randomWalkIK)
378 {
379  std::vector<double> seed, goal, solution;
380  const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
381  moveit::core::RobotState robot_state(robot_model_);
382  robot_state.setToDefaultValues();
383 
384  if (!seed_.empty())
385  robot_state.setJointGroupPositions(jmg_, seed_);
386 
387  bool publish_trajectory = false;
388  getParam<bool>("publish_trajectory", publish_trajectory);
389  moveit_msgs::DisplayTrajectory msg;
390  msg.model_id = robot_model_->getName();
391  moveit::core::robotStateToRobotStateMsg(robot_state, msg.trajectory_start);
392  msg.trajectory.resize(1);
393  robot_trajectory::RobotTrajectory traj(robot_model_, jmg_);
394 
395  unsigned int failures = 0;
396  static constexpr double NEAR_JOINT = 0.1;
397  const std::vector<double> consistency_limits(jmg_->getVariableCount(), 1.05 * NEAR_JOINT);
398  for (unsigned int i = 0; i < num_ik_tests_; ++i)
399  {
400  // remember previous pose
401  robot_state.copyJointGroupPositions(jmg_, seed);
402  // sample a new pose nearby
403  robot_state.setToRandomPositionsNearBy(jmg_, robot_state, NEAR_JOINT);
404  // get joints of new pose
405  robot_state.copyJointGroupPositions(jmg_, goal);
406  // compute target tip_frames
407  std::vector<geometry_msgs::Pose> poses;
408  ASSERT_TRUE(getPositionFK(tip_frames, goal, poses, robot_state));
409 
410  // compute IK
411  moveit_msgs::MoveItErrorCodes error_code;
412  kinematics_solver_->searchPositionIK(poses[0], seed, 0.1, consistency_limits, solution, error_code);
413  if (error_code.val != error_code.SUCCESS)
414  {
415  ++failures;
416  continue;
417  }
418 
419  // on success: validate reached poses
420  std::vector<geometry_msgs::Pose> reached_poses;
421  getPositionFK(tip_frames, solution, reached_poses, robot_state);
422  EXPECT_NEAR_POSES(poses, reached_poses, tolerance_);
423 
424  // The following joint state check is skipped when position_only_check_ is true,
425  // because matching two sets of joint states would imply fully matching the two corresponding poses
426  if (!position_only_check_)
427  {
428  // validate closeness of solution pose to goal
429  auto diff = Eigen::Map<Eigen::ArrayXd>(solution.data(), solution.size()) -
430  Eigen::Map<Eigen::ArrayXd>(goal.data(), goal.size());
431  if (!diff.isZero(1.05 * NEAR_JOINT))
432  {
433  ++failures;
434  ROS_WARN_STREAM("jump in [" << i << "]: " << diff.transpose());
435  }
436  }
437 
438  // update robot state to found pose
439  robot_state.setJointGroupPositions(jmg_, solution);
440  traj.addSuffixWayPoint(robot_state, 0.1);
441  }
442  EXPECT_LE(failures, (1.0 - EXPECTED_SUCCESS_RATE) * num_ik_tests_);
443 
444  if (publish_trajectory)
445  {
446  ros::NodeHandle nh;
448  spinner.start();
449  ros::Publisher pub = nh.advertise<moveit_msgs::DisplayTrajectory>("display_random_walk", 1, true);
450  traj.getRobotTrajectoryMsg(msg.trajectory[0]);
451  pub.publish(msg);
452  ros::WallDuration(0.1).sleep();
453  }
454 }
455 
457 {
459  return static_cast<double>(v);
460  else if (v.getType() == XmlRpc::XmlRpcValue::TypeInt)
461  return static_cast<int>(v);
462  else
463  return 0.0;
464 }
465 static void parseVector(XmlRpc::XmlRpcValue& vec, std::vector<double>& values, size_t num = 0)
466 {
467  ASSERT_EQ(vec.getType(), XmlRpc::XmlRpcValue::TypeArray);
468  if (num != 0)
469  {
470  ASSERT_EQ(static_cast<size_t>(vec.size()), num);
471  }
472  values.reserve(vec.size());
473  values.clear();
474  for (int i = 0; i < vec.size(); ++i) // NOLINT(modernize-loop-convert)
475  values.push_back(parseDouble(vec[i]));
476 }
477 static bool parseGoal(const std::string& name, XmlRpc::XmlRpcValue& value, Eigen::Isometry3d& goal, std::string& desc)
478 {
479  std::ostringstream oss;
480  std::vector<double> vec;
481  if (name == "pose")
482  {
483  parseVector(value["orientation"], vec, 4);
484  Eigen::Quaterniond q(vec[3], vec[0], vec[1], vec[2]); // w x y z
485  goal = q;
486  parseVector(value["position"], vec, 3);
487  goal.translation() = Eigen::Map<Eigen::Vector3d>(vec.data());
488  oss << name << " " << goal.translation().transpose() << " " << q.vec().transpose() << " " << q.w();
489  desc = oss.str();
490  return true;
491  }
492  for (unsigned char axis = 0; axis < 3; ++axis)
493  {
494  char axis_char = 'x' + axis;
495  // position offset
496  if (name == (std::string("pos.") + axis_char))
497  {
498  goal.translation()[axis] += parseDouble(value);
499  desc = name + " " + std::to_string(parseDouble(value));
500  return true;
501  }
502  // rotation offset
503  else if (name == (std::string("rot.") + axis_char))
504  {
505  goal *= Eigen::AngleAxisd(parseDouble(value), Eigen::Vector3d::Unit(axis));
506  desc = name + " " + std::to_string(parseDouble(value));
507  return true;
508  }
509  }
510  return false;
511 }
512 
514 {
515  XmlRpc::XmlRpcValue tests;
516  if (!getParam("unit_tests", tests))
517  return;
518 
519  std::vector<double> seed, sol;
520  const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
521  moveit::core::RobotState robot_state(robot_model_);
522  robot_state.setToDefaultValues();
523 
524  // initial joint pose from seed_ or defaults
525  if (!seed_.empty())
526  robot_state.setJointGroupPositions(jmg_, seed_);
527  robot_state.copyJointGroupPositions(jmg_, seed);
528 
529  // compute initial end-effector pose
530  std::vector<geometry_msgs::Pose> poses;
531  ASSERT_TRUE(getPositionFK(tip_frames, seed, poses, robot_state));
532  Eigen::Isometry3d initial, goal;
533  tf2::fromMsg(poses[0], initial);
534 
535  auto validate_ik = [&](const geometry_msgs::Pose& goal, std::vector<double>& truth) {
536  // compute IK
537  moveit_msgs::MoveItErrorCodes error_code;
538  kinematics_solver_->searchPositionIK(goal, seed, timeout_,
539  const_cast<const std::vector<double>&>(consistency_limits_), sol, error_code);
540  ASSERT_EQ(error_code.val, error_code.SUCCESS);
541 
542  // validate reached poses
543  std::vector<geometry_msgs::Pose> reached_poses;
544  getPositionFK(tip_frames, sol, reached_poses, robot_state);
545  EXPECT_NEAR_POSES({ goal }, reached_poses, tolerance_);
546 
547  // validate ground truth
548  if (!truth.empty())
549  {
550  ASSERT_EQ(truth.size(), sol.size()) << "Invalid size of ground truth joints vector";
551  Eigen::Map<Eigen::ArrayXd> solution(sol.data(), sol.size());
552  Eigen::Map<Eigen::ArrayXd> ground_truth(truth.data(), truth.size());
553  EXPECT_TRUE(solution.isApprox(ground_truth, 10 * tolerance_)) << solution.transpose() << std::endl
554  << ground_truth.transpose() << std::endl;
555  }
556  };
557 
558  ASSERT_EQ(tests.getType(), XmlRpc::XmlRpcValue::TypeArray);
559  std::vector<double> ground_truth;
560 
561  /* process tests definitions on parameter server of the form
562  - pos.x: +0.1
563  joints: [0, 0, 0, 0, 0, 0]
564  - pos.y: -0.1
565  joints: [0, 0, 0, 0, 0, 0]
566  */
567  for (int i = 0; i < tests.size(); ++i) // NOLINT(modernize-loop-convert)
568  {
569  goal = initial; // reset goal to initial
570  ground_truth.clear();
571 
572  ASSERT_EQ(tests[i].getType(), XmlRpc::XmlRpcValue::TypeStruct);
573  std::string desc;
574  for (std::pair<const std::string, XmlRpc::XmlRpcValue>& member : tests[i])
575  {
576  if (member.first == "joints")
577  parseVector(member.second, ground_truth);
578  else if (!parseGoal(member.first, member.second, goal, desc))
579  ROS_WARN_STREAM("unknown unit_tests' key: " << member.first);
580  }
581  {
582  SCOPED_TRACE(desc);
583  validate_ik(tf2::toMsg(goal), ground_truth);
584  }
585  }
586 }
587 
589 {
590  std::vector<double> seed, fk_values, solution;
591  moveit_msgs::MoveItErrorCodes error_code;
592  solution.resize(kinematics_solver_->getJointNames().size(), 0.0);
593  const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
594  moveit::core::RobotState robot_state(robot_model_);
595  robot_state.setToDefaultValues();
596 
597  unsigned int success = 0;
598  for (unsigned int i = 0; i < num_ik_tests_; ++i)
599  {
600  seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
601  fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
602  robot_state.setToRandomPositions(jmg_, this->rng_);
603  robot_state.copyJointGroupPositions(jmg_, fk_values);
604  std::vector<geometry_msgs::Pose> poses;
605  ASSERT_TRUE(getPositionFK(fk_names, fk_values, poses, robot_state));
606 
607  kinematics_solver_->searchPositionIK(poses[0], seed, timeout_, solution, error_code);
608  if (error_code.val == error_code.SUCCESS)
609  success++;
610  else
611  continue;
612 
613  std::vector<geometry_msgs::Pose> reached_poses;
614  getPositionFK(fk_names, solution, reached_poses, robot_state);
615  EXPECT_NEAR_POSES(poses, reached_poses, tolerance_);
616  }
617 
618  ROS_INFO_STREAM("Success Rate: " << (double)success / num_ik_tests_);
619  EXPECT_GE(success, EXPECTED_SUCCESS_RATE * num_ik_tests_);
620 }
621 
622 TEST_F(KinematicsTest, searchIKWithCallback)
623 {
624  std::vector<double> seed, fk_values, solution;
625  moveit_msgs::MoveItErrorCodes error_code;
626  solution.resize(kinematics_solver_->getJointNames().size(), 0.0);
627  const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
628  moveit::core::RobotState robot_state(robot_model_);
629  robot_state.setToDefaultValues();
630 
631  unsigned int success = 0;
632  for (unsigned int i = 0; i < num_ik_cb_tests_; ++i)
633  {
634  seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
635  fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
636  robot_state.setToRandomPositions(jmg_, this->rng_);
637  robot_state.copyJointGroupPositions(jmg_, fk_values);
638  std::vector<geometry_msgs::Pose> poses;
639  ASSERT_TRUE(getPositionFK(fk_names, fk_values, poses, robot_state));
640  if (poses[0].position.z <= 0.0f)
641  {
642  --i; // draw a new random state
643  continue;
644  }
645 
646  kinematics_solver_->searchPositionIK(
647  poses[0], fk_values, timeout_, solution,
648  [this, &robot_state](const geometry_msgs::Pose& /*unused*/, const std::vector<double>& joints,
649  moveit_msgs::MoveItErrorCodes& error_code) {
650  searchIKCallback(joints, error_code, robot_state);
651  },
652  error_code);
653  if (error_code.val == error_code.SUCCESS)
654  success++;
655  else
656  continue;
657 
658  std::vector<geometry_msgs::Pose> reached_poses;
659  getPositionFK(fk_names, solution, reached_poses, robot_state);
660  EXPECT_NEAR_POSES(poses, reached_poses, tolerance_);
661  }
662 
663  ROS_INFO_STREAM("Success Rate: " << (double)success / num_ik_cb_tests_);
664  EXPECT_GE(success, EXPECTED_SUCCESS_RATE * num_ik_cb_tests_);
665 }
666 
668 {
669  std::vector<double> fk_values, solution;
670  moveit_msgs::MoveItErrorCodes error_code;
671  solution.resize(kinematics_solver_->getJointNames().size(), 0.0);
672  const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
673  moveit::core::RobotState robot_state(robot_model_);
674  robot_state.setToDefaultValues();
675 
676  for (unsigned int i = 0; i < num_ik_tests_; ++i)
677  {
678  fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
679  robot_state.setToRandomPositions(jmg_, this->rng_);
680  robot_state.copyJointGroupPositions(jmg_, fk_values);
681  std::vector<geometry_msgs::Pose> poses, poses_from_ik;
682 
683  ASSERT_TRUE(getPositionFK(fk_names, fk_values, poses, robot_state));
684  kinematics_solver_->getPositionIK(poses[0], fk_values, solution, error_code);
685  EXPECT_EQ(error_code.val, error_code.SUCCESS);
686 
687  if (position_only_check_)
688  {
689  // The joint state check is skipped when position_only_check_ is true,
690  // because matching two sets of joint states would imply fully matching the two corresponding poses.
691  // Instead we only check if the derived position from the solution is close enough to the original one.
692  ASSERT_TRUE(getPositionFK(fk_names, solution, poses_from_ik, robot_state));
693  EXPECT_NEAR_POSES(poses, poses_from_ik, tolerance_);
694  }
695  else
696  {
697  // starting from the correct solution, should yield the same pose
698  Eigen::Map<Eigen::ArrayXd> sol(solution.data(), solution.size());
699  Eigen::Map<Eigen::ArrayXd> truth(fk_values.data(), fk_values.size());
700  EXPECT_TRUE(sol.isApprox(truth, tolerance_)) << sol.transpose() << std::endl << truth.transpose() << std::endl;
701  }
702  }
703 }
704 
705 TEST_F(KinematicsTest, getIKMultipleSolutions)
706 {
707  std::vector<double> seed, fk_values;
708  std::vector<std::vector<double>> solutions;
711 
712  const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
713  moveit::core::RobotState robot_state(robot_model_);
714  robot_state.setToDefaultValues();
715 
716  unsigned int success = 0;
717  for (unsigned int i = 0; i < num_ik_multiple_tests_; ++i)
718  {
719  seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
720  fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
721  robot_state.setToRandomPositions(jmg_, this->rng_);
722  robot_state.copyJointGroupPositions(jmg_, fk_values);
723  std::vector<geometry_msgs::Pose> poses;
724  ASSERT_TRUE(getPositionFK(fk_names, fk_values, poses, robot_state));
725 
726  solutions.clear();
727  kinematics_solver_->getPositionIK(poses, fk_values, solutions, result, options);
728 
730  success += solutions.empty() ? 0 : 1;
731  else
732  continue;
733 
734  std::vector<geometry_msgs::Pose> reached_poses;
735  for (const auto& s : solutions)
736  {
737  getPositionFK(fk_names, s, reached_poses, robot_state);
738  EXPECT_NEAR_POSES(poses, reached_poses, tolerance_);
739  }
740  }
741 
742  ROS_INFO_STREAM("Success Rate: " << (double)success / num_ik_multiple_tests_);
743  EXPECT_GE(success, EXPECTED_SUCCESS_RATE * num_ik_multiple_tests_);
744 }
745 
746 // validate that getPositionIK() retrieves closest solution to seed
747 TEST_F(KinematicsTest, getNearestIKSolution)
748 {
749  std::vector<std::vector<double>> solutions;
752 
753  std::vector<double> seed, fk_values, solution;
754  moveit_msgs::MoveItErrorCodes error_code;
755  const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
756  moveit::core::RobotState robot_state(robot_model_);
757  robot_state.setToDefaultValues();
758 
759  for (unsigned int i = 0; i < num_nearest_ik_tests_; ++i)
760  {
761  robot_state.setToRandomPositions(jmg_, this->rng_);
762  robot_state.copyJointGroupPositions(jmg_, fk_values);
763  std::vector<geometry_msgs::Pose> poses;
764  ASSERT_TRUE(getPositionFK(fk_names, fk_values, poses, robot_state));
765 
766  // sample seed vector
767  robot_state.setToRandomPositions(jmg_, this->rng_);
768  robot_state.copyJointGroupPositions(jmg_, seed);
769 
770  // getPositionIK for single solution
771  kinematics_solver_->getPositionIK(poses[0], seed, solution, error_code);
772 
773  // check if getPositionIK call for single solution returns solution
774  if (error_code.val != error_code.SUCCESS)
775  continue;
776 
777  const Eigen::Map<const Eigen::VectorXd> seed_eigen(seed.data(), seed.size());
778  double error_get_ik =
779  (Eigen::Map<const Eigen::VectorXd>(solution.data(), solution.size()) - seed_eigen).array().abs().sum();
780 
781  // getPositionIK for multiple solutions
782  solutions.clear();
783  kinematics_solver_->getPositionIK(poses, seed, solutions, result, options);
784 
785  // check if getPositionIK call for multiple solutions returns solution
787  << "Multiple solution call failed, while single solution call succeeded";
789  continue;
790 
791  double smallest_error_multiple_ik = std::numeric_limits<double>::max();
792  for (const auto& s : solutions)
793  {
794  double error_multiple_ik =
795  (Eigen::Map<const Eigen::VectorXd>(s.data(), s.size()) - seed_eigen).array().abs().sum();
796  if (error_multiple_ik <= smallest_error_multiple_ik)
797  smallest_error_multiple_ik = error_multiple_ik;
798  }
799  EXPECT_NEAR(smallest_error_multiple_ik, error_get_ik, tolerance_);
800  }
801 }
802 
803 int main(int argc, char** argv)
804 {
805  testing::InitGoogleTest(&argc, argv);
806  ros::init(argc, argv, "kinematics_plugin_test");
807  ros::NodeHandle nh;
808  int result = RUN_ALL_TESTS();
809  SharedData::release(); // avoid class_loader::LibraryUnloadException
810  return result;
811 }
XmlRpc::XmlRpcValue::size
int size() const
robot_model.h
KinematicsTest::rng_
random_numbers::RandomNumberGenerator rng_
Definition: test_kinematics_plugin.cpp:331
ros::WallDuration::sleep
bool sleep() const
EXPECT_NEAR_POSES
#define EXPECT_NEAR_POSES(lhs, rhs, near)
Definition: test_kinematics_plugin.cpp:349
ros::this_node::getNamespace
const ROSCPP_DECL std::string & getNamespace()
main
int main(int argc, char **argv)
Definition: test_kinematics_plugin.cpp:803
ros::Publisher
TEST_F
TEST_F(KinematicsTest, getFK)
Definition: test_kinematics_plugin.cpp:353
SharedData::root_link_
std::string root_link_
Definition: test_kinematics_plugin.cpp:82
KinematicsTest::num_fk_tests_
unsigned int num_fk_tests_
Definition: test_kinematics_plugin.cpp:340
KinematicsTest::consistency_limits_
std::vector< double > consistency_limits_
Definition: test_kinematics_plugin.cpp:337
SharedData::kinematics_loader_
std::unique_ptr< KinematicsLoader > kinematics_loader_
Definition: test_kinematics_plugin.cpp:81
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
KinematicsTest::searchIKCallback
void searchIKCallback(const std::vector< double > &joint_state, moveit_msgs::MoveItErrorCodes &error_code, moveit::core::RobotState &robot_state)
Definition: test_kinematics_plugin.cpp:277
SharedData::timeout_
double timeout_
Definition: test_kinematics_plugin.cpp:88
SharedData::seed_
std::vector< double > seed_
Definition: test_kinematics_plugin.cpp:86
tf2_eigen.h
tf2::fromMsg
void fromMsg(const A &, B &b)
s
XmlRpcServer s
ros::NodeHandle::getParam
bool getParam(const std::string &key, bool &b) const
KinematicsTest::isNear
testing::AssertionResult isNear(const char *expr1, const char *expr2, const char *, const geometry_msgs::Quaternion &val1, const geometry_msgs::Quaternion &val2, double abs_error)
Definition: test_kinematics_plugin.cpp:231
ros.h
KinematicsTest::num_ik_cb_tests_
unsigned int num_ik_cb_tests_
Definition: test_kinematics_plugin.cpp:341
ros::AsyncSpinner
KinematicsTest::expectNearHelper
testing::AssertionResult expectNearHelper(const char *expr1, const char *expr2, const char *abs_error_expr, const std::vector< geometry_msgs::Pose > &val1, const std::vector< geometry_msgs::Pose > &val2, double abs_error)
Definition: test_kinematics_plugin.cpp:248
KinematicsTest::SetUp
void SetUp() override
Definition: test_kinematics_plugin.cpp:188
XmlRpc::XmlRpcValue::TypeInt
TypeInt
SharedData::num_ik_tests_
int num_ik_tests_
Definition: test_kinematics_plugin.cpp:92
robot_trajectory::RobotTrajectory::addSuffixWayPoint
RobotTrajectory & addSuffixWayPoint(const moveit::core::RobotState &state, double dt)
SharedData::tolerance_
double tolerance_
Definition: test_kinematics_plugin.cpp:89
moveit::core::RobotState
moveit::core::RobotState::copyJointGroupPositions
void copyJointGroupPositions(const std::string &joint_group_name, std::vector< double > &gstate) const
moveit::core::RobotState::getGlobalLinkTransform
const Eigen::Isometry3d & getGlobalLinkTransform(const std::string &link_name)
moveit::core::RobotState::setToRandomPositions
void setToRandomPositions()
robot_state.h
KinematicsTest::position_only_check_
bool position_only_check_
Definition: test_kinematics_plugin.cpp:346
robot_trajectory::RobotTrajectory
parseGoal
static bool parseGoal(const std::string &name, XmlRpc::XmlRpcValue &value, Eigen::Isometry3d &goal, std::string &desc)
Definition: test_kinematics_plugin.cpp:477
EXPECTED_SUCCESS_RATE
const double EXPECTED_SUCCESS_RATE
Definition: test_kinematics_plugin.cpp:58
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
ros::NodeHandle::advertise
Publisher advertise(AdvertiseOptions &ops)
SharedData::position_only_check_
bool position_only_check_
Definition: test_kinematics_plugin.cpp:96
EXPECT_TRUE
#define EXPECT_TRUE(args)
XmlRpc::XmlRpcValue::TypeStruct
TypeStruct
spinner
void spinner()
KinematicsTest::root_link_
std::string root_link_
Definition: test_kinematics_plugin.cpp:332
KinematicsTest::timeout_
double timeout_
Definition: test_kinematics_plugin.cpp:338
robot_trajectory.h
ROS_WARN_STREAM
#define ROS_WARN_STREAM(args)
kinematics::KinematicErrors::OK
OK
KinematicsTest::isNear
testing::AssertionResult isNear(const char *expr1, const char *expr2, const char *, const geometry_msgs::Point &val1, const geometry_msgs::Point &val2, double abs_error)
Definition: test_kinematics_plugin.cpp:216
SharedData::num_ik_cb_tests_
int num_ik_cb_tests_
Definition: test_kinematics_plugin.cpp:91
XmlRpc::XmlRpcValue::TypeDouble
TypeDouble
robot_trajectory::RobotTrajectory::getRobotTrajectoryMsg
void getRobotTrajectoryMsg(moveit_msgs::RobotTrajectory &trajectory, const std::vector< std::string > &joint_filter=std::vector< std::string >()) const
SharedData::plugin_fk_support_
bool plugin_fk_support_
Definition: test_kinematics_plugin.cpp:95
SharedData::num_ik_multiple_tests_
int num_ik_multiple_tests_
Definition: test_kinematics_plugin.cpp:93
name
std::string name
ROBOT_DESCRIPTION_PARAM
const std::string ROBOT_DESCRIPTION_PARAM
Definition: test_kinematics_plugin.cpp:56
KinematicsTest::robot_model_
moveit::core::RobotModelPtr robot_model_
Definition: test_kinematics_plugin.cpp:328
EXPECT_NEAR
#define EXPECT_NEAR(a, b, prec)
KinematicsTest
Definition: test_kinematics_plugin.cpp:164
DEFAULT_TOLERANCE
const double DEFAULT_TOLERANCE
Definition: test_kinematics_plugin.cpp:59
SharedData::num_nearest_ik_tests_
int num_nearest_ik_tests_
Definition: test_kinematics_plugin.cpp:94
moveit::core::RobotState::setToDefaultValues
void setToDefaultValues()
KinematicsTest::getPositionFK
bool getPositionFK(const std::vector< std::string > &link_names, const std::vector< double > &joint_state, std::vector< geometry_msgs::Pose > &poses, moveit::core::RobotState &robot_state)
Definition: test_kinematics_plugin.cpp:295
moveit::core::RobotState::setToRandomPositionsNearBy
void setToRandomPositionsNearBy(const JointModelGroup *group, const RobotState &seed, double distance)
KinematicsTest::jmg_
moveit::core::JointModelGroup * jmg_
Definition: test_kinematics_plugin.cpp:329
XmlRpcValue.h
pluginlib::ClassLoader
class_loader.hpp
KinematicsTest::operator=
void operator=(const SharedData &data)
Definition: test_kinematics_plugin.cpp:167
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
kinematics::KinematicsResult
SharedData::robot_model_
moveit::core::RobotModelPtr robot_model_
Definition: test_kinematics_plugin.cpp:80
KinematicsTest::joints_
std::vector< std::string > joints_
Definition: test_kinematics_plugin.cpp:335
getParam
bool getParam(const std::string &param, T &val)
Definition: test_kinematics_plugin.cpp:62
XmlRpc::XmlRpcValue::getType
const Type & getType() const
SharedData::KinematicsLoader
pluginlib::ClassLoader< kinematics::KinematicsBase > KinematicsLoader
Definition: test_kinematics_plugin.cpp:78
SharedData::SharedData
SharedData()
Definition: test_kinematics_plugin.cpp:99
random_numbers::RandomNumberGenerator
XmlRpc::XmlRpcValue::TypeArray
TypeArray
moveit::core::RobotState::updateLinkTransforms
void updateLinkTransforms()
KinematicsTest::group_name_
std::string group_name_
Definition: test_kinematics_plugin.cpp:334
KinematicsTest::kinematics_solver_
kinematics::KinematicsBasePtr kinematics_solver_
Definition: test_kinematics_plugin.cpp:330
values
std::vector< double > values
SharedData::release
static void release()
Definition: test_kinematics_plugin.cpp:157
kinematics::KinematicsQueryOptions
SharedData::instance
static const SharedData & instance()
Definition: test_kinematics_plugin.cpp:152
SharedData
Definition: test_kinematics_plugin.cpp:75
kinematics_base.h
moveit::core::JointModelGroup
SharedData::initialize
void initialize()
Definition: test_kinematics_plugin.cpp:104
DEFAULT_SEARCH_DISCRETIZATION
const double DEFAULT_SEARCH_DISCRETIZATION
Definition: test_kinematics_plugin.cpp:57
tf2::toMsg
B toMsg(const A &a)
KinematicsTest::num_ik_tests_
unsigned int num_ik_tests_
Definition: test_kinematics_plugin.cpp:342
param
T param(const std::string &param_name, const T &default_val)
conversions.h
KinematicsTest::tip_link_
std::string tip_link_
Definition: test_kinematics_plugin.cpp:333
SharedData::group_name_
std::string group_name_
Definition: test_kinematics_plugin.cpp:84
KinematicsTest::plugin_fk_support_
bool plugin_fk_support_
Definition: test_kinematics_plugin.cpp:345
ros::WallDuration
KinematicsTest::num_nearest_ik_tests_
unsigned int num_nearest_ik_tests_
Definition: test_kinematics_plugin.cpp:344
moveit::core::RobotState::setJointGroupPositions
void setJointGroupPositions(const std::string &joint_group_name, const double *gstate)
SharedData::tip_link_
std::string tip_link_
Definition: test_kinematics_plugin.cpp:83
SharedData::createUniqueInstance
auto createUniqueInstance(const std::string &name) const
Definition: test_kinematics_plugin.cpp:147
parseDouble
static double parseDouble(XmlRpc::XmlRpcValue &v)
Definition: test_kinematics_plugin.cpp:456
KinematicsTest::num_ik_multiple_tests_
unsigned int num_ik_multiple_tests_
Definition: test_kinematics_plugin.cpp:343
kinematics::KinematicsResult::kinematic_error
KinematicError kinematic_error
SharedData::consistency_limits_
std::vector< double > consistency_limits_
Definition: test_kinematics_plugin.cpp:87
SharedData::joints_
std::vector< std::string > joints_
Definition: test_kinematics_plugin.cpp:85
SharedData::num_fk_tests_
int num_fk_tests_
Definition: test_kinematics_plugin.cpp:90
moveit::core::robotStateToRobotStateMsg
void robotStateToRobotStateMsg(const RobotState &state, moveit_msgs::RobotState &robot_state, bool copy_attached_bodies=true)
EXPECT_EQ
#define EXPECT_EQ(a, b)
XmlRpc::XmlRpcValue
ros::NodeHandle
KinematicsTest::seed_
std::vector< double > seed_
Definition: test_kinematics_plugin.cpp:336
parseVector
static void parseVector(XmlRpc::XmlRpcValue &vec, std::vector< double > &values, size_t num=0)
Definition: test_kinematics_plugin.cpp:465
KinematicsTest::tolerance_
double tolerance_
Definition: test_kinematics_plugin.cpp:339


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