moveit2
The MoveIt Motion Planning Framework for ROS 2.
test_cartesian_interpolator.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2019, PickNik Inc.
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 the Willow Garage 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 /* Author: Michael Lautman */
36 
41 
42 #include <urdf_parser/urdf_parser.h>
43 #include <gtest/gtest.h>
44 
45 #include <memory>
46 #include <sstream>
47 #include <algorithm>
48 #include <ctype.h>
49 
50 class OneRobot : public testing::Test
51 {
52 protected:
53  void SetUp() override
54  {
55  // TODO(mlautman): Use new testing framework for loading models
56  // https://ros-planning.github.io/moveit_tutorials/doc/tests/tests_tutorial.html
57  static const std::string MODEL2 =
58  "<?xml version=\"1.0\" ?>"
59  "<robot name=\"one_robot\">"
60  "<link name=\"base_link\">"
61  " <inertial>"
62  " <mass value=\"2.81\"/>"
63  " <origin rpy=\"0 0 0\" xyz=\"0.0 0.0 .0\"/>"
64  " <inertia ixx=\"0.1\" ixy=\"-0.2\" ixz=\"0.5\" iyy=\"-.09\" iyz=\"1\" izz=\"0.101\"/>"
65  " </inertial>"
66  " <collision name=\"my_collision\">"
67  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
68  " <geometry>"
69  " <box size=\"1 2 1\" />"
70  " </geometry>"
71  " </collision>"
72  " <visual>"
73  " <origin rpy=\"0 0 0\" xyz=\"0.0 0 0\"/>"
74  " <geometry>"
75  " <box size=\"1 2 1\" />"
76  " </geometry>"
77  " </visual>"
78  "</link>"
79  "<joint name=\"panda_joint0\" type=\"continuous\">"
80  " <axis xyz=\"0 0 1\"/>"
81  " <parent link=\"base_link\"/>"
82  " <child link=\"link_a\"/>"
83  " <origin rpy=\" 0.0 0 0 \" xyz=\"0.0 0 0 \"/>"
84  "</joint>"
85  "<link name=\"link_a\">"
86  " <inertial>"
87  " <mass value=\"1.0\"/>"
88  " <origin rpy=\"0 0 0\" xyz=\"0.0 0.0 .0\"/>"
89  " <inertia ixx=\"0.1\" ixy=\"-0.2\" ixz=\"0.5\" iyy=\"-.09\" iyz=\"1\" izz=\"0.101\"/>"
90  " </inertial>"
91  " <collision>"
92  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
93  " <geometry>"
94  " <box size=\"1 2 1\" />"
95  " </geometry>"
96  " </collision>"
97  " <visual>"
98  " <origin rpy=\"0 0 0\" xyz=\"0.0 0 0\"/>"
99  " <geometry>"
100  " <box size=\"1 2 1\" />"
101  " </geometry>"
102  " </visual>"
103  "</link>"
104  "<joint name=\"joint_b\" type=\"fixed\">"
105  " <parent link=\"link_a\"/>"
106  " <child link=\"link_b\"/>"
107  " <origin rpy=\" 0.0 -0.42 0 \" xyz=\"0.0 0.5 0 \"/>"
108  "</joint>"
109  "<link name=\"link_b\">"
110  " <inertial>"
111  " <mass value=\"1.0\"/>"
112  " <origin rpy=\"0 0 0\" xyz=\"0.0 0.0 .0\"/>"
113  " <inertia ixx=\"0.1\" ixy=\"-0.2\" ixz=\"0.5\" iyy=\"-.09\" iyz=\"1\" izz=\"0.101\"/>"
114  " </inertial>"
115  " <collision>"
116  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
117  " <geometry>"
118  " <box size=\"1 2 1\" />"
119  " </geometry>"
120  " </collision>"
121  " <visual>"
122  " <origin rpy=\"0 0 0\" xyz=\"0.0 0 0\"/>"
123  " <geometry>"
124  " <box size=\"1 2 1\" />"
125  " </geometry>"
126  " </visual>"
127  "</link>"
128  " <joint name=\"panda_joint1\" type=\"prismatic\">"
129  " <axis xyz=\"1 0 0\"/>"
130  " <limit effort=\"100.0\" lower=\"0.0\" upper=\"0.09\" velocity=\"0.2\"/>"
131  " <safety_controller k_position=\"20.0\" k_velocity=\"500.0\" soft_lower_limit=\"0.0\" "
132  "soft_upper_limit=\"0.089\"/>"
133  " <parent link=\"link_b\"/>"
134  " <child link=\"link_c\"/>"
135  " <origin rpy=\" 0.0 0.42 0.0 \" xyz=\"0.0 -0.1 0 \"/>"
136  " </joint>"
137  "<link name=\"link_c\">"
138  " <inertial>"
139  " <mass value=\"1.0\"/>"
140  " <origin rpy=\"0 0 0\" xyz=\"0.0 0 .0\"/>"
141  " <inertia ixx=\"0.1\" ixy=\"-0.2\" ixz=\"0.5\" iyy=\"-.09\" iyz=\"1\" izz=\"0.101\"/>"
142  " </inertial>"
143  " <collision>"
144  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
145  " <geometry>"
146  " <box size=\"1 2 1\" />"
147  " </geometry>"
148  " </collision>"
149  " <visual>"
150  " <origin rpy=\"0 0 0\" xyz=\"0.0 0 0\"/>"
151  " <geometry>"
152  " <box size=\"1 2 1\" />"
153  " </geometry>"
154  " </visual>"
155  "</link>"
156  " <joint name=\"mim_f\" type=\"prismatic\">"
157  " <axis xyz=\"1 0 0\"/>"
158  " <limit effort=\"100.0\" lower=\"0.0\" upper=\"0.19\" velocity=\"0.2\"/>"
159  " <parent link=\"link_c\"/>"
160  " <child link=\"link_d\"/>"
161  " <origin rpy=\" 0.0 0.0 0.0 \" xyz=\"0.1 0.1 0 \"/>"
162  " <mimic joint=\"joint_f\" multiplier=\"1.5\" offset=\"0.1\"/>"
163  " </joint>"
164  " <joint name=\"joint_f\" type=\"prismatic\">"
165  " <axis xyz=\"1 0 0\"/>"
166  " <limit effort=\"100.0\" lower=\"0.0\" upper=\"0.19\" velocity=\"0.2\"/>"
167  " <parent link=\"link_d\"/>"
168  " <child link=\"link_e\"/>"
169  " <origin rpy=\" 0.0 0.0 0.0 \" xyz=\"0.1 0.1 0 \"/>"
170  " </joint>"
171  "<link name=\"link_d\">"
172  " <collision>"
173  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
174  " <geometry>"
175  " <box size=\"1 2 1\" />"
176  " </geometry>"
177  " </collision>"
178  " <visual>"
179  " <origin rpy=\"0 1 0\" xyz=\"0 0.1 0\"/>"
180  " <geometry>"
181  " <box size=\"1 2 1\" />"
182  " </geometry>"
183  " </visual>"
184  "</link>"
185  "<link name=\"link_e\">"
186  " <collision>"
187  " <origin rpy=\"0 0 0\" xyz=\"0 0 0\"/>"
188  " <geometry>"
189  " <box size=\"1 2 1\" />"
190  " </geometry>"
191  " </collision>"
192  " <visual>"
193  " <origin rpy=\"0 1 0\" xyz=\"0 0.1 0\"/>"
194  " <geometry>"
195  " <box size=\"1 2 1\" />"
196  " </geometry>"
197  " </visual>"
198  "</link>"
199  "</robot>";
200 
201  static const std::string SMODEL2 =
202  "<?xml version=\"1.0\" ?>"
203  "<robot name=\"one_robot\">"
204  "<virtual_joint name=\"base_joint\" child_link=\"base_link\" parent_frame=\"odom_combined\" type=\"planar\"/>"
205  "<group name=\"base_from_joints\">"
206  "<joint name=\"base_joint\"/>"
207  "<joint name=\"panda_joint0\"/>"
208  "<joint name=\"panda_joint1\"/>"
209  "</group>"
210  "<group name=\"mim_joints\">"
211  "<joint name=\"joint_f\"/>"
212  "<joint name=\"mim_f\"/>"
213  "</group>"
214  "<group name=\"base_with_subgroups\">"
215  "<group name=\"base_from_base_to_tip\"/>"
216  "<joint name=\"panda_joint1\"/>"
217  "</group>"
218  "<group name=\"base_from_base_to_tip\">"
219  "<chain base_link=\"base_link\" tip_link=\"link_b\"/>"
220  "<joint name=\"base_joint\"/>"
221  "</group>"
222  "<group name=\"arm\">"
223  "<chain base_link=\"base_link\" tip_link=\"link_e\"/>"
224  "<joint name=\"base_joint\"/>"
225  "</group>"
226  "<group name=\"base_with_bad_subgroups\">"
227  "<group name=\"error\"/>"
228  "</group>"
229  "</robot>";
230 
231  urdf::ModelInterfaceSharedPtr urdf_model = urdf::parseURDF(MODEL2);
232  srdf::ModelSharedPtr srdf_model = std::make_shared<srdf::Model>();
233  srdf_model->initString(*urdf_model, SMODEL2);
234  robot_model_ = std::make_shared<moveit::core::RobotModel>(urdf_model, srdf_model);
235  }
236 
237  void TearDown() override
238  {
239  }
240 
241 protected:
242  moveit::core::RobotModelConstPtr robot_model_;
243 };
244 
245 std::size_t generateTestTraj(std::vector<std::shared_ptr<moveit::core::RobotState>>& traj,
246  const moveit::core::RobotModelConstPtr& robot_model_)
247 {
248  traj.clear();
249 
250  auto robot_state = std::make_shared<moveit::core::RobotState>(robot_model_);
251  robot_state->setToDefaultValues();
252  double ja, jc;
253 
254  // 3 waypoints with default joints
255  for (std::size_t traj_ix = 0; traj_ix < 3; ++traj_ix)
256  {
257  traj.push_back(std::make_shared<moveit::core::RobotState>(*robot_state));
258  }
259 
260  ja = robot_state->getVariablePosition("panda_joint0");
261  jc = robot_state->getVariablePosition("panda_joint1");
262 
263  // 4th waypoint with a small jump of 0.01 in revolute joint and prismatic joint. This should not be considered a jump
264  ja = ja - 0.01;
265  robot_state->setVariablePosition("panda_joint0", ja);
266  jc = jc - 0.01;
267  robot_state->setVariablePosition("panda_joint1", jc);
268  traj.push_back(std::make_shared<moveit::core::RobotState>(*robot_state));
269 
270  // 5th waypoint with a large jump of 1.01 in first revolute joint
271  ja = ja + 1.01;
272  robot_state->setVariablePosition("panda_joint0", ja);
273  traj.push_back(std::make_shared<moveit::core::RobotState>(*robot_state));
274 
275  // 6th waypoint with a large jump of 1.01 in first prismatic joint
276  jc = jc + 1.01;
277  robot_state->setVariablePosition("panda_joint1", jc);
278  traj.push_back(std::make_shared<moveit::core::RobotState>(*robot_state));
279 
280  // 7th waypoint with no jump
281  traj.push_back(std::make_shared<moveit::core::RobotState>(*robot_state));
282 
283  return traj.size();
284 }
285 
286 TEST_F(OneRobot, testGenerateTrajectory)
287 {
288  std::vector<std::shared_ptr<moveit::core::RobotState>> traj;
289 
290  // The full trajectory should be of length 7
291  const std::size_t expected_full_traj_len = 7;
292 
293  // Generate a test trajectory
294  std::size_t full_traj_len = generateTestTraj(traj, robot_model_);
295 
296  // Test the generateTestTraj still generates a trajectory of length 7
297  EXPECT_EQ(full_traj_len, expected_full_traj_len); // full traj should be 7 waypoints long
298 }
299 
300 TEST_F(OneRobot, checkAbsoluteJointSpaceJump)
301 {
302  const moveit::core::JointModelGroup* joint_model_group = robot_model_->getJointModelGroup("arm");
303  std::vector<std::shared_ptr<moveit::core::RobotState>> traj;
304 
305  // A revolute joint jumps 1.01 at the 5th waypoint and a prismatic joint jumps 1.01 at the 6th waypoint
306  const std::size_t expected_revolute_jump_traj_len = 4;
307  const std::size_t expected_prismatic_jump_traj_len = 5;
308 
309  // Pre-compute expected results for tests
310  std::size_t full_traj_len = generateTestTraj(traj, robot_model_);
311  const double expected_revolute_jump_fraction = (double)expected_revolute_jump_traj_len / (double)full_traj_len;
312  const double expected_prismatic_jump_fraction = (double)expected_prismatic_jump_traj_len / (double)full_traj_len;
313 
314  // Container for results
315  double fraction;
316 
317  // Direct call of absolute version
319  EXPECT_EQ(expected_revolute_jump_traj_len, traj.size()); // traj should be cut
320  EXPECT_NEAR(expected_revolute_jump_fraction, fraction, 0.01);
321 
322  // Indirect call using checkJointSpaceJumps
323  generateTestTraj(traj, robot_model_);
325  moveit::core::JumpThreshold(1.0, 1.0));
326  EXPECT_EQ(expected_revolute_jump_traj_len, traj.size()); // traj should be cut before the revolute jump
327  EXPECT_NEAR(expected_revolute_jump_fraction, fraction, 0.01);
328 
329  // Test revolute joints
330  generateTestTraj(traj, robot_model_);
332  moveit::core::JumpThreshold(1.0, 0.0));
333  EXPECT_EQ(expected_revolute_jump_traj_len, traj.size()); // traj should be cut before the revolute jump
334  EXPECT_NEAR(expected_revolute_jump_fraction, fraction, 0.01);
335 
336  // Test prismatic joints
337  generateTestTraj(traj, robot_model_);
339  moveit::core::JumpThreshold(0.0, 1.0));
340  EXPECT_EQ(expected_prismatic_jump_traj_len, traj.size()); // traj should be cut before the prismatic jump
341  EXPECT_NEAR(expected_prismatic_jump_fraction, fraction, 0.01);
342 
343  // Ignore all absolute jumps
344  generateTestTraj(traj, robot_model_);
346  moveit::core::JumpThreshold(0.0, 0.0));
347  EXPECT_EQ(full_traj_len, traj.size()); // traj should not be cut
348  EXPECT_NEAR(1.0, fraction, 0.01);
349 }
350 
351 TEST_F(OneRobot, checkRelativeJointSpaceJump)
352 {
353  const moveit::core::JointModelGroup* joint_model_group = robot_model_->getJointModelGroup("arm");
354  std::vector<std::shared_ptr<moveit::core::RobotState>> traj;
355 
356  // The first large jump of 1.01 occurs at the 5th waypoint so the test should trim the trajectory to length 4
357  const std::size_t expected_relative_jump_traj_len = 4;
358 
359  // Pre-compute expected results for tests
360  std::size_t full_traj_len = generateTestTraj(traj, robot_model_);
361  const double expected_relative_jump_fraction = (double)expected_relative_jump_traj_len / (double)full_traj_len;
362 
363  // Container for results
364  double fraction;
365 
366  // Direct call of relative version: 1.01 > 2.97 * (0.01 * 2 + 1.01 * 2)/6.
368  EXPECT_EQ(expected_relative_jump_traj_len, traj.size()); // traj should be cut before the first jump of 1.01
369  EXPECT_NEAR(expected_relative_jump_fraction, fraction, 0.01);
370 
371  // Indirect call of relative version using checkJointSpaceJumps
372  generateTestTraj(traj, robot_model_);
375  EXPECT_EQ(expected_relative_jump_traj_len, traj.size()); // traj should be cut before the first jump of 1.01
376  EXPECT_NEAR(expected_relative_jump_fraction, fraction, 0.01);
377 
378  // Trajectory should not be cut: 1.01 < 2.98 * (0.01 * 2 + 1.01 * 2)/6.
379  generateTestTraj(traj, robot_model_);
382  EXPECT_EQ(full_traj_len, traj.size()); // traj should not be cut
383  EXPECT_NEAR(1.0, fraction, 0.01);
384 }
385 
386 int main(int argc, char** argv)
387 {
388  testing::InitGoogleTest(&argc, argv);
389  return RUN_ALL_TESTS();
390 }
void TearDown() override
void SetUp() override
moveit::core::RobotModelConstPtr robot_model_
static Percentage checkAbsoluteJointSpaceJump(const JointModelGroup *group, std::vector< std::shared_ptr< RobotState >> &traj, double revolute_jump_threshold, double prismatic_jump_threshold)
Tests for absolute joint space jumps of the trajectory traj.
static Percentage checkRelativeJointSpaceJump(const JointModelGroup *group, std::vector< std::shared_ptr< RobotState >> &traj, double jump_threshold_factor)
Tests for relative joint space jumps of the trajectory traj.
static Percentage checkJointSpaceJump(const JointModelGroup *group, std::vector< std::shared_ptr< RobotState >> &traj, const JumpThreshold &jump_threshold)
Tests joint space jumps of a trajectory.
Struct for containing jump_threshold.
std::size_t generateTestTraj(std::vector< std::shared_ptr< moveit::core::RobotState >> &traj, const moveit::core::RobotModelConstPtr &robot_model_)
int main(int argc, char **argv)
TEST_F(OneRobot, testGenerateTrajectory)