move_group_interface_cpp_test.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2020, Tyler Weaver
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 PickNik Robotics nor the
18  * names of its contributors may be used to endorse or promote
19  * products derived from this software without specific prior
20  * written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
36 /* Author: Tyler Weaver, Boston Cleek */
37 
38 /* These integration tests are based on the tutorials for using move_group:
39  * https://moveit.github.io/moveit_tutorials/doc/move_group_interface/move_group_interface_tutorial.html
40  */
41 
42 // C++
43 #include <string>
44 #include <vector>
45 #include <map>
46 #include <future>
47 
48 // ROS
49 #include <ros/ros.h>
50 
51 // The Testing Framework and Utils
52 #include <gtest/gtest.h>
53 
54 // MoveIt
59 
60 // TF2
62 #include <tf2_eigen/tf2_eigen.h>
63 
64 // 10um acuracy tested for position and orientation
65 constexpr double EPSILON = 1e-5;
66 
67 static const std::string PLANNING_GROUP = "panda_arm";
68 constexpr double PLANNING_TIME_S = 30.0;
69 constexpr double MAX_VELOCITY_SCALE = 1.0;
70 constexpr double MAX_ACCELERATION_SCALE = 1.0;
71 constexpr double GOAL_TOLERANCE = 1e-6;
72 
73 class MoveGroupTestFixture : public ::testing::Test
74 {
75 public:
76  void SetUp() override
77  {
78  nh_ = ros::NodeHandle("/move_group_interface_cpp_test");
79  move_group_ = std::make_shared<moveit::planning_interface::MoveGroupInterface>(PLANNING_GROUP);
80 
81  // set velocity and acceleration scaling factors (full speed)
82  move_group_->setMaxVelocityScalingFactor(MAX_VELOCITY_SCALE);
83  move_group_->setMaxAccelerationScalingFactor(MAX_ACCELERATION_SCALE);
84 
85  // allow more time for planning
86  move_group_->setPlanningTime(PLANNING_TIME_S);
87 
88  // set the tolerance for the goals to be smaller than epsilon
89  move_group_->setGoalTolerance(GOAL_TOLERANCE);
90 
91  /* the tf buffer is not strictly needed,
92  but it's a simple way to add the codepaths to the tests */
93  psm_ = std::make_shared<planning_scene_monitor::PlanningSceneMonitor>("robot_description",
95  psm_->startSceneMonitor("/move_group/monitored_planning_scene");
96  psm_->requestPlanningSceneState();
97 
98  // give move_group_, planning_scene_interface_ and psm_ time to connect their topics
99  ros::Duration(0.5).sleep();
100  }
101 
102  // run updater() and ensure at least one geometry update was processed by the `move_group` node after doing so
103  void synchronizeGeometryUpdate(const std::function<void()>& updater)
104  {
105  SCOPED_TRACE("synchronizeGeometryUpdate");
106  std::promise<void> promise;
107  std::future<void> future = promise.get_future();
108  psm_->addUpdateCallback([this, &promise](planning_scene_monitor::PlanningSceneMonitor::SceneUpdateType t) {
110  promise.set_value();
111  psm_->clearUpdateCallbacks();
112  });
113  updater();
114  // the updater must have triggered a geometry update, otherwise we can't be sure about the state of the scene anymore
115  ASSERT_EQ(future.wait_for(std::chrono::seconds(5)), std::future_status::ready);
116  }
117 
118  void planAndMoveToPose(const geometry_msgs::Pose& pose)
119  {
120  SCOPED_TRACE("planAndMoveToPose");
121  ASSERT_TRUE(move_group_->setJointValueTarget(pose));
122  planAndMove();
123  }
124 
125  void planAndMove()
126  {
127  SCOPED_TRACE("planAndMove");
129  ASSERT_EQ(move_group_->plan(my_plan), moveit::core::MoveItErrorCode::SUCCESS);
130  ASSERT_EQ(move_group_->move(), moveit::core::MoveItErrorCode::SUCCESS);
131  }
132 
133  void testEigenPose(const Eigen::Isometry3d& expected, const Eigen::Isometry3d& actual)
134  {
135  SCOPED_TRACE("testEigenPose");
136  std::stringstream ss;
137  ss << "expected: \n" << expected.matrix() << "\nactual: \n" << actual.matrix();
138  EXPECT_TRUE(actual.isApprox(expected, EPSILON)) << ss.str();
139  }
140 
141  void testPose(const Eigen::Isometry3d& expected_pose)
142  {
143  SCOPED_TRACE("testPose(const Eigen::Isometry3d&)");
144  // get the pose of the end effector link after the movement
145  geometry_msgs::PoseStamped actual_pose_stamped = move_group_->getCurrentPose();
146  Eigen::Isometry3d actual_pose;
147  tf2::fromMsg(actual_pose_stamped.pose, actual_pose);
148 
149  // compare to planned pose
150  testEigenPose(expected_pose, actual_pose);
151  }
152 
153  void testPose(const geometry_msgs::Pose& expected_pose_msg)
154  {
155  SCOPED_TRACE("testPose(const geometry_msgs::Pose&)");
156  Eigen::Isometry3d expected_pose;
157  tf2::fromMsg(expected_pose_msg, expected_pose);
158  testPose(expected_pose);
159  }
160 
161  void testJointPositions(const std::vector<double>& expected)
162  {
163  SCOPED_TRACE("testJointPositions");
164  const moveit::core::JointModelGroup* joint_model_group =
165  move_group_->getCurrentState()->getJointModelGroup(PLANNING_GROUP);
166  std::vector<double> actual;
167  move_group_->getCurrentState()->copyJointGroupPositions(joint_model_group, actual);
168  ASSERT_EQ(expected.size(), actual.size());
169  for (size_t i = 0; i < actual.size(); ++i)
170  {
171  double delta = std::abs(expected[i] - actual[i]);
172  EXPECT_LT(delta, EPSILON) << "joint index: " << i << ", plan: " << expected[i] << ", result: " << actual[i];
173  }
174  }
175 
176 protected:
178  moveit::planning_interface::MoveGroupInterfacePtr move_group_;
180  planning_scene_monitor::PlanningSceneMonitorPtr psm_;
181 };
182 
183 TEST_F(MoveGroupTestFixture, PathConstraintCollisionTest)
184 {
185  SCOPED_TRACE("PathConstraintCollisionTest");
186 
188  // set a custom start state
189  // this simplifies planning for the orientation constraint bellow
190  geometry_msgs::Pose start_pose;
191  start_pose.orientation.w = 1.0;
192  start_pose.position.x = 0.3;
193  start_pose.position.y = 0.0;
194  start_pose.position.z = 0.6;
195  planAndMoveToPose(start_pose);
196 
198  // Test setting target pose with eigen and with geometry_msgs
199  geometry_msgs::Pose target_pose;
200  target_pose.orientation.w = 1.0;
201  target_pose.position.x = 0.3;
202  target_pose.position.y = -0.3;
203  target_pose.position.z = 0.6;
204 
205  // convert to eigen
206  Eigen::Isometry3d eigen_target_pose;
207  tf2::fromMsg(target_pose, eigen_target_pose);
208 
209  // set with eigen, get ros message representation
210  move_group_->setPoseTarget(eigen_target_pose);
211  geometry_msgs::PoseStamped set_target_pose = move_group_->getPoseTarget();
212  Eigen::Isometry3d eigen_set_target_pose;
213  tf2::fromMsg(set_target_pose.pose, eigen_set_target_pose);
214 
215  // expect that they are identical
216  testEigenPose(eigen_target_pose, eigen_set_target_pose);
217 
219  // create an orientation constraint
220  moveit_msgs::OrientationConstraint ocm;
221  ocm.link_name = move_group_->getEndEffectorLink();
222  ocm.header.frame_id = move_group_->getPlanningFrame();
223  ocm.orientation.w = 1.0;
224  ocm.absolute_x_axis_tolerance = 0.1;
225  ocm.absolute_y_axis_tolerance = 0.1;
226  ocm.absolute_z_axis_tolerance = 0.1;
227  ocm.weight = 1.0;
228  moveit_msgs::Constraints test_constraints;
229  test_constraints.orientation_constraints.push_back(ocm);
230  move_group_->setPathConstraints(test_constraints);
231 
233  // plan and move
234  planAndMove();
235 
236  // get the pose after the movement
237  testPose(eigen_target_pose);
238 
239  // clear path constraints
240  move_group_->clearPathConstraints();
241 
242  // move back to ready pose
243  move_group_->setNamedTarget("ready");
244  planAndMove();
245 }
246 
247 TEST_F(MoveGroupTestFixture, ModifyPlanningSceneAsyncInterfaces)
248 {
250  // Define a collision object ROS message.
251  moveit_msgs::CollisionObject collision_object;
252  collision_object.header.frame_id = move_group_->getPlanningFrame();
253 
254  // The id of the object is used to identify it.
255  collision_object.id = "box1";
256 
257  // Define a box to add to the world.
258  shape_msgs::SolidPrimitive primitive;
259  primitive.type = primitive.BOX;
260  primitive.dimensions.resize(3);
261  primitive.dimensions[0] = 0.1;
262  primitive.dimensions[1] = 1.0;
263  primitive.dimensions[2] = 1.0;
264 
265  // Define a pose for the box (specified relative to frame_id)
266  geometry_msgs::Pose box_pose;
267  box_pose.orientation.w = 1.0;
268  box_pose.position.x = 0.5;
269  box_pose.position.y = 0.0;
270  box_pose.position.z = 0.5;
271 
272  collision_object.primitives.push_back(primitive);
273  collision_object.primitive_poses.push_back(box_pose);
274  collision_object.operation = collision_object.ADD;
275 
276  std::vector<moveit_msgs::CollisionObject> collision_objects;
277  collision_objects.push_back(collision_object);
278 
279  // Now, let's add the collision object into the world
280  synchronizeGeometryUpdate([&]() { planning_scene_interface_.addCollisionObjects(collision_objects); });
281 
282  // attach and detach collision object
283  synchronizeGeometryUpdate([&]() { EXPECT_TRUE(move_group_->attachObject(collision_object.id)); });
284  EXPECT_EQ(planning_scene_interface_.getAttachedObjects().size(), std::size_t(1));
285  synchronizeGeometryUpdate([&]() { EXPECT_TRUE(move_group_->detachObject(collision_object.id)); });
286  EXPECT_EQ(planning_scene_interface_.getAttachedObjects().size(), std::size_t(0));
287 
288  // remove object from world
289  const std::vector<std::string> object_ids = { collision_object.id };
290  EXPECT_EQ(planning_scene_interface_.getObjects().size(), std::size_t(1));
291  synchronizeGeometryUpdate([&]() { planning_scene_interface_.removeCollisionObjects(object_ids); });
292  EXPECT_EQ(planning_scene_interface_.getObjects().size(), std::size_t(0));
293 }
294 
296 {
297  SCOPED_TRACE("CartPathTest");
298 
299  // Plan from current pose
300  const geometry_msgs::PoseStamped start_pose = move_group_->getCurrentPose();
301 
302  std::vector<geometry_msgs::Pose> waypoints;
303  waypoints.push_back(start_pose.pose);
304 
305  geometry_msgs::Pose target_waypoint = start_pose.pose;
306  target_waypoint.position.z -= 0.2;
307  waypoints.push_back(target_waypoint); // down
308 
309  target_waypoint.position.y -= 0.2;
310  waypoints.push_back(target_waypoint); // right
311 
312  target_waypoint.position.z += 0.2;
313  target_waypoint.position.y += 0.2;
314  target_waypoint.position.x -= 0.2;
315  waypoints.push_back(target_waypoint); // up and left
316 
317  moveit_msgs::RobotTrajectory trajectory;
318  const auto eef_step = 0.01;
319 
320  // test below is meaningless if Cartesian planning did not succeed
321  ASSERT_GE(EPSILON + move_group_->computeCartesianPath(waypoints, eef_step, trajectory), 1.0);
322 
323  // Execute trajectory
324  EXPECT_EQ(move_group_->execute(trajectory), moveit::core::MoveItErrorCode::SUCCESS);
325 
326  // get the pose after the movement
327  testPose(target_waypoint);
328 }
329 
330 TEST_F(MoveGroupTestFixture, JointSpaceGoalTest)
331 {
332  SCOPED_TRACE("JointSpaceGoalTest");
333 
334  // Next get the current set of joint values for the group.
335  std::vector<double> plan_joint_positions;
336  move_group_->getCurrentState()->copyJointGroupPositions(
337  move_group_->getCurrentState()->getJointModelGroup(PLANNING_GROUP), plan_joint_positions);
338 
339  // Now, let's modify the joint positions. (radians)
340  ASSERT_EQ(plan_joint_positions.size(), std::size_t(7));
341  plan_joint_positions = { 1.2, -1.0, -0.1, -2.4, 0.0, 1.5, 0.6 };
342  move_group_->setJointValueTarget(plan_joint_positions);
343 
344  // plan and move
345  planAndMove();
346 
347  // test that we moved to the expected joint positions
348  testJointPositions(plan_joint_positions);
349 }
350 
351 TEST_F(MoveGroupTestFixture, CartesianGoalTest)
352 {
353  move_group_->setPoseReferenceFrame("world");
354  move_group_->setEndEffectorLink("panda_hand");
355  geometry_msgs::Pose pose;
356  pose.position.x = 0.417;
357  pose.position.y = 0.240;
358  pose.position.z = 0.532;
359  pose.orientation.w = 1.0;
360  EXPECT_TRUE(move_group_->setJointValueTarget(pose));
361 }
362 
363 int main(int argc, char** argv)
364 {
365  ros::init(argc, argv, "move_group_interface_cpp_test");
366  testing::InitGoogleTest(&argc, argv);
367 
369  spinner.start();
370 
371  int result = RUN_ALL_TESTS();
372  return result;
373 }
MoveGroupTestFixture::psm_
planning_scene_monitor::PlanningSceneMonitorPtr psm_
Definition: move_group_interface_cpp_test.cpp:180
MoveGroupTestFixture::testPose
void testPose(const geometry_msgs::Pose &expected_pose_msg)
Definition: move_group_interface_cpp_test.cpp:153
MoveGroupTestFixture::testPose
void testPose(const Eigen::Isometry3d &expected_pose)
Definition: move_group_interface_cpp_test.cpp:141
moveit::planning_interface::MoveGroupInterface::Plan
The representation of a motion plan (as ROS messasges)
Definition: move_group_interface.h:187
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
tf2_eigen.h
tf2::fromMsg
void fromMsg(const A &, B &b)
ros.h
ros::AsyncSpinner
TEST_F
TEST_F(MoveGroupTestFixture, PathConstraintCollisionTest)
Definition: move_group_interface_cpp_test.cpp:183
moveit::planning_interface::PlanningSceneInterface
Definition: planning_scene_interface.h:116
MoveGroupTestFixture::nh_
ros::NodeHandle nh_
Definition: move_group_interface_cpp_test.cpp:177
MoveGroupTestFixture::SetUp
void SetUp() override
Definition: move_group_interface_cpp_test.cpp:76
PLANNING_GROUP
static const std::string PLANNING_GROUP
Definition: move_group_interface_cpp_test.cpp:67
moveit::planning_interface::getSharedTF
std::shared_ptr< tf2_ros::Buffer > getSharedTF()
Definition: common_objects.cpp:99
EXPECT_TRUE
#define EXPECT_TRUE(args)
planning_scene_monitor::PlanningSceneMonitor::SceneUpdateType
SceneUpdateType
spinner
void spinner()
MoveGroupTestFixture::planning_scene_interface_
moveit::planning_interface::PlanningSceneInterface planning_scene_interface_
Definition: move_group_interface_cpp_test.cpp:179
MoveGroupTestFixture
Definition: move_group_interface_cpp_test.cpp:73
MoveGroupTestFixture::planAndMoveToPose
void planAndMoveToPose(const geometry_msgs::Pose &pose)
Definition: move_group_interface_cpp_test.cpp:118
EPSILON
constexpr double EPSILON
Definition: move_group_interface_cpp_test.cpp:65
planning_scene_monitor.h
MoveGroupTestFixture::testEigenPose
void testEigenPose(const Eigen::Isometry3d &expected, const Eigen::Isometry3d &actual)
Definition: move_group_interface_cpp_test.cpp:133
MoveGroupTestFixture::synchronizeGeometryUpdate
void synchronizeGeometryUpdate(const std::function< void()> &updater)
Definition: move_group_interface_cpp_test.cpp:103
planning_scene_interface.h
common_objects.h
MoveGroupTestFixture::planAndMove
void planAndMove()
Definition: move_group_interface_cpp_test.cpp:125
move_group_interface.h
moveit::core::JointModelGroup
main
int main(int argc, char **argv)
Definition: move_group_interface_cpp_test.cpp:363
planning_scene_monitor::PlanningSceneMonitor::UPDATE_GEOMETRY
UPDATE_GEOMETRY
tf2_geometry_msgs.h
MoveGroupTestFixture::testJointPositions
void testJointPositions(const std::vector< double > &expected)
Definition: move_group_interface_cpp_test.cpp:161
MAX_VELOCITY_SCALE
constexpr double MAX_VELOCITY_SCALE
Definition: move_group_interface_cpp_test.cpp:69
ros::Duration::sleep
bool sleep() const
MoveGroupTestFixture::move_group_
moveit::planning_interface::MoveGroupInterfacePtr move_group_
Definition: move_group_interface_cpp_test.cpp:178
t
dictionary t
ros::Duration
PLANNING_TIME_S
constexpr double PLANNING_TIME_S
Definition: move_group_interface_cpp_test.cpp:68
EXPECT_EQ
#define EXPECT_EQ(a, b)
GOAL_TOLERANCE
constexpr double GOAL_TOLERANCE
Definition: move_group_interface_cpp_test.cpp:71
ros::NodeHandle
MAX_ACCELERATION_SCALE
constexpr double MAX_ACCELERATION_SCALE
Definition: move_group_interface_cpp_test.cpp:70


planning_interface
Author(s): Ioan Sucan
autogenerated on Thu Nov 21 2024 03:25:12