moveit2
The MoveIt Motion Planning Framework for ROS 2.
test_state_validity_checker.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2020, KU Leuven
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 KU Leuven 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: Jeroen De Maeyer */
51 #include "load_test_robot.h"
52 
53 #include <limits>
54 #include <ostream>
55 
56 #include <gtest/gtest.h>
57 
62 
63 #include <ompl/geometric/SimpleSetup.h>
64 
66 constexpr bool VERBOSE = false;
67 
68 static const rclcpp::Logger LOGGER = rclcpp::get_logger("moveit.ompl_planning.test.test_state_validity_checker");
69 
71 std::ostream& operator<<(std::ostream& os, const std::vector<double>& v)
72 {
73  os << "( ";
74  for (auto value : v)
75  os << value << ", ";
76  os << " )";
77  return os;
78 }
79 
81 class TestStateValidityChecker : public ompl_interface_testing::LoadTestRobot, public testing::Test
82 {
83 public:
84  TestStateValidityChecker(const std::string& robot_name, const std::string& group_name)
85  : LoadTestRobot(robot_name, group_name)
86  {
87  }
88 
90  {
91  SCOPED_TRACE("testConstructor");
92  EXPECT_TRUE(planning_context_.get());
93  }
94 
96  void testJointLimits(const std::vector<double>& position_in_limits)
97  {
98  SCOPED_TRACE("testJointLimits");
99 
100  // create a validity checker for this test
101  auto checker = std::make_shared<ompl_interface::StateValidityChecker>(planning_context_.get());
102  checker->setVerbose(VERBOSE);
103 
104  robot_state_->setJointGroupPositions(joint_model_group_, position_in_limits);
105 
106  // use a scoped OMPL state so we don't have to call allocState and freeState
107  // (as recommended in the OMPL documantion)
108  ompl::base::ScopedState<> ompl_state(state_space_);
109  state_space_->copyToOMPLState(ompl_state.get(), *robot_state_);
110 
111  RCLCPP_DEBUG_STREAM(LOGGER, ompl_state.reals());
112 
113  // assume the given position is not in self-collision
114  // and there are no collision objects or path constraints so this state should be valid
115  EXPECT_TRUE(checker->isValid(ompl_state.get()));
116 
117  // move first joint obviously outside any joint limits
118  ompl_state->as<ompl_interface::JointModelStateSpace::StateType>()->values[0] = std::numeric_limits<double>::max();
119  ompl_state->as<ompl_interface::JointModelStateSpace::StateType>()->clearKnownInformation();
120 
121  RCLCPP_DEBUG_STREAM(LOGGER, ompl_state.reals());
122 
123  EXPECT_FALSE(checker->isValid(ompl_state.get()));
124  }
125 
127  void testSelfCollision(const std::vector<double>& position_in_self_collision)
128  {
129  SCOPED_TRACE("testSelfCollision");
130 
131  // create a validity checker for this test
132  auto checker = std::make_shared<ompl_interface::StateValidityChecker>(planning_context_.get());
133  checker->setVerbose(VERBOSE);
134 
135  robot_state_->setJointGroupPositions(joint_model_group_, position_in_self_collision);
136 
137  // use a scoped OMPL state so we don't have to call allocState and freeState
138  // (as recommended in the OMPL documantion)
139  ompl::base::ScopedState<> ompl_state(state_space_);
140  state_space_->copyToOMPLState(ompl_state.get(), *robot_state_);
141 
142  RCLCPP_DEBUG_STREAM(LOGGER, ompl_state.reals());
143 
144  // the given state is known to be in self-collision, we check it here
145  EXPECT_FALSE(checker->isValid(ompl_state.get()));
146 
147  // but it should respect the joint limits
148  EXPECT_TRUE(robot_state_->satisfiesBounds());
149  }
150 
151  void testPathConstraints(const std::vector<double>& position_in_joint_limits)
152  {
153  SCOPED_TRACE("testPathConstraints");
154 
155  ASSERT_NE(planning_context_, nullptr) << "Initialize planning context before adding path constraints.";
156 
157  // set the robot to a known position that is within the joint limits and collision free
158  robot_state_->setJointGroupPositions(joint_model_group_, position_in_joint_limits);
159 
160  // create position constraints around the given robot state
161  moveit_msgs::msg::Constraints path_constraints;
162  Eigen::Isometry3d ee_pose = robot_state_->getGlobalLinkTransform(ee_link_name_);
163  path_constraints.name = "test_position_constraints";
164  path_constraints.position_constraints.push_back(createPositionConstraint(
165  { ee_pose.translation().x(), ee_pose.translation().y(), ee_pose.translation().z() }, { 0.1, 0.1, 0.1 }));
166 
167  moveit_msgs::msg::MoveItErrorCodes error_code_not_used;
168  ASSERT_TRUE(planning_context_->setPathConstraints(path_constraints, &error_code_not_used));
169 
170  auto checker = std::make_shared<ompl_interface::StateValidityChecker>(planning_context_.get());
171  checker->setVerbose(VERBOSE);
172 
173  // use a scoped OMPL state so we don't have to call allocState and freeState
174  // (as recommended in the OMPL documantion)
175  ompl::base::ScopedState<> ompl_state(state_space_);
176  state_space_->copyToOMPLState(ompl_state.get(), *robot_state_);
177 
178  RCLCPP_DEBUG_STREAM(LOGGER, ompl_state.reals());
179 
180  EXPECT_TRUE(checker->isValid(ompl_state.get()));
181 
182  // move the position constraints away from the current end-effector position to make it fail
183  moveit_msgs::msg::Constraints path_constraints_2(path_constraints);
184  path_constraints_2.position_constraints.at(0).constraint_region.primitive_poses.at(0).position.z += 0.2;
185 
186  ASSERT_TRUE(planning_context_->setPathConstraints(path_constraints_2, &error_code_not_used));
187 
188  // clear the cached validity of the state before checking again,
189  // otherwise the path constraints will not be checked.
190  ompl_state->as<ompl_interface::JointModelStateSpace::StateType>()->clearKnownInformation();
191 
192  EXPECT_FALSE(checker->isValid(ompl_state.get()));
193  }
194 
195 protected:
196  void SetUp() override
197  {
198  // setup all the input we need to create a StateValidityChecker
199  setupStateSpace();
201  };
202 
204  {
206  state_space_ = std::make_shared<ompl_interface::JointModelStateSpace>(space_spec);
207  state_space_->computeLocations(); // this gets normally called in the state space factory
208  }
209 
211  {
212  ASSERT_NE(state_space_, nullptr) << "Initialize state space before creating the planning context.";
213 
215  planning_context_spec_.ompl_simple_setup_ = std::make_shared<ompl::geometric::SimpleSetup>(state_space_);
217  std::make_shared<ompl_interface::ModelBasedPlanningContext>(group_name_, planning_context_spec_);
218 
219  planning_scene_ = std::make_shared<planning_scene::PlanningScene>(robot_model_);
220  planning_context_->setPlanningScene(planning_scene_);
222  start_state.setToDefaultValues();
223  planning_context_->setCompleteInitialState(start_state);
224  }
225 
227  moveit_msgs::msg::PositionConstraint createPositionConstraint(std::array<double, 3> position,
228  std::array<double, 3> dimensions)
229  {
230  shape_msgs::msg::SolidPrimitive box;
231  box.type = shape_msgs::msg::SolidPrimitive::BOX;
232  box.dimensions.resize(3);
233  box.dimensions[shape_msgs::msg::SolidPrimitive::BOX_X] = dimensions[0];
234  box.dimensions[shape_msgs::msg::SolidPrimitive::BOX_Y] = dimensions[1];
235  box.dimensions[shape_msgs::msg::SolidPrimitive::BOX_Z] = dimensions[2];
236 
237  geometry_msgs::msg::Pose box_pose;
238  box_pose.position.x = position[0];
239  box_pose.position.y = position[1];
240  box_pose.position.z = position[2];
241  box_pose.orientation.w = 1.0;
242 
243  moveit_msgs::msg::PositionConstraint pc;
244  pc.header.frame_id = base_link_name_;
245  pc.link_name = ee_link_name_;
246  pc.constraint_region.primitives.push_back(box);
247  pc.constraint_region.primitive_poses.push_back(box_pose);
248 
249  return pc;
250  }
251 
252  ompl_interface::ModelBasedStateSpacePtr state_space_;
254  ompl_interface::ModelBasedPlanningContextPtr planning_context_;
255  planning_scene::PlanningScenePtr planning_scene_;
256 };
257 
258 // /***************************************************************************
259 // * Run all tests on the Panda robot
260 // * ************************************************************************/
262 {
263 protected:
264  PandaValidity() : TestStateValidityChecker("panda", "panda_arm")
265  {
266  }
267 };
268 
269 TEST_F(PandaValidity, testConstructor)
270 {
271  testConstructor();
272 }
273 
274 TEST_F(PandaValidity, testJointLimits)
275 {
276  // use the panda "ready" state from the srdf config
277  // we know this state should be within limits and self-collision free
278  testJointLimits({ 0., -0.785, 0., -2.356, 0., 1.571, 0.785 });
279 }
280 
281 TEST_F(PandaValidity, testSelfCollision)
282 {
283  // the given state has self collision between "hand" and "panda_link2"
284  // (I just tried a couple of random states until I found one that collided.)
285  testSelfCollision({ 2.31827, -0.169668, 2.5225, -2.98568, -0.36355, 0.808339, 0.0843406 });
286 }
287 
288 TEST_F(PandaValidity, testPathConstraints)
289 {
290  // use the panda "ready" state from the srdf config
291  // we know this state should be within limits and self-collision free
292  testPathConstraints({ 0., -0.785, 0., -2.356, 0., 1.571, 0.785 });
293 }
294 
295 /***************************************************************************
296  * Run all tests on the Fanuc robot
297  * ************************************************************************/
299 {
300 protected:
301  FanucTest() : TestStateValidityChecker("fanuc", "manipulator")
302  {
303  }
304 };
305 
306 TEST_F(FanucTest, createStateValidityChecker)
307 {
308  testConstructor();
309 }
310 
311 TEST_F(FanucTest, testJointLimits)
312 {
313  // I assume the Fanucs's zero state is within limits and self-collision free
314  testJointLimits({ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 });
315 }
316 
317 TEST_F(FanucTest, testSelfCollision)
318 {
319  // the given state has self collision between "base_link" and "link_5"
320  // (I just tried a couple of random states until I found one that collided.)
321  testSelfCollision({ -2.95993, -0.682185, -2.43873, -0.939784, 3.0544, 0.882294 });
322 }
323 
324 TEST_F(FanucTest, testPathConstraints)
325 {
326  // I assume the Fanucs's zero state is within limits and self-collision free
327  testPathConstraints({ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 });
328 }
329 
330 /***************************************************************************
331  * MAIN
332  * ************************************************************************/
333 int main(int argc, char** argv)
334 {
335  testing::InitGoogleTest(&argc, argv);
336  return RUN_ALL_TESTS();
337 }
Generic implementation of the tests that can be executed on different robots.
ompl_interface::ModelBasedStateSpacePtr state_space_
void testJointLimits(const std::vector< double > &position_in_limits)
void testSelfCollision(const std::vector< double > &position_in_self_collision)
planning_scene::PlanningScenePtr planning_scene_
moveit_msgs::msg::PositionConstraint createPositionConstraint(std::array< double, 3 > position, std::array< double, 3 > dimensions)
Helper function to create a position constraint.
void testPathConstraints(const std::vector< double > &position_in_joint_limits)
ompl_interface::ModelBasedPlanningContextSpecification planning_context_spec_
ompl_interface::ModelBasedPlanningContextPtr planning_context_
TestStateValidityChecker(const std::string &robot_name, const std::string &group_name)
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition: robot_state.h:90
void setToDefaultValues()
Set all joints to their default positions. The default position is 0, or if that is not within bounds...
Robot independent test class setup.
moveit::core::RobotStatePtr robot_state_
moveit::core::RobotModelPtr robot_model_
LoadTestRobot(const std::string &robot_name, const std::string &group_name)
const moveit::core::JointModelGroup * joint_model_group_
x
Definition: pick.py:64
int main(int argc, char **argv)
std::ostream & operator<<(std::ostream &os, const std::vector< double > &v)
Pretty print std:vectors.
TEST_F(PandaValidity, testConstructor)
constexpr bool VERBOSE
This flag sets the verbosity level for the state validity checker.