moveit2
The MoveIt Motion Planning Framework for ROS 2.
test_time_optimal_trajectory_generation.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011, Georgia Tech Research Corporation
3  * All rights reserved.
4  *
5  * Author: Tobias Kunz <tobias@gatech.edu>
6  * Date: 05/2012
7  *
8  * Humanoid Robotics Lab Georgia Institute of Technology
9  * Director: Mike Stilman http://www.golems.org
10  *
11  * Algorithm details and publications:
12  * http://www.golems.org/node/1570
13  *
14  * This file is provided under the following "BSD-style" License:
15  * Redistribution and use in source and binary forms, with or
16  * without modification, are permitted provided that the following
17  * conditions are met:
18  * * Redistributions of source code must retain the above copyright
19  * notice, this list of conditions and the following disclaimer.
20  * * Redistributions in binary form must reproduce the above
21  * copyright notice, this list of conditions and the following
22  * disclaimer in the documentation and/or other materials provided
23  * with the distribution.
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <gtest/gtest.h>
42 
46 
47 namespace
48 {
49 // The URDF used in moveit::core::loadTestingRobotModel() does not contain acceleration limits,
50 // so add them here.
51 // TODO(andyz): Function won't be needed once this issue has been addressed:
52 // https://github.com/ros/urdfdom/issues/177
53 void setAccelerationLimits(const moveit::core::RobotModelPtr& robot_model)
54 {
55  const std::vector<moveit::core::JointModel*> joint_models = robot_model->getActiveJointModels();
56  for (auto& joint_model : joint_models)
57  {
58  std::vector<moveit_msgs::msg::JointLimits> joint_bounds_msg(joint_model->getVariableBoundsMsg());
59  // TODO: update individual bounds
60  for (auto& joint_bound : joint_bounds_msg)
61  {
62  joint_bound.has_acceleration_limits = true;
63  joint_bound.max_acceleration = 1.0;
64  }
65  joint_model->setVariableBounds(joint_bounds_msg);
66  }
67 }
68 } // namespace
69 
70 TEST(time_optimal_trajectory_generation, test1)
71 {
72  Eigen::VectorXd waypoint(4);
73  std::list<Eigen::VectorXd> waypoints;
74 
75  waypoint << 1424.0, 984.999694824219, 2126.0, 0.0;
76  waypoints.push_back(waypoint);
77  waypoint << 1423.0, 985.000244140625, 2126.0, 0.0;
78  waypoints.push_back(waypoint);
79 
80  Eigen::VectorXd max_velocities(4);
81  max_velocities << 1.3, 0.67, 0.67, 0.5;
82  Eigen::VectorXd max_accelerations(4);
83  max_accelerations << 0.00249, 0.00249, 0.00249, 0.00249;
84 
85  Trajectory trajectory(Path(waypoints, 100.0), max_velocities, max_accelerations, 10.0);
86  EXPECT_TRUE(trajectory.isValid());
87  EXPECT_DOUBLE_EQ(40.080256821829849, trajectory.getDuration());
88 
89  // Test start matches
90  EXPECT_DOUBLE_EQ(1424.0, trajectory.getPosition(0.0)[0]);
91  EXPECT_DOUBLE_EQ(984.999694824219, trajectory.getPosition(0.0)[1]);
92  EXPECT_DOUBLE_EQ(2126.0, trajectory.getPosition(0.0)[2]);
93  EXPECT_DOUBLE_EQ(0.0, trajectory.getPosition(0.0)[3]);
94 
95  // Test end matches
96  EXPECT_DOUBLE_EQ(1423.0, trajectory.getPosition(trajectory.getDuration())[0]);
97  EXPECT_DOUBLE_EQ(985.000244140625, trajectory.getPosition(trajectory.getDuration())[1]);
98  EXPECT_DOUBLE_EQ(2126.0, trajectory.getPosition(trajectory.getDuration())[2]);
99  EXPECT_DOUBLE_EQ(0.0, trajectory.getPosition(trajectory.getDuration())[3]);
100 
101  // Start at rest and end at rest
102  const double traj_duration = trajectory.getDuration();
103  EXPECT_NEAR(0.0, trajectory.getVelocity(0.0)[0], 0.1);
104  EXPECT_NEAR(0.0, trajectory.getVelocity(traj_duration)[0], 0.1);
105 }
106 
107 TEST(time_optimal_trajectory_generation, test2)
108 {
109  Eigen::VectorXd waypoint(4);
110  std::list<Eigen::VectorXd> waypoints;
111 
112  waypoint << 1427.0, 368.0, 690.0, 90.0;
113  waypoints.push_back(waypoint);
114  waypoint << 1427.0, 368.0, 790.0, 90.0;
115  waypoints.push_back(waypoint);
116  waypoint << 952.499938964844, 433.0, 1051.0, 90.0;
117  waypoints.push_back(waypoint);
118  waypoint << 452.5, 533.0, 1051.0, 90.0;
119  waypoints.push_back(waypoint);
120  waypoint << 452.5, 533.0, 951.0, 90.0;
121  waypoints.push_back(waypoint);
122 
123  Eigen::VectorXd max_velocities(4);
124  max_velocities << 1.3, 0.67, 0.67, 0.5;
125  Eigen::VectorXd max_accelerations(4);
126  max_accelerations << 0.002, 0.002, 0.002, 0.002;
127 
128  Trajectory trajectory(Path(waypoints, 100.0), max_velocities, max_accelerations, 10.0);
129  EXPECT_TRUE(trajectory.isValid());
130  EXPECT_DOUBLE_EQ(1922.1418427445944, trajectory.getDuration());
131 
132  // Test start matches
133  EXPECT_DOUBLE_EQ(1427.0, trajectory.getPosition(0.0)[0]);
134  EXPECT_DOUBLE_EQ(368.0, trajectory.getPosition(0.0)[1]);
135  EXPECT_DOUBLE_EQ(690.0, trajectory.getPosition(0.0)[2]);
136  EXPECT_DOUBLE_EQ(90.0, trajectory.getPosition(0.0)[3]);
137 
138  // Test end matches
139  EXPECT_DOUBLE_EQ(452.5, trajectory.getPosition(trajectory.getDuration())[0]);
140  EXPECT_DOUBLE_EQ(533.0, trajectory.getPosition(trajectory.getDuration())[1]);
141  EXPECT_DOUBLE_EQ(951.0, trajectory.getPosition(trajectory.getDuration())[2]);
142  EXPECT_DOUBLE_EQ(90.0, trajectory.getPosition(trajectory.getDuration())[3]);
143 
144  // Start at rest and end at rest
145  const double traj_duration = trajectory.getDuration();
146  EXPECT_NEAR(0.0, trajectory.getVelocity(0.0)[0], 0.1);
147  EXPECT_NEAR(0.0, trajectory.getVelocity(traj_duration)[0], 0.1);
148 }
149 
150 TEST(time_optimal_trajectory_generation, test3)
151 {
152  Eigen::VectorXd waypoint(4);
153  std::list<Eigen::VectorXd> waypoints;
154 
155  waypoint << 1427.0, 368.0, 690.0, 90.0;
156  waypoints.push_back(waypoint);
157  waypoint << 1427.0, 368.0, 790.0, 90.0;
158  waypoints.push_back(waypoint);
159  waypoint << 952.499938964844, 433.0, 1051.0, 90.0;
160  waypoints.push_back(waypoint);
161  waypoint << 452.5, 533.0, 1051.0, 90.0;
162  waypoints.push_back(waypoint);
163  waypoint << 452.5, 533.0, 951.0, 90.0;
164  waypoints.push_back(waypoint);
165 
166  Eigen::VectorXd max_velocities(4);
167  max_velocities << 1.3, 0.67, 0.67, 0.5;
168  Eigen::VectorXd max_accelerations(4);
169  max_accelerations << 0.002, 0.002, 0.002, 0.002;
170 
171  Trajectory trajectory(Path(waypoints, 100.0), max_velocities, max_accelerations);
172  EXPECT_TRUE(trajectory.isValid());
173  EXPECT_DOUBLE_EQ(1919.5597888812974, trajectory.getDuration());
174 
175  // Test start matches
176  EXPECT_DOUBLE_EQ(1427.0, trajectory.getPosition(0.0)[0]);
177  EXPECT_DOUBLE_EQ(368.0, trajectory.getPosition(0.0)[1]);
178  EXPECT_DOUBLE_EQ(690.0, trajectory.getPosition(0.0)[2]);
179  EXPECT_DOUBLE_EQ(90.0, trajectory.getPosition(0.0)[3]);
180 
181  // Test end matches
182  EXPECT_DOUBLE_EQ(452.5, trajectory.getPosition(trajectory.getDuration())[0]);
183  EXPECT_DOUBLE_EQ(533.0, trajectory.getPosition(trajectory.getDuration())[1]);
184  EXPECT_DOUBLE_EQ(951.0, trajectory.getPosition(trajectory.getDuration())[2]);
185  EXPECT_DOUBLE_EQ(90.0, trajectory.getPosition(trajectory.getDuration())[3]);
186 
187  // Start at rest and end at rest
188  const double traj_duration = trajectory.getDuration();
189  EXPECT_NEAR(0.0, trajectory.getVelocity(0.0)[0], 0.1);
190  EXPECT_NEAR(0.0, trajectory.getVelocity(traj_duration)[0], 0.1);
191 }
192 
193 // Test the version of computeTimeStamps that takes custom velocity/acceleration limits
194 TEST(time_optimal_trajectory_generation, testCustomLimits)
195 {
196  constexpr auto robot_name{ "panda" };
197  constexpr auto group_name{ "panda_arm" };
198 
199  auto robot_model = moveit::core::loadTestingRobotModel(robot_name);
200  ASSERT_TRUE(robot_model) << "Failed to load robot model" << robot_name;
201  setAccelerationLimits(robot_model);
202  auto group = robot_model->getJointModelGroup(group_name);
203  ASSERT_TRUE(group) << "Failed to load joint model group " << group_name;
204  moveit::core::RobotState waypoint_state(robot_model);
205  waypoint_state.setToDefaultValues();
206 
207  const double delta_t = 0.1;
208  robot_trajectory::RobotTrajectory trajectory(robot_model, group);
209  waypoint_state.setJointGroupPositions(group, std::vector<double>{ -0.5, -3.52, 1.35, -2.51, -0.88, 0.63, 0.0 });
210  trajectory.addSuffixWayPoint(waypoint_state, delta_t);
211  waypoint_state.setJointGroupPositions(group, std::vector<double>{ -0.45, -3.2, 1.2, -2.4, -0.8, 0.6, 0.0 });
212  trajectory.addSuffixWayPoint(waypoint_state, delta_t);
213 
215  // Custom velocity & acceleration limits for some joints
216  std::unordered_map<std::string, double> vel_limits{ { "panda_joint1", 1.3 }, { "panda_joint2", 2.3 },
217  { "panda_joint3", 3.3 }, { "panda_joint4", 4.3 },
218  { "panda_joint5", 5.3 }, { "panda_joint6", 6.3 },
219  { "panda_joint7", 7.3 } };
220  std::unordered_map<std::string, double> accel_limits{ { "panda_joint1", 1.3 }, { "panda_joint2", 2.3 },
221  { "panda_joint3", 3.3 }, { "panda_joint4", 4.3 },
222  { "panda_joint5", 5.3 }, { "panda_joint6", 6.3 },
223  { "panda_joint7", 7.3 } };
224  ASSERT_TRUE(totg.computeTimeStamps(trajectory, vel_limits, accel_limits)) << "Failed to compute time stamps";
225 }
226 
227 // Test that totg algorithm doesn't give large acceleration
228 TEST(time_optimal_trajectory_generation, testLargeAccel)
229 {
230  double path_tolerance = 0.1;
231  double resample_dt = 0.1;
232  Eigen::VectorXd waypoint(6);
233  std::list<Eigen::VectorXd> waypoints;
234  Eigen::VectorXd max_velocities(6);
235  Eigen::VectorXd max_accelerations(6);
236 
237  // Waypoints
238  // clang-format off
239  waypoint << 1.6113056281076339,
240  -0.21400163389235427,
241  -1.974502599739185,
242  9.9653618690354051e-12,
243  -1.3810916877429624,
244  1.5293902838041467;
245  waypoints.push_back(waypoint);
246 
247  waypoint << 1.6088016187976597,
248  -0.21792862470933924,
249  -1.9758628799742952,
250  0.00010424017303217738,
251  -1.3835690515335755,
252  1.5279972853269816;
253  waypoints.push_back(waypoint);
254 
255  waypoint << 1.5887695443178671,
256  -0.24934455124521923,
257  -1.9867451218551782,
258  0.00093816147756670078,
259  -1.4033879618584812,
260  1.5168532975096607;
261  waypoints.push_back(waypoint);
262 
263  waypoint << 1.1647412393815282,
264  -0.91434018564402375,
265  -2.2170946337498498,
266  0.018590164397622583,
267  -1.8229041212673529,
268  1.2809632867583278;
269  waypoints.push_back(waypoint);
270 
271  // Max velocities
272  max_velocities << 0.89535390627300004,
273  0.89535390627300004,
274  0.79587013890930003,
275  0.92022484811399996,
276  0.82074108075029995,
277  1.3927727430915;
278  // Max accelerations
279  max_accelerations << 0.82673490883799994,
280  0.78539816339699997,
281  0.60883578557700002,
282  3.2074759432319997,
283  1.4398966328939999,
284  4.7292792634680003;
285  // clang-format on
286 
287  Trajectory parameterized(Path(waypoints, path_tolerance), max_velocities, max_accelerations, 0.001);
288 
289  ASSERT_TRUE(parameterized.isValid());
290 
291  size_t sample_count = std::ceil(parameterized.getDuration() / resample_dt);
292  for (size_t sample = 0; sample <= sample_count; ++sample)
293  {
294  // always sample the end of the trajectory as well
295  double t = std::min(parameterized.getDuration(), sample * resample_dt);
296  Eigen::VectorXd acceleration = parameterized.getAcceleration(t);
297 
298  ASSERT_EQ(acceleration.size(), 6);
299  for (std::size_t i = 0; i < 6; ++i)
300  EXPECT_NEAR(acceleration(i), 0.0, 100.0) << "Invalid acceleration at position " << sample_count << '\n';
301  }
302 }
303 
304 // Test parameterizing a trajectory would always produce a trajectory with output end waypoint same as the input end
305 // waypoint
306 TEST(time_optimal_trajectory_generation, testLastWaypoint)
307 {
308  constexpr auto robot_name{ "panda" };
309  constexpr auto group_name{ "panda_arm" };
310 
311  auto robot_model = moveit::core::loadTestingRobotModel(robot_name);
312  ASSERT_TRUE(robot_model) << "Failed to load robot model" << robot_name;
313  setAccelerationLimits(robot_model);
314  auto group = robot_model->getJointModelGroup(group_name);
315  ASSERT_TRUE(group) << "Failed to load joint model group " << group_name;
316  moveit::core::RobotState waypoint_state(robot_model);
317  waypoint_state.setToDefaultValues();
318 
319  robot_trajectory::RobotTrajectory trajectory(robot_model, group);
320  auto add_waypoint = [&](const std::vector<double>& waypoint) {
321  waypoint_state.setJointGroupPositions(group, waypoint);
322  trajectory.addSuffixWayPoint(waypoint_state, 0.1);
323  };
324  add_waypoint({ 0.000000000, 0.000000000, 0, 0, 0, 0, 0 });
325  add_waypoint({ 0.009521808, 0.009521808, 0, 0, 0, 0, 0 });
326  add_waypoint({ 0.011902261, 0.011902261, 0, 0, 0, 0, 0 });
327  add_waypoint({ 0.016663165, 0.016663165, 0, 0, 0, 0, 0 });
328  add_waypoint({ 0.026184973, 0.026184973, 0, 0, 0, 0, 0 });
329  add_waypoint({ 0.034516555, 0.034516555, 0, 0, 0, 0, 0 });
330 
331  const std::vector<double> expected_last_waypoint = { 0.034516555, 0.034516555, 0, 0, 0, 0, 0 };
332  add_waypoint(expected_last_waypoint);
333 
335  ASSERT_TRUE(totg.computeTimeStamps(trajectory)) << "Failed to compute time stamps";
336  const auto robot_state = trajectory.getLastWayPoint();
337  std::vector<double> actual_last_waypoint;
338  robot_state.copyJointGroupPositions(group, actual_last_waypoint);
339  EXPECT_TRUE(std::equal(actual_last_waypoint.cbegin(), actual_last_waypoint.cend(), expected_last_waypoint.cbegin(),
340  expected_last_waypoint.cend(), [](const double rhs, const double lhs) {
341  return std::abs(rhs - lhs) < std::numeric_limits<double>::epsilon();
342  }));
343 }
344 
345 TEST(time_optimal_trajectory_generation, testPluginAPI)
346 {
347  constexpr auto robot_name{ "panda" };
348  constexpr auto group_name{ "panda_arm" };
349 
350  auto robot_model = moveit::core::loadTestingRobotModel(robot_name);
351  ASSERT_TRUE(robot_model) << "Failed to load robot model" << robot_name;
352  setAccelerationLimits(robot_model);
353  auto group = robot_model->getJointModelGroup(group_name);
354  ASSERT_TRUE(group) << "Failed to load joint model group " << group_name;
355  moveit::core::RobotState waypoint_state(robot_model);
356  waypoint_state.setToDefaultValues();
357 
358  robot_trajectory::RobotTrajectory trajectory(robot_model, group);
359  waypoint_state.setJointGroupPositions(group, std::vector<double>{ -0.5, -3.52, 1.35, -2.51, -0.88, 0.63, 0.0 });
360  trajectory.addSuffixWayPoint(waypoint_state, 0.1);
361  waypoint_state.setJointGroupPositions(group, std::vector<double>{ -0.5, -3.52, 1.35, -2.51, -0.88, 0.63, 0.0 });
362  trajectory.addSuffixWayPoint(waypoint_state, 0.1);
363  waypoint_state.setJointGroupPositions(group, std::vector<double>{ 0.0, -3.5, 1.4, -1.2, -1.0, -0.2, 0.0 });
364  trajectory.addSuffixWayPoint(waypoint_state, 0.1);
365  waypoint_state.setJointGroupPositions(group, std::vector<double>{ -0.5, -3.52, 1.35, -2.51, -0.88, 0.63, 0.0 });
366  trajectory.addSuffixWayPoint(waypoint_state, 0.1);
367  waypoint_state.setJointGroupPositions(group, std::vector<double>{ 0.0, -3.5, 1.4, -1.2, -1.0, -0.2, 0.0 });
368  trajectory.addSuffixWayPoint(waypoint_state, 0.1);
369  waypoint_state.setJointGroupPositions(group, std::vector<double>{ -0.5, -3.52, 1.35, -2.51, -0.88, 0.63, 0.0 });
370  trajectory.addSuffixWayPoint(waypoint_state, 0.1);
371 
372  // Number TOTG iterations
373  constexpr size_t totg_iterations = 10;
374 
375  // Test computing the dynamics repeatedly with the same totg instance
376  moveit_msgs::msg::RobotTrajectory first_trajectory_msg_start, first_trajectory_msg_end;
377  {
378  robot_trajectory::RobotTrajectory test_trajectory(trajectory, true /* deep copy */);
379 
380  // Test if the trajectory was copied correctly
381  ASSERT_EQ(test_trajectory.getDuration(), trajectory.getDuration());
382  moveit::core::JointBoundsVector test_bounds = test_trajectory.getRobotModel()->getActiveJointModelsBounds();
383  moveit::core::JointBoundsVector original_bounds = trajectory.getRobotModel()->getActiveJointModelsBounds();
384  ASSERT_EQ(test_bounds.size(), original_bounds.size());
385  for (size_t bound_idx = 0; bound_idx < test_bounds.at(0)->size(); ++bound_idx)
386  {
387  ASSERT_EQ(test_bounds.at(0)->at(bound_idx).max_velocity_, original_bounds.at(0)->at(bound_idx).max_velocity_);
388  ASSERT_EQ(test_bounds.at(0)->at(bound_idx).min_velocity_, original_bounds.at(0)->at(bound_idx).min_velocity_);
389  ASSERT_EQ(test_bounds.at(0)->at(bound_idx).velocity_bounded_,
390  original_bounds.at(0)->at(bound_idx).velocity_bounded_);
391 
392  ASSERT_EQ(test_bounds.at(0)->at(bound_idx).max_acceleration_,
393  original_bounds.at(0)->at(bound_idx).max_acceleration_);
394  ASSERT_EQ(test_bounds.at(0)->at(bound_idx).min_acceleration_,
395  original_bounds.at(0)->at(bound_idx).min_acceleration_);
396  ASSERT_EQ(test_bounds.at(0)->at(bound_idx).acceleration_bounded_,
397  original_bounds.at(0)->at(bound_idx).acceleration_bounded_);
398  }
399  ASSERT_EQ(test_trajectory.getWayPointDurationFromPrevious(1), trajectory.getWayPointDurationFromPrevious(1));
400 
402  ASSERT_TRUE(totg.computeTimeStamps(test_trajectory)) << "Failed to compute time stamps";
403 
404  test_trajectory.getRobotTrajectoryMsg(first_trajectory_msg_start);
405 
406  // Iteratively recompute timestamps with same totg instance
407  for (size_t i = 0; i < totg_iterations; ++i)
408  {
409  bool totg_success = totg.computeTimeStamps(test_trajectory);
410  EXPECT_TRUE(totg_success) << "Failed to compute time stamps with a same TOTG instance in iteration " << i;
411  }
412 
413  test_trajectory.getRobotTrajectoryMsg(first_trajectory_msg_end);
414  }
415 
416  // Test computing the dynamics repeatedly with one TOTG instance per call
417  moveit_msgs::msg::RobotTrajectory second_trajectory_msg_start, second_trajectory_msg_end;
418  {
419  robot_trajectory::RobotTrajectory test_trajectory(trajectory, true /* deep copy */);
420 
421  {
423  ASSERT_TRUE(totg.computeTimeStamps(test_trajectory)) << "Failed to compute time stamps";
424  }
425 
426  test_trajectory.getRobotTrajectoryMsg(second_trajectory_msg_start);
427 
428  // Iteratively recompute timestamps with new totg instances
429  for (size_t i = 0; i < totg_iterations; ++i)
430  {
432  bool totg_success = totg.computeTimeStamps(test_trajectory);
433  EXPECT_TRUE(totg_success) << "Failed to compute time stamps with a new TOTG instance in iteration " << i;
434  }
435 
436  test_trajectory.getRobotTrajectoryMsg(second_trajectory_msg_end);
437  }
438 
439  // Make sure trajectories produce equal waypoints independent of TOTG instances
440  ASSERT_EQ(first_trajectory_msg_start, second_trajectory_msg_start);
441  ASSERT_EQ(first_trajectory_msg_end, second_trajectory_msg_end);
442 
443  // Iterate on the original trajectory again
444  moveit_msgs::msg::RobotTrajectory third_trajectory_msg_end;
445 
446  {
448  ASSERT_TRUE(totg.computeTimeStamps(trajectory)) << "Failed to compute time stamps";
449  }
450 
451  for (size_t i = 0; i < totg_iterations; ++i)
452  {
454  bool totg_success = totg.computeTimeStamps(trajectory);
455  ASSERT_TRUE(totg_success) << "Failed to compute timestamps on a new TOTG instance in iteration " << i;
456  }
457 
458  // Compare with previous work
459  trajectory.getRobotTrajectoryMsg(third_trajectory_msg_end);
460 
461  // Make sure trajectories produce equal waypoints independent of TOTG instances
462  ASSERT_EQ(first_trajectory_msg_end, third_trajectory_msg_end);
463 }
464 
465 TEST(time_optimal_trajectory_generation, testFixedNumWaypoints)
466 {
467  // Test the version of computeTimeStamps() that gives a fixed num waypoints
468  constexpr size_t desired_num_waypoints = 42;
469 
470  constexpr auto robot_name{ "panda" };
471  constexpr auto group_name{ "panda_arm" };
472 
473  auto robot_model = moveit::core::loadTestingRobotModel(robot_name);
474  ASSERT_TRUE(robot_model) << "Failed to load robot model" << robot_name;
475  setAccelerationLimits(robot_model);
476  auto group = robot_model->getJointModelGroup(group_name);
477  ASSERT_TRUE(group) << "Failed to load joint model group " << group_name;
478  moveit::core::RobotState waypoint_state(robot_model);
479  waypoint_state.setToDefaultValues();
480 
481  const double delta_t = 0.1;
482  robot_trajectory::RobotTrajectory trajectory(robot_model, group);
483  waypoint_state.setJointGroupPositions(group, std::vector<double>{ -0.5, -3.52, 1.35, -2.51, -0.88, 0.63, 0.0 });
484  trajectory.addSuffixWayPoint(waypoint_state, delta_t);
485  waypoint_state.setJointGroupPositions(group, std::vector<double>{ -0.45, -3.2, 1.2, -2.4, -0.8, 0.6, 0.0 });
486  trajectory.addSuffixWayPoint(waypoint_state, delta_t);
487 
488  ASSERT_TRUE(trajectory_processing::totgComputeTimeStamps(desired_num_waypoints, trajectory))
489  << "Failed to compute time stamps";
490  // Allow +/-1 waypoint due to floating point error
491  EXPECT_NEAR(trajectory.getWayPointCount(), desired_num_waypoints, 1);
492 }
493 
494 TEST(time_optimal_trajectory_generation, testSingleDofDiscontinuity)
495 {
496  // Test a (prior) specific failure case
497  Eigen::VectorXd waypoint(1);
498  std::list<Eigen::VectorXd> waypoints;
499 
500  const double start_position = 1.881943;
501  waypoint << start_position;
502  waypoints.push_back(waypoint);
503  waypoint << 2.600542;
504  waypoints.push_back(waypoint);
505 
506  Eigen::VectorXd max_velocities(1);
507  max_velocities << 4.54;
508  Eigen::VectorXd max_accelerations(1);
509  max_accelerations << 28.0;
510 
511  Trajectory trajectory(Path(waypoints, 0.1 /* path tolerance */), max_velocities, max_accelerations,
512  0.001 /* timestep */);
513  EXPECT_TRUE(trajectory.isValid());
514 
515  EXPECT_GT(trajectory.getDuration(), 0.0);
516  const double traj_duration = trajectory.getDuration();
517  EXPECT_NEAR(0.320681, traj_duration, 1e-3);
518 
519  // Start matches
520  EXPECT_DOUBLE_EQ(start_position, trajectory.getPosition(0.0)[0]);
521  // Start at rest and end at rest
522  EXPECT_NEAR(0.0, trajectory.getVelocity(0.0)[0], 0.1);
523  EXPECT_NEAR(0.0, trajectory.getVelocity(traj_duration)[0], 0.1);
524 
525  // Check vels and accels at all points
526  for (double time = 0; time < traj_duration; time += 0.01)
527  {
528  // This trajectory has a single switching point
529  double t_switch = 0.1603407;
530  if (time < t_switch)
531  {
532  EXPECT_NEAR(trajectory.getAcceleration(time)[0], max_accelerations[0], 1e-3) << "Time: " << time;
533  }
534  else if (time > t_switch)
535  {
536  EXPECT_NEAR(trajectory.getAcceleration(time)[0], -max_accelerations[0], 1e-3) << "Time: " << time;
537  }
538  }
539 }
540 
541 TEST(time_optimal_trajectory_generation, testRelevantZeroMaxAccelerationsInvalidateTrajectory)
542 {
543  const Eigen::Vector2d max_velocity(1, 1);
544  const Path path(std::list<Eigen::VectorXd>{ Eigen::Vector2d(0, 0), Eigen::Vector2d(1, 1) });
545 
546  EXPECT_FALSE(Trajectory(path, max_velocity, /*max_acceleration=*/Eigen::Vector2d(0, 1)).isValid());
547  EXPECT_FALSE(Trajectory(path, max_velocity, /*max_acceleration=*/Eigen::Vector2d(1, 0)).isValid());
548  EXPECT_FALSE(Trajectory(path, max_velocity, /*max_acceleration=*/Eigen::Vector2d(0, 0)).isValid());
549 }
550 
551 TEST(time_optimal_trajectory_generation, testIrrelevantZeroMaxAccelerationsDontInvalidateTrajectory)
552 {
553  const Eigen::Vector2d max_velocity(1, 1);
554 
555  EXPECT_TRUE(Trajectory(Path(std::list<Eigen::VectorXd>{ Eigen::Vector2d(0, 0), Eigen::Vector2d(0, 1) }), max_velocity,
556  /*max_acceleration=*/Eigen::Vector2d(0, 1))
557  .isValid());
558  EXPECT_TRUE(Trajectory(Path(std::list<Eigen::VectorXd>{ Eigen::Vector2d(0, 0), Eigen::Vector2d(1, 0) }), max_velocity,
559  /*max_acceleration=*/Eigen::Vector2d(1, 0))
560  .isValid());
561 }
562 
563 TEST(time_optimal_trajectory_generation, testTimeStepZeroMakesTrajectoryInvalid)
564 {
565  EXPECT_FALSE(Trajectory(Path(std::list<Eigen::VectorXd>{ Eigen::Vector2d(0, 0), Eigen::Vector2d(1, 1) }),
566  /*max_velocity=*/Eigen::Vector2d(1, 1), /*max_acceleration=*/Eigen::Vector2d(1, 1),
567  /*time_step=*/0)
568  .isValid());
569 }
570 
571 int main(int argc, char** argv)
572 {
573  testing::InitGoogleTest(&argc, argv);
574  return RUN_ALL_TESTS();
575 }
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition: robot_state.h:90
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
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...
Definition: robot_state.h:691
void setToDefaultValues()
Set all joints to their default positions. The default position is 0, or if that is not within bounds...
Maintain a sequence of waypoints and the time durations between these waypoints.
const moveit::core::RobotModelConstPtr & getRobotModel() const
void getRobotTrajectoryMsg(moveit_msgs::msg::RobotTrajectory &trajectory, const std::vector< std::string > &joint_filter=std::vector< std::string >()) const
RobotTrajectory & addSuffixWayPoint(const moveit::core::RobotState &state, double dt)
Add a point to the trajectory.
double getWayPointDurationFromPrevious(std::size_t index) const
const moveit::core::RobotState & getLastWayPoint() const
bool computeTimeStamps(robot_trajectory::RobotTrajectory &trajectory, const double max_velocity_scaling_factor=1.0, const double max_acceleration_scaling_factor=1.0) const override
Compute a trajectory with waypoints spaced equally in time (according to resample_dt_)....
Eigen::VectorXd getAcceleration(double time) const
Return the acceleration vector for a given point in time.
bool isValid() const
Call this method after constructing the object to make sure the trajectory generation succeeded witho...
Eigen::VectorXd getPosition(double time) const
Return the position/configuration vector for a given point in time.
double getDuration() const
Returns the optimal duration of the trajectory.
Eigen::VectorXd getVelocity(double time) const
Return the velocity vector for a given point in time.
moveit::core::RobotModelPtr loadTestingRobotModel(const std::string &robot_name)
Loads a robot from moveit_resources.
std::vector< const JointModel::Bounds * > JointBoundsVector
bool totgComputeTimeStamps(const size_t num_waypoints, robot_trajectory::RobotTrajectory &trajectory, const double max_velocity_scaling_factor=1.0, const double max_acceleration_scaling_factor=1.0)
Compute a trajectory with the desired number of waypoints. Resampling the trajectory doesn't change t...
int main(int argc, char **argv)
TEST(time_optimal_trajectory_generation, test1)