diff --git a/include/hpp/manipulation/fwd.hh b/include/hpp/manipulation/fwd.hh index 2a1ea85dcf4ce4ea75c7c7e7f43cd80327b525d3..b3159434410000a6e40cd51e92a483878b2b0d0e 100644 --- a/include/hpp/manipulation/fwd.hh +++ b/include/hpp/manipulation/fwd.hh @@ -100,6 +100,8 @@ namespace hpp { typedef std::vector <ObjectPtr_t> Objects_t; typedef core::Constraint Constraint; typedef core::ConstraintPtr_t ConstraintPtr_t; + typedef constraints::Explicit Explicit; + typedef constraints::ExplicitPtr_t ExplicitPtr_t; typedef constraints::ImplicitPtr_t ImplicitPtr_t; typedef constraints::LockedJoint LockedJoint; typedef constraints::LockedJointPtr_t LockedJointPtr_t; @@ -112,6 +114,7 @@ namespace hpp { typedef core::ConfigurationShooter ConfigurationShooter; typedef core::ConfigurationShooterPtr_t ConfigurationShooterPtr_t; typedef core::ValidationReport ValidationReport; + typedef core::NumericalConstraints_t NumericalConstraints_t; typedef core::PathValidationPtr_t PathValidationPtr_t; typedef core::PathValidationReportPtr_t PathValidationReportPtr_t; typedef core::matrix_t matrix_t; diff --git a/include/hpp/manipulation/steering-method/cross-state-optimization.hh b/include/hpp/manipulation/steering-method/cross-state-optimization.hh index 8b555508fab9dcdbaa1025881e7a490043fd38b3..74905076999c3a9db6141df5a255c9c4b3d88833 100644 --- a/include/hpp/manipulation/steering-method/cross-state-optimization.hh +++ b/include/hpp/manipulation/steering-method/cross-state-optimization.hh @@ -1,5 +1,6 @@ // Copyright (c) 2017, Joseph Mirabel -// Authors: Joseph Mirabel (joseph.mirabel@laas.fr) +// Authors: Joseph Mirabel (joseph.mirabel@laas.fr), +// Florent Lamiraux (florent.lamiraux@laas.fr) // // This file is part of hpp-manipulation. // hpp-manipulation is free software: you can redistribute it @@ -18,6 +19,7 @@ # define HPP_MANIPULATION_STEERING_METHOD_CROSS_STATE_OPTIMIZATION_HH # include <hpp/core/steering-method.hh> +# include <hpp/core/config-projector.hh> # include <hpp/manipulation/config.hh> # include <hpp/manipulation/fwd.hh> @@ -36,7 +38,7 @@ namespace hpp { /// #### Methodology /// /// Given two configuration \f$ (q_1,q_2) \f$, this class formulates and - /// solves the problem as follows. + /// solves the problem as follows. /// - Compute the corresponding states \f$ (s_1, s_2) \f$. /// - For a each path \f$ (e_0, ... e_n) \f$ between \f$ (s_1, s_2) \f$ /// in the constraint graph, do: @@ -57,6 +59,8 @@ namespace hpp { public SteeringMethod { public: + struct OptimizationData; + static CrossStateOptimizationPtr_t create (const Problem& problem); /// \warning core::Problem will be casted to Problem @@ -71,12 +75,16 @@ namespace hpp { protected: CrossStateOptimization (const Problem& problem) : - SteeringMethod (problem) - {} + SteeringMethod (problem), + sameRightHandSide_ () + { + gatherGraphConstraints (); + } CrossStateOptimization (const CrossStateOptimization& other) : - SteeringMethod (other), - weak_ () + SteeringMethod (other), constraints_ (other.constraints_), + index_ (other.index_), sameRightHandSide_ + (other.sameRightHandSide_), weak_ () {} core::PathPtr_t impl_compute (ConfigurationIn_t q1, ConfigurationIn_t q2) const; @@ -88,8 +96,11 @@ namespace hpp { } private: + typedef constraints::solver::BySubstitution Solver_t; struct GraphSearchData; - struct OptimizationData; + + /// Gather constraints of all edges + void gatherGraphConstraints (); /// Step 1 of the algorithm /// \return whether the max depth was reached. @@ -99,10 +110,28 @@ namespace hpp { graph::Edges_t getTransitionList (GraphSearchData& data, const std::size_t& i) const; /// Step 3 of the algorithm - void buildOptimizationProblem (OptimizationData& d, const graph::Edges_t& edges) const; + bool buildOptimizationProblem + (OptimizationData& d, const graph::Edges_t& transitions) const; + + /// Step 4 of the algorithm + bool solveOptimizationProblem (OptimizationData& d) const; + + bool checkConstantRightHandSide (OptimizationData& d, + size_type index) const; core::PathVectorPtr_t buildPath (OptimizationData& d, const graph::Edges_t& edges) const; + bool contains (const Solver_t& solver, const ImplicitPtr_t& c) const; + + /// Vector of parameterizable edge numerical constraints + NumericalConstraints_t constraints_; + /// Map of indexes in constraints_ + std::map < std::string, std::size_t > index_; + + /// associative map that stores pairs of constraints of the form + /// (constraint, constraint/hold) + std::map <ImplicitPtr_t, ImplicitPtr_t> sameRightHandSide_; + /// Weak pointer to itself CrossStateOptimizationWkPtr_t weak_; }; // class CrossStateOptimization diff --git a/src/steering-method/cross-state-optimization.cc b/src/steering-method/cross-state-optimization.cc index 850392f90152496c79659140ff95237f1ec2d5ca..f07cbe776a68691e620c39dd735878eacc57b1d1 100644 --- a/src/steering-method/cross-state-optimization.cc +++ b/src/steering-method/cross-state-optimization.cc @@ -1,5 +1,6 @@ // Copyright (c) 2017, Joseph Mirabel -// Authors: Joseph Mirabel (joseph.mirabel@laas.fr) +// Authors: Joseph Mirabel (joseph.mirabel@laas.fr), +// Florent Lamiraux (florent.lamiraux@laas.fr) // // This file is part of hpp-manipulation. // hpp-manipulation is free software: you can redistribute it @@ -39,8 +40,6 @@ #include <hpp/manipulation/graph/edge.hh> #include <hpp/manipulation/graph/state.hh> -#include <../src/steering-method/cross-state-optimization/function.cc> - namespace hpp { namespace manipulation { namespace steeringMethod { @@ -142,6 +141,64 @@ namespace hpp { } }; + void CrossStateOptimization::gatherGraphConstraints () + { + typedef graph::Edge Edge; + typedef graph::EdgePtr_t EdgePtr_t; + typedef graph::GraphPtr_t GraphPtr_t; + typedef constraints::solver::BySubstitution Solver_t; + + std::map <ImplicitPtr_t, ImplicitPtr_t> constraintCopy, constraintOrig; + ImplicitPtr_t copy; + GraphPtr_t cg (problem_.constraintGraph ()); + const ConstraintsAndComplements_t& cac + (cg->constraintsAndComplements ()); + for (std::size_t i = 0; i < cg->nbComponents (); ++i) { + EdgePtr_t edge (HPP_DYNAMIC_PTR_CAST (Edge, cg->get (i).lock ())); + if (edge) { + const Solver_t& solver (edge->pathConstraint ()-> + configProjector ()->solver ()); + const NumericalConstraints_t& constraints + (solver.numericalConstraints ()); + for (NumericalConstraints_t::const_iterator it + (constraints.begin ()); it != constraints.end (); ++it) { + if ((*it)->parameterSize () > 0) { + const std::string& name ((*it)->function ().name ()); + if (index_.find (name) == index_.end ()) { + // constraint is not in map, add it + index_ [name] = constraints_.size (); + copy = (*it)->copy (); + constraintCopy [*it] = copy; + constraintOrig [copy] = *it; + // Check whether constraint is equivalent to a previous one + for (NumericalConstraints_t::const_iterator it1 + (constraints_.begin ()); it1 != constraints_.end (); + ++it1) { + for (ConstraintsAndComplements_t::const_iterator it2 + (cac.begin ()); it2 != cac.end (); ++it2) { + if (((constraintOrig [*it1] == it2->complement) && + (*it == it2->both)) || + ((constraintOrig [*it1] == it2->both) && + (*it == it2->complement))) { + assert (sameRightHandSide_.count (*it1) == 0); + assert (sameRightHandSide_.count (copy) == 0); + sameRightHandSide_ [*it1] = copy; + sameRightHandSide_ [copy] = *it1; + } + } + } + constraints_.push_back (copy); + hppDout (info, "Adding constraint \"" << name << "\""); + hppDout (info, "Edge \"" << edge->name () << "\""); + hppDout (info, "parameter size: " << (*it)->parameterSize ()); + + } + } + } + } + } + } + bool CrossStateOptimization::findTransitions (GraphSearchData& d) const { while (! d.queue1.empty()) @@ -209,452 +266,280 @@ namespace hpp { return transitions; } + namespace internal { + bool saturate (const core::DevicePtr_t& robot, vectorIn_t q, + vectorOut_t qSat, pinocchio::ArrayXb& saturatedDof) + { + qSat = q; + return hpp::pinocchio::saturate (robot, qSat, saturatedDof); + } + } // namespace internal + struct CrossStateOptimization::OptimizationData { + typedef constraints::solver::HierarchicalIterative::Saturation_t + Saturation_t; + enum RightHandSideStatus_t { + // Constraint is not in solver for this waypoint + ABSENT, + // right hand side of constraint for this waypoint is equal to + // right hand side for previous waypoint + EQUAL_TO_PREVIOUS, + // right hand side of constraint for this waypoint is equal to + // right hand side for initial configuration + EQUAL_TO_INIT, + // right hand side of constraint for this waypoint is equal to + // right hand side for goal configuration + EQUAL_TO_GOAL + }; // enum RightHandSideStatus_t const std::size_t N, nq, nv; - constraints::solver::BySubstitution solver; + std::vector <Solver_t> solvers; + // Waypoints lying in each intermediate state + matrix_t waypoint; Configuration_t q1, q2; - vector_t q; core::DevicePtr_t robot; - typedef std::vector<States_t> StatesPerConf_t; - StatesPerConf_t statesPerConf_; - struct RightHandSideSetter { - ImplicitPtr_t impF; - size_type expFidx; - Configuration_t* qrhs; - vector_t rhs; - RightHandSideSetter () : qrhs (NULL) {} - // TODO delete this constructor - RightHandSideSetter (ImplicitPtr_t _impF, size_type _expFidx, - Configuration_t* _qrhs) - : impF(_impF), expFidx(_expFidx), qrhs (_qrhs) {} - RightHandSideSetter (ImplicitPtr_t _impF, size_type _expFidx, - vector_t _rhs) - : impF(_impF), expFidx(_expFidx), qrhs (NULL), rhs (_rhs) {} - void apply(constraints::solver::BySubstitution& s) - { - if (expFidx >= 0) { - if (qrhs != NULL) s.explicitConstraintSet().rightHandSideFromInput - (expFidx, *qrhs); - else s.explicitConstraintSet().rightHandSide - (expFidx, rhs); - } else { - if (qrhs != NULL) s.rightHandSideFromConfig (impF, *qrhs); - else s.rightHandSide (impF, rhs); - } - } - }; - typedef std::vector<RightHandSideSetter> RightHandSideSetters_t; - RightHandSideSetters_t rhsSetters_; - - OptimizationData (const std::size_t& _N, const core::DevicePtr_t _robot) - : N (_N), nq (_robot->configSize()), nv (_robot->numberDof()), - solver (_robot->configSpace () ^ N), robot (_robot), - statesPerConf_ (N) + // Matrix specifying for each constraint and each waypoint how + // the right hand side is initialized in the solver. + Eigen::Matrix < vector_t, Eigen::Dynamic, Eigen::Dynamic > M_rhs; + Eigen::Matrix < RightHandSideStatus_t, Eigen::Dynamic, Eigen::Dynamic > + M_status; + + OptimizationData (const core::DevicePtr_t _robot, + const Configuration_t& _q1, + const Configuration_t& _q2, + const Edges_t& transitions + ) : + N (transitions.size () - 1), nq (_robot->configSize()), + nv (_robot->numberDof()), solvers (N, _robot->configSpace ()), + waypoint (nq, N), q1 (_q1), q2 (_q2), robot (_robot), M_rhs (), + M_status () { - solver.saturation (boost::bind(&OptimizationData::_saturate , this, - _1, _2, _3)); + assert (transitions.size () > 0); } + }; - void addGraphConstraints (const graph::GraphPtr_t& graph) - { - for (std::size_t i = 0; i < N; ++i) { - _add (graph->lockedJoints(), i); - _add (graph->numericalConstraints(), i); - } + bool CrossStateOptimization::checkConstantRightHandSide + (OptimizationData& d, size_type index) const + { + const ImplicitPtr_t c (constraints_ [index]); + c->rightHandSideFromConfig (d.q1); + vector_t rhsInit (c->rightHandSide ()); + c->rightHandSideFromConfig (d.q2); + vector_t rhsGoal (c->rightHandSide ()); + // Check that right hand sides are close to each other + value_type eps (problem_.constraintGraph ()->errorThreshold ()); + value_type eps2 (eps * eps); + if ((rhsGoal - rhsInit).squaredNorm () > eps2) { + return false; } - - void addConstraints (const StatePtr_t& state, const std::size_t& i) - { - bool alreadyAdded = ( - std::find (statesPerConf_[i].begin(), statesPerConf_[i].end(), state) - != statesPerConf_[i].end()); - if (alreadyAdded) return; - _add (state->lockedJoints(), i); - _add (state->numericalConstraints(), i); - statesPerConf_[i].push_back(state); + // Matrix of solver right hand sides + for (size_type j=0; j<d.M_rhs.cols (); ++j) { + d.M_rhs (index, j) = rhsInit; } + return true; + } - void addConstraints (const EdgePtr_t& trans, const std::size_t& i) - { - const LockedJoints_t& ljs = trans->lockedJoints(); - for (LockedJoints_t::const_iterator _lj = ljs.begin(); - _lj != ljs.end(); ++_lj) { - LockedJointPtr_t lj (*_lj); - std::ostringstream os; - os << lj->jointName() << " | " << i << " -> " << (i+1); - DifferentiableFunctionPtr_t f, f_implicit; - ImplicitPtr_t c_implicit; - // i = Input, o = Output, - // c = Config, v = Velocity - RowBlockIndices ic, oc, ov; - ColBlockIndices iv; - ComparisonTypes_t cts; - vector_t rhs; - if (i == 0) { - f = lj->explicitFunction(); - ic = _row(lj->inputConf() , 0); - oc = _row(lj->outputConf() , 0); - iv = _col(lj->inputVelocity() , 0); - ov = _row(lj->outputVelocity(), 0); - cts = lj->comparisonType(); - lj->rightHandSideFromConfig (q1); - rhs = lj->rightHandSide(); - } else if (i == N) { - f = lj->explicitFunction(); - // Currently, this function is mandatory because if the same - // joint is locked all along the path, then, one of the LockedJoint - // has to be treated implicitely. - // TODO it would be smarter to detect this case beforehand. If the - // chain in broken in the middle, an explicit formulation exists - // (by inverting the equality in the next else section) and we - // miss it. - f_implicit = _stateFunction (lj->functionPtr(), N-1); - ic = _row(lj->inputConf() , 0); - oc = _row(lj->outputConf() , (N-1) * nq); - iv = _col(lj->inputVelocity() , 0); - ov = _row(lj->outputVelocity(), (N-1) * nv); - cts = lj->comparisonType(); - lj->rightHandSideFromConfig (q2); - rhs = lj->rightHandSide(); - } else { - f = constraints::Identity::create (lj->configSpace(), os.str()); - ic = _row(lj->outputConf() , (i - 1) * nq); - oc = _row(lj->outputConf() , i * nq); - iv = _col(lj->outputVelocity(), (i - 1) * nv); - ov = _row(lj->outputVelocity(), i * nv); - cts = ComparisonTypes_t (lj->numberDof(), constraints::EqualToZero); - } - - // It is important to use the index of the function since the same - // function may be added several times on different part. - constraints::ExplicitPtr_t ec - (constraints::Explicit::create (solver.configSpace (), f, - ic.indices (), oc.indices (), - iv.indices (), ov.indices (), - cts)); - size_type expFidx = solver.explicitConstraintSet().add (ec); - if (expFidx < 0) { - if (f_implicit) { - c_implicit = constraints::Implicit::create (f_implicit, cts); - solver.add (c_implicit); - } else { - HPP_THROW (std::invalid_argument, - "Could not add locked joint " << lj->jointName() << - " of transition " << trans->name() << " at id " << i); - } + void displayRhsMatrix (const Eigen::Matrix < vector_t, Eigen::Dynamic, + Eigen::Dynamic >& m, + const NumericalConstraints_t& constraints) + { + std::ostringstream oss; oss.precision (5); + oss << "\\documentclass[12pt,landscape]{article}" << std::endl; + oss << "\\usepackage[a3paper]{geometry}" << std::endl; + oss << "\\begin {document}" << std::endl; + oss << "\\begin {tabular}{"; + for (size_type j=0; j<m.cols () + 1; ++j) + oss << "c"; + oss << "}" << std::endl; + for (size_type i=0; i<m.rows (); ++i) { + oss << constraints [i]->function ().name () << " & "; + for (size_type j=0; j<m.cols (); ++j) { + oss << "$\\left(\\begin{array}{c}" << std::endl; + for (size_type k=0; k<m (i,j).size (); ++k) { + oss << m (i,j) [k] << "\\\\" << std::endl; } - - // Setting the right hand side must be done later - if (rhs.size() > 0) { - assert (c_implicit); - rhsSetters_.push_back - (RightHandSideSetter(c_implicit, expFidx, rhs)); + oss << "\\end{array}\\right)$" << std::endl; + if (j < m.cols () - 1) { + oss << " & " << std::endl; } - f_implicit.reset(); - c_implicit.reset(); } + oss << "\\\\" << std::endl; + } + oss << "\\end{tabular}" << std::endl; + oss << "\\end {document}" << std::endl; + hppDout (info, oss.str ()); + } - // TODO handle numerical constraints - using namespace ::hpp::core; - constraints::ExplicitPtr_t enc; - const NumericalConstraints_t& ncs = trans->numericalConstraints(); - for (NumericalConstraints_t::const_iterator _nc = ncs.begin(); - _nc != ncs.end(); ++_nc) { - constraints::ImplicitPtr_t nc (*_nc); - enc = HPP_DYNAMIC_PTR_CAST (constraints::Explicit, nc); - - DifferentiableFunctionPtr_t f, ef; - constraints::ImplicitPtr_t constraint; - // i = Input, o = Output, - // c = Config, v = Velocity - // Configuration_t* qrhs; - RowBlockIndices ic, oc, ov; - ColBlockIndices iv; - ComparisonTypes_t cts; - vector_t rhs; - if (i == 0) { - f = _stateFunction(nc->functionPtr(), 0); - // if (enc) { - // ef = enc->explicitFunction(); - // ic = _row(enc->inputConf() , 0); - // oc = _row(enc->outputConf() , 0); - // iv = _col(enc->inputVelocity() , 0); - // ov = _row(enc->outputVelocity(), 0); - // } - // cts = ComparisonTypes_t (enc->outputDerivativeSize(), constraints::Equality); - cts = nc->comparisonType (); - nc->rightHandSideFromConfig (q1); - rhs = nc->rightHandSide(); - // qrhs = &q1; - } else if (i == N) { - f = _stateFunction(nc->functionPtr(), N - 1); - cts = nc->comparisonType (); - nc->rightHandSideFromConfig (q2); - rhs = nc->rightHandSide(); - // qrhs = &q2; - } else { - f = _edgeFunction (nc->functionPtr(), i - 1, i); - cts = ComparisonTypes_t (f->outputSize(), constraints::EqualToZero); - // qrhs = NULL; - } - constraint = constraints::Implicit::create (f, cts); - size_type expFidx = -1; - // if (ef) { - // constraints::ExplicitPtr_t ec - // (constraints::Explicit::create (solver.configSpace (), ef, - // ic.indices (), oc.indices (), - // iv.indices (), ov.indices (), - // cts)); - // expFidx = solver.explicitConstraintSet().add (ec); - // } - if (expFidx < 0) solver.add (constraint); - - // TODO This must be done later... - // if (qrhs != NULL) { - if (rhs.size() > 0) { - // solver.rightHandSideFromConfig (f, ef, rhs); - rhsSetters_.push_back (RightHandSideSetter - (constraint, expFidx, rhs)); + void displayStatusMatrix (const Eigen::Matrix < CrossStateOptimization::OptimizationData::RightHandSideStatus_t, Eigen::Dynamic, Eigen::Dynamic >& m, + const NumericalConstraints_t& constraints) + { + std::ostringstream oss; oss.precision (5); + oss << "\\documentclass[12pt,landscape]{article}" << std::endl; + oss << "\\usepackage[a3paper]{geometry}" << std::endl; + oss << "\\begin {document}" << std::endl; + oss << "\\begin {tabular}{"; + for (size_type j=0; j<m.cols () + 1; ++j) + oss << "c"; + oss << "}" << std::endl; + for (size_type i=0; i<m.rows (); ++i) { + oss << constraints [i]->function ().name () << " & "; + for (size_type j=0; j<m.cols (); ++j) { + oss << m (i,j); + if (j < m.cols () - 1) { + oss << " & " << std::endl; } } + oss << "\\\\" << std::endl; } + oss << "\\end{tabular}" << std::endl; + oss << "\\end {document}" << std::endl; + hppDout (info, oss.str ()); + } - void setRightHandSide () - { - for (std::size_t i = 0; i < rhsSetters_.size(); ++i) - rhsSetters_[i].apply(solver); - } - - bool solve () - { - if (N == 0) return true; - constraints::HierarchicalIterativeSolver::Status status = - solver.solve <constraints::solver::lineSearch::FixedSequence> (q); - bool success = (status == constraints::HierarchicalIterativeSolver::SUCCESS); - if (!success) { - hppDout (warning, "Projection failed with status " << status << - ". Configuration after projection is\n" - << pinocchio::displayConfig(q)); - } - return success; + bool CrossStateOptimization::contains + (const Solver_t& solver, const ImplicitPtr_t& c) const + { + if (solver.contains (c)) return true; + std::map <ImplicitPtr_t, ImplicitPtr_t>::const_iterator it + (sameRightHandSide_.find (c)); + if ((it != sameRightHandSide_.end () && + solver.contains (it->second))) { + return true; } + return false; + } - void _add (const NumericalConstraints_t& ncs, const std::size_t& i) - { - using namespace ::hpp::core; - constraints::ExplicitPtr_t enc; - for (NumericalConstraints_t::const_iterator _nc = ncs.begin(); - _nc != ncs.end(); ++_nc) { - constraints::ImplicitPtr_t nc (*_nc); - enc = HPP_DYNAMIC_PTR_CAST (constraints::Explicit, nc); - bool added = false; - if (enc) { - added = solver.explicitConstraintSet().add - (explicitConstraintOnWaypoint (enc, i)); + bool CrossStateOptimization::buildOptimizationProblem + (OptimizationData& d, const graph::Edges_t& transitions) const + { + if (d.N == 0) return true; + d.M_status.resize (constraints_.size (), d.N); + d.M_status.fill (OptimizationData::ABSENT); + d.M_rhs.resize (constraints_.size (), d.N); + d.M_rhs.fill (vector_t ()); + size_type index = 0; + // Loop over constraints + for (NumericalConstraints_t::const_iterator it (constraints_.begin ()); + it != constraints_.end (); ++it) { + const ImplicitPtr_t& c (*it); + // Loop forward over waypoints to determine right hand sides equal + // to initial configuration + for (std::size_t j = 0; j < d.N; ++j) { + // Get transition solver + const Solver_t& trSolver + (transitions [j]->pathConstraint ()->configProjector ()-> + solver ()); + if (contains (trSolver, c)) { + if ((j==0) || d.M_status (index, j-1) == + OptimizationData::EQUAL_TO_INIT) { + d.M_status (index, j) = OptimizationData::EQUAL_TO_INIT; + } else { + d.M_status (index, j) = OptimizationData::EQUAL_TO_PREVIOUS; + } } - if (!added) - solver.add (implicitConstraintOnWayPoint (nc, i)); - } - } - void _add (const LockedJoints_t& ljs, const std::size_t i) - { - for (LockedJoints_t::const_iterator _lj = ljs.begin(); - _lj != ljs.end(); ++_lj) { - LockedJointPtr_t lj (*_lj); - size_type expFidx = solver.explicitConstraintSet().add - (explicitConstraintOnWaypoint (lj, i)); - if (expFidx < 0) - throw std::invalid_argument ("Could not add locked joint " + lj->jointName()); - - // This must be done later - rhsSetters_.push_back - (RightHandSideSetter(constraints::Implicit::create - (DifferentiableFunctionPtr_t()), - expFidx, - lj->rightHandSide())); - // solver.rightHandSide (DifferentiableFunctionPtr_t(), - // lj->explicitFunction(), - // lj->rightHandSide()); } - } - - /// Apply constraint on a give waypoint - /// \param constaint explicit constraint defined on the configuration - /// space, - /// \param i rank of the waypoint in the vector of parameters. - constraints::ExplicitPtr_t explicitConstraintOnWaypoint - (const constraints::ExplicitPtr_t& constraint, - std::size_t i) - { - assert (i < N); - // size_type nq (constraint->explicitFunction ().inputSize ()), - // nv (constraint->function ().inputDerrivativeSize ()); - segments_t ic (constraint->inputConf ()), - oc (constraint->outputConf ()), - iv (constraint->inputVelocity ()), - ov (constraint->outputVelocity ()); - // TODO: implicit formulation should be built from input constraint - return constraints::Explicit::create - (solver.configSpace (), constraint->functionPtr (), - _row (ic, i*nq).indices (), _row (oc, i*nq).indices (), - _col (iv, i*nv).indices (), _col (ov, i*nv).indices (), - constraint->comparisonType ()); - } - - RowBlockIndices _row (segments_t s, const std::size_t& shift) - { - for (std::size_t j = 0; j < s.size(); ++j) s[j].first += shift; - return RowBlockIndices (s); - } - ColBlockIndices _col (segments_t s, const std::size_t& shift) - { - for (std::size_t j = 0; j < s.size(); ++j) s[j].first += shift; - return ColBlockIndices (s); - } - constraints::ImplicitPtr_t implicitConstraintOnWayPoint - (const constraints::ImplicitPtr_t& constraint, std::size_t i) - { - assert (i < N); - ImplicitPtr_t res (constraints::Implicit::create - (StateFunction::Ptr_t - (new StateFunction (constraint->functionPtr (), - N * nq, N * nv, - segment_t (i * nq, nq), - segment_t (i * nv, nv))), - constraint->comparisonType ())); - return res; - } - StateFunction::Ptr_t _stateFunction(const DifferentiableFunctionPtr_t inner, const std::size_t i) - { - assert (i < N); - return StateFunction::Ptr_t (new StateFunction (inner, N * nq, N * nv, - segment_t (i * nq, nq), segment_t (i * nv, nv) - )); - } - EdgeFunction::Ptr_t _edgeFunction(const DifferentiableFunctionPtr_t inner, - const std::size_t iL, const std::size_t iR) - { - assert (iL < N && iR < N); - return EdgeFunction::Ptr_t (new EdgeFunction (inner, N * nq, N * nv, - segment_t (iL * nq, nq), segment_t (iL * nv, nv), - segment_t (iR * nq, nq), segment_t (iR * nv, nv))); - } - void _integrate (vectorIn_t qin, vectorIn_t v, vectorOut_t qout) - { - for (std::size_t i = 0, iq = 0, iv = 0; i < N; ++i, iq += nq, iv += nv) - pinocchio::integrate (robot, - qin.segment(iq,nq), - v.segment(iv,nv), - qout.segment(iq,nq)); - } - bool _saturate (vectorIn_t q, vectorOut_t qSat, Eigen::VectorXi& sat) - { - bool ret = false; - const pinocchio::Model& model = robot->model(); - - for (std::size_t i = 1; i < model.joints.size(); ++i) { - const size_type jnq = model.joints[i].nq(); - const size_type jnv = model.joints[i].nv(); - const size_type jiq = model.joints[i].idx_q(); - const size_type jiv = model.joints[i].idx_v(); - for (std::size_t k = 0; k < N; ++k) { - const size_type idx_q = k * nq + jiq; - const size_type idx_v = k * nv + jiv; - for (size_type j = 0; j < jnq; ++j) { - const size_type iq = idx_q + j; - const size_type iv = idx_v + std::min(j,jnv-1); - if (q[iq] >= model.upperPositionLimit[jiq + j]) { - qSat [iq] = model.upperPositionLimit[jiq + j]; - sat[iv] = 1; - ret = true; - } else if (q[iq] <= model.lowerPositionLimit[jiq + j]) { - qSat [iq] = model.lowerPositionLimit[jiq + j]; - sat[iv] = -1; - ret = true; - } else - qSat [iq] = q [iq]; - sat[iv] = 0; + // Loop backward over waypoints to determine right hand sides equal + // to initial configuration + for (size_type j = d.N-1; j > 0; --j) { + // Get transition solver + const Solver_t& trSolver + (transitions [(std::size_t)j+1]->pathConstraint ()-> + configProjector ()->solver ()); + if (contains (trSolver, c)) { + if ((j==(size_type) d.N-1) || d.M_status (index, j+1) == + OptimizationData::EQUAL_TO_GOAL) { + // If constraint right hand side is already equal to + // initial config, check that right hand side is equal + // for init and goal configs. + if (d.M_status (index, j) == + OptimizationData::EQUAL_TO_INIT) { + if (checkConstantRightHandSide (d, index)) { + // stop for this constraint + break; + } else { + // Right hand side of constraint should be equal along the + // whole path but is different at init and at goal configs. + return false; + } + } else { + d.M_status (index, j) = OptimizationData::EQUAL_TO_GOAL; + } } } } - - const hpp::pinocchio::ExtraConfigSpace& ecs = robot->extraConfigSpace(); - const size_type& d = ecs.dimension(); - - for (size_type k = 0; k < d; ++k) { - for (std::size_t j = 0; j < N; ++j) { - const size_type iq = j * nq + model.nq + k; - const size_type iv = j * nv + model.nv + k; - if (q[iq] >= ecs.upper(k)) { - sat[iv] = 1; - ret = true; - } else if (q[iq] <= ecs.lower(k)) { - sat[iv] = -1; - ret = true; - } else - sat[iv] = 0; + ++index; + } // for (NumericalConstraints_t::const_iterator it + displayStatusMatrix (d.M_status, constraints_); + graph::GraphPtr_t cg (problem_.constraintGraph ()); + // Fill solvers with graph, node and edge constraints + for (std::size_t j = 0; j < d.N; ++j) { + graph::StatePtr_t state (transitions [(std::size_t)j]->to ()); + // initialize solver with state constraints + d.solvers [(std::size_t)j] = state->configConstraint ()-> + configProjector ()->solver (); + // Add graph constraints + const NumericalConstraints_t c (cg->numericalConstraints ()); + for (NumericalConstraints_t::const_iterator it (c.begin ()); + it != c.end (); ++it) { + d.solvers [(std::size_t)j].add (*it); + } + // Add edge constraints + for (std::size_t i=0; i<constraints_.size (); ++i) { + if (d.M_status (i, j) != OptimizationData::ABSENT) { + d.solvers [(std::size_t)j].add (constraints_ [i]); } } - return ret; } - }; + return true; + } - void CrossStateOptimization::buildOptimizationProblem ( - OptimizationData& d, const Edges_t& transitions) const + bool CrossStateOptimization::solveOptimizationProblem + (OptimizationData& d) const { - if (d.N == 0) return; - size_type maxIter = problem_.getParameter ("CrossStateOptimization/maxIteration").intValue(); - value_type thr = problem_.getParameter ("CrossStateOptimization/errorThreshold").floatValue(); - d.solver.maxIterations (maxIter); - d.solver.errorThreshold (thr); - - // Add graph constraints (decoupled) - d.addGraphConstraints (problem_.constraintGraph()); - - std::size_t i = 0; - for (Edges_t::const_iterator _t = transitions.begin(); - _t != transitions.end(); ++_t) - { - const EdgePtr_t& t = *_t; - bool first = (i == 0); - bool last = (i == d.N); - - // Add state constraints (decoupled) - if (!last ) d.addConstraints (t->to() , i); - if (!last ) d.addConstraints (t->state(), i); - if (!first) d.addConstraints (t->from() , i - 1); - if (!first) d.addConstraints (t->state(), i - 1); - - // Add transition constraints (coupled) - d.addConstraints (t, i); - - ++i; - } - - d.solver.explicitConstraintSetHasChanged(); - d.setRightHandSide(); - - hppDout (info, "Solver informations\n" << d.solver); - - // Initial guess - std::vector<size_type> ks; - size_type K = 0; - ks.resize(d.N); - for (std::size_t i = 0; i < d.N + 1; ++i) { - if (!transitions[i]->isShort()) ++K; - if (i < d.N) ks[i] = K; - } - if (K==0) { - ++K; - for (std::size_t i = d.N/2; i < d.N; ++i) - ks[i] = 1; - } - d.q.resize (d.N * d.nq); - for (std::size_t i = 0; i < d.N; ++i) { - value_type u = value_type(ks[i]) / value_type(K); - pinocchio::interpolate (d.robot, d.q1, d.q2, u, d.q.segment (i * d.nq, d.nq)); + // Iterate on waypoint solvers, for each of them + // 1. initialize right hand side, + // 2. solve. + for (std::size_t j=0; j<d.solvers.size (); ++j) { + Solver_t& solver (d.solvers [j]); + for (std::size_t i=0; i<constraints_.size (); ++i) { + const ImplicitPtr_t& c (constraints_ [i]); + switch (d.M_status ((size_type)i, (size_type)j)) { + case OptimizationData::EQUAL_TO_PREVIOUS: + assert (j != 0); + solver.rightHandSideFromConfig (c, d.waypoint.col (j-1)); + break; + case OptimizationData::EQUAL_TO_INIT: + solver.rightHandSideFromConfig (c, d.q1); + break; + case OptimizationData::EQUAL_TO_GOAL: + solver.rightHandSideFromConfig (c, d.q2); + break; + case OptimizationData::ABSENT: + default: + ; + } + } + if (j==0) d.waypoint.col (j) = d.q1; + else d.waypoint.col (j) = d.waypoint.col (j-1); + Solver_t::Status status = solver.solve + (d.waypoint.col (j), + constraints::solver::lineSearch::Backtracking ()); + + switch (status) { + case Solver_t::ERROR_INCREASED: + hppDout (info, "error increased."); + return false; + case Solver_t::MAX_ITERATION_REACHED: + hppDout (info, "max iteration reached."); + return false; + case Solver_t::INFEASIBLE: + hppDout (info, "infeasible."); + return false; + case Solver_t::SUCCESS: + hppDout (info, "success."); + } } + return true; } core::PathVectorPtr_t CrossStateOptimization::buildPath ( @@ -680,17 +565,15 @@ namespace hpp { if (first && last) status = t->build (path, d.q1, d.q2); else if (first) - status = t->build (path, d.q1, d.q.head (d.nq)); + status = t->build (path, d.q1, d.waypoint.col (i)); else if (last) - status = t->build (path, d.q.tail (d.nq), d.q2); + status = t->build (path, d.waypoint.col (i-1), d.q2); else { - std::size_t j = (i-1) * d.nq; - status = t->build (path, d.q.segment (j, d.nq), d.q.segment (j + d.nq, d.nq)); + status = t->build (path, d.waypoint.col (i-1), d.waypoint.col (i)); } if (!status || !path) { - hppDout (warning, "Could not build path from solution " - << pinocchio::displayConfig(d.q)); + hppDout (warning, "Could not build path from solution "); return PathVectorPtr_t(); } pv->appendPath(path); @@ -726,26 +609,16 @@ namespace hpp { hppDout (info, ss.str()); #endif // HPP_DEBUG - OptimizationData optData (transitions.size() - 1, problem().robot()); - optData.q1 = q1; - optData.q2 = q2; - bool ok = true; - try { - buildOptimizationProblem (optData, transitions); - } catch (const std::invalid_argument& e) { - hppDout (info, e.what ()); - ok = false; - } - - if (ok && optData.solve()) { - core::PathPtr_t path = buildPath (optData, transitions); - if (path) return path; - hppDout (info, "Failed to build path from solution: " - << pinocchio::displayConfig(optData.q)); - } else { - hppDout (info, "Failed to solve"); + OptimizationData optData (problem().robot(), q1, q2, transitions); + if (buildOptimizationProblem (optData, transitions)) { + if (solveOptimizationProblem (optData)) { + core::PathPtr_t path = buildPath (optData, transitions); + if (path) return path; + hppDout (info, "Failed to build path from solution: "); + } else { + hppDout (info, "Failed to solve"); + } } - ++idxSol; transitions = getTransitionList(d, idxSol); } diff --git a/src/steering-method/cross-state-optimization/function.cc b/src/steering-method/cross-state-optimization/function.cc deleted file mode 100644 index 10443ef1030de854a4f4b9cb0aaeb202857f5fb2..0000000000000000000000000000000000000000 --- a/src/steering-method/cross-state-optimization/function.cc +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2017, 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/constraints/differentiable-function.hh> - -#include <hpp/manipulation/config.hh> - -namespace hpp { - namespace manipulation { - namespace steeringMethod { - typedef core::segment_t segment_t; - namespace { - std::string toStr (const segment_t& s) - { - std::ostringstream os; - os << "[ " << s.first << ", " << s.first + s.second << " ]"; - return os.str(); - } - } - - /// Apply the constraint on a subspace of the input space. - /// i.e.: \f$ f (q_0, ... , q_n) = f_{inner} (q_k) \f$ - class HPP_MANIPULATION_LOCAL StateFunction : - public constraints::DifferentiableFunction - { - public: - typedef boost::shared_ptr<StateFunction> Ptr_t; - StateFunction (const DifferentiableFunctionPtr_t& inner, - const size_type& nArgs, const size_type& nDers, - const segment_t& inArgs, const segment_t& inDers) : - DifferentiableFunction (nArgs, nDers, - inner->outputSpace(), inner->name() + " | " + toStr(inArgs)), - inner_ (inner), sa_ (inArgs), sd_ (inDers) - { - activeParameters_.setConstant(false); - activeParameters_.segment(sa_.first, sa_.second) - = inner_->activeParameters(); - - activeDerivativeParameters_.setConstant(false); - activeDerivativeParameters_.segment(sd_.first, sd_.second) - = inner_->activeDerivativeParameters(); - } - - protected: - void impl_compute (pinocchio::LiegroupElementRef y, vectorIn_t arg) const - { - inner_->value(y, arg.segment (sa_.first, sa_.second)); - } - - void impl_jacobian (matrixOut_t J, vectorIn_t arg) const - { - inner_->jacobian(J.middleCols (sd_.first, sd_.second), - arg.segment (sa_.first, sa_.second)); - } - - std::ostream& print (std::ostream& os) const - { - constraints::DifferentiableFunction::print(os); - return os << incindent << iendl << *inner_ << decindent; - } - - DifferentiableFunctionPtr_t inner_; - const segment_t sa_, sd_; - }; // class Function - - /// Compute the difference between the value of the function in two points. - /// i.e.: \f$ f (q_0, ... , q_n) = f_{inner} (q_{left}) - f_{inner} (q_{right}) \f$ - class HPP_MANIPULATION_LOCAL EdgeFunction : - public constraints::DifferentiableFunction - { - public: - typedef boost::shared_ptr<EdgeFunction> Ptr_t; - EdgeFunction (const DifferentiableFunctionPtr_t& inner, - const size_type& nArgs, const size_type& nDers, - const segment_t& lInArgs, const segment_t& lInDers, - const segment_t& rInArgs, const segment_t& rInDers) : - DifferentiableFunction (nArgs, nDers, - LiegroupSpace::Rn(inner->outputSpace()->nv()), - inner->name() + " | " + toStr(lInArgs) + " - " + toStr(rInArgs)), - inner_ (inner), - lsa_ (lInArgs), lsd_ (lInDers), - rsa_ (rInArgs), rsd_ (rInDers), - l_ (inner->outputSpace()), r_ (inner->outputSpace()) - { - activeParameters_.setConstant(false); - activeParameters_.segment(lsa_.first, lsa_.second) - = inner_->activeParameters(); - activeParameters_.segment(rsa_.first, rsa_.second) - = inner_->activeParameters(); - - activeDerivativeParameters_.setConstant(false); - activeDerivativeParameters_.segment(lsd_.first, lsd_.second) - = inner_->activeDerivativeParameters(); - activeDerivativeParameters_.segment(rsd_.first, rsd_.second) - = inner_->activeDerivativeParameters(); - } - - protected: - void impl_compute (pinocchio::LiegroupElementRef y, vectorIn_t arg) const - { - inner_->value(l_, arg.segment (lsa_.first, lsa_.second)); - inner_->value(r_, arg.segment (rsa_.first, rsa_.second)); - y.vector() = l_ - r_; - } - - void impl_jacobian (matrixOut_t J, vectorIn_t arg) const - { - inner_->jacobian( - J.middleCols (lsd_.first, lsd_.second), - arg.segment (lsa_.first, lsa_.second)); - inner_->jacobian( - J.middleCols (rsd_.first, rsd_.second), - arg.segment (rsa_.first, rsa_.second)); - J.middleCols (rsd_.first, rsd_.second) *= -1; - } - - std::ostream& print (std::ostream& os) const - { - constraints::DifferentiableFunction::print(os); - return os << incindent << iendl << *inner_ << decindent; - } - - DifferentiableFunctionPtr_t inner_; - const segment_t lsa_, lsd_; - const segment_t rsa_, rsd_; - - mutable LiegroupElement l_, r_; - }; // class Function - } // namespace steeringMethod - } // namespace manipulation -} // namespace hpp