moveit2
The MoveIt Motion Planning Framework for ROS 2.
test_constrained_state_validity_checker.cpp
Go to the documentation of this file.
1 
2 /*********************************************************************
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2020, KU Leuven
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of KU Leuven nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior 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: Jeroen De Maeyer */
37 
38 #include "load_test_robot.h"
39 
40 #include <limits>
41 #include <ostream>
42 
43 #include <gtest/gtest.h>
44 
49 #include <moveit/utils/logger.hpp>
50 
51 #include <ompl/geometric/SimpleSetup.h>
52 #include <ompl/base/Constraint.h>
53 #include <ompl/base/ConstrainedSpaceInformation.h>
54 #include <ompl/base/spaces/constraint/ProjectedStateSpace.h>
55 
57 constexpr bool VERBOSE = false;
58 
59 rclcpp::Logger getLogger()
60 {
61  return moveit::getLogger("test_constrained_state_validity_checker");
62 }
63 
65 std::ostream& operator<<(std::ostream& os, const std::vector<double>& v)
66 {
67  os << "( ";
68  for (auto value : v)
69  os << value << ", ";
70  os << " )";
71  return os;
72 }
73 
75 class DummyConstraint : public ompl::base::Constraint
76 {
77 public:
78  DummyConstraint(const unsigned int num_dofs) : ompl::base::Constraint(num_dofs, 1)
79  {
80  }
81  void function(const Eigen::Ref<const Eigen::VectorXd>& /*unused*/, Eigen::Ref<Eigen::VectorXd> out) const override
82  {
83  out[0] = 0.0;
84  }
85 };
86 
89 {
90 public:
91  TestStateValidityChecker(const std::string& robot_name, const std::string& group_name)
92  : LoadTestRobot(robot_name, group_name)
93  {
94  initial_robot_state_ = std::make_shared<moveit::core::RobotState>(robot_model_);
95  initial_robot_state_->setToDefaultValues();
96  }
97 
99  {
100  SCOPED_TRACE("testEqualityPositionConstraints");
101  EXPECT_TRUE(planning_context_.get());
102  }
103 
104  void testJointLimits(const std::vector<double>& position_in_limits)
105  {
106  SCOPED_TRACE("testJointLimits");
107 
108  // create a validity checker for this test
109  auto checker = std::make_shared<ompl_interface::ConstrainedPlanningStateValidityChecker>(planning_context_.get());
110  checker->setVerbose(VERBOSE);
111 
112  // setup an ompl state with the "position_in_limits" joint values
113  robot_state_->setJointGroupPositions(joint_model_group_, position_in_limits);
114  ompl::base::ScopedState<> ompl_state(constrained_state_space_);
115  state_space_->copyToOMPLState(ompl_state.get(), *robot_state_);
116 
117  // cast ompl state to a specific type, useful in the rest of this test
118  auto state = ompl_state->as<ompl::base::ConstrainedStateSpace::StateType>()
119  ->getState()
121 
122  RCLCPP_DEBUG_STREAM(getLogger(),
123  std::vector<double>(state->values, state->values + joint_model_group_->getVariableCount()));
124 
125  // assume the default position in not in self-collision
126  // and there are no collision objects of path constraints so this state should be valid
127  bool result = checker->isValid(ompl_state.get());
128  EXPECT_TRUE(result);
129 
130  // do the same for the version with distance
131  double distance = 0.0;
132  result = checker->isValid(ompl_state.get(), distance);
133 
134  RCLCPP_DEBUG(getLogger(), "Distance from the isValid function '%f': ", distance);
135  EXPECT_TRUE(result);
136  EXPECT_GT(distance, 0.0);
137 
138  // move first joint obviously outside any joint limits
139  state->values[0] = std::numeric_limits<double>::max();
140  state->clearKnownInformation(); // make sure the validity checker does not use the cached value
141 
142  RCLCPP_DEBUG_STREAM(getLogger(),
143  std::vector<double>(state->values, state->values + joint_model_group_->getVariableCount()));
144 
145  bool result_2 = checker->isValid(ompl_state.get());
146  EXPECT_FALSE(result_2);
147 
148  // do the same for the version with distance
149  double distance_2 = 0.0;
150  result_2 = checker->isValid(ompl_state.get(), distance_2);
151  EXPECT_FALSE(result_2);
152  // isValid function returned false before any distance calculation is done
153  EXPECT_EQ(distance_2, 0.0);
154  }
155 
156  void testSelfCollision(const std::vector<double>& position_in_self_collision)
157  {
158  SCOPED_TRACE("testSelfCollision");
159 
160  // create a validity checker for this test
161  auto checker = std::make_shared<ompl_interface::ConstrainedPlanningStateValidityChecker>(planning_context_.get());
162  checker->setVerbose(VERBOSE);
163 
164  robot_state_->setJointGroupPositions(joint_model_group_, position_in_self_collision);
165 
166  // use a scoped OMPL state so we don't have to call allocState and freeState
167  // (as recommended in the OMPL documantion)
168  ompl::base::ScopedState<> ompl_state(constrained_state_space_);
169  state_space_->copyToOMPLState(ompl_state.get(), *robot_state_);
170 
171  // The code below is just to print the state to the debug stream
172  auto state = ompl_state->as<ompl::base::ConstrainedStateSpace::StateType>()
173  ->getState()
175 
176  // ompl_state.reals() throws a segmentation fault for this state type
177  // use a more involved conversion to std::vector for logging
178  RCLCPP_DEBUG_STREAM(getLogger(),
179  std::vector<double>(state->values, state->values + joint_model_group_->getVariableCount()));
180 
181  // the given state is known to be in self-collision, we check it here
182  bool result = checker->isValid(ompl_state.get());
183  EXPECT_FALSE(result);
184 
185  // do the same for the version with distance
186  double distance = 0.0;
187  result = checker->isValid(ompl_state.get(), distance);
188  EXPECT_FALSE(result);
189 
190  // but it should respect the joint limits
191  bool result_2 = robot_state_->satisfiesBounds();
192  EXPECT_TRUE(result_2);
193  }
194 
195  // /***************************************************************************
196  // * END Test implementation
197  // * ************************************************************************/
198 
199 protected:
200  void SetUp() override
201  {
202  // setup all the input we need to create a StateValidityChecker
203  setupStateSpace();
205  };
206 
207  void TearDown() override
208  {
209  }
210 
212  {
213  // note: make_shared throws if the allocations below fail, making the test fail when necessary
215  state_space_ = std::make_shared<ompl_interface::ConstrainedPlanningStateSpace>(space_spec);
216  state_space_->computeLocations(); // this gets called in the state space factory normally
217 
218  auto dummy_constraint = std::make_shared<DummyConstraint>(num_dofs_);
219  constrained_state_space_ = std::make_shared<ompl::base::ProjectedStateSpace>(state_space_, dummy_constraint);
220  }
221 
223  {
224  ASSERT_NE(state_space_, nullptr) << "Initialize state space before creating the planning context.";
226 
227  // IMPORTANT, we need to create the simple setup with a ConstrainedSpaceInformation object,
228  // not with the state space, as this will allocate the wrong type of SpaceInformation
229  auto csi = std::make_shared<ompl::base::ConstrainedSpaceInformation>(constrained_state_space_);
230  planning_context_spec_.ompl_simple_setup_ = std::make_shared<ompl::geometric::SimpleSetup>(csi);
231 
232  // check if the we succeeded in the comment above
233  auto si = planning_context_spec_.ompl_simple_setup_->getSpaceInformation();
234  auto si_constrained = dynamic_cast<ompl::base::ConstrainedSpaceInformation*>(si.get());
235  ASSERT_NE(si_constrained, nullptr);
236 
238  std::make_shared<ompl_interface::ModelBasedPlanningContext>(group_name_, planning_context_spec_);
239 
240  planning_scene_ = std::make_shared<planning_scene::PlanningScene>(robot_model_);
241  planning_context_->setPlanningScene(planning_scene_);
242  planning_context_->setCompleteInitialState(*initial_robot_state_);
243 
244  RCLCPP_DEBUG(getLogger(), "Planning context with name '%s' is ready (but not configured).",
245  planning_context_->getName().c_str());
246  }
247 
248  moveit::core::RobotStatePtr initial_robot_state_;
249 
250  ompl_interface::ModelBasedStateSpacePtr state_space_;
252  ompl_interface::ModelBasedPlanningContextPtr planning_context_;
253  planning_scene::PlanningScenePtr planning_scene_;
254 
255  ompl::base::ConstrainedStateSpacePtr constrained_state_space_;
256 };
257 
258 // /***************************************************************************
259 // * Run all tests on the Panda robot
260 // * ************************************************************************/
262 {
263 protected:
265  {
266  }
267 };
268 
270 {
271  testConstructor();
272 }
273 
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 
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 /***************************************************************************
289  * Run all tests on the Fanuc robot
290  * ************************************************************************/
292 {
293 protected:
295  {
296  }
297 };
298 
299 TEST_F(FanucTestStateValidityChecker, createStateValidityChecker)
300 {
301  testConstructor();
302 }
303 
305 {
306  // I assume the Fanucs's zero state is within limits and self-collision free
307  testJointLimits({ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 });
308 }
309 
311 {
312  // the given state has self collision between "base_link" and "link_5"
313  // (I just tried a couple of random states until I found one that collided.)
314  testSelfCollision({ -2.95993, -0.682185, -2.43873, -0.939784, 3.0544, 0.882294 });
315 }
316 
317 /* (Note: the PR2 has no collision geometry in the moveit_resources package.) */
318 
319 /***************************************************************************
320  * MAIN
321  * ************************************************************************/
322 int main(int argc, char** argv)
323 {
324  testing::InitGoogleTest(&argc, argv);
325  return RUN_ALL_TESTS();
326 }
Dummy constraint for testing, always satisfied. We need this to create and OMPL ConstrainedStateSpace...
DummyConstraint(const unsigned int num_dofs)
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_
ompl::base::ConstrainedStateSpacePtr constrained_state_space_
ompl_interface::ModelBasedPlanningContextSpecification planning_context_spec_
ompl_interface::ModelBasedPlanningContextPtr planning_context_
TestStateValidityChecker(const std::string &robot_name, const std::string &group_name)
unsigned int getVariableCount() const
Get the number of variables that describe this joint group. This includes variables necessary for mim...
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_
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79
double distance(const urdf::Pose &transform)
Definition: pr2_arm_ik.h:55
int main(int argc, char **argv)
std::ostream & operator<<(std::ostream &os, const std::vector< double > &v)
Pretty print std:vectors.
rclcpp::Logger getLogger()
constexpr bool VERBOSE
This flag sets the verbosity level for the state validity checker.
TEST_F(PandaValidityCheckerTests, testConstructor)
bool getState(const std::shared_ptr< moveit_msgs::srv::GetRobotStateFromWarehouse::Request > &request, const std::shared_ptr< moveit_msgs::srv::GetRobotStateFromWarehouse::Response > &response, moveit_warehouse::RobotStateStorage &rs)