diff --git a/CMakeLists.txt b/CMakeLists.txt index a52b2ea0099bfc576bd80565d54164059c5f556d..f77206ac8a2a5df7ec2da6da41ce93f13118be30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,7 @@ SET (${PROJECT_NAME}_HEADERS include/hpp/manipulation/graph/fwd.hh include/hpp/manipulation/graph/dot.hh include/hpp/manipulation/graph/helper.hh + include/hpp/manipulation/graph/validation.hh include/hpp/manipulation/path-optimization/small-steps.hh include/hpp/manipulation/path-optimization/enforce-transition-semantic.hh diff --git a/include/hpp/manipulation/graph/fwd.hh b/include/hpp/manipulation/graph/fwd.hh index 425bd85d2149e2ce7c38dcb95fd78866737a8a26..5806bde298bcec447467651f2f0186b609b1e512 100644 --- a/include/hpp/manipulation/graph/fwd.hh +++ b/include/hpp/manipulation/graph/fwd.hh @@ -60,6 +60,9 @@ namespace hpp { typedef boost::shared_ptr <StateHistogram> StateHistogramPtr_t; typedef boost::shared_ptr <LeafHistogram> LeafHistogramPtr_t; typedef std::list < HistogramPtr_t > Histograms_t; + + class Validation; + typedef boost::shared_ptr < Validation > ValidationPtr_t; } // namespace graph } // namespace manipulation } // namespace hpp diff --git a/include/hpp/manipulation/graph/validation.hh b/include/hpp/manipulation/graph/validation.hh new file mode 100644 index 0000000000000000000000000000000000000000..62f53b54c3d7253b8e219968cb981efbc12ec359 --- /dev/null +++ b/include/hpp/manipulation/graph/validation.hh @@ -0,0 +1,108 @@ +// Copyright (c) 2019, LAAS-CNRS +// Authors: Joseph Mirabel (joseph.mirabel@laas.fr) +// +// This file is part of hpp-manipulation. +// hpp-manipulation is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// hpp-manipulation is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Lesser Public License for more details. You should have +// received a copy of the GNU Lesser General Public License along with +// hpp-manipulation. If not, see <http://www.gnu.org/licenses/>. + +#ifndef HPP_MANIPULATION_GRAPH_VALIDATION_REPORT_HH +# define HPP_MANIPULATION_GRAPH_VALIDATION_REPORT_HH + +# include <string> +# include <vector> +# include <hpp/core/validation-report.hh> + +# include <hpp/manipulation/config.hh> +# include <hpp/manipulation/fwd.hh> +# include <hpp/manipulation/graph/fwd.hh> + +namespace hpp { + namespace manipulation { + namespace graph { + /// \addtogroup constraint_graph + /// \{ + + /// Check that graph components are valid. + /// + /// A stringified validation report can be obtained via + /// Validation::print or operator<< (std::ostream&, const Validation&). + class HPP_MANIPULATION_DLLAPI Validation + { + public: + Validation(const core::ProblemPtr_t& problem) + : problem_ (problem) {} + + void clear () + { + warnings_.clear(); + errors_.clear(); + } + + bool hasWarnings () const { return !warnings_.empty(); } + + bool hasErrors () const { return !errors_.empty(); } + + virtual std::ostream& print (std::ostream& os) const; + + /// Validate a graph component. + /// It dynamically casts in order to call the right function among + /// the validation method below. + /// + /// \return true if the component could not be proven infeasible. + /// \note Even if true is returned, the report can contain warnings. + bool validate (const GraphComponentPtr_t& comp); + + /// Validate a state + /// \return true if the state could not be proven infeasible. + /// \note Even if true is returned, the report can contain warnings. + bool validateState (const StatePtr_t& state); + + /// Validate an edge + /// \return true if the edge could not be proven infeasible. + /// \note Even if true is returned, the report can contain warnings. + bool validateEdge (const EdgePtr_t & edge); + + /// Validate an graph + /// \return true if no component of the graph could not be proven infeasible. + /// \note Even if true is returned, the report can contain warnings. + bool validateGraph (const GraphPtr_t& graph); + + + private: + void addWarning (const GraphComponentPtr_t& c, const std::string& w) + { + warnings_.push_back (Message (c, w)); + } + + void addError (const GraphComponentPtr_t& c, const std::string& w) + { + errors_.push_back (Message (c, w)); + } + + typedef std::pair<GraphComponentPtr_t, std::string> Message; + std::vector<Message> warnings_, errors_; + + core::ProblemPtr_t problem_; + }; + + inline std::ostream& operator<< (std::ostream& os, const Validation& v) + { + return v.print(os); + } + + /// \} + } // namespace graph + } // namespace manipulation + +} // namespace hpp + +#endif // HPP_MANIPULATION_GRAPH_VALIDATION_REPORT_HH diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 22dd3287aff4aec63da19bbe194b4a2247ce4b3f..0ae2c0ecb6b5ff76d5a8902ebd461fa6a955c59d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,6 +44,7 @@ SET(SOURCES graph/helper.cc graph/dot.cc + graph/validation.cc path-optimization/random-shortcut.cc path-optimization/enforce-transition-semantic.cc diff --git a/src/graph/validation.cc b/src/graph/validation.cc new file mode 100644 index 0000000000000000000000000000000000000000..2c36740d6ac4d91366a73c0053c54dd732ae6d2a --- /dev/null +++ b/src/graph/validation.cc @@ -0,0 +1,165 @@ +// Copyright (c) 2019, LAAS-CNRS +// Authors: Joseph Mirabel (joseph.mirabel@laas.fr) +// +// This file is part of hpp-manipulation. +// hpp-manipulation is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation, either version +// 3 of the License, or (at your option) any later version. +// +// hpp-manipulation is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Lesser Public License for more details. You should have +// received a copy of the GNU Lesser General Public License along with +// hpp-manipulation. If not, see <http://www.gnu.org/licenses/>. + +#define HPP_DEBUG 1 + +#include "hpp/manipulation/graph/validation.hh" + +#include <sstream> + +#include <hpp/util/indent.hh> + +#include <hpp/core/collision-validation.hh> +#include <hpp/core/configuration-shooter.hh> +#include <hpp/core/relative-motion.hh> + +#include "hpp/manipulation/problem.hh" +#include "hpp/manipulation/graph/graph.hh" +#include "hpp/manipulation/graph/edge.hh" +#include "hpp/manipulation/graph/state.hh" +#include "hpp/manipulation/graph/state-selector.hh" + +namespace hpp { + namespace manipulation { + namespace graph { + std::ostream& Validation::print (std::ostream& os) const + { + for (std::size_t i = 0; i < warnings_.size(); ++i) { + os << "Warning " << i + << " (" << (warnings_[i].first ? warnings_[i].first->name() : "no component") + << ')' << incendl << warnings_[i].second << decendl; + } + for (std::size_t i = 0; i < errors_.size(); ++i) { + os << "Error " << i + << " (" << (errors_[i].first ? errors_[i].first->name() : "no component") + << ')' << incendl << errors_[i].second << decendl; + } + return os; + } + + bool Validation::validate (const GraphComponentPtr_t& comp) + { + if (HPP_DYNAMIC_PTR_CAST(State, comp)) + return validateState (HPP_DYNAMIC_PTR_CAST(State, comp)); + else if (HPP_DYNAMIC_PTR_CAST(Edge, comp)) + return validateEdge (HPP_DYNAMIC_PTR_CAST(Edge, comp)); + else if (HPP_DYNAMIC_PTR_CAST(Graph, comp)) + return validateGraph (HPP_DYNAMIC_PTR_CAST(Graph, comp)); + else + return true; + } + + bool Validation::validateState (const StatePtr_t& state) + { + std::ostringstream oss; + oss << incindent; + bool success = true; + + // 1. try to generate a configuration in this state. + bool projOk; + Configuration_t q; + std::size_t i, Nrand = 100; + + for (i = 0; i < Nrand; i++) { + problem_->configurationShooter()->shoot(q); + projOk = state->configConstraint()->apply (q); + if (projOk) break; + } + if (!projOk) { + oss << "Failed to apply the constraints to " << Nrand << "random configurations."; + addError (state, oss.str()); + return false; + } + if (4 * i > 3 * Nrand) { + oss << "Success rate of constraint projection is " << i / Nrand << '.'; + addWarning (state, oss.str()); + oss.clear(); + } + + // 2. check the collision pairs which will be disabled because of the + // constraint. + core::CollisionValidationPtr_t colValidation ( + core::CollisionValidation::create (problem_->robot())); + for (std::size_t i = 0; i < problem_->collisionObstacles().size(); ++i) + colValidation->addObstacle (problem_->collisionObstacles()[i]); + colValidation->computeAllContacts (true); + + typedef core::RelativeMotion RelativeMotion; + RelativeMotion::matrix_type relMotion = RelativeMotion::matrix (problem_->robot()); + RelativeMotion::fromConstraint (relMotion, problem_->robot(), + state->configConstraint()); + + // Invert the relative motions. + hppDout (info, "Relative motion matrix:\n" << relMotion); + for (size_type r = 0; r < relMotion.rows(); ++r) + for (size_type c = 0; c < r; ++c) { + if (relMotion(r,c) != relMotion(c,r)) { + hppDout (error, "Relative motion matrix not symmetric."); + } + switch (relMotion(r,c)) { + case RelativeMotion::Constrained: + relMotion(r,c) = relMotion(c,r) = RelativeMotion::Unconstrained; + break; + case RelativeMotion::Parameterized: + case RelativeMotion::Unconstrained: + relMotion(r,c) = relMotion(c,r) = RelativeMotion::Constrained; + break; + default: + throw std::logic_error ("Relative motion not understood"); + } + } + hppDout (info, "Relative motion matrix:\n" << relMotion); + + colValidation->filterCollisionPairs (relMotion); + core::ValidationReportPtr_t colReport; + bool colOk = colValidation->validate (q, colReport); + + if (!colOk) { + oss << "The following collision pairs will always collide." << incendl << *colReport << decindent; + addError (state, oss.str()); + success = false; + } + + return success; + } + + bool Validation::validateEdge (const EdgePtr_t &) + { + return true; + } + + bool Validation::validateGraph (const GraphPtr_t& graph) + { + if (!graph) return false; + bool success = true; + + States_t states = graph->stateSelector()->getStates(); + for (States_t::const_iterator _state = states.begin(); + _state != states.end(); ++_state) { + if (!validateState(*_state)) success = false; + } + + states = graph->stateSelector()->getWaypointStates(); + for (States_t::const_iterator _state = states.begin(); + _state != states.end(); ++_state) { + if (!validateState(*_state)) success = false; + } + + return success; + } + } // namespace graph + } // namespace manipulation +} // namespace hpp