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>
72 typedef pluginlib::ClassLoader<kinematics::KinematicsBase> KinematicsLoader;
74 moveit::core::RobotModelPtr robot_model_;
75 std::unique_ptr<KinematicsLoader> kinematics_loader_;
76 std::string root_link_;
77 std::string tip_link_;
78 std::string group_name_;
79 std::string ik_plugin_name_;
80 std::vector<std::string> joints_;
81 std::vector<double> seed_;
82 std::vector<double> consistency_limits_;
88 int num_ik_multiple_tests_;
89 int num_nearest_ik_tests_;
90 bool publish_trajectory_;
92 SharedData(
const SharedData&) =
delete;
100 rclcpp::NodeOptions node_options;
101 node_options.automatically_declare_parameters_from_overrides(
true);
102 node_ = rclcpp::Node::make_shared(
"moveit_kinematics_test", node_options);
107 robot_model_ = std::make_shared<moveit::core::RobotModel>(
rdf_loader.getURDF(),
rdf_loader.getSRDF());
108 ASSERT_TRUE(
bool(robot_model_)) <<
"Failed to load robot model";
111 kinematics_loader_ = std::make_unique<KinematicsLoader>(
"moveit_core",
"kinematics::KinematicsBase");
112 ASSERT_TRUE(
bool(kinematics_loader_)) <<
"Failed to instantiate ClassLoader";
115 ASSERT_TRUE(
node_->get_parameter(
"group", group_name_));
116 ASSERT_TRUE(
node_->get_parameter(
"tip_link", tip_link_));
117 ASSERT_TRUE(
node_->get_parameter(
"root_link", root_link_));
118 ASSERT_TRUE(
node_->get_parameter(
"joint_names", joints_));
119 node_->get_parameter_or(
"seed", seed_, { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 });
120 ASSERT_TRUE(seed_.empty() || seed_.size() == joints_.size()) <<
"If set, 'seed' size must match 'joint_names' size";
121 node_->get_parameter_or(
"consistency_limits", consistency_limits_, consistency_limits_);
122 ASSERT_TRUE(consistency_limits_.empty() || consistency_limits_.size() == joints_.size())
123 <<
"If set, 'consistency_limits' size must match 'joint_names' size";
124 ASSERT_TRUE(
node_->get_parameter(
"ik_timeout", timeout_));
125 ASSERT_TRUE(timeout_ > 0.0) <<
"'ik_timeout' must be more than 0.0 seconds";
126 ASSERT_TRUE(
node_->get_parameter(
"tolerance", tolerance_));
127 ASSERT_TRUE(tolerance_ > 0.0) <<
"'tolerance' must be greater than 0.0";
128 ASSERT_TRUE(
node_->get_parameter(
"num_fk_tests", num_fk_tests_));
129 ASSERT_TRUE(
node_->get_parameter(
"num_ik_cb_tests", num_ik_cb_tests_));
130 ASSERT_TRUE(
node_->get_parameter(
"num_ik_tests", num_ik_tests_));
131 ASSERT_TRUE(
node_->get_parameter(
"num_ik_multiple_tests", num_ik_multiple_tests_));
132 ASSERT_TRUE(
node_->get_parameter(
"num_nearest_ik_tests", num_nearest_ik_tests_));
133 ASSERT_TRUE(
node_->get_parameter(
"ik_plugin_name", ik_plugin_name_));
134 node_->get_parameter_or(
"publish_trajectory", publish_trajectory_,
false);
136 ASSERT_TRUE(robot_model_->hasJointModelGroup(group_name_));
137 ASSERT_TRUE(robot_model_->hasLinkModel(root_link_));
138 ASSERT_TRUE(robot_model_->hasLinkModel(tip_link_));
142 std::shared_ptr<rclcpp::Node>
node_;
146 return kinematics_loader_->createUniqueInstance(name);
156 SharedData& shared =
const_cast<SharedData&
>(
instance());
157 shared.kinematics_loader_.reset();
197 <<
"Solver failed to initialize";
209 testing::AssertionResult
isNear(
const char* expr1,
const char* expr2,
const char* ,
210 const geometry_msgs::msg::Point& val1,
const geometry_msgs::msg::Point& val2,
214 if (std::fabs(val1.x - val2.x) <= abs_error &&
215 std::fabs(val1.y - val2.y) <= abs_error &&
216 std::fabs(val1.z - val2.z) <= abs_error)
217 return testing::AssertionSuccess();
219 return testing::AssertionFailure()
220 << std::setprecision(std::numeric_limits<double>::digits10 + 2)
221 <<
"Expected: " << expr1 <<
" [" << val1.x <<
", " << val1.y <<
", " << val1.z <<
"]\n"
222 <<
"Actual: " << expr2 <<
" [" << val2.x <<
", " << val2.y <<
", " << val2.z <<
']';
225 testing::AssertionResult
isNear(
const char* expr1,
const char* expr2,
const char* ,
226 const geometry_msgs::msg::Quaternion& val1,
227 const geometry_msgs::msg::Quaternion& val2,
double abs_error)
229 if ((std::fabs(val1.x - val2.x) <= abs_error && std::fabs(val1.y - val2.y) <= abs_error &&
230 std::fabs(val1.z - val2.z) <= abs_error && std::fabs(val1.w - val2.w) <= abs_error) ||
231 (std::fabs(val1.x + val2.x) <= abs_error && std::fabs(val1.y + val2.y) <= abs_error &&
232 std::fabs(val1.z + val2.z) <= abs_error && std::fabs(val1.w + val2.w) <= abs_error))
233 return testing::AssertionSuccess();
236 return testing::AssertionFailure()
237 << std::setprecision(std::numeric_limits<double>::digits10 + 2)
238 <<
"Expected: " << expr1 <<
" [" << val1.w <<
", " << val1.x <<
", " << val1.y <<
", " << val1.z <<
"]\n"
239 <<
"Actual: " << expr2 <<
" [" << val2.w <<
", " << val2.x <<
", " << val2.y <<
", " << val2.z <<
']';
242 testing::AssertionResult
expectNearHelper(
const char* expr1,
const char* expr2,
const char* abs_error_expr,
243 const std::vector<geometry_msgs::msg::Pose>& val1,
244 const std::vector<geometry_msgs::msg::Pose>& val2,
double abs_error)
246 if (val1.size() != val2.size())
248 return testing::AssertionFailure() <<
"Different vector sizes"
249 <<
"\nExpected: " << expr1 <<
" (" << val1.size() <<
')'
250 <<
"\nActual: " << expr2 <<
" (" << val2.size() <<
')';
253 for (
size_t i = 0; i < val1.size(); ++i)
255 ::std::stringstream ss;
256 ss <<
'[' << i <<
"].position";
257 GTEST_ASSERT_(
isNear((expr1 + ss.str()).c_str(), (expr2 + ss.str()).c_str(), abs_error_expr, val1[i].position,
258 val2[i].position, abs_error),
259 GTEST_NONFATAL_FAILURE_);
262 ss <<
'[' << i <<
"].orientation";
263 GTEST_ASSERT_(
isNear((expr1 + ss.str()).c_str(), (expr2 + ss.str()).c_str(), abs_error_expr, val1[i].orientation,
264 val2[i].orientation, abs_error),
265 GTEST_NONFATAL_FAILURE_);
267 return testing::AssertionSuccess();
270 void searchIKCallback(
const std::vector<double>& joint_state, moveit_msgs::msg::MoveItErrorCodes& error_code)
272 std::vector<std::string> link_names = {
tip_link_ };
273 std::vector<geometry_msgs::msg::Pose> poses;
276 error_code.val = error_code.PLANNING_FAILED;
280 EXPECT_GT(poses[0].position.z, 0.0f);
281 if (poses[0].position.z > 0.0)
283 error_code.val = error_code.SUCCESS;
287 error_code.val = error_code.PLANNING_FAILED;
296 random_numbers::RandomNumberGenerator
rng_{ 42 };
314#define EXPECT_NEAR_POSES(lhs, rhs, near) \
315 SCOPED_TRACE("EXPECT_NEAR_POSES(" #lhs ", " #rhs ")"); \
316 GTEST_ASSERT_(expectNearHelper(#lhs, #rhs, #near, lhs, rhs, near), GTEST_NONFATAL_FAILURE_);
320 std::vector<double> joints(kinematics_solver_->getJointNames().size(), 0.0);
321 const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
325 for (
unsigned int i = 0; i < num_fk_tests_; ++i)
329 std::vector<geometry_msgs::msg::Pose> fk_poses;
330 EXPECT_TRUE(kinematics_solver_->getPositionFK(tip_frames, joints, fk_poses));
333 std::vector<geometry_msgs::msg::Pose> model_poses;
334 model_poses.reserve(tip_frames.size());
335 for (
const auto& tip : tip_frames)
344 std::vector<double> seed, goal, solution;
345 const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
352 moveit_msgs::msg::DisplayTrajectory msg;
353 msg.model_id = robot_model_->getName();
355 msg.trajectory.resize(1);
358 unsigned int failures = 0;
359 static constexpr double NEAR_JOINT = 0.1;
360 const std::vector<double> consistency_limits(jmg_->getVariableCount(), 1.05 * NEAR_JOINT);
361 for (
unsigned int i = 0; i < num_ik_tests_; ++i)
370 std::vector<geometry_msgs::msg::Pose> poses;
371 ASSERT_TRUE(kinematics_solver_->getPositionFK(tip_frames, goal, poses));
374 moveit_msgs::msg::MoveItErrorCodes error_code;
375 kinematics_solver_->searchPositionIK(poses[0], seed, 0.1, consistency_limits, solution, error_code);
376 if (error_code.val != error_code.SUCCESS)
383 std::vector<geometry_msgs::msg::Pose> reached_poses;
384 kinematics_solver_->getPositionFK(tip_frames, solution, reached_poses);
388 auto diff = Eigen::Map<Eigen::ArrayXd>(solution.data(), solution.size()) -
389 Eigen::Map<Eigen::ArrayXd>(goal.data(), goal.size());
390 if (!diff.isZero(1.05 * NEAR_JOINT))
393 RCLCPP_WARN_STREAM(
getLogger(),
"jump in [" << i <<
"]: " << diff.transpose());
401 if (publish_trajectory_)
403 auto pub = node_->create_publisher<moveit_msgs::msg::DisplayTrajectory>(
"display_random_walk", 1);
406 rclcpp::executors::SingleThreadedExecutor executor;
407 executor.add_node(node_);
408 executor.spin_some();
412static bool parsePose(
const std::vector<double>& pose_values, Eigen::Isometry3d& goal)
414 std::vector<double> vec;
415 Eigen::Quaterniond q;
416 if (pose_values.size() == 6)
418 q = Eigen::AngleAxisd(pose_values[3], Eigen::Vector3d::UnitX()) *
419 Eigen::AngleAxisd(pose_values[4], Eigen::Vector3d::UnitY()) *
420 Eigen::AngleAxisd(pose_values[5], Eigen::Vector3d::UnitZ());
422 else if (pose_values.size() == 7)
424 q = Eigen::Quaterniond(pose_values[3], pose_values[4], pose_values[5], pose_values[6]);
432 goal.translation() = Eigen::Vector3d(pose_values[0], pose_values[1], pose_values[2]);
439 static const std::string TEST_POSES_PARAM =
"unit_test_poses";
440 size_t expected_test_poses = 0;
441 node_->get_parameter_or(TEST_POSES_PARAM +
".size", expected_test_poses, expected_test_poses);
443 std::vector<double> sol;
444 const std::vector<std::string>& tip_frames = kinematics_solver_->getTipFrames();
450 std::vector<geometry_msgs::msg::Pose> poses;
451 ASSERT_TRUE(kinematics_solver_->getPositionFK(tip_frames, seed_, poses));
452 Eigen::Isometry3d initial, goal;
453 tf2::fromMsg(poses[0], initial);
455 RCLCPP_DEBUG(
getLogger(),
"Initial: %f %f %f %f %f %f %f\n", poses[0].position.x, poses[0].position.y,
456 poses[0].position.z, poses[0].orientation.x, poses[0].orientation.y, poses[0].orientation.z,
457 poses[0].orientation.w);
459 auto validate_ik = [&](
const geometry_msgs::msg::Pose& goal, std::vector<double>& truth) {
461 moveit_msgs::msg::MoveItErrorCodes error_code;
463 RCLCPP_DEBUG(
getLogger(),
"Goal %f %f %f %f %f %f %f\n", goal.position.x, goal.position.y, goal.position.z,
464 goal.orientation.x, goal.orientation.y, goal.orientation.z, goal.orientation.w);
466 kinematics_solver_->searchPositionIK(goal, seed_, timeout_,
467 const_cast<const std::vector<double>&
>(consistency_limits_), sol, error_code);
468 ASSERT_EQ(error_code.val, error_code.SUCCESS);
471 std::vector<geometry_msgs::msg::Pose> reached_poses;
472 kinematics_solver_->getPositionFK(tip_frames, sol, reached_poses);
478 ASSERT_EQ(truth.size(), sol.size()) <<
"Invalid size of ground truth joints vector";
479 Eigen::Map<Eigen::ArrayXd> solution(sol.data(), sol.size());
480 Eigen::Map<Eigen::ArrayXd> ground_truth(truth.data(), truth.size());
481 EXPECT_TRUE(solution.isApprox(ground_truth, 10 * tolerance_)) << solution.transpose() <<
'\n'
482 << ground_truth.transpose() <<
'\n';
486 std::vector<double> ground_truth, pose_values;
487 constexpr char pose_type_relative[] =
"relative";
488 constexpr char pose_type_absolute[] =
"absolute";
500 for (
size_t i = 0; i < expected_test_poses; ++i)
502 const std::string pose_name =
"pose_" + std::to_string(i);
503 const std::string pose_param = TEST_POSES_PARAM +
"." + pose_name;
505 ground_truth.clear();
507 node_->get_parameter_or(pose_param +
".joints", ground_truth, ground_truth);
508 if (!ground_truth.empty())
510 ASSERT_EQ(ground_truth.size(), joints_.size())
511 <<
"Test pose '" << pose_name <<
"' has invalid 'joints' vector size";
515 node_->get_parameter_or(pose_param +
".pose", pose_values, pose_values);
516 ASSERT_TRUE(pose_values.size() == 6 || pose_values.size() == 7)
517 <<
"Test pose '" << pose_name <<
"' has invalid 'pose' vector size";
519 Eigen::Isometry3d pose;
520 ASSERT_TRUE(parsePose(pose_values, pose)) <<
"Failed to parse 'pose' vector in: " << pose_name;
521 std::string pose_type =
"pose_type_relative";
522 node_->get_parameter_or(pose_param +
".type", pose_type, pose_type);
523 if (pose_type == pose_type_relative)
527 else if (pose_type == pose_type_absolute)
533 FAIL() <<
"Found invalid 'type' in " << pose_name <<
": should be one of '" << pose_type_relative <<
"' or '"
534 << pose_type_absolute <<
'\'';
540 validate_ik(tf2::toMsg(goal), ground_truth);
547 std::vector<double> seed, fk_values, solution;
548 moveit_msgs::msg::MoveItErrorCodes error_code;
549 solution.resize(kinematics_solver_->getJointNames().size(), 0.0);
550 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
554 unsigned int success = 0;
555 for (
unsigned int i = 0; i < num_ik_tests_; ++i)
557 seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
558 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
561 std::vector<geometry_msgs::msg::Pose> poses;
562 ASSERT_TRUE(kinematics_solver_->getPositionFK(fk_names, fk_values, poses));
564 kinematics_solver_->searchPositionIK(poses[0], seed, timeout_, solution, error_code);
565 if (error_code.val == error_code.SUCCESS)
574 std::vector<geometry_msgs::msg::Pose> reached_poses;
575 kinematics_solver_->getPositionFK(fk_names, solution, reached_poses);
579 if (num_ik_cb_tests_ > 0)
581 RCLCPP_INFO_STREAM(
getLogger(),
"Success Rate: " <<
static_cast<double>(success) / num_ik_tests_);
588 std::vector<double> seed, fk_values, solution;
589 moveit_msgs::msg::MoveItErrorCodes error_code;
590 solution.resize(kinematics_solver_->getJointNames().size(), 0.0);
591 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
595 unsigned int success = 0;
596 for (
unsigned int i = 0; i < num_ik_cb_tests_; ++i)
598 seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
599 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
602 std::vector<geometry_msgs::msg::Pose> poses;
603 ASSERT_TRUE(kinematics_solver_->getPositionFK(fk_names, fk_values, poses));
604 if (poses[0].position.z <= 0.0f)
610 kinematics_solver_->searchPositionIK(
611 poses[0], fk_values, timeout_, solution,
612 [
this](
const geometry_msgs::msg::Pose&,
const std::vector<double>& joints,
613 moveit_msgs::msg::MoveItErrorCodes& error_code) { searchIKCallback(joints, error_code); },
615 if (error_code.val == error_code.SUCCESS)
624 std::vector<geometry_msgs::msg::Pose> reached_poses;
625 kinematics_solver_->getPositionFK(fk_names, solution, reached_poses);
629 if (num_ik_cb_tests_ > 0)
631 RCLCPP_INFO_STREAM(
getLogger(),
"Success Rate: " <<
static_cast<double>(success) / num_ik_cb_tests_);
638 std::vector<double> fk_values, solution;
639 moveit_msgs::msg::MoveItErrorCodes error_code;
640 solution.resize(kinematics_solver_->getJointNames().size(), 0.0);
641 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
645 for (
unsigned int i = 0; i < num_ik_tests_; ++i)
647 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
650 std::vector<geometry_msgs::msg::Pose> poses;
652 ASSERT_TRUE(kinematics_solver_->getPositionFK(fk_names, fk_values, poses));
653 kinematics_solver_->getPositionIK(poses[0], fk_values, solution, error_code);
655 EXPECT_EQ(error_code.val, error_code.SUCCESS);
657 Eigen::Map<Eigen::ArrayXd> sol(solution.data(), solution.size());
658 Eigen::Map<Eigen::ArrayXd> truth(fk_values.data(), fk_values.size());
659 EXPECT_TRUE(sol.isApprox(truth, tolerance_)) << sol.transpose() <<
'\n' << truth.transpose() <<
'\n';
665 std::vector<double> seed, fk_values;
666 std::vector<std::vector<double>> solutions;
670 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
674 unsigned int success = 0;
675 for (
unsigned int i = 0; i < num_ik_multiple_tests_; ++i)
677 seed.resize(kinematics_solver_->getJointNames().size(), 0.0);
678 fk_values.resize(kinematics_solver_->getJointNames().size(), 0.0);
681 std::vector<geometry_msgs::msg::Pose> poses;
682 ASSERT_TRUE(kinematics_solver_->getPositionFK(fk_names, fk_values, poses));
685 kinematics_solver_->getPositionIK(poses, fk_values, solutions, result, options);
689 success += solutions.empty() ? 0 : 1;
696 std::vector<geometry_msgs::msg::Pose> reached_poses;
697 for (
const auto& s : solutions)
699 kinematics_solver_->getPositionFK(fk_names, s, reached_poses);
704 if (num_ik_cb_tests_ > 0)
706 RCLCPP_INFO_STREAM(
getLogger(),
"Success Rate: " <<
static_cast<double>(success) / num_ik_multiple_tests_);
714 std::vector<std::vector<double>> solutions;
718 std::vector<double> seed, fk_values, solution;
719 moveit_msgs::msg::MoveItErrorCodes error_code;
720 const std::vector<std::string>& fk_names = kinematics_solver_->getTipFrames();
724 for (
unsigned int i = 0; i < num_nearest_ik_tests_; ++i)
728 std::vector<geometry_msgs::msg::Pose> poses;
729 ASSERT_TRUE(kinematics_solver_->getPositionFK(fk_names, fk_values, poses));
736 kinematics_solver_->getPositionIK(poses[0], seed, solution, error_code);
739 if (error_code.val != error_code.SUCCESS)
742 const Eigen::Map<const Eigen::VectorXd> seed_eigen(seed.data(), seed.size());
743 double error_get_ik =
744 (Eigen::Map<const Eigen::VectorXd>(solution.data(), solution.size()) - seed_eigen).array().abs().sum();
748 kinematics_solver_->getPositionIK(poses, seed, solutions, result, options);
752 <<
"Multiple solution call failed, while single solution call succeeded";
756 double smallest_error_multiple_ik = std::numeric_limits<double>::max();
757 for (
const auto& s : solutions)
759 double error_multiple_ik =
760 (Eigen::Map<const Eigen::VectorXd>(s.data(), s.size()) - seed_eigen).array().abs().sum();
761 if (error_multiple_ik <= smallest_error_multiple_ik)
762 smallest_error_multiple_ik = error_multiple_ik;
764 EXPECT_NEAR(smallest_error_multiple_ik, error_get_ik, tolerance_);
770 testing::InitGoogleTest(&argc, argv);
771 rclcpp::init(argc, argv);
772 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_
friend class KinematicsTest
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)