moveit2
The MoveIt Motion Planning Framework for ROS 2.
unittest_trajectory_functions.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2018 Pilz GmbH & Co. KG
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Pilz GmbH & Co. KG nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 #include <gtest/gtest.h>
36 
37 #include <map>
38 #include <math.h>
39 #include <string>
40 #include <vector>
41 
42 #include <Eigen/Geometry>
43 #include <kdl/frames.hpp>
44 #include <kdl/path_roundedcomposite.hpp>
45 #include <kdl/rotational_interpolation_sa.hpp>
46 #include <kdl/trajectory.hpp>
47 #include <kdl/trajectory_segment.hpp>
48 #include <kdl/velocityprofile_trap.hpp>
53 #include <tf2_eigen/tf2_eigen.hpp>
54 #include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
55 
60 #include "test_utils.h"
61 
62 #define _USE_MATH_DEFINES
63 
64 static constexpr double EPSILON{ 1.0e-6 };
65 static constexpr double IK_SEED_OFFSET{ 0.1 };
66 static constexpr double L0{ 0.2604 }; // Height of foot
67 static constexpr double L1{ 0.3500 }; // Height of first connector
68 static constexpr double L2{ 0.3070 }; // Height of second connector
69 static constexpr double L3{ 0.0840 }; // Distance last joint to flange
70 
71 // parameters from parameter server
72 const std::string PARAM_PLANNING_GROUP_NAME("planning_group");
73 const std::string GROUP_TIP_LINK_NAME("group_tip_link");
74 const std::string ROBOT_TCP_LINK_NAME("tcp_link");
75 const std::string IK_FAST_LINK_NAME("ik_fast_link");
76 const std::string RANDOM_TEST_NUMBER("random_test_number");
77 
81 class TrajectoryFunctionsTestBase : public testing::Test
82 {
83 protected:
88  void SetUp() override
89  {
90  rclcpp::NodeOptions node_options;
91  node_options.automatically_declare_parameters_from_overrides(true);
92  node_ = rclcpp::Node::make_shared("unittest_trajectory_functions", node_options);
93 
94  // load robot model
95  rm_loader_ = std::make_unique<robot_model_loader::RobotModelLoader>(node_);
96  robot_model_ = rm_loader_->getModel();
97  ASSERT_TRUE(bool(robot_model_)) << "Failed to load robot model";
98  planning_scene_ = std::make_shared<planning_scene::PlanningScene>(robot_model_);
99 
100  // get parameters
101  ASSERT_TRUE(node_->has_parameter("planning_group"));
102  node_->get_parameter<std::string>("planning_group", planning_group_);
103  ASSERT_TRUE(node_->has_parameter("group_tip_link"));
104  node_->get_parameter<std::string>("group_tip_link", group_tip_link_);
105  ASSERT_TRUE(node_->has_parameter("tcp_link"));
106  node_->get_parameter<std::string>("tcp_link", tcp_link_);
107  ASSERT_TRUE(node_->has_parameter("ik_fast_link"));
108  node_->get_parameter<std::string>("ik_fast_link", ik_fast_link_);
109  ASSERT_TRUE(node_->has_parameter("random_test_number"));
110  node_->get_parameter<int>("random_test_number", random_test_number_);
111 
112  // check robot model
114 
115  // initialize the zero state configurationg and test joint state
116  joint_names_ = robot_model_->getJointModelGroup(planning_group_)->getActiveJointModelNames();
117  for (const auto& joint_name : joint_names_)
118  {
119  zero_state_[joint_name] = 0.0;
120  }
121  }
122 
123  void TearDown() override
124  {
125  robot_model_.reset();
126  }
127 
135  bool tfNear(const Eigen::Isometry3d& pose1, const Eigen::Isometry3d& pose2, const double& epsilon);
136 
137 protected:
138  // ros stuff
139  rclcpp::Node::SharedPtr node_;
140  moveit::core::RobotModelConstPtr robot_model_;
141  std::unique_ptr<robot_model_loader::RobotModelLoader> rm_loader_;
142  planning_scene::PlanningSceneConstPtr planning_scene_;
143 
144  // test parameters from parameter server
147  std::vector<std::string> joint_names_;
148  std::map<std::string, double> zero_state_;
149 
150  // random seed
151  uint32_t random_seed_{ 100 };
152  random_numbers::RandomNumberGenerator rng_{ random_seed_ };
153 };
154 
155 bool TrajectoryFunctionsTestBase::tfNear(const Eigen::Isometry3d& pose1, const Eigen::Isometry3d& pose2,
156  const double& epsilon)
157 {
158  for (std::size_t i = 0; i < 3; ++i)
159  for (std::size_t j = 0; j < 4; ++j)
160  {
161  if (fabs(pose1(i, j) - pose2(i, j)) > fabs(epsilon))
162  return false;
163  }
164  return true;
165 }
166 
171 {
172 };
173 
174 // TODO(henningkayser): re-enable gripper tests
175 // /**
176 // * @brief Parametrized class for tests, that only run with a gripper
177 // */
178 // class TrajectoryFunctionsTestOnlyGripper : public TrajectoryFunctionsTestBase
179 // {
180 // };
181 
188 {
189  Eigen::Isometry3d tip_pose;
190  std::map<std::string, double> test_state = zero_state_;
191  EXPECT_TRUE(pilz_industrial_motion_planner::computeLinkFK(planning_scene_, group_tip_link_, test_state, tip_pose));
192  EXPECT_NEAR(tip_pose(0, 3), 0, EPSILON);
193  EXPECT_NEAR(tip_pose(1, 3), 0, EPSILON);
194  EXPECT_NEAR(tip_pose(2, 3), L0 + L1 + L2 + L3, EPSILON);
195 
196  test_state[joint_names_.at(1)] = M_PI_2;
197  EXPECT_TRUE(pilz_industrial_motion_planner::computeLinkFK(planning_scene_, group_tip_link_, test_state, tip_pose));
198  EXPECT_NEAR(tip_pose(0, 3), L1 + L2 + L3, EPSILON);
199  EXPECT_NEAR(tip_pose(1, 3), 0, EPSILON);
200  EXPECT_NEAR(tip_pose(2, 3), L0, EPSILON);
201 
202  test_state[joint_names_.at(1)] = -M_PI_2;
203  test_state[joint_names_.at(2)] = M_PI_2;
204  EXPECT_TRUE(pilz_industrial_motion_planner::computeLinkFK(planning_scene_, group_tip_link_, test_state, tip_pose));
205  EXPECT_NEAR(tip_pose(0, 3), -L1, EPSILON);
206  EXPECT_NEAR(tip_pose(1, 3), 0, EPSILON);
207  EXPECT_NEAR(tip_pose(2, 3), L0 - L2 - L3, EPSILON);
208 
209  // wrong link name
210  std::string link_name = "wrong_link_name";
211  EXPECT_FALSE(pilz_industrial_motion_planner::computeLinkFK(planning_scene_, link_name, test_state, tip_pose));
212 }
213 
218 {
219  // Load solver
220  const moveit::core::JointModelGroup* jmg = robot_model_->getJointModelGroup(planning_group_);
221  const kinematics::KinematicsBaseConstPtr& solver = jmg->getSolverInstance();
222 
223  if (!solver)
224  {
225  throw("No IK solver configured for group '" + planning_group_ + "'");
226  }
227  // robot state
228  moveit::core::RobotState rstate(robot_model_);
229 
230  while (random_test_number_ > 0)
231  {
232  // sample random robot state
233  rstate.setToRandomPositions(jmg, rng_);
234  rstate.update();
235  geometry_msgs::msg::Pose pose_expect = tf2::toMsg(rstate.getFrameTransform(ik_fast_link_));
236 
237  // prepare inverse kinematics
238  std::vector<geometry_msgs::msg::Pose> ik_poses;
239  ik_poses.push_back(pose_expect);
240  std::vector<double> ik_seed, ik_expect, ik_actual;
241  for (const auto& joint_name : jmg->getActiveJointModelNames())
242  {
243  ik_expect.push_back(rstate.getVariablePosition(joint_name));
244  if (rstate.getVariablePosition(joint_name) > 0)
245  ik_seed.push_back(rstate.getVariablePosition(joint_name) - IK_SEED_OFFSET);
246  else
247  ik_seed.push_back(rstate.getVariablePosition(joint_name) + IK_SEED_OFFSET);
248  }
249 
250  std::vector<std::vector<double>> ik_solutions;
252  moveit_msgs::msg::MoveItErrorCodes err_code;
254 
255  // compute all ik solutions
256  EXPECT_TRUE(solver->getPositionIK(ik_poses, ik_seed, ik_solutions, ik_result, options));
257 
258  // compute one ik solution
259  EXPECT_TRUE(solver->getPositionIK(pose_expect, ik_seed, ik_actual, err_code));
260 
261  ASSERT_EQ(ik_expect.size(), ik_actual.size());
262 
263  for (std::size_t i = 0; i < ik_expect.size(); ++i)
264  {
265  EXPECT_NEAR(ik_actual.at(i), ik_expect.at(i), 4 * IK_SEED_OFFSET);
266  }
267 
268  --random_test_number_;
269  }
270 }
271 
277 {
278  // robot state
279  moveit::core::RobotState rstate(robot_model_);
280  const moveit::core::JointModelGroup* jmg = robot_model_->getJointModelGroup(planning_group_);
281 
282  while (random_test_number_ > 0)
283  {
284  // sample random robot state
285  rstate.setToRandomPositions(jmg, rng_);
286 
287  Eigen::Isometry3d pose_expect = rstate.getFrameTransform(tcp_link_);
288 
289  // copy the random state and set ik seed
290  std::map<std::string, double> ik_seed, ik_expect;
291  for (const auto& joint_name : joint_names_)
292  {
293  ik_expect[joint_name] = rstate.getVariablePosition(joint_name);
294  if (rstate.getVariablePosition(joint_name) > 0)
295  ik_seed[joint_name] = rstate.getVariablePosition(joint_name) - IK_SEED_OFFSET;
296  else
297  ik_seed[joint_name] = rstate.getVariablePosition(joint_name) + IK_SEED_OFFSET;
298  }
299 
300  rstate.setVariablePositions(ik_seed);
301  rstate.update();
302 
303  // compute the ik
304  std::map<std::string, double> ik_actual;
305 
306  EXPECT_TRUE(rstate.setFromIK(robot_model_->getJointModelGroup(planning_group_), pose_expect, tcp_link_));
307 
308  for (const auto& joint_name : joint_names_)
309  {
310  ik_actual[joint_name] = rstate.getVariablePosition(joint_name);
311  }
312 
313  // compare ik solution and expected value
314  for (const auto& joint_pair : ik_actual)
315  {
316  EXPECT_NEAR(joint_pair.second, ik_expect.at(joint_pair.first), 4 * IK_SEED_OFFSET);
317  }
318 
319  // compute the pose from ik_solution
320  rstate.setVariablePositions(ik_actual);
321  rstate.update();
322  Eigen::Isometry3d pose_actual = rstate.getFrameTransform(tcp_link_);
323 
324  EXPECT_TRUE(tfNear(pose_expect, pose_actual, EPSILON));
325 
326  --random_test_number_;
327  }
328 }
329 
335 {
336  // robot state
337  moveit::core::RobotState rstate(robot_model_);
338 
339  const std::string frame_id = robot_model_->getModelFrame();
340  const moveit::core::JointModelGroup* jmg = robot_model_->getJointModelGroup(planning_group_);
341 
342  while (random_test_number_ > 0)
343  {
344  // sample random robot state
345  rstate.setToRandomPositions(jmg, rng_);
346 
347  Eigen::Isometry3d pose_expect = rstate.getFrameTransform(tcp_link_);
348 
349  // copy the random state and set ik seed
350  std::map<std::string, double> ik_seed, ik_expect;
351  for (const auto& joint_name : robot_model_->getJointModelGroup(planning_group_)->getActiveJointModelNames())
352  {
353  ik_expect[joint_name] = rstate.getVariablePosition(joint_name);
354  if (rstate.getVariablePosition(joint_name) > 0)
355  {
356  ik_seed[joint_name] = rstate.getVariablePosition(joint_name) - IK_SEED_OFFSET;
357  }
358  else
359  {
360  ik_seed[joint_name] = rstate.getVariablePosition(joint_name) + IK_SEED_OFFSET;
361  }
362  }
363 
364  // compute the ik
365  std::map<std::string, double> ik_actual;
366  EXPECT_TRUE(pilz_industrial_motion_planner::computePoseIK(planning_scene_, planning_group_, tcp_link_, pose_expect,
367  frame_id, ik_seed, ik_actual, false));
368 
369  // compare ik solution and expected value
370  for (const auto& joint_pair : ik_actual)
371  {
372  EXPECT_NEAR(joint_pair.second, ik_expect.at(joint_pair.first), 4 * IK_SEED_OFFSET);
373  }
374 
375  --random_test_number_;
376  }
377 }
378 
382 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testComputePoseIKInvalidGroupName)
383 {
384  const std::string frame_id = robot_model_->getModelFrame();
385  Eigen::Isometry3d pose_expect;
386 
387  std::map<std::string, double> ik_seed;
388 
389  // compute the ik
390  std::map<std::string, double> ik_actual;
391  EXPECT_FALSE(pilz_industrial_motion_planner::computePoseIK(planning_scene_, "InvalidGroupName", tcp_link_,
392  pose_expect, frame_id, ik_seed, ik_actual, false));
393 }
394 
398 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testComputePoseIKInvalidLinkName)
399 {
400  const std::string frame_id = robot_model_->getModelFrame();
401  Eigen::Isometry3d pose_expect;
402 
403  std::map<std::string, double> ik_seed;
404 
405  // compute the ik
406  std::map<std::string, double> ik_actual;
407  EXPECT_FALSE(pilz_industrial_motion_planner::computePoseIK(planning_scene_, planning_group_, "WrongLink", pose_expect,
408  frame_id, ik_seed, ik_actual, false));
409 }
410 
416 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testComputePoseIKInvalidFrameId)
417 {
418  Eigen::Isometry3d pose_expect;
419 
420  std::map<std::string, double> ik_seed;
421 
422  // compute the ik
423  std::map<std::string, double> ik_actual;
424  EXPECT_FALSE(pilz_industrial_motion_planner::computePoseIK(planning_scene_, planning_group_, tcp_link_, pose_expect,
425  "InvalidFrameId", ik_seed, ik_actual, false));
426 }
427 
428 // /**
429 // * @brief Test if activated self collision for a pose that would be in self
430 // * collision without the check results in a
431 // * valid ik solution.
432 // */
433 // TEST_F(TrajectoryFunctionsTestOnlyGripper, testComputePoseIKSelfCollisionForValidPosition)
434 // {
435 // const std::string frame_id = robot_model_->getModelFrame();
436 // const moveit::core::JointModelGroup* jmg = robot_model_->getJointModelGroup(planning_group_);
437 //
438 // // create seed
439 // std::vector<double> ik_seed_states = { -0.553, 0.956, 1.758, 0.146, -1.059, 1.247 };
440 // auto joint_names = jmg->getActiveJointModelNames();
441 //
442 // std::map<std::string, double> ik_seed;
443 // for (unsigned int i = 0; i < ik_seed_states.size(); ++i)
444 // {
445 // ik_seed[joint_names[i]] = ik_seed_states[i];
446 // }
447 //
448 // // create expected pose
449 // geometry_msgs::msg::Pose pose;
450 // pose.position.x = -0.454;
451 // pose.position.y = -0.15;
452 // pose.position.z = 0.431;
453 // pose.orientation.y = 0.991562;
454 // pose.orientation.w = -0.1296328;
455 // Eigen::Isometry3d pose_expect;
456 // normalizeQuaternion(pose.orientation);
457 // tf2::fromMsg(pose, pose_expect);
458 //
459 // // compute the ik without self collision check and expect the resulting pose
460 // // to be in self collision.
461 // std::map<std::string, double> ik_actual1;
462 // EXPECT_TRUE(pilz_industrial_motion_planner::computePoseIK(planning_scene_, planning_group_, tcp_link_, pose_expect,
463 // frame_id, ik_seed, ik_actual1, false));
464 //
465 // moveit::core::RobotState rstate(robot_model_);
466 // planning_scene::PlanningScene rscene(robot_model_);
467 //
468 // std::vector<double> ik_state;
469 // std::transform(ik_actual1.begin(), ik_actual1.end(), std::back_inserter(ik_state),
470 // [](const auto& pair) { return pair.second; });
471 //
472 // rstate.setJointGroupPositions(jmg, ik_state);
473 // rstate.update();
474 //
475 // collision_detection::CollisionRequest collision_req;
476 // collision_req.group_name = jmg->getName();
477 // collision_detection::CollisionResult collision_res;
478 //
479 // rscene.checkSelfCollision(collision_req, collision_res, rstate);
480 //
481 // EXPECT_TRUE(collision_res.collision);
482 //
483 // // compute the ik with collision detection activated and expect the resulting
484 // // pose to be without self collision.
485 // std::map<std::string, double> ik_actual2;
486 // EXPECT_TRUE(pilz_industrial_motion_planner::computePoseIK(robot_model_, planning_group_, tcp_link_, pose_expect,
487 // frame_id, ik_seed, ik_actual2, true));
488 //
489 // std::vector<double> ik_state2;
490 // std::transform(ik_actual2.begin(), ik_actual2.end(), std::back_inserter(ik_state2),
491 // [](const auto& pair) { return pair.second; });
492 // rstate.setJointGroupPositions(jmg, ik_state2);
493 // rstate.update();
494 //
495 // collision_detection::CollisionResult collision_res2;
496 // rscene.checkSelfCollision(collision_req, collision_res2, rstate);
497 //
498 // EXPECT_FALSE(collision_res2.collision);
499 // }
500 
505 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testComputePoseIKSelfCollisionForInvalidPose)
506 {
507  // robot state
508  moveit::core::RobotState rstate(robot_model_);
509 
510  const std::string frame_id = robot_model_->getModelFrame();
511  const moveit::core::JointModelGroup* jmg = robot_model_->getJointModelGroup(planning_group_);
512 
513  // create seed
514  std::map<std::string, double> ik_seed;
515  for (const auto& joint_name : jmg->getActiveJointModelNames())
516  {
517  ik_seed[joint_name] = 0;
518  }
519 
520  // create goal
521  std::vector<double> ik_goal = { 0, 2.3, -2.3, 0, 0, 0 };
522 
523  rstate.setJointGroupPositions(jmg, ik_goal);
524 
525  Eigen::Isometry3d pose_expect = rstate.getFrameTransform(tcp_link_);
526 
527  // compute the ik with disabled collision check
528  std::map<std::string, double> ik_actual;
529  EXPECT_TRUE(pilz_industrial_motion_planner::computePoseIK(planning_scene_, planning_group_, tcp_link_, pose_expect,
530  frame_id, ik_seed, ik_actual, false));
531 
532  // compute the ik with enabled collision check
533  EXPECT_FALSE(pilz_industrial_motion_planner::computePoseIK(planning_scene_, planning_group_, tcp_link_, pose_expect,
534  frame_id, ik_seed, ik_actual, true));
535 }
536 
547 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testVerifySampleJointLimitsWithSmallDuration)
548 {
549  const std::map<std::string, double> position_last, velocity_last, position_current;
550  double duration_last{ 0.0 };
552 
553  double duration_current = 10e-7;
554 
555  EXPECT_FALSE(pilz_industrial_motion_planner::verifySampleJointLimits(position_last, velocity_last, position_current,
556  duration_last, duration_current, joint_limits));
557 }
558 
569 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testVerifySampleJointLimitsVelocityViolation)
570 {
571  const std::string test_joint_name{ "joint" };
572 
573  std::map<std::string, double> position_last{ { test_joint_name, 2.0 } };
574  std::map<std::string, double> position_current{ { test_joint_name, 10.0 } };
575  std::map<std::string, double> velocity_last;
576  double duration_current{ 1.0 };
577  double duration_last{ 0.0 };
579 
581  // Calculate the max allowed velocity in such a way that it is always smaller
582  // than the current velocity.
583  test_joint_limits.max_velocity =
584  ((position_current.at(test_joint_name) - position_last.at(test_joint_name)) / duration_current) - 1.0;
585  test_joint_limits.has_velocity_limits = true;
586  joint_limits.addLimit(test_joint_name, test_joint_limits);
587 
588  EXPECT_FALSE(pilz_industrial_motion_planner::verifySampleJointLimits(position_last, velocity_last, position_current,
589  duration_last, duration_current, joint_limits));
590 }
591 
602 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testVerifySampleJointLimitsAccelerationViolation)
603 {
604  const std::string test_joint_name{ "joint" };
605 
606  double duration_current = 1.0;
607  double duration_last = 1.0;
608 
609  std::map<std::string, double> position_last{ { test_joint_name, 2.0 } };
610  std::map<std::string, double> position_current{ { test_joint_name, 20.0 } };
611  double velocity_current =
612  ((position_current.at(test_joint_name) - position_last.at(test_joint_name)) / duration_current);
613  std::map<std::string, double> velocity_last{ { test_joint_name, 9.0 } };
615 
617  // Calculate the max allowed velocity in such a way that it is always bigger
618  // than the current velocity.
619  test_joint_limits.max_velocity = velocity_current + 1.0;
620  test_joint_limits.has_velocity_limits = true;
621 
622  double acceleration_current =
623  (velocity_current - velocity_last.at(test_joint_name)) / (duration_last + duration_current) * 2;
624  // Calculate the max allowed acceleration in such a way that it is always
625  // smaller than the current acceleration.
626  test_joint_limits.max_acceleration = acceleration_current - 1.0;
627  test_joint_limits.has_acceleration_limits = true;
628 
629  joint_limits.addLimit(test_joint_name, test_joint_limits);
630 
631  EXPECT_FALSE(pilz_industrial_motion_planner::verifySampleJointLimits(position_last, velocity_last, position_current,
632  duration_last, duration_current, joint_limits));
633 }
634 
645 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testVerifySampleJointLimitsDecelerationViolation)
646 {
647  const std::string test_joint_name{ "joint" };
648 
649  double duration_current = 1.0;
650  double duration_last = 1.0;
651 
652  std::map<std::string, double> position_last{ { test_joint_name, 20.0 } };
653  std::map<std::string, double> position_current{ { test_joint_name, 2.0 } };
654  double velocity_current =
655  ((position_current.at(test_joint_name) - position_last.at(test_joint_name)) / duration_current);
656  std::map<std::string, double> velocity_last{ { test_joint_name, 19.0 } };
658 
660  // Calculate the max allowed velocity in such a way that it is always bigger
661  // than the current velocity.
662  test_joint_limits.max_velocity = fabs(velocity_current) + 1.0;
663  test_joint_limits.has_velocity_limits = true;
664 
665  double acceleration_current =
666  (velocity_current - velocity_last.at(test_joint_name)) / (duration_last + duration_current) * 2;
667  // Calculate the max allowed deceleration in such a way that it is always
668  // bigger than the current acceleration.
669  test_joint_limits.max_deceleration = acceleration_current + 1.0;
670  test_joint_limits.has_deceleration_limits = true;
671 
672  joint_limits.addLimit(test_joint_name, test_joint_limits);
673 
674  EXPECT_FALSE(pilz_industrial_motion_planner::verifySampleJointLimits(position_last, velocity_last, position_current,
675  duration_last, duration_current, joint_limits));
676 }
677 
692 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testGenerateJointTrajectoryWithInvalidCartesianTrajectory)
693 {
694  // Create random test trajectory
695  // Note: 'path' is deleted by KDL::Trajectory_Segment
696  KDL::Path_RoundedComposite* path =
697  new KDL::Path_RoundedComposite(0.2, 0.01, new KDL::RotationalInterpolation_SingleAxis());
698  path->Add(KDL::Frame(KDL::Rotation::RPY(0, 0, 0), KDL::Vector(-1, 0, 0)));
699  path->Finish();
700  // Note: 'velprof' is deleted by KDL::Trajectory_Segment
701  KDL::VelocityProfile* vel_prof = new KDL::VelocityProfile_Trap(0.5, 0.1);
702  vel_prof->SetProfile(0, path->PathLength());
703  KDL::Trajectory_Segment kdl_trajectory(path, vel_prof);
704 
706  std::string group_name{ "invalid_group_name" };
707  std::map<std::string, double> initial_joint_position;
708  double sampling_time{ 0.1 };
709  trajectory_msgs::msg::JointTrajectory joint_trajectory;
710  moveit_msgs::msg::MoveItErrorCodes error_code;
711  bool check_self_collision{ false };
712 
714  planning_scene_, joint_limits, kdl_trajectory, group_name, tcp_link_, initial_joint_position, sampling_time,
715  joint_trajectory, error_code, check_self_collision));
716 
717  std::map<std::string, double> initial_joint_velocity;
718 
720  cart_traj.group_name = group_name;
721  cart_traj.link_name = tcp_link_;
723  cart_traj.points.push_back(cart_traj_point);
724 
726  planning_scene_, joint_limits, cart_traj, group_name, tcp_link_, initial_joint_position, initial_joint_velocity,
727  joint_trajectory, error_code, check_self_collision));
728 }
729 
741 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testDetermineAndCheckSamplingTimeInvalidVectorSize)
742 {
743  robot_trajectory::RobotTrajectoryPtr first_trajectory =
744  std::make_shared<robot_trajectory::RobotTrajectory>(robot_model_, planning_group_);
745  robot_trajectory::RobotTrajectoryPtr second_trajectory =
746  std::make_shared<robot_trajectory::RobotTrajectory>(robot_model_, planning_group_);
747  double epsilon{ 0.0 };
748  double sampling_time{ 0.0 };
749 
750  moveit::core::RobotState rstate(robot_model_);
751  first_trajectory->insertWayPoint(0, rstate, 0.1);
752  second_trajectory->insertWayPoint(0, rstate, 0.1);
753 
754  EXPECT_FALSE(pilz_industrial_motion_planner::determineAndCheckSamplingTime(first_trajectory, second_trajectory,
755  epsilon, sampling_time));
756 }
757 
769 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testDetermineAndCheckSamplingTimeCorrectSamplingTime)
770 {
771  robot_trajectory::RobotTrajectoryPtr first_trajectory =
772  std::make_shared<robot_trajectory::RobotTrajectory>(robot_model_, planning_group_);
773  robot_trajectory::RobotTrajectoryPtr second_trajectory =
774  std::make_shared<robot_trajectory::RobotTrajectory>(robot_model_, planning_group_);
775  double epsilon{ 0.0001 };
776  double sampling_time{ 0.0 };
777  double expected_sampling_time{ 0.1 };
778 
779  moveit::core::RobotState rstate(robot_model_);
780  first_trajectory->insertWayPoint(0, rstate, expected_sampling_time);
781  first_trajectory->insertWayPoint(1, rstate, expected_sampling_time);
782 
783  second_trajectory->insertWayPoint(0, rstate, expected_sampling_time);
784  second_trajectory->insertWayPoint(1, rstate, expected_sampling_time);
785  second_trajectory->insertWayPoint(2, rstate, expected_sampling_time);
786 
787  EXPECT_TRUE(pilz_industrial_motion_planner::determineAndCheckSamplingTime(first_trajectory, second_trajectory,
788  epsilon, sampling_time));
789  EXPECT_EQ(expected_sampling_time, sampling_time);
790 }
791 
803 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testDetermineAndCheckSamplingTimeViolateSamplingTime)
804 {
805  robot_trajectory::RobotTrajectoryPtr first_trajectory =
806  std::make_shared<robot_trajectory::RobotTrajectory>(robot_model_, planning_group_);
807  robot_trajectory::RobotTrajectoryPtr second_trajectory =
808  std::make_shared<robot_trajectory::RobotTrajectory>(robot_model_, planning_group_);
809  double epsilon{ 0.0001 };
810  double sampling_time{ 0.0 };
811  double expected_sampling_time{ 0.1 };
812 
813  moveit::core::RobotState rstate(robot_model_);
814  first_trajectory->insertWayPoint(0, rstate, expected_sampling_time);
815  first_trajectory->insertWayPoint(1, rstate, expected_sampling_time);
816  first_trajectory->insertWayPoint(2, rstate, expected_sampling_time);
817  // Violate sampling time
818  first_trajectory->insertWayPoint(2, rstate, expected_sampling_time + 1.0);
819  first_trajectory->insertWayPoint(3, rstate, expected_sampling_time);
820 
821  second_trajectory->insertWayPoint(0, rstate, expected_sampling_time);
822  second_trajectory->insertWayPoint(1, rstate, expected_sampling_time);
823  second_trajectory->insertWayPoint(2, rstate, expected_sampling_time);
824  second_trajectory->insertWayPoint(3, rstate, expected_sampling_time);
825 
826  EXPECT_FALSE(pilz_industrial_motion_planner::determineAndCheckSamplingTime(first_trajectory, second_trajectory,
827  epsilon, sampling_time));
828  EXPECT_EQ(expected_sampling_time, sampling_time);
829 }
830 
842 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testIsRobotStateEqualPositionUnequal)
843 {
844  moveit::core::RobotState rstate_1 = moveit::core::RobotState(robot_model_);
845  moveit::core::RobotState rstate_2 = moveit::core::RobotState(robot_model_);
846 
847  double default_joint_position[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
848  rstate_1.setJointGroupPositions(planning_group_, default_joint_position);
849  // Ensure that the joint positions of both robot states are different
850  default_joint_position[0] = default_joint_position[0] + 70.0;
851  rstate_2.setJointGroupPositions(planning_group_, default_joint_position);
852 
853  double epsilon{ 0.0001 };
854  EXPECT_FALSE(pilz_industrial_motion_planner::isRobotStateEqual(rstate_1, rstate_2, planning_group_, epsilon));
855 }
856 
868 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testIsRobotStateEqualVelocityUnequal)
869 {
870  moveit::core::RobotState rstate_1 = moveit::core::RobotState(robot_model_);
871  moveit::core::RobotState rstate_2 = moveit::core::RobotState(robot_model_);
872 
873  // Ensure that the joint positions of both robot state are equal
874  double default_joint_position[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
875  rstate_1.setJointGroupPositions(planning_group_, default_joint_position);
876  rstate_2.setJointGroupPositions(planning_group_, default_joint_position);
877 
878  double default_joint_velocity[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
879  rstate_1.setJointGroupVelocities(planning_group_, default_joint_velocity);
880  // Ensure that the joint velocites of both robot states are different
881  default_joint_velocity[1] = default_joint_velocity[1] + 10.0;
882  rstate_2.setJointGroupVelocities(planning_group_, default_joint_velocity);
883 
884  double epsilon{ 0.0001 };
885  EXPECT_FALSE(pilz_industrial_motion_planner::isRobotStateEqual(rstate_1, rstate_2, planning_group_, epsilon));
886 }
887 
899 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testIsRobotStateEqualAccelerationUnequal)
900 {
901  moveit::core::RobotState rstate_1 = moveit::core::RobotState(robot_model_);
902  moveit::core::RobotState rstate_2 = moveit::core::RobotState(robot_model_);
903 
904  // Ensure that the joint positions of both robot state are equal
905  double default_joint_position[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
906  rstate_1.setJointGroupPositions(planning_group_, default_joint_position);
907  rstate_2.setJointGroupPositions(planning_group_, default_joint_position);
908 
909  // Ensure that the joint velocities of both robot state are equal
910  double default_joint_velocity[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
911  rstate_1.setJointGroupVelocities(planning_group_, default_joint_velocity);
912  rstate_2.setJointGroupVelocities(planning_group_, default_joint_velocity);
913 
914  double default_joint_acceleration[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
915  rstate_1.setJointGroupAccelerations(planning_group_, default_joint_acceleration);
916  // Ensure that the joint accelerations of both robot states are different
917  default_joint_acceleration[1] = default_joint_acceleration[1] + 10.0;
918  rstate_2.setJointGroupAccelerations(planning_group_, default_joint_acceleration);
919 
920  double epsilon{ 0.0001 };
921  EXPECT_FALSE(pilz_industrial_motion_planner::isRobotStateEqual(rstate_1, rstate_2, planning_group_, epsilon));
922 }
923 
935 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testIsRobotStateStationaryVelocityUnequal)
936 {
937  moveit::core::RobotState rstate_1 = moveit::core::RobotState(robot_model_);
938 
939  // Ensure that the joint velocities are NOT zero
940  double default_joint_velocity[6] = { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
941  rstate_1.setJointGroupVelocities(planning_group_, default_joint_velocity);
942 
943  double epsilon{ 0.0001 };
944  EXPECT_FALSE(pilz_industrial_motion_planner::isRobotStateStationary(rstate_1, planning_group_, epsilon));
945 }
946 
958 TEST_F(TrajectoryFunctionsTestFlangeAndGripper, testIsRobotStateStationaryAccelerationUnequal)
959 {
960  moveit::core::RobotState rstate_1 = moveit::core::RobotState(robot_model_);
961 
962  // Ensure that the joint velocities are zero
963  double default_joint_velocity[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
964  rstate_1.setJointGroupVelocities(planning_group_, default_joint_velocity);
965 
966  // Ensure that the joint acceleration are NOT zero
967  double default_joint_acceleration[6] = { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
968  rstate_1.setJointGroupAccelerations(planning_group_, default_joint_acceleration);
969 
970  double epsilon{ 0.0001 };
971  EXPECT_FALSE(pilz_industrial_motion_planner::isRobotStateStationary(rstate_1, planning_group_, epsilon));
972 }
973 
974 int main(int argc, char** argv)
975 {
976  rclcpp::init(argc, argv);
977  testing::InitGoogleTest(&argc, argv);
978  return RUN_ALL_TESTS();
979 }
std::unique_ptr< robot_model_loader::RobotModelLoader > rm_loader_
void SetUp() override
Create test scenario for trajectory functions.
random_numbers::RandomNumberGenerator rng_
moveit::core::RobotModelConstPtr robot_model_
std::map< std::string, double > zero_state_
planning_scene::PlanningSceneConstPtr planning_scene_
bool tfNear(const Eigen::Isometry3d &pose1, const Eigen::Isometry3d &pose2, const double &epsilon)
check if two transformations are close
Parametrized class for tests with and without gripper.
const kinematics::KinematicsBaseConstPtr getSolverInstance() const
const std::vector< std::string > & getActiveJointModelNames() const
Get the names of the active joints in this group. These are the names of the joints returned by getJo...
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition: robot_state.h:90
void setVariablePositions(const double *position)
It is assumed positions is an array containing the new positions for all variables in this state....
void setJointGroupAccelerations(const std::string &joint_group_name, const double *gstate)
Given accelerations for the variables that make up a group, in the order found in the group (includin...
Definition: robot_state.h:848
void setJointGroupVelocities(const std::string &joint_group_name, const double *gstate)
Given velocities for the variables that make up a group, in the order found in the group (including v...
Definition: robot_state.h:748
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...
Definition: robot_state.h:605
const Eigen::Isometry3d & getFrameTransform(const std::string &frame_id, bool *frame_found=nullptr)
Get the transformation matrix from the model frame (root of model) to the frame identified by frame_i...
void setToRandomPositions()
Set all joints to random values. Values will be within default bounds.
double getVariablePosition(const std::string &variable) const
Get the position of a particular variable. An exception is thrown if the variable is not known.
Definition: robot_state.h:207
void update(bool force=false)
Update all transforms.
bool setFromIK(const JointModelGroup *group, const geometry_msgs::msg::Pose &pose, double timeout=0.0, const GroupStateValidityCallbackFn &constraint=GroupStateValidityCallbackFn(), const kinematics::KinematicsQueryOptions &options=kinematics::KinematicsQueryOptions(), const kinematics::KinematicsBase::IKCostFn &cost_function=kinematics::KinematicsBase::IKCostFn())
If the group this state corresponds to is a chain and a solver is available, then the joint values ca...
Container for JointLimits, essentially a map with convenience functions. Adds the ability to as for l...
frame_id
Definition: pick.py:63
bool generateJointTrajectory(const planning_scene::PlanningSceneConstPtr &scene, const JointLimitsContainer &joint_limits, const KDL::Trajectory &trajectory, const std::string &group_name, const std::string &link_name, const std::map< std::string, double > &initial_joint_position, const double &sampling_time, trajectory_msgs::msg::JointTrajectory &joint_trajectory, moveit_msgs::msg::MoveItErrorCodes &error_code, bool check_self_collision=false)
Generate joint trajectory from a KDL Cartesian trajectory.
bool isRobotStateStationary(const moveit::core::RobotState &state, const std::string &group, double EPSILON)
check if the robot state have zero velocity/acceleration
bool determineAndCheckSamplingTime(const robot_trajectory::RobotTrajectoryPtr &first_trajectory, const robot_trajectory::RobotTrajectoryPtr &second_trajectory, double EPSILON, double &sampling_time)
Determines the sampling time and checks that both trajectroies use the same sampling time.
bool verifySampleJointLimits(const std::map< std::string, double > &position_last, const std::map< std::string, double > &velocity_last, const std::map< std::string, double > &position_current, double duration_last, double duration_current, const JointLimitsContainer &joint_limits)
verify the velocity/acceleration limits of current sample (based on backward difference computation) ...
bool computePoseIK(const planning_scene::PlanningSceneConstPtr &scene, const std::string &group_name, const std::string &link_name, const Eigen::Isometry3d &pose, const std::string &frame_id, const std::map< std::string, double > &seed, std::map< std::string, double > &solution, bool check_self_collision=true, const double timeout=0.0)
compute the inverse kinematics of a given pose, also check robot self collision
bool isRobotStateEqual(const moveit::core::RobotState &state1, const moveit::core::RobotState &state2, const std::string &joint_group_name, double epsilon)
Check if the two robot states have the same joint position/velocity/acceleration.
bool computeLinkFK(const planning_scene::PlanningSceneConstPtr &scene, const std::string &link_name, const std::map< std::string, double > &joint_state, Eigen::Isometry3d &pose)
compute the pose of a link at give robot state
void checkRobotModel(const moveit::core::RobotModelConstPtr &robot_model, const std::string &group_name, const std::string &link_name)
A set of options for the kinematics solver.
std::vector< CartesianTrajectoryPoint > points
Extends joint_limits_interface::JointLimits with a deceleration parameter.
double max_deceleration
maximum deceleration MUST(!) be negative
const std::string PARAM_PLANNING_GROUP_NAME("planning_group")
const std::string RANDOM_TEST_NUMBER("random_test_number")
int main(int argc, char **argv)
const std::string IK_FAST_LINK_NAME("ik_fast_link")
TEST_F(TrajectoryFunctionsTestFlangeAndGripper, TipLinkFK)
Test the forward kinematics function with simple robot poses for robot tip link using robot model wit...
const std::string ROBOT_TCP_LINK_NAME("tcp_link")
const std::string GROUP_TIP_LINK_NAME("group_tip_link")