38#include <gtest/gtest.h>
41#include <pluginlib/class_loader.hpp>
42#include <rclcpp/executors/single_threaded_executor.hpp>
43#include <rclcpp/rclcpp.hpp>
44#include <tf2_eigen/tf2_eigen.hpp>
54#include <moveit_msgs/msg/display_trajectory.hpp>
67static const std::string UNDEFINED =
"<undefined>";
73 typedef pluginlib::ClassLoader<kinematics::KinematicsBase> KinematicsLoader;
75 moveit::core::RobotModelPtr robot_model_;
76 std::unique_ptr<KinematicsLoader> kinematics_loader_;
77 std::string root_link_;
78 std::string tip_link_;
79 std::string group_name_;
80 std::string ik_plugin_name_;
81 std::vector<std::string> joints_;
82 std::vector<double> seed_;
83 std::vector<double> consistency_limits_;
89 int num_ik_multiple_tests_;
90 int num_nearest_ik_tests_;
91 bool publish_trajectory_;
101 rclcpp::NodeOptions node_options;
102 node_options.automatically_declare_parameters_from_overrides(
true);
103 node_ = rclcpp::Node::make_shared(
"moveit_kinematics_test", node_options);
108 robot_model_ = std::make_shared<moveit::core::RobotModel>(
rdf_loader.getURDF(),
rdf_loader.getSRDF());
109 ASSERT_TRUE(
bool(robot_model_)) <<
"Failed to load robot model";
112 kinematics_loader_ = std::make_unique<KinematicsLoader>(
"moveit_core",
"kinematics::KinematicsBase");
113 ASSERT_TRUE(
bool(kinematics_loader_)) <<
"Failed to instantiate ClassLoader";
116 ASSERT_TRUE(
node_->get_parameter(
"group", group_name_));
117 ASSERT_TRUE(
node_->get_parameter(
"tip_link", tip_link_));
118 ASSERT_TRUE(
node_->get_parameter(
"root_link", root_link_));
119 ASSERT_TRUE(
node_->get_parameter(
"joint_names", joints_));
120 node_->get_parameter_or(
"seed", seed_, { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 });
121 ASSERT_TRUE(seed_.empty() || seed_.size() == joints_.size()) <<
"If set, 'seed' size must match 'joint_names' size";
122 node_->get_parameter_or(
"consistency_limits", consistency_limits_, consistency_limits_);
123 ASSERT_TRUE(consistency_limits_.empty() || consistency_limits_.size() == joints_.size())
124 <<
"If set, 'consistency_limits' size must match 'joint_names' size";
125 ASSERT_TRUE(
node_->get_parameter(
"ik_timeout", timeout_));
126 ASSERT_TRUE(timeout_ > 0.0) <<
"'ik_timeout' must be more than 0.0 seconds";
127 ASSERT_TRUE(
node_->get_parameter(
"tolerance", tolerance_));
128 ASSERT_TRUE(tolerance_ > 0.0) <<
"'tolerance' must be greater than 0.0";
129 ASSERT_TRUE(
node_->get_parameter(
"num_fk_tests", num_fk_tests_));
130 ASSERT_TRUE(
node_->get_parameter(
"num_ik_cb_tests", num_ik_cb_tests_));
131 ASSERT_TRUE(
node_->get_parameter(
"num_ik_tests", num_ik_tests_));
132 ASSERT_TRUE(
node_->get_parameter(
"num_ik_multiple_tests", num_ik_multiple_tests_));
133 ASSERT_TRUE(
node_->get_parameter(
"num_nearest_ik_tests", num_nearest_ik_tests_));
134 ASSERT_TRUE(
node_->get_parameter(
"ik_plugin_name", ik_plugin_name_));
135 node_->get_parameter_or(
"publish_trajectory", publish_trajectory_,
false);
137 ASSERT_TRUE(robot_model_->hasJointModelGroup(group_name_));
138 ASSERT_TRUE(robot_model_->hasLinkModel(root_link_));
139 ASSERT_TRUE(robot_model_->hasLinkModel(tip_link_));
143 std::shared_ptr<rclcpp::Node>
node_;
147 return kinematics_loader_->createUniqueInstance(name);
158 shared.kinematics_loader_.reset();
198 <<
"Solver failed to initialize";
210 testing::AssertionResult
isNear(
const char* expr1,
const char* expr2,
const char* ,
211 const geometry_msgs::msg::Point& val1,
const geometry_msgs::msg::Point& val2,
215 if (std::fabs(val1.x - val2.x) <= abs_error &&
216 std::fabs(val1.y - val2.y) <= abs_error &&
217 std::fabs(val1.z - val2.z) <= abs_error)
218 return testing::AssertionSuccess();
220 return testing::AssertionFailure()
221 << std::setprecision(std::numeric_limits<double>::digits10 + 2)
222 <<
"Expected: " << expr1 <<
" [" << val1.x <<
", " << val1.y <<
", " << val1.z <<
"]\n"
223 <<
"Actual: " << expr2 <<
" [" << val2.x <<
", " << val2.y <<
", " << val2.z <<
']';
226 testing::AssertionResult
isNear(
const char* expr1,
const char* expr2,
const char* ,
227 const geometry_msgs::msg::Quaternion& val1,
228 const geometry_msgs::msg::Quaternion& val2,
double abs_error)
230 if ((std::fabs(val1.x - val2.x) <= abs_error && std::fabs(val1.y - val2.y) <= abs_error &&
231 std::fabs(val1.z - val2.z) <= abs_error && std::fabs(val1.w - val2.w) <= abs_error) ||
232 (std::fabs(val1.x + val2.x) <= abs_error && std::fabs(val1.y + val2.y) <= abs_error &&
233 std::fabs(val1.z + val2.z) <= abs_error && std::fabs(val1.w + val2.w) <= abs_error))
234 return testing::AssertionSuccess();
237 return testing::AssertionFailure()
238 << std::setprecision(std::numeric_limits<double>::digits10 + 2)
239 <<
"Expected: " << expr1 <<
" [" << val1.w <<
", " << val1.x <<
", " << val1.y <<
", " << val1.z <<
"]\n"
240 <<
"Actual: " << expr2 <<
" [" << val2.w <<
", " << val2.x <<
", " << val2.y <<
", " << val2.z <<
']';
243 testing::AssertionResult
expectNearHelper(
const char* expr1,
const char* expr2,
const char* abs_error_expr,
244 const std::vector<geometry_msgs::msg::Pose>& val1,
245 const std::vector<geometry_msgs::msg::Pose>& val2,
double abs_error)
247 if (val1.size() != val2.size())
249 return testing::AssertionFailure() <<
"Different vector sizes"
250 <<
"\nExpected: " << expr1 <<
" (" << val1.size() <<
')'
251 <<
"\nActual: " << expr2 <<
" (" << val2.size() <<
')';
254 for (
size_t i = 0; i < val1.size(); ++i)
256 ::std::stringstream ss;
257 ss <<
'[' << i <<
"].position";
258 GTEST_ASSERT_(
isNear((expr1 + ss.str()).c_str(), (expr2 + ss.str()).c_str(), abs_error_expr, val1[i].position,
259 val2[i].position, abs_error),
260 GTEST_NONFATAL_FAILURE_);
263 ss <<
'[' << i <<
"].orientation";
264 GTEST_ASSERT_(
isNear((expr1 + ss.str()).c_str(), (expr2 + ss.str()).c_str(), abs_error_expr, val1[i].orientation,
265 val2[i].orientation, abs_error),
266 GTEST_NONFATAL_FAILURE_);
268 return testing::AssertionSuccess();
271 void searchIKCallback(
const std::vector<double>& joint_state, moveit_msgs::msg::MoveItErrorCodes& error_code)
273 std::vector<std::string> link_names = {
tip_link_ };
274 std::vector<geometry_msgs::msg::Pose> poses;
277 error_code.val = error_code.PLANNING_FAILED;
281 EXPECT_GT(poses[0].position.z, 0.0f);
282 if (poses[0].position.z > 0.0)
284 error_code.val = error_code.SUCCESS;
288 error_code.val = error_code.PLANNING_FAILED;
297 random_numbers::RandomNumberGenerator
rng_{ 42 };
315#define EXPECT_NEAR_POSES(lhs, rhs, near) \
316 SCOPED_TRACE("EXPECT_NEAR_POSES(" #lhs ", " #rhs ")"); \
317 GTEST_ASSERT_(expectNearHelper(#lhs, #rhs, #near, lhs, rhs, near), GTEST_NONFATAL_FAILURE_);
321 std::vector<double> joints(kinematics_solver_->getJointNames().size(), 0.0);
322 const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
326 for (
unsigned int i = 0; i < num_fk_tests_; ++i)
330 std::vector<geometry_msgs::msg::Pose> fk_poses;
331 EXPECT_TRUE(kinematics_solver_->getPositionFK(tip_frames, joints, fk_poses));
334 std::vector<geometry_msgs::msg::Pose> model_poses;
335 model_poses.reserve(tip_frames.size());
336 for (
const auto& tip : tip_frames)
345 std::vector<double> seed, goal, solution;
346 const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
353 moveit_msgs::msg::DisplayTrajectory msg;
354 msg.model_id = robot_model_->getName();
356 msg.trajectory.resize(1);
359 unsigned int failures = 0;
360 static constexpr double NEAR_JOINT = 0.1;
361 const std::vector<double> consistency_limits(jmg_->getVariableCount(), 1.05 * NEAR_JOINT);
362 for (
unsigned int i = 0; i < num_ik_tests_; ++i)
371 std::vector<geometry_msgs::msg::Pose> poses;
372 ASSERT_TRUE(kinematics_solver_->getPositionFK(tip_frames, goal, poses));
375 moveit_msgs::msg::MoveItErrorCodes error_code;
376 kinematics_solver_->searchPositionIK(poses[0], seed, 0.1, consistency_limits, solution, error_code);
377 if (error_code.val != error_code.SUCCESS)
384 std::vector<geometry_msgs::msg::Pose> reached_poses;
385 kinematics_solver_->getPositionFK(tip_frames, solution, reached_poses);
389 auto diff = Eigen::Map<Eigen::ArrayXd>(solution.data(), solution.size()) -
390 Eigen::Map<Eigen::ArrayXd>(goal.data(), goal.size());
391 if (!diff.isZero(1.05 * NEAR_JOINT))
394 RCLCPP_WARN_STREAM(
getLogger(),
"jump in [" << i <<
"]: " << diff.transpose());
402 if (publish_trajectory_)
404 auto pub = node_->create_publisher<moveit_msgs::msg::DisplayTrajectory>(
"display_random_walk", 1);
407 rclcpp::executors::SingleThreadedExecutor executor;
408 executor.add_node(node_);
409 executor.spin_some();
413static bool parsePose(
const std::vector<double>& pose_values, Eigen::Isometry3d& goal)
415 std::vector<double> vec;
416 Eigen::Quaterniond q;
417 if (pose_values.size() == 6)
419 q = Eigen::AngleAxisd(pose_values[3], Eigen::Vector3d::UnitX()) *
420 Eigen::AngleAxisd(pose_values[4], Eigen::Vector3d::UnitY()) *
421 Eigen::AngleAxisd(pose_values[5], Eigen::Vector3d::UnitZ());
423 else if (pose_values.size() == 7)
425 q = Eigen::Quaterniond(pose_values[3], pose_values[4], pose_values[5], pose_values[6]);
433 goal.translation() = Eigen::Vector3d(pose_values[0], pose_values[1], pose_values[2]);
440 static const std::string TEST_POSES_PARAM =
"unit_test_poses";
441 size_t expected_test_poses = 0;
442 node_->get_parameter_or(TEST_POSES_PARAM +
".size", expected_test_poses, expected_test_poses);
444 std::vector<double> sol;
445 const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
451 std::vector<geometry_msgs::msg::Pose> poses;
452 ASSERT_TRUE(kinematics_solver_->getPositionFK(tip_frames, seed_, poses));
453 Eigen::Isometry3d initial, goal;
454 tf2::fromMsg(poses[0], initial);
456 RCLCPP_DEBUG(
getLogger(),
"Initial: %f %f %f %f %f %f %f\n", poses[0].position.x, poses[0].position.y,
457 poses[0].position.z, poses[0].orientation.x, poses[0].orientation.y, poses[0].orientation.z,
458 poses[0].orientation.w);
460 auto validate_ik = [&](
const geometry_msgs::msg::Pose& goal, std::vector<double>& truth) {
462 moveit_msgs::msg::MoveItErrorCodes error_code;
464 RCLCPP_DEBUG(
getLogger(),
"Goal %f %f %f %f %f %f %f\n", goal.position.x, goal.position.y, goal.position.z,
465 goal.orientation.x, goal.orientation.y, goal.orientation.z, goal.orientation.w);
467 kinematics_solver_->searchPositionIK(goal, seed_, timeout_,
468 const_cast<const std::vector<double>&
>(consistency_limits_), sol, error_code);
469 ASSERT_EQ(error_code.val, error_code.SUCCESS);
472 std::vector<geometry_msgs::msg::Pose> reached_poses;
473 kinematics_solver_->getPositionFK(tip_frames, sol, reached_poses);
479 ASSERT_EQ(truth.size(), sol.size()) <<
"Invalid size of ground truth joints vector";
480 Eigen::Map<Eigen::ArrayXd> solution(sol.data(), sol.size());
481 Eigen::Map<Eigen::ArrayXd> ground_truth(truth.data(), truth.size());
482 EXPECT_TRUE(solution.isApprox(ground_truth, 10 * tolerance_)) << solution.transpose() <<
'\n'
483 << ground_truth.transpose() <<
'\n';
487 std::vector<double> ground_truth, pose_values;
488 constexpr char pose_type_relative[] =
"relative";
489 constexpr char pose_type_absolute[] =
"absolute";
501 for (
size_t i = 0; i < expected_test_poses; ++i)
503 const std::string pose_name =
"pose_" + std::to_string(i);
504 const std::string pose_param = TEST_POSES_PARAM +
"." + pose_name;
506 ground_truth.clear();
508 node_->get_parameter_or(pose_param +
".joints", ground_truth, ground_truth);
509 if (!ground_truth.empty())
511 ASSERT_EQ(ground_truth.size(), joints_.size())
512 <<
"Test pose '" << pose_name <<
"' has invalid 'joints' vector size";
516 node_->get_parameter_or(pose_param +
".pose", pose_values, pose_values);
517 ASSERT_TRUE(pose_values.size() == 6 || pose_values.size() == 7)
518 <<
"Test pose '" << pose_name <<
"' has invalid 'pose' vector size";
520 Eigen::Isometry3d pose;
521 ASSERT_TRUE(parsePose(pose_values, pose)) <<
"Failed to parse 'pose' vector in: " << pose_name;
522 std::string pose_type =
"pose_type_relative";
523 node_->get_parameter_or(pose_param +
".type", pose_type, pose_type);
524 if (pose_type == pose_type_relative)
528 else if (pose_type == pose_type_absolute)
534 FAIL() <<
"Found invalid 'type' in " << pose_name <<
": should be one of '" << pose_type_relative <<
"' or '"
535 << pose_type_absolute <<
'\'';
541 validate_ik(tf2::toMsg(goal), ground_truth);
548 std::vector<double> seed, fk_values, solution;
549 moveit_msgs::msg::MoveItErrorCodes error_code;
550 solution.resize(kinematics_solver_->getJointNames().size(), 0.0);
551 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
555 unsigned int success = 0;
556 for (
unsigned int i = 0; i < num_ik_tests_; ++i)
558 seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
559 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
562 std::vector<geometry_msgs::msg::Pose> poses;
563 ASSERT_TRUE(kinematics_solver_->getPositionFK(fk_names, fk_values, poses));
565 kinematics_solver_->searchPositionIK(poses[0], seed, timeout_, solution, error_code);
566 if (error_code.val == error_code.SUCCESS)
575 std::vector<geometry_msgs::msg::Pose> reached_poses;
576 kinematics_solver_->getPositionFK(fk_names, solution, reached_poses);
580 if (num_ik_cb_tests_ > 0)
582 RCLCPP_INFO_STREAM(
getLogger(),
"Success Rate: " <<
static_cast<double>(success) / num_ik_tests_);
589 std::vector<double> seed, fk_values, solution;
590 moveit_msgs::msg::MoveItErrorCodes error_code;
591 solution.resize(kinematics_solver_->getJointNames().size(), 0.0);
592 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
596 unsigned int success = 0;
597 for (
unsigned int i = 0; i < num_ik_cb_tests_; ++i)
599 seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
600 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
603 std::vector<geometry_msgs::msg::Pose> poses;
604 ASSERT_TRUE(kinematics_solver_->getPositionFK(fk_names, fk_values, poses));
605 if (poses[0].position.z <= 0.0f)
611 kinematics_solver_->searchPositionIK(
612 poses[0], fk_values, timeout_, solution,
613 [
this](
const geometry_msgs::msg::Pose&,
const std::vector<double>& joints,
614 moveit_msgs::msg::MoveItErrorCodes& error_code) { searchIKCallback(joints, error_code); },
616 if (error_code.val == error_code.SUCCESS)
625 std::vector<geometry_msgs::msg::Pose> reached_poses;
626 kinematics_solver_->getPositionFK(fk_names, solution, reached_poses);
630 if (num_ik_cb_tests_ > 0)
632 RCLCPP_INFO_STREAM(
getLogger(),
"Success Rate: " <<
static_cast<double>(success) / num_ik_cb_tests_);
639 std::vector<double> fk_values, solution;
640 moveit_msgs::msg::MoveItErrorCodes error_code;
641 solution.resize(kinematics_solver_->getJointNames().size(), 0.0);
642 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
646 for (
unsigned int i = 0; i < num_ik_tests_; ++i)
648 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
651 std::vector<geometry_msgs::msg::Pose> poses;
653 ASSERT_TRUE(kinematics_solver_->getPositionFK(fk_names, fk_values, poses));
654 kinematics_solver_->getPositionIK(poses[0], fk_values, solution, error_code);
656 EXPECT_EQ(error_code.val, error_code.SUCCESS);
658 Eigen::Map<Eigen::ArrayXd> sol(solution.data(), solution.size());
659 Eigen::Map<Eigen::ArrayXd> truth(fk_values.data(), fk_values.size());
660 EXPECT_TRUE(sol.isApprox(truth, tolerance_)) << sol.transpose() <<
'\n' << truth.transpose() <<
'\n';
666 std::vector<double> seed, fk_values;
667 std::vector<std::vector<double>> solutions;
671 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
675 unsigned int success = 0;
676 for (
unsigned int i = 0; i < num_ik_multiple_tests_; ++i)
678 seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
679 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
682 std::vector<geometry_msgs::msg::Pose> poses;
683 ASSERT_TRUE(kinematics_solver_->getPositionFK(fk_names, fk_values, poses));
686 kinematics_solver_->getPositionIK(poses, fk_values, solutions, result, options);
690 success += solutions.empty() ? 0 : 1;
697 std::vector<geometry_msgs::msg::Pose> reached_poses;
698 for (
const auto& s : solutions)
700 kinematics_solver_->getPositionFK(fk_names, s, reached_poses);
705 if (num_ik_cb_tests_ > 0)
707 RCLCPP_INFO_STREAM(
getLogger(),
"Success Rate: " <<
static_cast<double>(success) / num_ik_multiple_tests_);
715 std::vector<std::vector<double>> solutions;
719 std::vector<double> seed, fk_values, solution;
720 moveit_msgs::msg::MoveItErrorCodes error_code;
721 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
725 for (
unsigned int i = 0; i < num_nearest_ik_tests_; ++i)
729 std::vector<geometry_msgs::msg::Pose> poses;
730 ASSERT_TRUE(kinematics_solver_->getPositionFK(fk_names, fk_values, poses));
737 kinematics_solver_->getPositionIK(poses[0], seed, solution, error_code);
740 if (error_code.val != error_code.SUCCESS)
743 const Eigen::Map<const Eigen::VectorXd> seed_eigen(seed.data(), seed.size());
744 double error_get_ik =
745 (Eigen::Map<const Eigen::VectorXd>(solution.data(), solution.size()) - seed_eigen).array().abs().sum();
749 kinematics_solver_->getPositionIK(poses, seed, solutions, result, options);
753 <<
"Multiple solution call failed, while single solution call succeeded";
757 double smallest_error_multiple_ik = std::numeric_limits<double>::max();
758 for (
const auto& s : solutions)
760 double error_multiple_ik =
761 (Eigen::Map<const Eigen::VectorXd>(s.data(), s.size()) - seed_eigen).array().abs().sum();
762 if (error_multiple_ik <= smallest_error_multiple_ik)
763 smallest_error_multiple_ik = error_multiple_ik;
765 EXPECT_NEAR(smallest_error_multiple_ik, error_get_ik, tolerance_);
771 testing::InitGoogleTest(&argc, argv);
772 rclcpp::init(argc, argv);
773 int result = RUN_ALL_TESTS();
testing::AssertionResult isNear(const char *expr1, const char *expr2, const char *, const geometry_msgs::msg::Point &val1, const geometry_msgs::msg::Point &val2, double abs_error)
std::vector< double > consistency_limits_
void searchIKCallback(const std::vector< double > &joint_state, moveit_msgs::msg::MoveItErrorCodes &error_code)
moveit::core::RobotModelPtr robot_model_
random_numbers::RandomNumberGenerator rng_
unsigned int num_ik_multiple_tests_
std::vector< double > seed_
std::string ik_plugin_name_
unsigned int num_ik_tests_
kinematics::KinematicsBasePtr kinematics_solver_
void operator=(const SharedData &data)
moveit::core::JointModelGroup * jmg_
unsigned int num_nearest_ik_tests_
unsigned int num_fk_tests_
testing::AssertionResult expectNearHelper(const char *expr1, const char *expr2, const char *abs_error_expr, const std::vector< geometry_msgs::msg::Pose > &val1, const std::vector< geometry_msgs::msg::Pose > &val2, double abs_error)
unsigned int num_ik_cb_tests_
std::vector< std::string > joints_
rclcpp::Node::SharedPtr node_
testing::AssertionResult isNear(const char *expr1, const char *expr2, const char *, const geometry_msgs::msg::Quaternion &val1, const geometry_msgs::msg::Quaternion &val2, double abs_error)
auto createUniqueInstance(const std::string &name) const
static const SharedData & instance()
std::shared_ptr< rclcpp::Node > node_
Representation of a robot's state. This includes position, velocity, acceleration and effort.
void setJointGroupPositions(const std::string &joint_group_name, const double *gstate)
Given positions for the variables that make up a group, in the order found in the group (including va...
void copyJointGroupPositions(const std::string &joint_group_name, std::vector< double > &gstate) const
For a given group, copy the position values of the variables that make up the group into another loca...
void setToRandomPositionsNearBy(const JointModelGroup *group, const RobotState &seed, double distance)
Set all joints in group to random values near the value in seed. distance is the maximum amount each ...
void updateLinkTransforms()
Update the reference frame transforms for links. This call is needed before using the transforms of l...
void setToRandomPositions()
Set all joints to random values. Values will be within default bounds.
void setToDefaultValues()
Set all joints to their default positions. The default position is 0, or if that is not within bounds...
const Eigen::Isometry3d & getGlobalLinkTransform(const std::string &link_name)
Get the link transform w.r.t. the root link (model frame) of the RobotModel. This is typically the ro...
Maintain a sequence of waypoints and the time durations between these waypoints.
RobotTrajectory & addSuffixWayPoint(const moveit::core::RobotState &state, double dt)
Add a point to the trajectory.
void getRobotTrajectoryMsg(moveit_msgs::msg::RobotTrajectory &trajectory, const std::vector< std::string > &joint_filter=std::vector< std::string >()) const
void robotStateToRobotStateMsg(const RobotState &state, moveit_msgs::msg::RobotState &robot_state, bool copy_attached_bodies=true)
Convert a MoveIt robot state to a robot state message.
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
A set of options for the kinematics solver.
KinematicError kinematic_error
int main(int argc, char **argv)
rclcpp::Logger getLogger()
const double EXPECTED_SUCCESS_RATE
const double DEFAULT_SEARCH_DISCRETIZATION
#define EXPECT_NEAR_POSES(lhs, rhs, near)
const std::string ROBOT_DESCRIPTION_PARAM
TEST_F(KinematicsTest, getFK)