moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
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
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
57constexpr bool VERBOSE = false;
58
59rclcpp::Logger getLogger()
60{
61 return moveit::getLogger("moveit.planners.ompl.test_constrained_state_validity_checker");
62}
63
65std::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
75class DummyConstraint : public ompl::base::Constraint
76{
77public:
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{
90public:
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
199protected:
200 void SetUp() override
201 {
202 // setup all the input we need to create a StateValidityChecker
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{
263protected:
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{
293protected:
295 {
296 }
297};
298
299TEST_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 * ************************************************************************/
322int 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)
void function(const Eigen::Ref< const Eigen::VectorXd > &, Eigen::Ref< Eigen::VectorXd > out) const override
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
int main(int argc, char **argv)
rclcpp::Logger getLogger()
constexpr bool VERBOSE
This flag sets the verbosity level for the state validity checker.
TEST_F(PandaValidityCheckerTests, testConstructor)
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.
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)