38 #include <gtest/gtest.h>
48 #include <moveit/rdf_loader/rdf_loader.h>
53 #include <moveit_msgs/DisplayTrajectory.h>
62 inline bool getParam(
const std::string& param, T& val)
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";
113 kinematics_loader_ = std::make_unique<KinematicsLoader>(
"moveit_core",
"kinematics::KinematicsBase");
192 std::string plugin_name;
193 ASSERT_TRUE(
getParam(
"ik_plugin_name", plugin_name));
203 <<
"Solver failed to initialize";
216 testing::AssertionResult
isNear(
const char* expr1,
const char* expr2,
const char* ,
217 const geometry_msgs::Point& val1,
const geometry_msgs::Point& val2,
double abs_error)
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();
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 <<
"]";
231 testing::AssertionResult
isNear(
const char* expr1,
const char* expr2,
const char* ,
232 const geometry_msgs::Quaternion& val1,
const geometry_msgs::Quaternion& val2,
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();
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 <<
"]";
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)
252 if (val1.size() != val2.size())
253 return testing::AssertionFailure() <<
"Different vector sizes"
254 <<
"\nExpected: " << expr1 <<
" (" << val1.size() <<
")"
255 <<
"\nActual: " << expr2 <<
" (" << val2.size() <<
")";
257 for (
size_t i = 0; i < val1.size(); ++i)
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_);
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_);
274 return testing::AssertionSuccess();
277 void searchIKCallback(
const std::vector<double>& joint_state, moveit_msgs::MoveItErrorCodes& error_code,
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))
284 error_code.val = error_code.PLANNING_FAILED;
288 EXPECT_GT(poses[0].position.z, 0.0f);
289 if (poses[0].position.z > 0.0)
290 error_code.val = error_code.SUCCESS;
292 error_code.val = error_code.PLANNING_FAILED;
295 bool getPositionFK(
const std::vector<std::string>& link_names,
const std::vector<double>& joint_state,
311 std::vector<double> joint_state_backup;
317 poses.reserve(link_names.size());
318 for (
const std::string& link_name : link_names)
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_);
355 std::vector<double> joints(kinematics_solver_->getJointNames().size(), 0.0);
356 const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
360 for (
unsigned int i = 0; i < num_fk_tests_; ++i)
364 std::vector<geometry_msgs::Pose> fk_poses;
365 EXPECT_TRUE(getPositionFK(tip_frames, joints, fk_poses, robot_state));
368 std::vector<geometry_msgs::Pose> model_poses;
369 model_poses.reserve(tip_frames.size());
370 for (
const auto& tip : tip_frames)
379 std::vector<double> seed, goal, solution;
380 const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
387 bool publish_trajectory =
false;
388 getParam<bool>(
"publish_trajectory", publish_trajectory);
389 moveit_msgs::DisplayTrajectory msg;
390 msg.model_id = robot_model_->getName();
392 msg.trajectory.resize(1);
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)
407 std::vector<geometry_msgs::Pose> poses;
408 ASSERT_TRUE(getPositionFK(tip_frames, goal, poses, robot_state));
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)
420 std::vector<geometry_msgs::Pose> reached_poses;
421 getPositionFK(tip_frames, solution, reached_poses, robot_state);
426 if (!position_only_check_)
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))
444 if (publish_trajectory)
459 return static_cast<double>(v);
461 return static_cast<int>(v);
470 ASSERT_EQ(
static_cast<size_t>(vec.
size()), num);
474 for (
int i = 0; i < vec.
size(); ++i)
479 std::ostringstream oss;
480 std::vector<double> vec;
484 Eigen::Quaterniond q(vec[3], vec[0], vec[1], vec[2]);
487 goal.translation() = Eigen::Map<Eigen::Vector3d>(vec.data());
488 oss <<
name <<
" " << goal.translation().transpose() <<
" " << q.vec().transpose() <<
" " << q.w();
492 for (
unsigned char axis = 0; axis < 3; ++axis)
494 char axis_char =
'x' + axis;
496 if (
name == (std::string(
"pos.") + axis_char))
503 else if (
name == (std::string(
"rot.") + axis_char))
505 goal *= Eigen::AngleAxisd(
parseDouble(value), Eigen::Vector3d::Unit(axis));
519 std::vector<double> seed, sol;
520 const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
530 std::vector<geometry_msgs::Pose> poses;
531 ASSERT_TRUE(getPositionFK(tip_frames, seed, poses, robot_state));
532 Eigen::Isometry3d initial, goal;
535 auto validate_ik = [&](
const geometry_msgs::Pose& goal, std::vector<double>& truth) {
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);
543 std::vector<geometry_msgs::Pose> reached_poses;
544 getPositionFK(tip_frames, sol, reached_poses, robot_state);
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;
559 std::vector<double> ground_truth;
567 for (
int i = 0; i < tests.
size(); ++i)
570 ground_truth.clear();
574 for (std::pair<const std::string, XmlRpc::XmlRpcValue>& member : tests[i])
576 if (member.first ==
"joints")
578 else if (!
parseGoal(member.first, member.second, goal, desc))
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();
597 unsigned int success = 0;
598 for (
unsigned int i = 0; i < num_ik_tests_; ++i)
600 seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
601 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
604 std::vector<geometry_msgs::Pose> poses;
605 ASSERT_TRUE(getPositionFK(fk_names, fk_values, poses, robot_state));
607 kinematics_solver_->searchPositionIK(poses[0], seed, timeout_, solution, error_code);
608 if (error_code.val == error_code.SUCCESS)
613 std::vector<geometry_msgs::Pose> reached_poses;
614 getPositionFK(fk_names, solution, reached_poses, robot_state);
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();
631 unsigned int success = 0;
632 for (
unsigned int i = 0; i < num_ik_cb_tests_; ++i)
634 seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
635 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
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)
646 kinematics_solver_->searchPositionIK(
647 poses[0], fk_values, timeout_, solution,
648 [
this, &robot_state](
const geometry_msgs::Pose& ,
const std::vector<double>& joints,
649 moveit_msgs::MoveItErrorCodes& error_code) {
650 searchIKCallback(joints, error_code, robot_state);
653 if (error_code.val == error_code.SUCCESS)
658 std::vector<geometry_msgs::Pose> reached_poses;
659 getPositionFK(fk_names, solution, reached_poses, robot_state);
663 ROS_INFO_STREAM(
"Success Rate: " << (
double)success / num_ik_cb_tests_);
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();
676 for (
unsigned int i = 0; i < num_ik_tests_; ++i)
678 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
681 std::vector<geometry_msgs::Pose> poses, poses_from_ik;
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);
687 if (position_only_check_)
692 ASSERT_TRUE(getPositionFK(fk_names, solution, poses_from_ik, robot_state));
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;
707 std::vector<double> seed, fk_values;
708 std::vector<std::vector<double>> solutions;
712 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
716 unsigned int success = 0;
717 for (
unsigned int i = 0; i < num_ik_multiple_tests_; ++i)
719 seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
720 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
723 std::vector<geometry_msgs::Pose> poses;
724 ASSERT_TRUE(getPositionFK(fk_names, fk_values, poses, robot_state));
727 kinematics_solver_->getPositionIK(poses, fk_values, solutions, result, options);
730 success += solutions.empty() ? 0 : 1;
734 std::vector<geometry_msgs::Pose> reached_poses;
735 for (
const auto&
s : solutions)
737 getPositionFK(fk_names,
s, reached_poses, robot_state);
742 ROS_INFO_STREAM(
"Success Rate: " << (
double)success / num_ik_multiple_tests_);
749 std::vector<std::vector<double>> solutions;
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();
759 for (
unsigned int i = 0; i < num_nearest_ik_tests_; ++i)
763 std::vector<geometry_msgs::Pose> poses;
764 ASSERT_TRUE(getPositionFK(fk_names, fk_values, poses, robot_state));
771 kinematics_solver_->getPositionIK(poses[0], seed, solution, error_code);
774 if (error_code.val != error_code.SUCCESS)
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();
783 kinematics_solver_->getPositionIK(poses, seed, solutions, result, options);
787 <<
"Multiple solution call failed, while single solution call succeeded";
791 double smallest_error_multiple_ik = std::numeric_limits<double>::max();
792 for (
const auto&
s : solutions)
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;
799 EXPECT_NEAR(smallest_error_multiple_ik, error_get_ik, tolerance_);
803 int main(
int argc,
char** argv)
805 testing::InitGoogleTest(&argc, argv);
806 ros::init(argc, argv,
"kinematics_plugin_test");
808 int result = RUN_ALL_TESTS();