moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
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 PickNik 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
42
43#include <rclcpp/node.hpp>
44
45using namespace moveit::core;
46
47class SimpleRobot : public testing::Test
48{
49protected:
50 void SetUp() override
51 {
52 RobotModelBuilder builder("simple", "a");
53 builder.addChain("a->b", "continuous");
54 builder.addChain("b->c", "prismatic");
55 builder.addGroupChain("a", "c", "group");
56 robot_model_ = builder.build();
57 }
58
59 void TearDown() override
60 {
61 }
62
63protected:
64 RobotModelConstPtr robot_model_;
65
66 static std::size_t generateTestTraj(std::vector<std::shared_ptr<RobotState>>& traj,
67 const RobotModelConstPtr& robot_model_);
68};
69
70std::size_t SimpleRobot::generateTestTraj(std::vector<std::shared_ptr<RobotState>>& traj,
71 const RobotModelConstPtr& robot_model_)
72{
73 traj.clear();
74
75 auto robot_state = std::make_shared<RobotState>(robot_model_);
76 robot_state->setToDefaultValues();
77 double ja, jc;
78
79 // 3 waypoints with default joints
80 for (std::size_t traj_ix = 0; traj_ix < 3; ++traj_ix)
81 {
82 traj.push_back(std::make_shared<RobotState>(*robot_state));
83 }
84
85 ja = robot_state->getVariablePosition("a-b-joint"); // revolute joint
86 jc = robot_state->getVariablePosition("b-c-joint"); // prismatic joint
87
88 // 4th waypoint with a small jump of 0.01 in revolute joint and prismatic joint. This should not be considered a jump
89 ja = ja - 0.01;
90 robot_state->setVariablePosition("a-b-joint", ja);
91 jc = jc - 0.01;
92 robot_state->setVariablePosition("b-c-joint", jc);
93 traj.push_back(std::make_shared<RobotState>(*robot_state));
94
95 // 5th waypoint with a large jump of 1.01 in first revolute joint
96 ja = ja + 1.01;
97 robot_state->setVariablePosition("a-b-joint", ja);
98 traj.push_back(std::make_shared<RobotState>(*robot_state));
99
100 // 6th waypoint with a large jump of 1.01 in first prismatic joint
101 jc = jc + 1.01;
102 robot_state->setVariablePosition("b-c-joint", jc);
103 traj.push_back(std::make_shared<RobotState>(*robot_state));
104
105 // 7th waypoint with no jump
106 traj.push_back(std::make_shared<RobotState>(*robot_state));
107
108 return traj.size();
109}
110
111TEST_F(SimpleRobot, testGenerateTrajectory)
112{
113 std::vector<std::shared_ptr<RobotState>> traj;
114
115 // The full trajectory should be of length 7
116 const std::size_t expected_full_traj_len = 7;
117
118 // Generate a test trajectory
119 std::size_t full_traj_len = generateTestTraj(traj, robot_model_);
120
121 // Test the generateTestTraj still generates a trajectory of length 7
122 EXPECT_EQ(full_traj_len, expected_full_traj_len); // full traj should be 7 waypoints long
123}
124
125TEST_F(SimpleRobot, checkAbsoluteJointSpaceJump)
126{
127 const JointModelGroup* joint_model_group = robot_model_->getJointModelGroup("group");
128 std::vector<std::shared_ptr<RobotState>> traj;
129
130 // A revolute joint jumps 1.01 at the 5th waypoint and a prismatic joint jumps 1.01 at the 6th waypoint
131 const std::size_t expected_revolute_jump_traj_len = 4;
132 const std::size_t expected_prismatic_jump_traj_len = 5;
133
134 // Pre-compute expected results for tests
135 std::size_t full_traj_len = generateTestTraj(traj, robot_model_);
136 const double expected_revolute_jump_fraction =
137 static_cast<double>(expected_revolute_jump_traj_len) / static_cast<double>(full_traj_len);
138 const double expected_prismatic_jump_fraction =
139 static_cast<double>(expected_prismatic_jump_traj_len) / static_cast<double>(full_traj_len);
140
141 // Container for results
142 double fraction;
143
144 // Revolute and prismatic joints.
145 generateTestTraj(traj, robot_model_);
146 fraction = CartesianInterpolator::checkJointSpaceJump(joint_model_group, traj, JumpThreshold::absolute(1.0, 1.0));
147 EXPECT_EQ(expected_revolute_jump_traj_len, traj.size()); // traj should be cut before the revolute jump
148 EXPECT_NEAR(expected_revolute_jump_fraction, fraction, 0.01);
149
150 // Test revolute joints
151 generateTestTraj(traj, robot_model_);
152 fraction = CartesianInterpolator::checkJointSpaceJump(joint_model_group, traj, JumpThreshold::absolute(0.01, 10.0));
153 EXPECT_EQ(expected_revolute_jump_traj_len, traj.size()); // traj should be cut before the revolute jump
154 EXPECT_NEAR(expected_revolute_jump_fraction, fraction, 0.01);
155
156 // Test prismatic joints
157 generateTestTraj(traj, robot_model_);
158 fraction = CartesianInterpolator::checkJointSpaceJump(joint_model_group, traj, JumpThreshold::absolute(10.0, 0.01));
159 EXPECT_EQ(expected_prismatic_jump_traj_len, traj.size()); // traj should be cut before the prismatic jump
160 EXPECT_NEAR(expected_prismatic_jump_fraction, fraction, 0.01);
161
162 // Pre-condition checks.
163 EXPECT_ANY_THROW(
164 CartesianInterpolator::checkJointSpaceJump(joint_model_group, traj, JumpThreshold::absolute(0.0, 0.01)));
165 EXPECT_ANY_THROW(
166 CartesianInterpolator::checkJointSpaceJump(joint_model_group, traj, JumpThreshold::absolute(0.01, 0.0)));
167}
168
169TEST_F(SimpleRobot, checkRelativeJointSpaceJump)
170{
171 const JointModelGroup* joint_model_group = robot_model_->getJointModelGroup("group");
172 std::vector<std::shared_ptr<RobotState>> traj;
173
174 // The first large jump of 1.01 occurs at the 5th waypoint so the test should trim the trajectory to length 4
175 const std::size_t expected_relative_jump_traj_len = 4;
176
177 // Pre-compute expected results for tests
178 std::size_t full_traj_len = generateTestTraj(traj, robot_model_);
179 const double expected_relative_jump_fraction =
180 static_cast<double>(expected_relative_jump_traj_len) / static_cast<double>(full_traj_len);
181
182 // Container for results
183 double fraction = 0.0;
184
185 // Call of relative version: 1.01 > 2.97 * (0.01 * 2 + 1.01 * 2)/6.
186 generateTestTraj(traj, robot_model_);
187 fraction = CartesianInterpolator::checkJointSpaceJump(joint_model_group, traj, JumpThreshold::relative(2.97));
188 EXPECT_EQ(expected_relative_jump_traj_len, traj.size()); // traj should be cut before the first jump of 1.01
189 EXPECT_NEAR(expected_relative_jump_fraction, fraction, 0.01);
190
191 // Trajectory should not be cut: 1.01 < 2.98 * (0.01 * 2 + 1.01 * 2)/6.
192 generateTestTraj(traj, robot_model_);
193 fraction = CartesianInterpolator::checkJointSpaceJump(joint_model_group, traj, JumpThreshold::relative(2.98));
194 EXPECT_EQ(full_traj_len, traj.size()); // traj should not be cut
195 EXPECT_NEAR(1.0, fraction, 0.01);
196
197 // Pre-condition checks.
198 EXPECT_ANY_THROW(CartesianInterpolator::checkJointSpaceJump(joint_model_group, traj, JumpThreshold::relative(0.0)));
199}
200
201// TODO - The tests below fail since no kinematic plugins are found. Move the tests to IK plugin package.
202// class PandaRobot : public testing::Test
203// {
204// protected:
205// static void SetUpTestSuite() // setup resources shared between tests
206// {
207// robot_model_ = loadTestingRobotModel("panda");
208// jmg = robot_model_->getJointModelGroup("panda_arm");
209// link = robot_model_->getLinkModel("panda_link8");
210// ASSERT_TRUE(link);
211// node = rclcpp::Node::make_shared("test_cartesian_interpolator");
212// loadIKPluginForGroup(node, jmg, "panda_link0", link->getName());
213// }
214//
215// static void TearDownTestSuite()
216// {
217// robot_model_.reset();
218// }
219//
220// void SetUp() override
221// {
222// start_state = std::make_shared<RobotState>(robot_model_);
223// ASSERT_TRUE(start_state->setToDefaultValues(jmg, "ready"));
224// start_pose = start_state->getGlobalLinkTransform(link);
225// }
226//
227// double computeCartesianPath(std::vector<std::shared_ptr<RobotState>>& result, const Eigen::Vector3d& translation,
228// bool global)
229// {
230// return CartesianInterpolator::computeCartesianPath(start_state.get(), jmg, result, link, translation, global,
231// MaxEEFStep(0.1), JumpThreshold::Disabled(),
232// GroupStateValidityCallbackFn(),
233// kinematics::KinematicsQueryOptions());
234// }
235//
236// double computeCartesianPath(std::vector<std::shared_ptr<RobotState>>& result, const Eigen::Isometry3d& target,
237// bool global, const Eigen::Isometry3d& offset = Eigen::Isometry3d::Identity())
238// {
239// return CartesianInterpolator::computeCartesianPath(start_state.get(), jmg, result, link, target, global,
240// MaxEEFStep(0.1), JumpThreshold::Disabled(),
241// GroupStateValidityCallbackFn(),
242// kinematics::KinematicsQueryOptions(),
243// kinematics::KinematicsBase::IKCostFn(), offset);
244// }
245//
246// protected:
247// static RobotModelPtr robot_model_;
248// static JointModelGroup* jmg_;
249// static const LinkModel* link_;
250// static rclcpp::Node::SharedPtr node;
251//
252// const double prec_ = 1e-8;
253// RobotStatePtr start_state_;
254// Eigen::Isometry3d start_pose_;
255// std::vector<std::shared_ptr<RobotState>> result_;
256// };
257//
258// TEST_F(PandaRobot, testVectorGlobal)
259// {
260// Eigen::Vector3d translation(0.2, 0, 0); // move by 0.2 along world's x axis
261// ASSERT_DOUBLE_EQ(computeCartesianPath(result, translation, true), 0.2); // moved full distance of 0.2
262// // first pose of trajectory should be identical to start_pose
263// EXPECT_EIGEN_EQ(result.front()->getGlobalLinkTransform(link), start_pose);
264// // last pose of trajectory should have same orientation, and offset of 0.2 along world's x-axis
265// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link).linear(), start_pose.linear(), prec);
266// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link).translation(),
267// start_pose.translation() + translation, prec);
268// }
269// TEST_F(PandaRobot, testVectorLocal)
270// {
271// Eigen::Vector3d translation(0.2, 0, 0); // move by 0.2 along link's x axis
272// ASSERT_DOUBLE_EQ(computeCartesianPath(result, translation, false), 0.2); // moved full distance of 0.2
273// // first pose of trajectory should be identical to start_pose
274// EXPECT_EIGEN_EQ(result.front()->getGlobalLinkTransform(link), start_pose);
275// // last pose of trajectory should have same orientation, and offset of 0.2 along link's x-axis
276// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link).linear(), start_pose.linear(), prec);
277// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link).translation(), start_pose * translation, prec);
278// }
279
280// TEST_F(PandaRobot, testTranslationGlobal)
281// {
282// Eigen::Isometry3d goal = start_pose;
283// goal.translation().x() += 0.2; // move by 0.2 along world's x-axis
284
285// ASSERT_DOUBLE_EQ(computeCartesianPath(result, goal, true), 1.0); // 100% of distance generated
286// // first pose of trajectory should be identical to start_pose
287// EXPECT_EIGEN_EQ(result.front()->getGlobalLinkTransform(link), start_pose);
288// // last pose of trajectory should have same orientation, but offset of 0.2 along world's x-axis
289// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link).linear(), start_pose.linear(), prec);
290// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link).translation(), goal.translation(), prec);
291// }
292// TEST_F(PandaRobot, testTranslationLocal)
293// {
294// Eigen::Isometry3d offset(Eigen::Translation3d(0.2, 0, 0)); // move along link's x-axis
295// ASSERT_DOUBLE_EQ(computeCartesianPath(result, offset, false), 1.0); // 100% of distance generated
296// // first pose of trajectory should be identical to start_pose
297// EXPECT_EIGEN_EQ(result.front()->getGlobalLinkTransform(link), start_pose);
298// // last pose of trajectory should have same orientation, but offset of 0.2 along link's x-axis
299// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link).linear(), start_pose.linear(), prec);
300// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link).translation(), start_pose * offset.translation(),
301// prec);
302// }
303
304// TEST_F(PandaRobot, testRotationLocal)
305// {
306// // 45° rotation about links's x-axis
307// Eigen::Isometry3d rot(Eigen::AngleAxisd(M_PI / 4, Eigen::Vector3d::UnitX()));
308// Eigen::Isometry3d goal = start_pose * rot;
309
310// ASSERT_DOUBLE_EQ(computeCartesianPath(result, rot, false), 1.0); // 100% of distance generated
311// // first pose of trajectory should be identical to start_pose
312// EXPECT_EIGEN_EQ(result.front()->getGlobalLinkTransform(link), start_pose);
313// // last pose of trajectory should have same position,
314// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link).translation(), start_pose.translation(), prec);
315// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link), goal, prec);
316// }
317// TEST_F(PandaRobot, testRotationGlobal)
318// {
319// // 45° rotation about links's x-axis
320// Eigen::Isometry3d rot(Eigen::AngleAxisd(M_PI / 4, Eigen::Vector3d::UnitX()));
321// Eigen::Isometry3d goal = start_pose * rot;
322
323// ASSERT_DOUBLE_EQ(computeCartesianPath(result, goal, true), 1.0); // 100% of distance generated
324// // first pose of trajectory should be identical to start_pose
325// EXPECT_EIGEN_NEAR(result.front()->getGlobalLinkTransform(link), start_pose, prec);
326// // last pose of trajectory should have same position,
327// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link).translation(), start_pose.translation(), prec);
328// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link), goal, prec);
329// }
330// TEST_F(PandaRobot, testRotationOffset)
331// {
332// // define offset to virtual center frame
333// Eigen::Isometry3d offset = Eigen::Translation3d(0, 0, 0.2) * Eigen::AngleAxisd(-M_PI / 4,
334// Eigen::Vector3d::UnitZ());
335// // 45° rotation about center's x-axis
336// Eigen::Isometry3d rot(Eigen::AngleAxisd(M_PI / 4, Eigen::Vector3d::UnitX()));
337// Eigen::Isometry3d goal = start_pose * offset * rot;
338
339// ASSERT_DOUBLE_EQ(computeCartesianPath(result, goal, true, offset), 1.0); // 100% of distance generated
340// // first pose of trajectory should be identical to start_pose
341// EXPECT_EIGEN_NEAR(result.front()->getGlobalLinkTransform(link), start_pose, prec);
342
343// // All waypoints of trajectory should have same position in virtual frame
344// for (const auto& waypoint : result)
345// EXPECT_EIGEN_NEAR((waypoint->getGlobalLinkTransform(link) * offset).translation(),
346// (start_pose * offset).translation(), prec);
347// // goal should be reached by virtual frame
348// EXPECT_EIGEN_NEAR(result.back()->getGlobalLinkTransform(link) * offset, goal, prec);
349// }
350
351int main(int argc, char** argv)
352{
353 testing::InitGoogleTest(&argc, argv);
354 rclcpp::init(argc, argv);
355 return RUN_ALL_TESTS();
356}
static std::size_t generateTestTraj(std::vector< std::shared_ptr< RobotState > > &traj, const RobotModelConstPtr &robot_model_)
RobotModelConstPtr robot_model_
static Percentage checkJointSpaceJump(const JointModelGroup *group, std::vector< std::shared_ptr< RobotState > > &path, const JumpThreshold &jump_threshold)
Checks if a path has a joint-space jump, and truncates the path at the jump.
Easily build different robot models for testing. Essentially a programmer-friendly light wrapper arou...
void addChain(const std::string &section, const std::string &type, const std::vector< geometry_msgs::msg::Pose > &joint_origins={}, urdf::Vector3 joint_axis=urdf::Vector3(1.0, 0.0, 0.0))
Adds a chain of links and joints to the builder. The joint names are generated automatically as "<par...
moveit::core::RobotModelPtr build()
Builds and returns the robot model added to the builder.
void addGroupChain(const std::string &base_link, const std::string &tip_link, const std::string &name="")
Adds a new group using a chain of links. The group is the parent joint of each link in the chain.
Core components of MoveIt.
static JumpThreshold relative(double relative_factor)
Detect joint-space jumps relative to the average joint-space displacement along the path.
static JumpThreshold absolute(double revolute, double prismatic)
Detect joint-space jumps greater than the given absolute thresholds.
int main(int argc, char **argv)
TEST_F(SimpleRobot, testGenerateTrajectory)