diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b3c6d5d279faff95b6e37dec7e3e88142e81214..3f5a62a20e65ed4c8acb484f6d6f57d534b946f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,8 @@ SET (${PROJECT_NAME}_HEADERS include/hpp/manipulation/roadmap.hh include/hpp/manipulation/roadmap-node.hh include/hpp/manipulation/connected-component.hh + include/hpp/manipulation/symbolic-component.hh + include/hpp/manipulation/symbolic-planner.hh include/hpp/manipulation/manipulation-planner.hh include/hpp/manipulation/graph-path-validation.hh include/hpp/manipulation/graph-steering-method.hh @@ -92,6 +94,7 @@ SET (${PROJECT_NAME}_HEADERS include/hpp/manipulation/path-optimization/small-steps.hh include/hpp/manipulation/path-optimization/config-optimization.hh + include/hpp/manipulation/path-optimization/keypoints.hh ) ADD_SUBDIRECTORY(src) diff --git a/include/hpp/manipulation/fwd.hh b/include/hpp/manipulation/fwd.hh index 8c4fb07869ca802a5eecba85ea4fced65655bac8..6a5d3ff7ae3fa288aa38c03347430eefbc1d317a 100644 --- a/include/hpp/manipulation/fwd.hh +++ b/include/hpp/manipulation/fwd.hh @@ -58,6 +58,11 @@ namespace hpp { typedef std::vector<RoadmapNodePtr_t> RoadmapNodes_t; HPP_PREDEF_CLASS (ConnectedComponent); typedef boost::shared_ptr<ConnectedComponent> ConnectedComponentPtr_t; + HPP_PREDEF_CLASS (SymbolicComponent); + typedef boost::shared_ptr<SymbolicComponent> SymbolicComponentPtr_t; + typedef std::set<SymbolicComponentPtr_t> SymbolicComponents_t; + HPP_PREDEF_CLASS (WeighedSymbolicComponent); + typedef boost::shared_ptr<WeighedSymbolicComponent> WeighedSymbolicComponentPtr_t; HPP_PREDEF_CLASS (WeighedDistance); typedef boost::shared_ptr<WeighedDistance> WeighedDistancePtr_t; typedef constraints::RelativeOrientation RelativeOrientation; @@ -75,6 +80,8 @@ namespace hpp { typedef model::vectorOut_t vectorOut_t; HPP_PREDEF_CLASS (ManipulationPlanner); typedef boost::shared_ptr < ManipulationPlanner > ManipulationPlannerPtr_t; + HPP_PREDEF_CLASS (SymbolicPlanner); + typedef boost::shared_ptr < SymbolicPlanner > SymbolicPlannerPtr_t; HPP_PREDEF_CLASS (GraphPathValidation); typedef boost::shared_ptr < GraphPathValidation > GraphPathValidationPtr_t; HPP_PREDEF_CLASS (GraphSteeringMethod); @@ -128,6 +135,8 @@ namespace hpp { namespace pathOptimization { HPP_PREDEF_CLASS (SmallSteps); typedef boost::shared_ptr < SmallSteps > SmallStepsPtr_t; + HPP_PREDEF_CLASS (Keypoints); + typedef boost::shared_ptr < Keypoints > KeypointsPtr_t; } // namespace pathOptimization } // namespace manipulation } // namespace hpp diff --git a/include/hpp/manipulation/graph/edge.hh b/include/hpp/manipulation/graph/edge.hh index f4c26d3a047d7a22af5a3dd1dc5fdd0228322f18..44762a26bef923b16d1b7b7e3f1945a199f3e3c8 100644 --- a/include/hpp/manipulation/graph/edge.hh +++ b/include/hpp/manipulation/graph/edge.hh @@ -19,6 +19,7 @@ #include <hpp/core/constraint-set.hh> #include <hpp/core/steering-method.hh> +#include <hpp/core/relative-motion.hh> #include <hpp/core/path.hh> #include "hpp/manipulation/config.hh" @@ -84,6 +85,8 @@ namespace hpp { class HPP_MANIPULATION_DLLAPI Edge : public GraphComponent { public: + typedef core::RelativeMotion RelativeMotion; + /// Destructor virtual ~Edge (); @@ -160,6 +163,11 @@ namespace hpp { return pathValidation_->get(); } + const RelativeMotion::matrix_type& relativeMotion () const + { + return relMotion_; + } + /// Get direction of the path compare to the edge /// \return true is reverse virtual bool direction (const core::PathPtr_t& path) const; @@ -226,6 +234,7 @@ namespace hpp { SteeringMethod_t* steeringMethod_; /// Path validation associated to the edge + mutable RelativeMotion::matrix_type relMotion_; PathValidation_t* pathValidation_; /// Weak pointer to itself. diff --git a/include/hpp/manipulation/graph/graph-component.hh b/include/hpp/manipulation/graph/graph-component.hh index 409286ba0749932a1d8b700762cbea89d11de730..502c8093d2aba6b831415406821b77951cbc5612 100644 --- a/include/hpp/manipulation/graph/graph-component.hh +++ b/include/hpp/manipulation/graph/graph-component.hh @@ -73,10 +73,16 @@ namespace hpp { (const DifferentiableFunctionPtr_t& function, const ComparisonTypePtr_t& ineq) HPP_MANIPULATION_DEPRECATED; + /// Reset the numerical constraints stored in the component. + virtual void resetNumericalConstraints (); + /// Add core::LockedJoint constraint to the component. virtual void addLockedJointConstraint (const LockedJointPtr_t& constraint); + /// Reset the locked joint in the component. + virtual void resetLockedJoints (); + /// Insert the numerical constraints in a ConfigProjector /// \return true is at least one NumericalConstraintPtr_t was inserted. bool insertNumericalConstraints (ConfigProjectorPtr_t& proj) const; diff --git a/include/hpp/manipulation/graph/helper.hh b/include/hpp/manipulation/graph/helper.hh index 6ad3c141daf7e804c71e1dec49f4b902e2e5394b..fce9ee88a28a2bac94f3b9cd57002cdd0613a217 100644 --- a/include/hpp/manipulation/graph/helper.hh +++ b/include/hpp/manipulation/graph/helper.hh @@ -98,6 +98,20 @@ namespace hpp { WithPrePlace = 1 << 4 }; + struct Rule { + std::string gripper_; + std::string handle_; + bool link_; + Rule() : gripper_(""), handle_(""), link_(false) {} + Rule(std::string gripper, std::string handle, bool link) { + gripper_ = gripper; + handle_ = handle; + link_ = link; + } + }; + + typedef std::vector<Rule> Rules_t; + /// Create edges according to the case. /// gCase is a logical OR combination of GraspingCase and PlacementCase /// @@ -178,7 +192,8 @@ namespace hpp { void graphBuilder ( const Objects_t& objects, const Grippers_t& grippers, - GraphPtr_t graph); + GraphPtr_t graph, + const Rules_t& rules = Rules_t ()); struct ObjectDef_t { std::string name; @@ -191,6 +206,7 @@ namespace hpp { const StringList_t& griNames, const std::list <ObjectDef_t>& objs, const StringList_t& envNames, + const Rules_t& rules, const value_type& prePlaceWidth = 0.05); /// \} } // namespace helper diff --git a/include/hpp/manipulation/path-optimization/keypoints.hh b/include/hpp/manipulation/path-optimization/keypoints.hh new file mode 100644 index 0000000000000000000000000000000000000000..3bb13f5cb274a0f9f470f54d89253c65f8b2f348 --- /dev/null +++ b/include/hpp/manipulation/path-optimization/keypoints.hh @@ -0,0 +1,78 @@ +// Copyright (c) 2016, Joseph Mirabel +// 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_PATHOPTIMIZATION_KEYPOINTS_HH +#define HPP_MANIPULATION_PATHOPTIMIZATION_KEYPOINTS_HH + +# include <hpp/core/path-optimizer.hh> +# include <hpp/core/path-optimizer.hh> + +# include <hpp/manipulation/fwd.hh> +# include <hpp/manipulation/config.hh> +# include <hpp/manipulation/problem.hh> +# include <hpp/manipulation/graph/fwd.hh> + +namespace hpp { + namespace manipulation { + namespace pathOptimization { + using hpp::core::Path; + using hpp::core::PathPtr_t; + using hpp::core::PathVector; + using hpp::core::PathVectorPtr_t; + /// \addtogroup path_optimization + /// \{ + + class HPP_MANIPULATION_DLLAPI Keypoints : public core::PathOptimizer + { + public: + static KeypointsPtr_t create (const core::Problem& problem) { + const Problem& p = dynamic_cast <const Problem&> (problem); + return KeypointsPtr_t (new Keypoints (p)); + } + + virtual PathVectorPtr_t optimize (const PathVectorPtr_t& path); + + protected: + /// Constructor + Keypoints (const Problem& problem) : + PathOptimizer (problem), problem_ (problem) {} + + private: + const Problem& problem_; + struct InterKeypointPath { + PathVectorPtr_t path; + bool isShort; + graph::EdgePtr_t edge; + }; + typedef std::vector<InterKeypointPath> IKPvector_t; + + IKPvector_t split (PathVectorPtr_t path) const; + + PathVectorPtr_t shorten (const IKPvector_t& paths, + std::size_t i1, std::size_t i2) const; + + IKPvector_t replaceInPath(const IKPvector_t& input, + const PathVectorPtr_t& shortcut, + std::size_t i1, std::size_t i2) const; + }; + + /// \} + + } // namespace pathOptimization + } // namespace manipulation +} // namespace hpp + +#endif // HPP_MANIPULATION_PATHOPTIMIZATION_KEYPOINTS_HH diff --git a/include/hpp/manipulation/roadmap-node.hh b/include/hpp/manipulation/roadmap-node.hh index 430687d7d5e69a5cae050d6978ff6f2bb19e0e47..2fa8992e4b71af97c8aa60743e2b1e1b3163cb6e 100644 --- a/include/hpp/manipulation/roadmap-node.hh +++ b/include/hpp/manipulation/roadmap-node.hh @@ -80,10 +80,21 @@ namespace hpp { /// \} + void symbolicComponent (const SymbolicComponentPtr_t& sc) + { + symbolicCC_ = sc; + } + + const SymbolicComponentPtr_t& symbolicComponent () const + { + return symbolicCC_; + } + private: CachingSystem cacheSystem_; graph::NodePtr_t node_; + SymbolicComponentPtr_t symbolicCC_; }; } // namespace manipulation } // namespace hpp diff --git a/include/hpp/manipulation/roadmap.hh b/include/hpp/manipulation/roadmap.hh index 68091f2391d5f80ea26415cecba89f417114e556..8d6096ea185ffb39a8f3dfb5d780821d4b01c1d6 100644 --- a/include/hpp/manipulation/roadmap.hh +++ b/include/hpp/manipulation/roadmap.hh @@ -62,6 +62,12 @@ namespace hpp { /// Get graph node corresponding to given roadmap node graph::NodePtr_t getNode(RoadmapNodePtr_t node); + /// Get the symbolic components + const SymbolicComponents_t& symbolicComponents () const + { + return symbolicCCs_; + } + protected: /// Register a new configuration. void statInsert (const RoadmapNodePtr_t& n); @@ -76,7 +82,9 @@ namespace hpp { { weak_ = shPtr; } - + + virtual void addEdge (const core::EdgePtr_t& edge); + private: typedef std::list < graph::HistogramPtr_t > Histograms; /// Keep track of the leaf that are explored. @@ -84,6 +92,7 @@ namespace hpp { Histograms histograms_; graph::GraphPtr_t graph_; RoadmapWkPtr_t weak_; + SymbolicComponents_t symbolicCCs_; }; /// \} } // namespace manipulation diff --git a/include/hpp/manipulation/symbolic-component.hh b/include/hpp/manipulation/symbolic-component.hh new file mode 100644 index 0000000000000000000000000000000000000000..c80add3462c73bdf5838179941d6db8eee63aa3e --- /dev/null +++ b/include/hpp/manipulation/symbolic-component.hh @@ -0,0 +1,120 @@ +// +// Copyright (c) 2016 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_SYMBOLIC_COMPONENT_HH +#define HPP_MANIPULATION_SYMBOLIC_COMPONENT_HH + +#include <hpp/manipulation/config.hh> +#include <hpp/manipulation/fwd.hh> +#include <hpp/manipulation/graph/fwd.hh> + +#include <hpp/manipulation/roadmap-node.hh> + +namespace hpp { + namespace manipulation { + /// Set of configurations accessible to each others by a single edge, + /// with the same right hand side. + /// + /// This assumes the roadmap is not directed. + class HPP_MANIPULATION_DLLAPI SymbolicComponent + { + public: + typedef std::set<SymbolicComponentPtr_t> SymbolicComponents_t; + + /// return a shared pointer to new instance + static SymbolicComponentPtr_t create (const RoadmapPtr_t& roadmap); + + /// Merge two symbolic components + /// \param other manipulation symbolic component to merge into this one. + /// \note other will be empty after calling this method. + virtual void merge (SymbolicComponentPtr_t otherCC); + + bool canMerge (const SymbolicComponentPtr_t& otherCC) const; + + /// Add otherCC to the list of reachable components + /// + /// This also add this object to the list of ancestor of otherCC. + void canReach (const SymbolicComponentPtr_t& otherCC); + + /// Add roadmap node to connected component + /// \param roadmap node to be added + void addNode (const RoadmapNodePtr_t& node); + + virtual void setFirstNode (const RoadmapNodePtr_t& node); + + core::ConnectedComponentPtr_t connectedComponent () const + { + assert (!nodes_.empty()); + return nodes_.front()->connectedComponent(); + }; + + const RoadmapNodes_t& nodes() const + { + return nodes_; + } + + protected: + SymbolicComponent(const RoadmapPtr_t& r) + : roadmap_(r) {} + + void init (const SymbolicComponentWkPtr_t& shPtr) + { + weak_ = shPtr; + } + + graph::NodePtr_t state_; + RoadmapNodes_t nodes_; + + private: + RoadmapPtr_t roadmap_; + SymbolicComponents_t to_, from_; + SymbolicComponentWkPtr_t weak_; + }; // class SymbolicComponent + + class HPP_MANIPULATION_DLLAPI WeighedSymbolicComponent : + public SymbolicComponent + { + public: + void merge (SymbolicComponentPtr_t otherCC); + + void setFirstNode (const RoadmapNodePtr_t& node); + + static WeighedSymbolicComponentPtr_t create (const RoadmapPtr_t& roadmap); + + std::size_t indexOf (const graph::EdgePtr_t e) const; + + void normalizeProba () + { + const value_type s = p_.sum(); + p_ /= s; + } + + value_type weight_; + /// Transition probabilities + vector_t p_; + std::vector<graph::EdgePtr_t> edges_; + + protected: + WeighedSymbolicComponent(const RoadmapPtr_t& r) + : SymbolicComponent(r), weight_(1) {} + + private: + }; // class SymbolicComponent + } // namespace manipulation +} // namespace hpp +#endif // HPP_MANIPULATION_SYMBOLIC_COMPONENT_HH diff --git a/include/hpp/manipulation/symbolic-planner.hh b/include/hpp/manipulation/symbolic-planner.hh new file mode 100644 index 0000000000000000000000000000000000000000..556dcbe652d268287a8ff479072db171a6f3a47d --- /dev/null +++ b/include/hpp/manipulation/symbolic-planner.hh @@ -0,0 +1,155 @@ +// Copyright (c) 2014, 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_SYMBOLIC_PLANNER_HH +# define HPP_MANIPULATION_SYMBOLIC_PLANNER_HH + +#include <hpp/core/path-planner.hh> + +#include <hpp/statistics/success-bin.hh> + +#include "hpp/manipulation/graph/statistics.hh" + +#include "hpp/manipulation/fwd.hh" +#include "hpp/manipulation/config.hh" +#include "hpp/manipulation/graph/fwd.hh" + +namespace hpp { + namespace manipulation { + /// \addtogroup path_planning + /// \{ + + class HPP_MANIPULATION_DLLAPI SymbolicPlanner : + public hpp::core::PathPlanner + { + public: + typedef std::list<std::size_t> ErrorFreqs_t; + struct ExtendStatus { + int status; + int info; + graph::EdgePtr_t edge; + PathValidationReportPtr_t validationReport; + }; + + /// Create an instance and return a shared pointer to the instance + static SymbolicPlannerPtr_t create (const core::Problem& problem, + const core::RoadmapPtr_t& roadmap); + + /// One step of extension. + /// + /// A set of constraints is chosen using the graph of constraints. + /// A constraint extension is done using a chosen set. + /// + virtual void oneStep (); + + /// Extend configuration q_near toward q_rand. + /// \param q_near the configuration to be extended. + /// \param q_rand the configuration toward extension is performed. + /// \retval validPath the longest valid path (possibly of length 0), + /// resulting from the extension. + /// \retval status an integer + /// \return True if the returned path is valid. + bool extend (RoadmapNodePtr_t q_near, const ConfigurationPtr_t &q_rand, + core::PathPtr_t& validPath, ExtendStatus& status); + + /// Get the number of occurrence of each errors. + /// + /// \sa ManipulationPlanner::errorList + ErrorFreqs_t getEdgeStat (const graph::EdgePtr_t& edge) const; + + /// Get the list of possible outputs of the extension step. + /// + /// \sa ManipulationPlanner::getEdgeStat + static StringList_t errorList (); + + protected: + /// Protected constructor + SymbolicPlanner (const Problem& problem, + const RoadmapPtr_t& roadmap); + + /// Store weak pointer to itself + void init (const SymbolicPlannerWkPtr_t& weak); + + private: + /// Try to connect nodes of the roadmap to other connected components. + /// \return the number of connection made. + std::size_t tryConnectToRoadmap (const core::Nodes_t nodes); + /// Try to connect nodes in a list between themselves. + /// \return the number of connection made. + std::size_t tryConnectNewNodes (const core::Nodes_t nodes); + + void updateWeightsAndProbabilities ( + const RoadmapNodePtr_t& near, const RoadmapNodePtr_t& newN, + const ExtendStatus& es); + + /// The transitions probabilities are updated as follow: + /// + /// \li let \f$p_{edge}\f$ be the edge probability before update, + /// \li the edge probability is multiplied by \f$\alpha\f$, + /// \li all other probabilities are multiplied by \f$ \frac{1 - \alpha * p_{edge}}{1 - p_{edge}} \f$ + /// + /// \param alpha should be between 0 and 1. + /// + /// \note Theoretically, the described operation preserves the + /// normalization of the sum. WeighedSymbolicComponent::normalizeProba + /// is called to avoid numerical errors. + static void updateEdgeProba ( + const graph::EdgePtr_t edge, + WeighedSymbolicComponentPtr_t wsc, + const value_type alpha); + + /// Configuration shooter + ConfigurationShooterPtr_t shooter_; + /// Pointer to the problem + const Problem& problem_; + /// Pointer to the roadmap + RoadmapPtr_t roadmap_; + /// weak pointer to itself + SymbolicPlannerWkPtr_t weakPtr_; + + /// Keep track of success and failure for method + /// extend. + typedef ::hpp::statistics::SuccessStatistics SuccessStatistics; + typedef ::hpp::statistics::SuccessBin SuccessBin; + typedef ::hpp::statistics::SuccessBin::Reason Reason; + SuccessStatistics& edgeStat (const graph::EdgePtr_t& edge); + std::vector<int> indexPerEdgeStatistics_; + std::vector<SuccessStatistics> perEdgeStatistics_; + + /// A Reason is associated to each EdgePtr_t that generated a failure. + enum TypeOfFailure { + SUCCESS = -1, + PROJECTION = 0, + STEERING_METHOD = 1, + PATH_VALIDATION_ZERO = 2, + PATH_PROJECTION_ZERO = 3, + UNKNOWN = 4, + PATH_PROJECTION_SHORTER = 5, + PATH_VALIDATION_SHORTER = 6, + PARTLY_EXTENDED = 7, + PATH_PROJECTION_AND_VALIDATION_SHORTER = PATH_PROJECTION_SHORTER + PATH_VALIDATION_SHORTER + }; + static const std::vector<Reason> reasons_; + + const value_type extendStep_; + + mutable Configuration_t qProj_; + }; + /// \} + } // namespace manipulation +} // namespace hpp + +#endif // HPP_MANIPULATION_SYMBOLIC_PLANNER_HH diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b9edbd26044ec15463b1a7e84dfa5a0030003975..98ea6c21ddfccfeab44ef184f608176eb32bfe93 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,10 +22,12 @@ SET(LIBRARY_NAME ${PROJECT_NAME}) SET(SOURCES axial-handle.cc handle.cc + symbolic-planner.cc manipulation-planner.cc problem-solver.cc roadmap.cc connected-component.cc + symbolic-component.cc constraint-set.cc roadmap-node.cc device.cc @@ -47,6 +49,7 @@ SET(SOURCES graph/dot.cc path-optimization/config-optimization.cc + path-optimization/keypoints.cc ) IF(HPP_MANIPULATION_HAS_WHOLEBODY_STEP) diff --git a/src/graph/edge.cc b/src/graph/edge.cc index c51297f0bc2f4df1e4846a4844ee171604959a8c..b5c133871af781b54bcbf1be74edc03163e3b900 100644 --- a/src/graph/edge.cc +++ b/src/graph/edge.cc @@ -260,10 +260,9 @@ namespace hpp { // TODO this path validation will not contain obstacles added after // its creation. pathValidation_->set(problem->pathValidationFactory ()); - using core::RelativeMotion; - RelativeMotion::matrix_type matrix (RelativeMotion::matrix (g->robot())); - RelativeMotion::fromConstraint (matrix, g->robot(), constraint); - pathValidation_->get()->filterCollisionPairs (matrix); + relMotion_ = RelativeMotion::matrix (g->robot()); + RelativeMotion::fromConstraint (relMotion_, g->robot(), constraint); + pathValidation_->get()->filterCollisionPairs (relMotion_); return constraint; } @@ -287,7 +286,7 @@ namespace hpp { if (constraints->isSatisfied (q1)) { if (constraints->isSatisfied (q2)) { path = (*steeringMethod_->get()) (q1, q2); - return path; + return (bool)path; } hppDout(info, "q2 does not satisfy the constraints"); } diff --git a/src/graph/graph-component.cc b/src/graph/graph-component.cc index cadd90580c1b2e901d760880882064c59497cc6a..7d5214906bb9d0263ed7218d60e5d2854e48a58e 100644 --- a/src/graph/graph-component.cc +++ b/src/graph/graph-component.cc @@ -75,12 +75,23 @@ namespace hpp { addNumericalConstraint (NumericalConstraint::create (function,ineq)); } + void GraphComponent::resetNumericalConstraints () + { + numericalConstraints_.clear(); + passiveDofs_.clear(); + } + void GraphComponent::addLockedJointConstraint (const LockedJointPtr_t& constraint) { lockedJoints_.push_back (constraint); } + void GraphComponent::resetLockedJoints () + { + lockedJoints_.clear(); + } + bool GraphComponent::insertNumericalConstraints (ConfigProjectorPtr_t& proj) const { IntervalsContainer_t::const_iterator itpdof = passiveDofs_.begin (); diff --git a/src/graph/helper.cc b/src/graph/helper.cc index eb8a2ee49946b72fdecfaf91aed564f418eea6df..35ad3516ca13567da233d1e8db131ca955350ef9 100644 --- a/src/graph/helper.cc +++ b/src/graph/helper.cc @@ -16,7 +16,11 @@ #include <hpp/manipulation/graph/helper.hh> +#include <tr1/unordered_map> +#include <tr1/unordered_set> + #include <boost/array.hpp> +#include <boost/regex.hpp> #include <boost/foreach.hpp> #include <boost/assign/list_of.hpp> @@ -519,15 +523,47 @@ namespace hpp { /// - the values correpond to the index of the handle (0..nbHandle-1), or /// nbHandle to mean no handle. typedef std::vector <index_t> GraspV_t; + struct CompiledRule { + enum Result { + Accept, + Refuse, + NoMatch, + Undefined + }; + boost::regex gripper, handle; + bool link; + CompiledRule (const Rule& r) : + gripper (r.gripper_), handle (r.handle_), link (r.link_) {} + Result check (const std::string& g, const std::string& h) const + { + if (boost::regex_match(g, gripper)) + if (boost::regex_match(h, handle)) + return (link ? Accept : Refuse); + return NoMatch; + } + }; + typedef std::vector<CompiledRule> CompiledRules_t; struct Result { GraphPtr_t graph; - std::vector<NodeAndManifold_t> nodes; + typedef unsigned long nodeid_type; + std::tr1::unordered_map<nodeid_type, NodeAndManifold_t> nodes; + typedef std::pair<nodeid_type, nodeid_type> edgeid_type; + struct edgeid_hash { + std::tr1::hash<edgeid_type::first_type> first; + std::tr1::hash<edgeid_type::second_type> second; + std::size_t operator() (const edgeid_type& eid) const { + return first(eid.first) + second(eid.second); + } + }; + std::tr1::unordered_set<edgeid_type, edgeid_hash> edges; std::vector< boost::array<NumericalConstraintPtr_t,3> > graspCs; index_t nG, nOH; GraspV_t dims; const Grippers_t& gs; const Objects_t& ohs; + CompiledRules_t rules; + mutable Eigen::MatrixXi rulesCache; Result (const Grippers_t& grippers, const Objects_t& objects, GraphPtr_t g) : graph (g), nG (grippers.size ()), nOH (0), gs (grippers), ohs (objects) @@ -539,16 +575,71 @@ namespace hpp { dims[0] = nOH + 1; for (index_t i = 1; i < nG; ++i) dims[i] = dims[i-1] * (nOH + 1); - nodes.resize (dims[nG-1] * (nOH + 1)); graspCs.resize (nG * nOH); + rulesCache = Eigen::MatrixXi::Constant(nG, nOH + 1, CompiledRule::Undefined); + } + + void setRules (const Rules_t& r) + { + for (Rules_t::const_iterator _r = r.begin(); _r != r.end(); ++_r) + rules.push_back (CompiledRule(*_r)); + } + + bool graspIsAllowed (const GraspV_t& idxOH) const + { + assert (idxOH.size () == nG); + for (std::size_t i = 0; i < nG; ++i) { + const std::string& g = gs[i]->name(), + h = (idxOH[i] == nOH) ? "" : handle (idxOH[i])->name (); + if ((CompiledRule::Result)rulesCache(i, idxOH[i]) == CompiledRule::Undefined) { + CompiledRule::Result status = CompiledRule::Accept; + for (std::size_t r = 0; r < rules.size(); ++r) { + status = rules[r].check(g,h); + if (status == CompiledRule::Accept) break; + else if (status == CompiledRule::Refuse) break; + status = CompiledRule::Accept; + } + rulesCache(i, idxOH[i]) = status; + } + bool keep = ((CompiledRule::Result)rulesCache(i, idxOH[i]) == CompiledRule::Accept); + if (!keep) return false; + } + return true; + } + + inline nodeid_type nodeid (const GraspV_t& iG) + { + nodeid_type iGOH = iG[0]; + nodeid_type res; + for (index_t i = 1; i < nG; ++i) { + res = iGOH + dims[i] * (iG[i]); + if (res < iGOH) { + hppDout (info, "Node ID overflowed. There are too many states..."); + } + iGOH = res; + // iGOH += dims[i] * (iG[i]); + } + return iGOH; + } + + bool hasNode (const GraspV_t& iG) + { + return nodes.count(nodeid(iG)) > 0; } NodeAndManifold_t& operator() (const GraspV_t& iG) { - index_t iGOH = iG[0]; - for (index_t i = 1; i < nG; ++i) - iGOH += dims[i] * (iG[i]); - return nodes [iGOH]; + return nodes [nodeid(iG)]; + } + + bool hasEdge (const GraspV_t& g1, const GraspV_t& g2) + { + return edges.count(edgeid_type(nodeid(g1), nodeid(g2))) > 0; + } + + void addEdge (const GraspV_t& g1, const GraspV_t& g2) + { + edges.insert(edgeid_type(nodeid(g1), nodeid(g2))); } inline boost::array<NumericalConstraintPtr_t,3>& graspConstraint ( @@ -712,6 +803,11 @@ namespace hpp { const GraspV_t& gFrom, const GraspV_t& gTo, const index_t iG, const int priority) { + if (r.hasEdge(gFrom, gTo)) { + hppDout (warning, "Prevented creation of duplicated edge\nfrom " + << r.name (gFrom) << "\nto " << r.name (gTo)); + return; + } const NodeAndManifold_t& from = makeNode (r, gFrom, priority), to = makeNode (r, gTo, priority+1); const Object_t& o = r.object (gTo[iG]); @@ -818,6 +914,7 @@ namespace hpp { grasp.foliated () , place.foliated(), submanifold); } + r.addEdge(gFrom, gTo); } /// idx are the available grippers @@ -828,6 +925,9 @@ namespace hpp { if (idx_g.empty () || idx_oh.empty ()) return; IndexV_t nIdx_g (idx_g.size() - 1); IndexV_t nIdx_oh (idx_oh.size() - 1); + bool curGraspIsAllowed = r.graspIsAllowed(grasps); + if (curGraspIsAllowed) makeNode (r, grasps, depth); + for (IndexV_t::const_iterator itx_g = idx_g.begin (); itx_g != idx_g.end (); ++itx_g) { // Copy all element except itx_g @@ -839,7 +939,12 @@ namespace hpp { // Create the edge for the selected grasp GraspV_t nGrasps = grasps; nGrasps [*itx_g] = *itx_oh; - makeEdge (r, grasps, nGrasps, *itx_g, depth); + + bool nextGraspIsAllowed = r.graspIsAllowed(nGrasps); + if (nextGraspIsAllowed) makeNode (r, nGrasps, depth + 1); + + if (curGraspIsAllowed && nextGraspIsAllowed) + makeEdge (r, grasps, nGrasps, *itx_g, depth); // Copy all element except itx_oh std::copy (boost::next (itx_oh), idx_oh.end (), @@ -855,13 +960,15 @@ namespace hpp { void graphBuilder ( const Objects_t& objects, const Grippers_t& grippers, - GraphPtr_t graph) + GraphPtr_t graph, + const Rules_t& rules) { if (!graph) throw std::logic_error ("The graph must be initialized"); NodeSelectorPtr_t ns = graph->nodeSelector (); if (!ns) throw std::logic_error ("The graph does not have a NodeSelector"); Result r (grippers, objects, graph); + r.setRules (rules); IndexV_t availG (r.nG), availOH (r.nOH); for (index_t i = 0; i < r.nG; ++i) availG[i] = i; @@ -870,6 +977,9 @@ namespace hpp { GraspV_t iG (r.nG, r.nOH); recurseGrippers (r, availG, availOH, iG, 0); + + hppDout (info, "Created a graph with " << r.nodes.size() << " states " + "and " << r.edges.size() << " edges."); } GraphPtr_t graphBuilder ( @@ -878,6 +988,7 @@ namespace hpp { const StringList_t& griNames, const std::list <ObjectDef_t>& objs, const StringList_t& envNames, + const std::vector <Rule>& rules, const value_type& prePlaceWidth) { const Device& robot = *(ps->robot ()); @@ -934,7 +1045,7 @@ namespace hpp { graph->maxIterations (ps->maxIterations ()); graph->errorThreshold (ps->errorThreshold ()); - graphBuilder (objects, grippers, graph); + graphBuilder (objects, grippers, graph, rules); ps->constraintGraph (graph); return graph; } diff --git a/src/path-optimization/keypoints.cc b/src/path-optimization/keypoints.cc new file mode 100644 index 0000000000000000000000000000000000000000..f0b167136808a6d42b5fab82f2a7717a697b60fd --- /dev/null +++ b/src/path-optimization/keypoints.cc @@ -0,0 +1,211 @@ +// Copyright (c) 2016, Joseph Mirabel +// 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/>. + +#include <hpp/manipulation/path-optimization/keypoints.hh> + +#include <hpp/core/path.hh> +#include <hpp/core/path-vector.hh> +#include <hpp/core/path-projector.hh> +#include <hpp/core/path-validation.hh> + +#include <hpp/manipulation/graph/edge.hh> +#include <hpp/manipulation/graph/graph.hh> +#include <hpp/manipulation/constraint-set.hh> +#include <hpp/manipulation/graph-steering-method.hh> + +namespace hpp { + namespace manipulation { + namespace pathOptimization { + using std::make_pair; + + PathVectorPtr_t Keypoints::optimize (const PathVectorPtr_t& path) + { + PathVectorPtr_t input = PathVector::create ( + path->outputSize(), path->outputDerivativeSize()); + PathVectorPtr_t output = path; + path->flatten (input); + // Step 1: find the key waypoints + // paths is a vector of + IKPvector_t paths = split (input); + + // Step 2: for each couple of consecutive path between keywaypoints, + // try to find a better intermediate key waypoint. + std::size_t i1 = 0; + bool success = false; + for (std::size_t i2 = 1; i2 < paths.size(); ++i2) { + if (paths[i2].isShort) continue; // So that i2 = i1 + 2 + // Try to generate a new key waypoint + PathVectorPtr_t shortcut = shorten (paths, i1, i2); + if (shortcut) { + paths = replaceInPath (paths, shortcut, i1, i2); + success = true; + while (i2 < paths.size() && !paths[i2].isShort) ++i2; + } + i1 = i2; + } + + if (success) { + // Build path + PathVectorPtr_t output = PathVector::create (path->outputSize (), + path->outputDerivativeSize ()); + for (std::size_t j = 0; j < paths.size(); ++j) + output->concatenate (*paths[j].path); + } + return output; + } + + Keypoints::IKPvector_t Keypoints::split (PathVectorPtr_t input) const + { + IKPvector_t paths; + + ConstraintSetPtr_t c; + for (std::size_t i_s = 0; i_s < input->numberPaths ();) { + InterKeypointPath ikpp; + ikpp.path = PathVector::create ( + input->outputSize(), input->outputDerivativeSize()); + PathPtr_t current = input->pathAtRank (i_s); + ikpp.path->appendPath (current); + c = HPP_DYNAMIC_PTR_CAST (ConstraintSet, current->constraints ()); + if (c) ikpp.edge = c->edge (); + ikpp.isShort = ikpp.edge && ikpp.edge->isShort(); + std::size_t i_e = i_s + 1; + for (; i_e < input->numberPaths (); ++i_e) { + current = input->pathAtRank (i_e); + c = HPP_DYNAMIC_PTR_CAST (ConstraintSet, current->constraints ()); + if (!c && ikpp.edge) { + hppDout(info, "No manipulation::ConstraintSet"); + break; + } + if (c && ikpp.edge->node() != c->edge ()->node()) break; + if (ikpp.isShort != c->edge()->isShort()) // We do not optimize edges marked as short + break; + ikpp.path->appendPath (current); + } + hppDout(info, "Edge name: " << ikpp.edge->name()); + i_s = i_e; + paths.push_back (ikpp); + } + return paths; + } + + PathVectorPtr_t Keypoints::shorten (const IKPvector_t& paths, + std::size_t i1, std::size_t i2) const + { + PathVectorPtr_t result; + const std::size_t maxTrials = 10; + value_type cur_length = 0; + for (std::size_t i = i1; i < i2 + 1; ++i) + cur_length += paths[i].path->length(); + + const core::SteeringMethodPtr_t& sm (problem_.steeringMethod ()); + PathProjectorPtr_t pathProjector (problem().pathProjector ()); + core::PathValidationPtr_t pathValidation (problem ().pathValidation ()); + + // Get all the possible edges of the graph. + const InterKeypointPath& ikpp1 = paths[i1]; + const InterKeypointPath& ikpp2 = paths[i2]; + graph::Edges_t edges; + graph::Graph& graph = *problem_.constraintGraph (); + try { + edges = graph.getEdges (ikpp1.edge->node(), ikpp2.edge->node()); + } catch (const std::logic_error& e) { + hppDout (error, e.what ()); + return PathVectorPtr_t (); + } + + value_type t0, t1; + Configuration_t q (ikpp1.path->outputSize()), + q1 (ikpp1.path->outputSize()), + q4 (ikpp1.path->outputSize()); + + // Unused variables + PathValidationReportPtr_t report; + PathPtr_t unusedP; + + for (std::size_t n1 = 0; n1 < maxTrials; ++n1) { + // Generate a random parameter on p1. + t0 = ikpp1.path->timeRange().first; + t1 = t0 + ikpp1.path->timeRange().second * rand ()/RAND_MAX; + // Get the configuration and project it. + if (!(*ikpp1.path) (q1, t1)) continue; + for (graph::Edges_t::const_iterator _edges = edges.begin(); + _edges != edges.end(); ++_edges) { + // TODO: For level set edges, something different should be done. + // Maybe, we must add a virtual generateInIntersection (qfrom, qto, qrand) + // that would be specialized for each edge. + q = q1; + if ((*_edges)->applyConstraints (q1,q)) { + PathPtr_t short1, proj1; + if ((*_edges)->build (short1, q1, q)) { + if (!short1 || !pathProjector->apply(short1, proj1)) continue; + if (!pathValidation->validate (proj1, false, unusedP, report)) continue; + + // short1 is a valid path leading to the target node. + // Try to connect this to ikpp2.path + for (std::size_t n2 = 0; n2 < maxTrials; ++n2) { + value_type t3, t4, t5; + t3 = ikpp2.path->timeRange().first; + t4 = t3 + ikpp2.path->timeRange().second * rand ()/RAND_MAX; + t5 = t3 + ikpp2.path->timeRange().second; + if (!(*ikpp2.path) (q4, t4)) continue; + PathPtr_t short2, proj2; + short2 = (*sm)(q, q4); + if (!short2 || !pathProjector->apply(short2, proj2)) continue; + if (!pathValidation->validate (proj2, false, unusedP, report)) continue; + // short2 is possible. Build the path and check it is shorter. + + PathVectorPtr_t tmp = PathVector::create (ikpp1.path->outputSize (), + ikpp1.path->outputDerivativeSize ()); + tmp->concatenate (*(ikpp1.path->extract + (make_pair (t0, t1))-> as <PathVector> ())); + tmp->appendPath (short1); + tmp->appendPath (short2); + tmp->concatenate (*(ikpp2.path->extract + (make_pair (t4, t5))-> as <PathVector> ())); + + if (cur_length >= tmp->length()) { + cur_length = tmp->length(); + result = tmp; + } + } + } + } + } + } + return result; + } + + Keypoints::IKPvector_t Keypoints::replaceInPath(const IKPvector_t& input, + const PathVectorPtr_t& shortcut, + std::size_t i1, std::size_t i2) const + { + PathVectorPtr_t flat = PathVector::create (shortcut->outputSize (), + shortcut->outputDerivativeSize ()); + shortcut->flatten(flat); + + PathVectorPtr_t output = PathVector::create (shortcut->outputSize (), + shortcut->outputDerivativeSize ()); + for (std::size_t j = 0; j < i1; ++j) + output->concatenate (*input[j].path); + output->concatenate (*flat); + for (std::size_t j = i2 + 1; j < input.size(); ++j) + output->concatenate (*input[j].path); + + return split (output); + } + } // namespace pathOptimization + } // namespace manipulation +} // namespace hpp diff --git a/src/problem-solver.cc b/src/problem-solver.cc index 7bddf6e6608c09449272427a24edf44d964e8e23..80521aab768c7f7826fe4d84acf793c0b6434366 100644 --- a/src/problem-solver.cc +++ b/src/problem-solver.cc @@ -38,6 +38,7 @@ #include "hpp/manipulation/device.hh" #include "hpp/manipulation/handle.hh" #include "hpp/manipulation/graph/graph.hh" +#include "hpp/manipulation/symbolic-planner.hh" #include "hpp/manipulation/manipulation-planner.hh" #include "hpp/manipulation/roadmap.hh" #include "hpp/manipulation/constraint-set.hh" @@ -46,6 +47,7 @@ #include "hpp/manipulation/graph-node-optimizer.hh" #include "hpp/manipulation/graph-steering-method.hh" #include "hpp/manipulation/path-optimization/config-optimization.hh" +#include "hpp/manipulation/path-optimization/keypoints.hh" #if HPP_MANIPULATION_HAS_WHOLEBODY_STEP #include <hpp/wholebody-step/small-steps.hh> @@ -80,6 +82,8 @@ namespace hpp { { parent_t::add<core::PathPlannerBuilder_t> ("M-RRT", ManipulationPlanner::create); + parent_t::add<core::PathPlannerBuilder_t> + ("SymbolicPlanner", SymbolicPlanner::create); using core::PathOptimizerBuilder_t; parent_t::add <PathOptimizerBuilder_t> ("Graph-RandomShortcut", GraphOptimizer::create <core::RandomShortcut>); @@ -99,6 +103,9 @@ namespace hpp { parent_t::add <SteeringMethodBuilder_t> ("Graph-SteeringMethodStraight", GraphSteeringMethod::create <core::SteeringMethodStraight>); + parent_t::add <PathOptimizerBuilder_t> ("KeypointsShortcut", + pathOptimization::Keypoints::create); + #if HPP_MANIPULATION_HAS_WHOLEBODY_STEP parent_t::add <PathOptimizerBuilder_t> ("Walkgen", wholebodyStep::SmallSteps::create); diff --git a/src/roadmap.cc b/src/roadmap.cc index 009a7dbdac660b2db0bd627a9835b76272cf0a24..fef69ea0c6c8e8841bcf49208c42101b8c4362a8 100644 --- a/src/roadmap.cc +++ b/src/roadmap.cc @@ -17,10 +17,14 @@ #include "hpp/manipulation/roadmap.hh" #include <hpp/util/pointer.hh> + +#include <hpp/core/edge.hh> #include <hpp/core/distance.hh> -#include <hpp/manipulation/connected-component.hh> +#include <hpp/manipulation/roadmap.hh> +#include <hpp/manipulation/graph/node.hh> #include <hpp/manipulation/roadmap-node.hh> +#include <hpp/manipulation/symbolic-component.hh> namespace hpp { namespace manipulation { @@ -46,8 +50,11 @@ namespace hpp { void Roadmap::push_node (const core::NodePtr_t& n) { - statInsert (static_cast <RoadmapNodePtr_t> (n)); Parent::push_node (n); + const RoadmapNodePtr_t& node = + static_cast <const RoadmapNodePtr_t> (n); + statInsert (node); + symbolicCCs_.insert(node->symbolicComponent()); } void Roadmap::statInsert (const RoadmapNodePtr_t& n) @@ -99,7 +106,11 @@ namespace hpp { core::NodePtr_t Roadmap::createNode (const ConfigurationPtr_t& q) const { // call RoadmapNode constructor with new manipulation connected component - return RoadmapNodePtr_t (new RoadmapNode (q, ConnectedComponent::create(weak_))); + RoadmapNodePtr_t node = new RoadmapNode (q, ConnectedComponent::create(weak_)); + SymbolicComponentPtr_t sc = WeighedSymbolicComponent::create (weak_.lock()); + node->symbolicComponent (sc); + sc->setFirstNode(node); + return node; } graph::NodePtr_t Roadmap::getNode(RoadmapNodePtr_t node) @@ -107,5 +118,23 @@ namespace hpp { return graph_->getNode(node); } + void Roadmap::addEdge (const core::EdgePtr_t& edge) + { + Parent::addEdge(edge); + const RoadmapNodePtr_t& f = static_cast <const RoadmapNodePtr_t> (edge->from()); + const RoadmapNodePtr_t& t = static_cast <const RoadmapNodePtr_t> (edge->to()); + SymbolicComponentPtr_t scf = f->symbolicComponent(); + SymbolicComponentPtr_t sct = t->symbolicComponent(); + scf->canReach(sct); + if (scf->canMerge(sct)) { + if (scf->nodes().size() > sct->nodes().size()) { + scf->merge(sct); + symbolicCCs_.erase(sct); + } else { + sct->merge(scf); + symbolicCCs_.erase(scf); + } + } + } } // namespace manipulation } // namespace hpp diff --git a/src/symbolic-component.cc b/src/symbolic-component.cc new file mode 100644 index 0000000000000000000000000000000000000000..d678f56c813a4ae8a16e427c923a86b6951bc176 --- /dev/null +++ b/src/symbolic-component.cc @@ -0,0 +1,126 @@ +// Copyright (c) 2016, Joseph Mirabel +// 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/>. + +#include <hpp/manipulation/symbolic-component.hh> + +#include <hpp/manipulation/roadmap.hh> + +namespace hpp { + namespace manipulation { + SymbolicComponentPtr_t SymbolicComponent::create (const RoadmapPtr_t& roadmap) + { + SymbolicComponentPtr_t shPtr = + SymbolicComponentPtr_t(new SymbolicComponent(roadmap)); + shPtr->init(shPtr); + return shPtr; + } + + void SymbolicComponent::addNode (const RoadmapNodePtr_t& node) + { + assert(state_); + graph::NodePtr_t state = roadmap_->getNode(node); + + // Sanity check + if (state_ == state) // Oops + throw std::runtime_error ("RoadmapNode of SymbolicComponent must be in" + " the same state"); + nodes_.push_back(node); + } + + void SymbolicComponent::setFirstNode (const RoadmapNodePtr_t& node) + { + assert(!state_); + state_ = roadmap_->getNode(node); + nodes_.push_back(node); + } + + bool SymbolicComponent::canMerge (const SymbolicComponentPtr_t& otherCC) const + { + if (otherCC->state_ != state_) return false; + SymbolicComponents_t::const_iterator it = std::find + (to_.begin(), to_.end(), otherCC); + if (it == to_.end()) return false; + it = std::find + (from_.begin(), from_.end(), otherCC); + if (it == from_.end()) return false; + return true; + } + + void SymbolicComponent::canReach (const SymbolicComponentPtr_t& otherCC) + { + to_.insert(otherCC); + otherCC->from_.insert(weak_.lock()); + } + + void SymbolicComponent::merge (SymbolicComponentPtr_t other) + { + assert (canMerge(other)); + if (other == weak_.lock()) return; + + // Tell other's nodes that they now belong to this connected component + for (RoadmapNodes_t::iterator itNode = other->nodes_.begin (); + itNode != other->nodes_.end (); ++itNode) { + (*itNode)->symbolicComponent (weak_.lock ()); + } + // Add other's nodes to this list. + nodes_.insert (nodes_.end (), other->nodes_.begin(), other->nodes_.end()); + + from_.erase (other); + other->from_.erase (weak_.lock()); + from_.insert (other->from_.begin(), other->from_.end()); + to_.erase (other); + other->to_.erase (weak_.lock()); + to_.insert (other->to_.begin(), other->to_.end()); + } + + WeighedSymbolicComponentPtr_t WeighedSymbolicComponent::create (const RoadmapPtr_t& roadmap) + { + WeighedSymbolicComponentPtr_t shPtr = WeighedSymbolicComponentPtr_t + (new WeighedSymbolicComponent(roadmap)); + shPtr->init(shPtr); + return shPtr; + } + + void WeighedSymbolicComponent::merge (SymbolicComponentPtr_t otherCC) + { + WeighedSymbolicComponentPtr_t other = + HPP_DYNAMIC_PTR_CAST(WeighedSymbolicComponent, otherCC); + value_type r = nodes_.size() / (nodes_.size() + other->nodes_.size()); + + SymbolicComponent::merge(otherCC); + weight_ *= other->weight_; // TODO a geometric mean would be more natural. + p_ = r * p_ + (1 - r) * other->p_; + } + + void WeighedSymbolicComponent::setFirstNode (const RoadmapNodePtr_t& node) + { + SymbolicComponent::setFirstNode(node); + std::vector<value_type> p = state_->neighbors().probabilities(); + p_.resize(p.size()); + edges_ = state_->neighbors().values(); + for (std::size_t i = 0; i < p.size(); ++i) + p_[i] = p[i]; + } + + std::size_t WeighedSymbolicComponent::indexOf (const graph::EdgePtr_t e) const + { + std::size_t i = 0; + for (; i < edges_.size(); ++i) + if (edges_[i] == e) break; + return i; + } + } // namespace manipulation +} // namespace hpp diff --git a/src/symbolic-planner.cc b/src/symbolic-planner.cc new file mode 100644 index 0000000000000000000000000000000000000000..362ecf4ead1f111da5a78504c58a8056a38c5f50 --- /dev/null +++ b/src/symbolic-planner.cc @@ -0,0 +1,668 @@ +// Copyright (c) 2014, 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/>. + +#include "hpp/manipulation/symbolic-planner.hh" + +#include <boost/tuple/tuple.hpp> +#include <boost/assign/list_of.hpp> + +#include <hpp/util/pointer.hh> +#include <hpp/util/timer.hh> +#include <hpp/util/assertion.hh> + +#include <hpp/model/configuration.hh> +#include <hpp/model/collision-object.hh> + +#include <hpp/core/roadmap.hh> +#include <hpp/core/distance.hh> +#include <hpp/core/path-validation.hh> +#include <hpp/core/connected-component.hh> +#include <hpp/core/path-projector.hh> +#include <hpp/core/projection-error.hh> +#include <hpp/core/nearest-neighbor.hh> +#include <hpp/core/basic-configuration-shooter.hh> +#include <hpp/core/path-validation-report.hh> +#include <hpp/core/collision-validation-report.hh> + +#include "hpp/manipulation/graph/statistics.hh" +#include "hpp/manipulation/device.hh" +#include "hpp/manipulation/problem.hh" +#include "hpp/manipulation/roadmap.hh" +#include "hpp/manipulation/roadmap-node.hh" +#include "hpp/manipulation/symbolic-component.hh" +#include "hpp/manipulation/graph/edge.hh" +#include "hpp/manipulation/graph/graph.hh" +#include "hpp/manipulation/graph/node-selector.hh" + +#define CastToWSC_ptr(var, scPtr) \ + WeighedSymbolicComponentPtr_t var = \ + HPP_DYNAMIC_PTR_CAST(WeighedSymbolicComponent,scPtr) + +namespace hpp { + namespace manipulation { + using core::CollisionValidationReport; + using core::CollisionValidationReportPtr_t; + using core::CollisionObjectPtr_t; + typedef graph::Edge::RelativeMotion RelativeMotion; + + namespace { + HPP_DEFINE_TIMECOUNTER(oneStep); + HPP_DEFINE_TIMECOUNTER(extend); + HPP_DEFINE_TIMECOUNTER(tryConnect); + HPP_DEFINE_TIMECOUNTER(nearestNeighbor); + HPP_DEFINE_TIMECOUNTER(delayedEdges); + HPP_DEFINE_TIMECOUNTER(tryConnectNewNodes); + HPP_DEFINE_TIMECOUNTER(tryConnectToRoadmap); + /// extend steps + HPP_DEFINE_TIMECOUNTER(chooseEdge); + HPP_DEFINE_TIMECOUNTER(applyConstraints); + HPP_DEFINE_TIMECOUNTER(buildPath); + HPP_DEFINE_TIMECOUNTER(projectPath); + HPP_DEFINE_TIMECOUNTER(validatePath); + + struct WeighedSymbolicComponentComp { + bool operator() (const SymbolicComponentPtr_t& lhs, const SymbolicComponentPtr_t& rhs) + { + return HPP_DYNAMIC_PTR_CAST(WeighedSymbolicComponent, lhs)->weight_ + > HPP_DYNAMIC_PTR_CAST(WeighedSymbolicComponent, rhs)->weight_; + } + }; + typedef std::list<WeighedSymbolicComponentPtr_t> SymbolicComponentList_t; + SymbolicComponentList_t sorted_list (const SymbolicComponents_t& sc) + { + SymbolicComponentList_t l; + WeighedSymbolicComponentComp comp; + for (SymbolicComponents_t::const_iterator _sc = sc.begin(); + _sc != sc.end(); ++_sc) + l.insert (std::upper_bound(l.begin(), l.end(), *_sc, comp), + HPP_DYNAMIC_PTR_CAST(WeighedSymbolicComponent, *_sc)); + return l; + } + + struct DistanceToConfiguration { + const Configuration_t& q_; + core::Distance& d_; + value_type mind_; + RoadmapNodePtr_t node_; + DistanceToConfiguration(const Configuration_t& q, core::Distance& d) + : q_(q), d_(d), mind_(std::numeric_limits<value_type>::max()) {} + void operator() (const RoadmapNodePtr_t& n) + { + value_type dist = d_ (*(n->configuration()), q_); + if (dist < mind_) { + node_ = n; + mind_ = dist; + } + } + }; + + bool belongs (const ConfigurationPtr_t& q, const core::Nodes_t& nodes) + { + for (core::Nodes_t::const_iterator itNode = nodes.begin (); + itNode != nodes.end (); ++itNode) { + if (*((*itNode)->configuration ()) == *q) return true; + } + return false; + } + } + + const std::vector<SymbolicPlanner::Reason> + SymbolicPlanner::reasons_ = boost::assign::list_of + (SuccessBin::createReason ("[Fail] Projection")) + (SuccessBin::createReason ("[Fail] SteeringMethod")) + (SuccessBin::createReason ("[Fail] Path validation returned length 0")) + (SuccessBin::createReason ("[Fail] Path could not be projected")) + (SuccessBin::createReason ("[Fail] Unknow")) + (SuccessBin::createReason ("[Info] Path could not be fully projected")) + (SuccessBin::createReason ("[Info] Path could not be fully validated")) + (SuccessBin::createReason ("[Info] Extended partly")); + + SymbolicPlannerPtr_t SymbolicPlanner::create (const core::Problem& problem, + const core::RoadmapPtr_t& roadmap) + { + SymbolicPlanner* ptr; + core::RoadmapPtr_t r2 = roadmap; + RoadmapPtr_t r; + try { + const Problem& p = dynamic_cast < const Problem& > (problem); + RoadmapPtr_t r = HPP_DYNAMIC_PTR_CAST (Roadmap, r2); + ptr = new SymbolicPlanner (p, r); + } catch (std::exception&) { + if (!r) + throw std::invalid_argument ("The roadmap must be of type hpp::manipulation::Roadmap."); + else + throw std::invalid_argument ("The problem must be of type hpp::manipulation::Problem."); + } + SymbolicPlannerPtr_t shPtr (ptr); + ptr->init (shPtr); + return shPtr; + } + + SymbolicPlanner::ErrorFreqs_t SymbolicPlanner::getEdgeStat + (const graph::EdgePtr_t& edge) const + { + const std::size_t& id = edge->id (); + ErrorFreqs_t ret; + if (indexPerEdgeStatistics_.size() <= id || + indexPerEdgeStatistics_[id] < 0) { + for (std::size_t i = 0; i < reasons_.size(); ++i) ret.push_back (0); + } else { + const SuccessStatistics& ss = + perEdgeStatistics_[indexPerEdgeStatistics_[id]]; + ret.push_back (ss.nbSuccess ()); + for (std::size_t i = 0; i < reasons_.size(); ++i) + ret.push_back (ss.nbFailure (reasons_[i])); + } + return ret; + } + + StringList_t SymbolicPlanner::errorList () + { + StringList_t ret; + ret.push_back ("Success"); + for (std::size_t i = 0; i < reasons_.size(); ++i) ret.push_back (reasons_[i].what); + return ret; + } + + void SymbolicPlanner::oneStep () + { + HPP_START_TIMECOUNTER(oneStep); + + // Get the robot + DevicePtr_t robot = HPP_DYNAMIC_PTR_CAST(Device, problem ().robot ()); + HPP_ASSERT(robot); + // Get the roadmap and the symbolic components + RoadmapPtr_t rdm = HPP_DYNAMIC_PTR_CAST(Roadmap, roadmap()); + HPP_ASSERT(rdm); + SymbolicComponentList_t scs = sorted_list (rdm->symbolicComponents()); + + core::Nodes_t newNodes; + core::PathPtr_t path; + + typedef boost::tuple <RoadmapNodePtr_t, ConfigurationPtr_t, core::PathPtr_t, + ExtendStatus> + DelayedEdge_t; + typedef std::vector <DelayedEdge_t> DelayedEdges_t; + DelayedEdges_t delayedEdges; + + // Pick a random node + ConfigurationPtr_t q_rand = shooter_->shoot(); + + // Extend each connected component + for (core::ConnectedComponents_t::const_iterator itcc = + rdm->connectedComponents ().begin (); + itcc != rdm->connectedComponents ().end (); ++itcc) { + // Find the symbolic component in this connected component which has + // the highest value. + SymbolicComponentPtr_t sc; + for (SymbolicComponentList_t::iterator _sc = scs.begin(); + _sc != scs.end(); ++_sc) { + sc = *_sc; + if (sc->connectedComponent() == *itcc) break; + } + if (!sc) { + hppDout (error, "There should always be a connected component corresponding to symbolic component"); + continue; + } + + // Find the nearest neighbor. + HPP_START_TIMECOUNTER(nearestNeighbor); + DistanceToConfiguration dtc (*q_rand, *rdm->distance()); + RoadmapNodePtr_t near = + std::for_each(sc->nodes().begin(), sc->nodes().end(), dtc).node_; + HPP_STOP_TIMECOUNTER(nearestNeighbor); + HPP_DISPLAY_LAST_TIMECOUNTER(nearestNeighbor); + if (!near) continue; + + // Extension + HPP_START_TIMECOUNTER(extend); + ExtendStatus status; + bool pathIsValid = extend (near, q_rand, path, status); + HPP_STOP_TIMECOUNTER(extend); + HPP_DISPLAY_LAST_TIMECOUNTER(extend); + + // Insert new path to q_near in roadmap + if (pathIsValid) { + value_type t_final = path->timeRange ().second; + if (t_final != path->timeRange ().first) { + bool success; + ConfigurationPtr_t q_new (new Configuration_t + ((*path) (t_final, success))); + delayedEdges.push_back (DelayedEdge_t (near, q_new, path, status)); + } + } else { + updateWeightsAndProbabilities (near, RoadmapNodePtr_t(), status); + } + } + + // Insert delayed edges + HPP_START_TIMECOUNTER(delayedEdges); + for (DelayedEdges_t::const_iterator itEdge = delayedEdges.begin (); + itEdge != delayedEdges.end (); ++itEdge) { + const core::NodePtr_t& near = itEdge-> get <0> (); + const ConfigurationPtr_t& q_new = itEdge-> get <1> (); + const core::PathPtr_t& validPath = itEdge-> get <2> (); + core::NodePtr_t newNode = rdm->addNode (q_new); + roadmap()->addEdge (near, newNode, validPath); + core::interval_t timeRange = validPath->timeRange (); + roadmap()->addEdge (newNode, near, validPath->extract + (core::interval_t (timeRange.second , + timeRange.first))); + updateWeightsAndProbabilities ( + static_cast<RoadmapNode*>(near), + static_cast<RoadmapNode*>(newNode), + itEdge->get<3>()); + } + HPP_STOP_TIMECOUNTER(delayedEdges); + + // Try to connect the new nodes together + HPP_START_TIMECOUNTER(tryConnectNewNodes); + const std::size_t nbConn = tryConnectNewNodes (newNodes); + HPP_STOP_TIMECOUNTER(tryConnectNewNodes); + HPP_DISPLAY_LAST_TIMECOUNTER(tryConnectNewNodes); + if (nbConn == 0) { + HPP_START_TIMECOUNTER(tryConnectToRoadmap); + tryConnectToRoadmap (newNodes); + HPP_STOP_TIMECOUNTER(tryConnectToRoadmap); + HPP_DISPLAY_LAST_TIMECOUNTER(tryConnectToRoadmap); + } + HPP_STOP_TIMECOUNTER(oneStep); + HPP_DISPLAY_LAST_TIMECOUNTER(oneStep); + HPP_DISPLAY_TIMECOUNTER(oneStep); + HPP_DISPLAY_TIMECOUNTER(extend); + HPP_DISPLAY_TIMECOUNTER(tryConnect); + HPP_DISPLAY_TIMECOUNTER(tryConnectNewNodes); + HPP_DISPLAY_TIMECOUNTER(tryConnectToRoadmap); + HPP_DISPLAY_TIMECOUNTER(nearestNeighbor); + HPP_DISPLAY_TIMECOUNTER(delayedEdges); + HPP_DISPLAY_TIMECOUNTER(chooseEdge); + HPP_DISPLAY_TIMECOUNTER(applyConstraints); + HPP_DISPLAY_TIMECOUNTER(buildPath); + HPP_DISPLAY_TIMECOUNTER(projectPath); + HPP_DISPLAY_TIMECOUNTER(validatePath); + } + + bool SymbolicPlanner::extend( + RoadmapNodePtr_t n_near, + const ConfigurationPtr_t& q_rand, + core::PathPtr_t& validPath, + ExtendStatus& status) + { + status.info = SUCCESS; + graph::GraphPtr_t graph = problem_.constraintGraph (); + // Select next node in the constraint graph. + const ConfigurationPtr_t q_near = n_near->configuration (); + + HPP_START_TIMECOUNTER (chooseEdge); + // This code should go into a NodeSelector derived class. + WeighedSymbolicComponentPtr_t wscPtr = HPP_DYNAMIC_PTR_CAST + (WeighedSymbolicComponent, n_near->symbolicComponent()); + if (wscPtr) { + WeighedSymbolicComponent wsc = *wscPtr; + value_type R = rand() / RAND_MAX; + std::size_t i = 0; + for (value_type sum = wsc.p_[0]; sum < R; sum += wsc.p_[i]) { ++i; } + assert(i < wsc.edges_.size()); + status.edge = wsc.edges_[i]; + } else status.edge = graph->chooseEdge (n_near); + HPP_STOP_TIMECOUNTER (chooseEdge); + if (!status.edge) { + status.status = UNKNOWN; + return false; + } + + qProj_ = *q_rand; + HPP_START_TIMECOUNTER (applyConstraints); + SuccessStatistics& es = edgeStat (status.edge); + if (!status.edge->applyConstraints (n_near, qProj_)) { + HPP_STOP_TIMECOUNTER (applyConstraints); + es.addFailure (reasons_[PROJECTION]); + status.status = PROJECTION; + return false; + } + HPP_STOP_TIMECOUNTER (applyConstraints); + + core::PathPtr_t path; + HPP_START_TIMECOUNTER (buildPath); + if (!status.edge->build (path, *q_near, qProj_)) { + HPP_STOP_TIMECOUNTER (buildPath); + es.addFailure (reasons_[STEERING_METHOD]); + status.status = STEERING_METHOD; + return false; + } + HPP_STOP_TIMECOUNTER (buildPath); + + core::PathPtr_t projPath; + bool projShorter = false; + PathProjectorPtr_t pathProjector = problem_.pathProjector (); + if (pathProjector) { + HPP_START_TIMECOUNTER (projectPath); + projShorter = !pathProjector->apply (path, projPath); + if (projShorter) { + if (!projPath || projPath->length () == 0) { + HPP_STOP_TIMECOUNTER (projectPath); + es.addFailure (reasons_[PATH_PROJECTION_ZERO]); + status.status = PATH_PROJECTION_ZERO; + return false; + } + status.info = PATH_PROJECTION_SHORTER; + es.addFailure (reasons_[PATH_PROJECTION_SHORTER]); + } + HPP_STOP_TIMECOUNTER (projectPath); + } else projPath = path; + GraphPathValidationPtr_t pathValidation (problem_.pathValidation ()); + core::PathPtr_t fullValidPath; + + HPP_START_TIMECOUNTER (validatePath); + bool fullyValid = false; + try { + fullyValid = pathValidation->validate + (projPath, false, fullValidPath, status.validationReport); + } catch (const core::projection_error& e) { + hppDout (error, e.what ()); + es.addFailure (reasons_[PATH_VALIDATION_ZERO]); + status.status = PATH_VALIDATION_ZERO; + return false; + } + HPP_STOP_TIMECOUNTER (validatePath); + + if (fullValidPath->length () == 0) { + es.addFailure (reasons_[PATH_VALIDATION_ZERO]); + validPath = fullValidPath; + status.status = PATH_PROJECTION_ZERO; + return false; + } else { + if (!fullyValid) { + es.addFailure (reasons_[PATH_VALIDATION_SHORTER]); + status.status = (projShorter?PATH_PROJECTION_AND_VALIDATION_SHORTER:PATH_VALIDATION_SHORTER); + } + if (extendStep_ == 1 || fullyValid) { + validPath = fullValidPath; + } else { + const value_type& length = fullValidPath->length(); + const value_type& t_init = fullValidPath->timeRange ().first; + try { + validPath = fullValidPath->extract + (core::interval_t(t_init, t_init + length * extendStep_)); + } catch (const core::projection_error& e) { + hppDout (error, e.what()); + es.addFailure (reasons_[PATH_PROJECTION_SHORTER]); + status.status = UNKNOWN; + return false; + } + } + hppDout (info, "Extension:" << std::endl + << es); + } + if (!projShorter && fullValidPath) es.addSuccess (); + else es.addFailure (reasons_[PARTLY_EXTENDED]); + status.status = SUCCESS; + return true; + } + + SymbolicPlanner::SuccessStatistics& SymbolicPlanner::edgeStat + (const graph::EdgePtr_t& edge) + { + const std::size_t& id = edge->id (); + if (indexPerEdgeStatistics_.size () <= id) { + indexPerEdgeStatistics_.resize (id + 1, -1); + } + if (indexPerEdgeStatistics_[id] < 0) { + indexPerEdgeStatistics_[id] = perEdgeStatistics_.size(); + perEdgeStatistics_.push_back (SuccessStatistics (edge->name (), 2)); + } + return perEdgeStatistics_[indexPerEdgeStatistics_[id]]; + } + + inline std::size_t SymbolicPlanner::tryConnectToRoadmap (const core::Nodes_t nodes) + { + const core::SteeringMethodPtr_t& sm (problem ().steeringMethod ()); + core::PathValidationPtr_t pathValidation (problem ().pathValidation ()); + PathProjectorPtr_t pathProjector (problem().pathProjector ()); + core::PathPtr_t path, projPath, validPath; + graph::GraphPtr_t graph = problem_.constraintGraph (); + bool connectSucceed = false; + std::size_t nbConnection = 0; + const std::size_t K = 7; + value_type distance; + for (core::Nodes_t::const_iterator itn1 = nodes.begin (); + itn1 != nodes.end (); ++itn1) { + ConfigurationPtr_t q1 ((*itn1)->configuration ()); + connectSucceed = false; + for (core::ConnectedComponents_t::const_iterator itcc = + roadmap ()->connectedComponents ().begin (); + itcc != roadmap ()->connectedComponents ().end (); ++itcc) { + if (*itcc == (*itn1)->connectedComponent ()) + continue; + core::Nodes_t knearest = roadmap()->nearestNeighbor () + ->KnearestSearch (q1, *itcc, K, distance); + for (core::Nodes_t::const_iterator itn2 = knearest.begin (); + itn2 != knearest.end (); ++itn2) { + bool _1to2 = (*itn1)->isOutNeighbor (*itn2); + bool _2to1 = (*itn1)->isInNeighbor (*itn2); + if (_1to2 && _2to1) { + hppDout (info, "the two nodes are already connected"); + continue; + } + ConfigurationPtr_t q2 ((*itn2)->configuration ()); + assert (*q1 != *q2); + path = (*sm) (*q1, *q2); + if (!path) continue; + if (pathProjector) { + if (!pathProjector->apply (path, projPath)) continue; + } else projPath = path; + PathValidationReportPtr_t report; + if (pathValidation->validate (projPath, false, validPath, report)) { + nbConnection++; + if (!_1to2) roadmap ()->addEdge (*itn1, *itn2, projPath); + if (!_2to1) { + core::interval_t timeRange = projPath->timeRange (); + roadmap ()->addEdge (*itn2, *itn1, projPath->extract + (core::interval_t (timeRange.second, + timeRange.first))); + } + connectSucceed = true; + break; + } + } + if (connectSucceed) break; + } + } + return nbConnection; + } + + inline std::size_t SymbolicPlanner::tryConnectNewNodes (const core::Nodes_t nodes) + { + const core::SteeringMethodPtr_t& sm (problem ().steeringMethod ()); + core::PathValidationPtr_t pathValidation (problem ().pathValidation ()); + PathProjectorPtr_t pathProjector (problem().pathProjector ()); + core::PathPtr_t path, projPath, validPath; + graph::GraphPtr_t graph = problem_.constraintGraph (); + std::size_t nbConnection = 0; + for (core::Nodes_t::const_iterator itn1 = nodes.begin (); + itn1 != nodes.end (); ++itn1) { + ConfigurationPtr_t q1 ((*itn1)->configuration ()); + for (core::Nodes_t::const_iterator itn2 = boost::next (itn1); + itn2 != nodes.end (); ++itn2) { + if ((*itn1)->connectedComponent () == (*itn2)->connectedComponent ()) + continue; + bool _1to2 = (*itn1)->isOutNeighbor (*itn2); + bool _2to1 = (*itn1)->isInNeighbor (*itn2); + if (_1to2 && _2to1) { + hppDout (info, "the two nodes are already connected"); + continue; + } + ConfigurationPtr_t q2 ((*itn2)->configuration ()); + assert (*q1 != *q2); + path = (*sm) (*q1, *q2); + if (!path) continue; + if (pathProjector) { + if (!pathProjector->apply (path, projPath)) continue; + } else projPath = path; + PathValidationReportPtr_t report; + if (pathValidation->validate (projPath, false, validPath, report)) { + nbConnection++; + if (!_1to2) roadmap ()->addEdge (*itn1, *itn2, projPath); + if (!_2to1) { + core::interval_t timeRange = projPath->timeRange (); + roadmap ()->addEdge (*itn2, *itn1, projPath->extract + (core::interval_t (timeRange.second, + timeRange.first))); + } + } + } + } + return nbConnection; + } + + inline void SymbolicPlanner::updateWeightsAndProbabilities ( + const RoadmapNodePtr_t& near, const RoadmapNodePtr_t& newN, + const ExtendStatus& extend) + { + // 0.99 ^ 26 * 1.3 ~= 1 => A new extension followed by 26 failures + // results in similar weights for the symbolic components. + const value_type weightInc = 1.3; + const value_type weightDec = 0.99; + CastToWSC_ptr (oldWSC, near->symbolicComponent()); + CollisionValidationReportPtr_t colRep; + switch (extend.status) { + case SUCCESS: + { + CastToWSC_ptr (newWSC, newN->symbolicComponent()); + switch (extend.info) { + case SUCCESS: + // If the corresponding edge is a loop, no adjustment. + // Otherwise, a new symbolic component has been created. This new SC + // should have higher priority than the old one. + if (oldWSC != newWSC) { + newWSC->weight_ = oldWSC->weight_ * weightInc; + // Maybe we should decrease the edge probability as well. + // A new connection with this edge should be tried whenever + // we have tried to extend the new SC and we could not. + } + oldWSC->weight_ *= weightDec; + updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big. + break; + case PATH_PROJECTION_SHORTER: + case PATH_PROJECTION_AND_VALIDATION_SHORTER: + // Hard to say what to do on this case... + break; + case PATH_VALIDATION_SHORTER: + // The path was made shorter, check the validation report to know + // why. + colRep = HPP_DYNAMIC_PTR_CAST(CollisionValidationReport, + extend.validationReport->configurationReport); + if (colRep) { + CollisionObjectPtr_t o1 = colRep->object1, o2 = colRep->object2; + // If it is a collision between the robot and an unactuated object, + // which cannot move in this state. + if (o1->joint() != NULL && o2->joint() != NULL) { + // TODO: Currently, the RelativeMotion matrix does not cover cases + // like ConvexShapeContact (2 function making a 6D constraints). + RelativeMotion::matrix_type m = extend.edge->relativeMotion(); + size_type i0 = RelativeMotion::idx(NULL); + size_type i1 = RelativeMotion::idx(o1->joint()); + size_type i2 = RelativeMotion::idx(o2->joint()); + if (m(i0, i1) == RelativeMotion::Parameterized + || m(i0, i1) == RelativeMotion::Constrained) { + // Object1 should be moved out of the way first... + updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big. + } + if (m(i0, i2) == RelativeMotion::Parameterized + || m(i0, i2) == RelativeMotion::Constrained) { + // Object2 should be moved out of the way first... + updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big. + } + } + } + break; + } + } + break; + // For cases below, newN is a NULL pointer. + case PROJECTION: + case PATH_PROJECTION_ZERO: // unclear for PATH_PROJECTION_ZERO + // It was not possible to project. It may be that projection is not + // possible, or only that it is not yet possible. + // We decrease the probability of sampling this edge. + updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big. + oldWSC->weight_ *= weightDec; + break; + case PATH_VALIDATION_ZERO: + // The path validation report will say what's wrong. + // The only interesting thing are collisions with objects. + colRep = HPP_DYNAMIC_PTR_CAST(CollisionValidationReport, + extend.validationReport->configurationReport); + if (colRep) { + CollisionObjectPtr_t o1 = colRep->object1, o2 = colRep->object2; + // If it is a collision between the robot and an unactuated object, + // which cannot move in this state. + if (o1->joint() != NULL && o2->joint() != NULL) { + // TODO: Currently, the RelativeMotion matrix does not cover cases + // like ConvexShapeContact (2 function making a 6D constraints). + RelativeMotion::matrix_type m = extend.edge->relativeMotion(); + size_type i0 = RelativeMotion::idx(NULL); + size_type i1 = RelativeMotion::idx(o1->joint()); + size_type i2 = RelativeMotion::idx(o2->joint()); + if (m(i0, i1) == RelativeMotion::Parameterized + || m(i0, i1) == RelativeMotion::Constrained) { + // Object1 should be moved out of the way first... + updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big. + } + if (m(i0, i2) == RelativeMotion::Parameterized + || m(i0, i2) == RelativeMotion::Constrained) { + // Object2 should be moved out of the way first... + updateEdgeProba(extend.edge, oldWSC, 0.99); // Rate of change should not be too big. + } + } + } + break; + case UNKNOWN: + case STEERING_METHOD: + // Hard to say what to do. + oldWSC->weight_ *= weightDec; + break; + } + } + + void SymbolicPlanner::updateEdgeProba ( + const graph::EdgePtr_t edge, + WeighedSymbolicComponentPtr_t wsc, + const value_type alpha) { + std::size_t i = wsc->indexOf (edge); + assert(i < wsc->p_.size()); + const value_type pi = wsc->p_[i]; + wsc->p_ *= ( 1 - alpha * pi ) / ( 1 - pi ); + wsc->p_[i] = alpha * pi; + wsc->normalizeProba(); + } + + SymbolicPlanner::SymbolicPlanner (const Problem& problem, + const RoadmapPtr_t& roadmap) : + core::PathPlanner (problem, roadmap), + shooter_ (problem.configurationShooter()), + problem_ (problem), roadmap_ (roadmap), + extendStep_ (1), + qProj_ (problem.robot ()->configSize ()) + {} + + void SymbolicPlanner::init (const SymbolicPlannerWkPtr_t& weak) + { + core::PathPlanner::init (weak); + weakPtr_ = weak; + } + } // namespace manipulation +} // namespace hpp