Directory: | ./ |
---|---|
File: | src/steering-method/cross-state-optimization.cc |
Date: | 2025-03-07 11:10:46 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 10 | 325 | 3.1% |
Branches: | 20 | 680 | 2.9% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2017, Joseph Mirabel | ||
2 | // Authors: Joseph Mirabel (joseph.mirabel@laas.fr), | ||
3 | // Florent Lamiraux (florent.lamiraux@laas.fr) | ||
4 | // | ||
5 | |||
6 | // Redistribution and use in source and binary forms, with or without | ||
7 | // modification, are permitted provided that the following conditions are | ||
8 | // met: | ||
9 | // | ||
10 | // 1. Redistributions of source code must retain the above copyright | ||
11 | // notice, this list of conditions and the following disclaimer. | ||
12 | // | ||
13 | // 2. Redistributions in binary form must reproduce the above copyright | ||
14 | // notice, this list of conditions and the following disclaimer in the | ||
15 | // documentation and/or other materials provided with the distribution. | ||
16 | // | ||
17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
21 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | ||
28 | // DAMAGE. | ||
29 | |||
30 | #include <hpp/constraints/affine-function.hh> | ||
31 | #include <hpp/constraints/explicit.hh> | ||
32 | #include <hpp/constraints/locked-joint.hh> | ||
33 | #include <hpp/constraints/solver/by-substitution.hh> | ||
34 | #include <hpp/core/configuration-shooter.hh> | ||
35 | #include <hpp/core/path-vector.hh> | ||
36 | #include <hpp/manipulation/graph/edge.hh> | ||
37 | #include <hpp/manipulation/graph/state.hh> | ||
38 | #include <hpp/manipulation/steering-method/cross-state-optimization.hh> | ||
39 | #include <hpp/pinocchio/configuration.hh> | ||
40 | #include <hpp/pinocchio/joint-collection.hh> | ||
41 | #include <hpp/util/exception-factory.hh> | ||
42 | #include <map> | ||
43 | #include <pinocchio/multibody/model.hpp> | ||
44 | #include <queue> | ||
45 | #include <vector> | ||
46 | |||
47 | namespace hpp { | ||
48 | namespace manipulation { | ||
49 | namespace steeringMethod { | ||
50 | using Eigen::ColBlockIndices; | ||
51 | using Eigen::RowBlockIndices; | ||
52 | |||
53 | using graph::EdgePtr_t; | ||
54 | using graph::Edges_t; | ||
55 | using graph::LockedJoints_t; | ||
56 | using graph::Neighbors_t; | ||
57 | using graph::NumericalConstraints_t; | ||
58 | using graph::segments_t; | ||
59 | using graph::StatePtr_t; | ||
60 | using graph::States_t; | ||
61 | |||
62 | ✗ | CrossStateOptimizationPtr_t CrossStateOptimization::create( | |
63 | const ProblemConstPtr_t& problem) { | ||
64 | ✗ | CrossStateOptimizationPtr_t shPtr(new CrossStateOptimization(problem)); | |
65 | ✗ | shPtr->init(shPtr); | |
66 | ✗ | return shPtr; | |
67 | } | ||
68 | |||
69 | ✗ | CrossStateOptimizationPtr_t CrossStateOptimization::create( | |
70 | const core::ProblemConstPtr_t& problem) { | ||
71 | ✗ | assert(HPP_DYNAMIC_PTR_CAST(const Problem, problem)); | |
72 | ✗ | ProblemConstPtr_t p(HPP_STATIC_PTR_CAST(const Problem, problem)); | |
73 | ✗ | return create(p); | |
74 | } | ||
75 | |||
76 | ✗ | core::SteeringMethodPtr_t CrossStateOptimization::copy() const { | |
77 | ✗ | CrossStateOptimization* ptr = new CrossStateOptimization(*this); | |
78 | ✗ | CrossStateOptimizationPtr_t shPtr(ptr); | |
79 | ✗ | ptr->init(shPtr); | |
80 | ✗ | return shPtr; | |
81 | } | ||
82 | |||
83 | struct CrossStateOptimization::GraphSearchData { | ||
84 | StatePtr_t s1, s2; | ||
85 | |||
86 | // Datas for findNextTransitions | ||
87 | struct state_with_depth { | ||
88 | StatePtr_t s; | ||
89 | EdgePtr_t e; | ||
90 | std::size_t l; // depth to root | ||
91 | std::size_t i; // index in parent state_with_depths_t | ||
92 | ✗ | inline state_with_depth() : s(), e(), l(0), i(0) {} | |
93 | ✗ | inline state_with_depth(EdgePtr_t _e, std::size_t _l, std::size_t _i) | |
94 | ✗ | : s(_e->stateFrom()), e(_e), l(_l), i(_i) {} | |
95 | }; | ||
96 | typedef std::vector<state_with_depth> state_with_depths_t; | ||
97 | typedef std::map<StatePtr_t, state_with_depths_t> StateMap_t; | ||
98 | /// std::size_t is the index in state_with_depths_t at StateMap_t::iterator | ||
99 | struct state_with_depth_ptr_t { | ||
100 | StateMap_t::iterator state; | ||
101 | std::size_t parentIdx; | ||
102 | ✗ | state_with_depth_ptr_t(const StateMap_t::iterator& it, std::size_t idx) | |
103 | ✗ | : state(it), parentIdx(idx) {} | |
104 | }; | ||
105 | typedef std::queue<state_with_depth_ptr_t> Queue_t; | ||
106 | typedef std::set<EdgePtr_t> VisitedEdge_t; | ||
107 | std::size_t maxDepth; | ||
108 | StateMap_t parent1; // TODO, parent2; | ||
109 | Queue_t queue1; | ||
110 | VisitedEdge_t visitedEdge_; | ||
111 | |||
112 | ✗ | const state_with_depth& getParent(const state_with_depth_ptr_t& _p) const { | |
113 | ✗ | const state_with_depths_t& parents = _p.state->second; | |
114 | ✗ | return parents[_p.parentIdx]; | |
115 | } | ||
116 | |||
117 | ✗ | state_with_depth_ptr_t addInitState() { | |
118 | StateMap_t::iterator next = | ||
119 | ✗ | parent1.insert(StateMap_t::value_type(s1, state_with_depths_t(1))) | |
120 | ✗ | .first; | |
121 | ✗ | return state_with_depth_ptr_t(next, 0); | |
122 | } | ||
123 | |||
124 | ✗ | state_with_depth_ptr_t addParent(const state_with_depth_ptr_t& _p, | |
125 | const EdgePtr_t& transition) { | ||
126 | ✗ | const state_with_depths_t& parents = _p.state->second; | |
127 | ✗ | const state_with_depth& from = parents[_p.parentIdx]; | |
128 | |||
129 | // Insert state to if necessary | ||
130 | StateMap_t::iterator next = | ||
131 | parent1 | ||
132 | ✗ | .insert(StateMap_t::value_type(transition->stateTo(), | |
133 | ✗ | state_with_depths_t())) | |
134 | ✗ | .first; | |
135 | |||
136 | ✗ | next->second.push_back( | |
137 | ✗ | state_with_depth(transition, from.l + 1, _p.parentIdx)); | |
138 | |||
139 | ✗ | return state_with_depth_ptr_t(next, next->second.size() - 1); | |
140 | } | ||
141 | }; | ||
142 | |||
143 | ✗ | void CrossStateOptimization::gatherGraphConstraints() { | |
144 | typedef graph::Edge Edge; | ||
145 | typedef graph::EdgePtr_t EdgePtr_t; | ||
146 | typedef graph::GraphPtr_t GraphPtr_t; | ||
147 | typedef constraints::solver::BySubstitution Solver_t; | ||
148 | |||
149 | ✗ | GraphPtr_t cg(problem_->constraintGraph()); | |
150 | ✗ | const ConstraintsAndComplements_t& cac(cg->constraintsAndComplements()); | |
151 | ✗ | for (std::size_t i = 0; i < cg->nbComponents(); ++i) { | |
152 | ✗ | EdgePtr_t edge(HPP_DYNAMIC_PTR_CAST(Edge, cg->get(i).lock())); | |
153 | ✗ | if (edge) { | |
154 | const Solver_t& solver( | ||
155 | ✗ | edge->pathConstraint()->configProjector()->solver()); | |
156 | ✗ | const NumericalConstraints_t& constraints(solver.numericalConstraints()); | |
157 | ✗ | for (NumericalConstraints_t::const_iterator it(constraints.begin()); | |
158 | ✗ | it != constraints.end(); ++it) { | |
159 | ✗ | if ((*it)->parameterSize() > 0) { | |
160 | ✗ | const std::string& name((*it)->function().name()); | |
161 | ✗ | if (index_.find(name) == index_.end()) { | |
162 | // constraint is not in map, add it | ||
163 | ✗ | index_[name] = constraints_.size(); | |
164 | // Check whether constraint is equivalent to a previous one | ||
165 | ✗ | for (NumericalConstraints_t::const_iterator it1( | |
166 | ✗ | constraints_.begin()); | |
167 | ✗ | it1 != constraints_.end(); ++it1) { | |
168 | ✗ | for (ConstraintsAndComplements_t::const_iterator it2(cac.begin()); | |
169 | ✗ | it2 != cac.end(); ++it2) { | |
170 | ✗ | if (((**it1 == *(it2->complement)) && (**it == *(it2->both))) || | |
171 | ✗ | ((**it1 == *(it2->both)) && (**it == *(it2->complement)))) { | |
172 | ✗ | assert(sameRightHandSide_.count(*it1) == 0); | |
173 | ✗ | assert(sameRightHandSide_.count(*it) == 0); | |
174 | ✗ | sameRightHandSide_[*it1] = *it; | |
175 | ✗ | sameRightHandSide_[*it] = *it1; | |
176 | } | ||
177 | } | ||
178 | } | ||
179 | ✗ | constraints_.push_back(*it); | |
180 | hppDout(info, "Adding constraint \"" << name << "\""); | ||
181 | hppDout(info, "Edge \"" << edge->name() << "\""); | ||
182 | hppDout(info, "parameter size: " << (*it)->parameterSize()); | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | |||
190 | ✗ | bool CrossStateOptimization::findTransitions(GraphSearchData& d) const { | |
191 | ✗ | while (!d.queue1.empty()) { | |
192 | ✗ | GraphSearchData::state_with_depth_ptr_t _state = d.queue1.front(); | |
193 | |||
194 | ✗ | const GraphSearchData::state_with_depth& parent = d.getParent(_state); | |
195 | ✗ | if (parent.l >= d.maxDepth) return true; | |
196 | ✗ | d.queue1.pop(); | |
197 | |||
198 | ✗ | bool done = false; | |
199 | |||
200 | ✗ | const Neighbors_t& neighbors = _state.state->first->neighbors(); | |
201 | ✗ | for (Neighbors_t::const_iterator _n = neighbors.begin(); | |
202 | ✗ | _n != neighbors.end(); ++_n) { | |
203 | ✗ | EdgePtr_t transition = _n->second; | |
204 | |||
205 | // Avoid identical consecutive transition | ||
206 | ✗ | if (transition == parent.e) continue; | |
207 | |||
208 | // If transition has already been visited, continue | ||
209 | // if (d.visitedEdge_.count (transition) == 1) continue; | ||
210 | |||
211 | // TODO | ||
212 | // If (transition->to() == d.s2) check if this list is feasible. | ||
213 | // - If a constraint with non-constant right hand side is present | ||
214 | // in all transitions, then the rhs from d.q1 and d.q2 should be | ||
215 | // equal | ||
216 | |||
217 | // Insert parent | ||
218 | ✗ | d.queue1.push(d.addParent(_state, transition)); | |
219 | |||
220 | ✗ | done = done || (transition->stateTo() == d.s2); | |
221 | } | ||
222 | ✗ | if (done) break; | |
223 | } | ||
224 | ✗ | return false; | |
225 | } | ||
226 | |||
227 | ✗ | Edges_t CrossStateOptimization::getTransitionList(GraphSearchData& d, | |
228 | const std::size_t& i) const { | ||
229 | ✗ | assert(d.parent1.find(d.s2) != d.parent1.end()); | |
230 | ✗ | const GraphSearchData::state_with_depths_t& roots = d.parent1[d.s2]; | |
231 | ✗ | Edges_t transitions; | |
232 | ✗ | if (i >= roots.size()) return transitions; | |
233 | |||
234 | ✗ | const GraphSearchData::state_with_depth* current = &roots[i]; | |
235 | ✗ | transitions.reserve(current->l); | |
236 | ✗ | graph::WaypointEdgePtr_t we; | |
237 | ✗ | while (current->e) { | |
238 | ✗ | assert(current->l > 0); | |
239 | ✗ | we = HPP_DYNAMIC_PTR_CAST(graph::WaypointEdge, current->e); | |
240 | ✗ | if (we) { | |
241 | ✗ | for (int i = (int)we->nbWaypoints(); i >= 0; --i) | |
242 | ✗ | transitions.push_back(we->waypoint(i)); | |
243 | } else { | ||
244 | ✗ | transitions.push_back(current->e); | |
245 | } | ||
246 | ✗ | current = &d.parent1[current->s][current->i]; | |
247 | } | ||
248 | ✗ | std::reverse(transitions.begin(), transitions.end()); | |
249 | ✗ | return transitions; | |
250 | } | ||
251 | |||
252 | namespace internal { | ||
253 | ✗ | bool saturate(const core::DevicePtr_t& robot, vectorIn_t q, vectorOut_t qSat, | |
254 | pinocchio::ArrayXb& saturatedDof) { | ||
255 | ✗ | qSat = q; | |
256 | ✗ | return hpp::pinocchio::saturate(robot, qSat, saturatedDof); | |
257 | } | ||
258 | } // namespace internal | ||
259 | |||
260 | struct CrossStateOptimization::OptimizationData { | ||
261 | typedef constraints::solver::HierarchicalIterative::Saturation_t Saturation_t; | ||
262 | enum RightHandSideStatus_t { | ||
263 | // Constraint is not in solver for this waypoint | ||
264 | ABSENT, | ||
265 | // right hand side of constraint for this waypoint is equal to | ||
266 | // right hand side for previous waypoint | ||
267 | EQUAL_TO_PREVIOUS, | ||
268 | // right hand side of constraint for this waypoint is equal to | ||
269 | // right hand side for initial configuration | ||
270 | EQUAL_TO_INIT, | ||
271 | // right hand side of constraint for this waypoint is equal to | ||
272 | // right hand side for goal configuration | ||
273 | EQUAL_TO_GOAL | ||
274 | }; // enum RightHandSideStatus_t | ||
275 | const std::size_t N, nq, nv; | ||
276 | std::vector<Solver_t> solvers; | ||
277 | // Waypoints lying in each intermediate state | ||
278 | matrix_t waypoint; | ||
279 | // Initial guess of each solver stored as matrix columns | ||
280 | matrix_t qInit; | ||
281 | Configuration_t q1, q2; | ||
282 | core::DevicePtr_t robot; | ||
283 | // Matrix specifying for each constraint and each waypoint how | ||
284 | // the right hand side is initialized in the solver. | ||
285 | Eigen::Matrix<LiegroupElement, Eigen::Dynamic, Eigen::Dynamic> M_rhs; | ||
286 | Eigen::Matrix<RightHandSideStatus_t, Eigen::Dynamic, Eigen::Dynamic> M_status; | ||
287 | // Number of trials to generate each waypoint configuration | ||
288 | ✗ | OptimizationData(const core::ProblemConstPtr_t _problem, | |
289 | const Configuration_t& _q1, const Configuration_t& _q2, | ||
290 | const Edges_t& transitions) | ||
291 | ✗ | : N(transitions.size() - 1), | |
292 | ✗ | nq(_problem->robot()->configSize()), | |
293 | ✗ | nv(_problem->robot()->numberDof()), | |
294 | ✗ | solvers(N, _problem->robot()->configSpace()), | |
295 | ✗ | waypoint(nq, N), | |
296 | ✗ | qInit(nq, N), | |
297 | ✗ | q1(_q1), | |
298 | ✗ | q2(_q2), | |
299 | ✗ | robot(_problem->robot()), | |
300 | ✗ | M_rhs(), | |
301 | ✗ | M_status() { | |
302 | ✗ | for (auto solver : solvers) { | |
303 | // Set maximal number of iterations for each solver | ||
304 | ✗ | solver.maxIterations( | |
305 | ✗ | _problem->getParameter("CrossStateOptimization/maxIteration") | |
306 | .intValue()); | ||
307 | // Set error threshold for each solver | ||
308 | ✗ | solver.errorThreshold( | |
309 | ✗ | _problem->getParameter("CrossStateOptimization/errorThreshold") | |
310 | ✗ | .floatValue()); | |
311 | } | ||
312 | ✗ | assert(transitions.size() > 0); | |
313 | } | ||
314 | }; | ||
315 | |||
316 | ✗ | bool CrossStateOptimization::checkConstantRightHandSide(OptimizationData& d, | |
317 | size_type index) const { | ||
318 | ✗ | const ImplicitPtr_t c(constraints_[index]); | |
319 | ✗ | LiegroupElement rhsInit(c->function().outputSpace()); | |
320 | ✗ | c->rightHandSideFromConfig(d.q1, rhsInit); | |
321 | ✗ | LiegroupElement rhsGoal(c->function().outputSpace()); | |
322 | ✗ | c->rightHandSideFromConfig(d.q2, rhsGoal); | |
323 | // Check that right hand sides are close to each other | ||
324 | ✗ | value_type eps(problem_->constraintGraph()->errorThreshold()); | |
325 | ✗ | value_type eps2(eps * eps); | |
326 | ✗ | if ((rhsGoal - rhsInit).squaredNorm() > eps2) { | |
327 | ✗ | return false; | |
328 | } | ||
329 | // Matrix of solver right hand sides | ||
330 | ✗ | for (size_type j = 0; j < d.M_rhs.cols(); ++j) { | |
331 | ✗ | d.M_rhs(index, j) = rhsInit; | |
332 | } | ||
333 | ✗ | return true; | |
334 | } | ||
335 | |||
336 | ✗ | void displayRhsMatrix( | |
337 | const Eigen::Matrix<vector_t, Eigen::Dynamic, Eigen::Dynamic>& m, | ||
338 | const NumericalConstraints_t& constraints) { | ||
339 | ✗ | std::ostringstream oss; | |
340 | ✗ | oss.precision(5); | |
341 | ✗ | oss << "\\documentclass[12pt,landscape]{article}" << std::endl; | |
342 | ✗ | oss << "\\usepackage[a3paper]{geometry}" << std::endl; | |
343 | ✗ | oss << "\\begin {document}" << std::endl; | |
344 | ✗ | oss << "\\begin {tabular}{"; | |
345 | ✗ | for (size_type j = 0; j < m.cols() + 1; ++j) oss << "c"; | |
346 | ✗ | oss << "}" << std::endl; | |
347 | ✗ | for (size_type i = 0; i < m.rows(); ++i) { | |
348 | ✗ | oss << constraints[i]->function().name() << " & "; | |
349 | ✗ | for (size_type j = 0; j < m.cols(); ++j) { | |
350 | ✗ | oss << "$\\left(\\begin{array}{c}" << std::endl; | |
351 | ✗ | for (size_type k = 0; k < m(i, j).size(); ++k) { | |
352 | ✗ | oss << m(i, j)[k] << "\\\\" << std::endl; | |
353 | } | ||
354 | ✗ | oss << "\\end{array}\\right)$" << std::endl; | |
355 | ✗ | if (j < m.cols() - 1) { | |
356 | ✗ | oss << " & " << std::endl; | |
357 | } | ||
358 | } | ||
359 | ✗ | oss << "\\\\" << std::endl; | |
360 | } | ||
361 | ✗ | oss << "\\end{tabular}" << std::endl; | |
362 | ✗ | oss << "\\end {document}" << std::endl; | |
363 | hppDout(info, oss.str()); | ||
364 | } | ||
365 | |||
366 | ✗ | void displayStatusMatrix( | |
367 | const Eigen::Matrix< | ||
368 | CrossStateOptimization::OptimizationData::RightHandSideStatus_t, | ||
369 | Eigen::Dynamic, Eigen::Dynamic>& m, | ||
370 | const NumericalConstraints_t& constraints, | ||
371 | const graph::Edges_t& transitions) { | ||
372 | ✗ | std::ostringstream oss; | |
373 | ✗ | oss.precision(5); | |
374 | ✗ | oss << "\\documentclass[12pt,landscape]{article}" << std::endl; | |
375 | ✗ | oss << "\\usepackage[a3paper]{geometry}" << std::endl; | |
376 | ✗ | oss << "\\begin {document}" << std::endl; | |
377 | ✗ | oss << "\\paragraph{Edges}" << std::endl; | |
378 | ✗ | oss << "\\begin{enumerate}" << std::endl; | |
379 | ✗ | for (auto edge : transitions) { | |
380 | ✗ | oss << "\\item " << edge->name() << std::endl; | |
381 | } | ||
382 | ✗ | oss << "\\end{enumerate}" << std::endl; | |
383 | ✗ | oss << "\\begin {tabular}{"; | |
384 | ✗ | for (size_type j = 0; j < m.cols() + 1; ++j) oss << "c"; | |
385 | ✗ | oss << "}" << std::endl; | |
386 | ✗ | for (size_type i = 0; i < m.rows(); ++i) { | |
387 | ✗ | oss << constraints[i]->function().name() << " & "; | |
388 | ✗ | for (size_type j = 0; j < m.cols(); ++j) { | |
389 | ✗ | oss << m(i, j); | |
390 | ✗ | if (j < m.cols() - 1) { | |
391 | ✗ | oss << " & " << std::endl; | |
392 | } | ||
393 | } | ||
394 | ✗ | oss << "\\\\" << std::endl; | |
395 | } | ||
396 | ✗ | oss << "\\end{tabular}" << std::endl; | |
397 | ✗ | oss << "\\end {document}" << std::endl; | |
398 | hppDout(info, oss.str()); | ||
399 | } | ||
400 | |||
401 | ✗ | bool CrossStateOptimization::contains(const Solver_t& solver, | |
402 | const ImplicitPtr_t& c) const { | ||
403 | ✗ | if (solver.contains(c)) return true; | |
404 | std::map<ImplicitPtr_t, ImplicitPtr_t>::const_iterator it( | ||
405 | ✗ | sameRightHandSide_.find(c)); | |
406 | ✗ | if ((it != sameRightHandSide_.end() && solver.contains(it->second))) { | |
407 | ✗ | return true; | |
408 | } | ||
409 | ✗ | return false; | |
410 | } | ||
411 | |||
412 | ✗ | bool CrossStateOptimization::buildOptimizationProblem( | |
413 | OptimizationData& d, const graph::Edges_t& transitions) const { | ||
414 | ✗ | if (d.N == 0) return true; | |
415 | ✗ | d.M_status.resize(constraints_.size(), d.N); | |
416 | ✗ | d.M_status.fill(OptimizationData::ABSENT); | |
417 | ✗ | d.M_rhs.resize(constraints_.size(), d.N); | |
418 | ✗ | d.M_rhs.fill(LiegroupElement()); | |
419 | ✗ | size_type index = 0; | |
420 | // Loop over constraints | ||
421 | ✗ | for (NumericalConstraints_t::const_iterator it(constraints_.begin()); | |
422 | ✗ | it != constraints_.end(); ++it) { | |
423 | ✗ | const ImplicitPtr_t& c(*it); | |
424 | // Loop forward over waypoints to determine right hand sides equal | ||
425 | // to initial configuration | ||
426 | ✗ | for (std::size_t j = 0; j < d.N; ++j) { | |
427 | // Get transition solver | ||
428 | const Solver_t& trSolver( | ||
429 | ✗ | transitions[j]->pathConstraint()->configProjector()->solver()); | |
430 | ✗ | if (contains(trSolver, c)) { | |
431 | ✗ | if ((j == 0) || | |
432 | ✗ | d.M_status(index, j - 1) == OptimizationData::EQUAL_TO_INIT) { | |
433 | ✗ | d.M_status(index, j) = OptimizationData::EQUAL_TO_INIT; | |
434 | } else { | ||
435 | ✗ | d.M_status(index, j) = OptimizationData::EQUAL_TO_PREVIOUS; | |
436 | } | ||
437 | } | ||
438 | } | ||
439 | // Loop backward over waypoints to determine right hand sides equal | ||
440 | // to final configuration | ||
441 | ✗ | for (size_type j = d.N - 1; j > 0; --j) { | |
442 | // Get transition solver | ||
443 | ✗ | const Solver_t& trSolver(transitions[(std::size_t)j + 1] | |
444 | ✗ | ->pathConstraint() | |
445 | ✗ | ->configProjector() | |
446 | ✗ | ->solver()); | |
447 | ✗ | if (contains(trSolver, c)) { | |
448 | ✗ | if ((j == (size_type)d.N - 1) || | |
449 | ✗ | d.M_status(index, j + 1) == OptimizationData::EQUAL_TO_GOAL) { | |
450 | // If constraint right hand side is already equal to | ||
451 | // initial config, check that right hand side is equal | ||
452 | // for init and goal configs. | ||
453 | ✗ | if (d.M_status(index, j) == OptimizationData::EQUAL_TO_INIT) { | |
454 | ✗ | if (checkConstantRightHandSide(d, index)) { | |
455 | // stop for this constraint | ||
456 | ✗ | break; | |
457 | } else { | ||
458 | // Right hand side of constraint should be equal along the | ||
459 | // whole path but is different at init and at goal configs. | ||
460 | ✗ | return false; | |
461 | } | ||
462 | } else { | ||
463 | ✗ | d.M_status(index, j) = OptimizationData::EQUAL_TO_GOAL; | |
464 | } | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | ✗ | ++index; | |
469 | } // for (NumericalConstraints_t::const_iterator it | ||
470 | ✗ | displayStatusMatrix(d.M_status, constraints_, transitions); | |
471 | ✗ | graph::GraphPtr_t cg(problem_->constraintGraph()); | |
472 | // Fill solvers with target constraints of transition | ||
473 | ✗ | for (std::size_t j = 0; j < d.N; ++j) { | |
474 | ✗ | d.solvers[(std::size_t)j] = transitions[(std::size_t)j] | |
475 | ✗ | ->targetConstraint() | |
476 | ✗ | ->configProjector() | |
477 | ✗ | ->solver(); | |
478 | } | ||
479 | // Initial guess | ||
480 | ✗ | std::vector<size_type> ks; | |
481 | ✗ | size_type K = 0; | |
482 | ✗ | ks.resize(d.N); | |
483 | ✗ | for (std::size_t i = 0; i < d.N + 1; ++i) { | |
484 | ✗ | if (!transitions[i]->isShort()) ++K; | |
485 | ✗ | if (i < d.N) ks[i] = K; | |
486 | } | ||
487 | ✗ | if (K == 0) { | |
488 | ✗ | ++K; | |
489 | ✗ | for (std::size_t i = d.N / 2; i < d.N; ++i) ks[i] = 1; | |
490 | } | ||
491 | ✗ | for (std::size_t i = 0; i < d.N; ++i) { | |
492 | ✗ | value_type u = value_type(ks[i]) / value_type(K); | |
493 | ✗ | pinocchio::interpolate(d.robot, d.q1, d.q2, u, d.qInit.col(i)); | |
494 | hppDout(info, "qInit = " << pinocchio::displayConfig(d.qInit.col(i))); | ||
495 | } | ||
496 | |||
497 | ✗ | return true; | |
498 | } | ||
499 | |||
500 | ✗ | bool CrossStateOptimization::solveOptimizationProblem( | |
501 | OptimizationData& d) const { | ||
502 | // Iterate on waypoint solvers, for each of them | ||
503 | // 1. initialize right hand side, | ||
504 | // 2. solve. | ||
505 | ✗ | for (std::size_t j = 0; j < d.solvers.size(); ++j) { | |
506 | ✗ | Solver_t& solver(d.solvers[j]); | |
507 | ✗ | for (std::size_t i = 0; i < constraints_.size(); ++i) { | |
508 | ✗ | const ImplicitPtr_t& c(constraints_[i]); | |
509 | ✗ | switch (d.M_status((size_type)i, (size_type)j)) { | |
510 | ✗ | case OptimizationData::EQUAL_TO_PREVIOUS: | |
511 | ✗ | assert(j != 0); | |
512 | ✗ | solver.rightHandSideFromConfig(c, d.waypoint.col(j - 1)); | |
513 | ✗ | break; | |
514 | ✗ | case OptimizationData::EQUAL_TO_INIT: | |
515 | ✗ | solver.rightHandSideFromConfig(c, d.q1); | |
516 | ✗ | break; | |
517 | ✗ | case OptimizationData::EQUAL_TO_GOAL: | |
518 | ✗ | solver.rightHandSideFromConfig(c, d.q2); | |
519 | ✗ | break; | |
520 | ✗ | case OptimizationData::ABSENT: | |
521 | default:; | ||
522 | } | ||
523 | } | ||
524 | ✗ | if (j == 0) | |
525 | ✗ | d.waypoint.col(j) = d.qInit.col(j); | |
526 | else | ||
527 | ✗ | d.waypoint.col(j) = d.waypoint.col(j - 1); | |
528 | ✗ | Solver_t::Status status = solver.solve( | |
529 | ✗ | d.waypoint.col(j), constraints::solver::lineSearch::Backtracking()); | |
530 | ✗ | size_type nbTry = 0; | |
531 | size_type nRandomConfigs( | ||
532 | ✗ | problem() | |
533 | ✗ | ->getParameter("CrossStateOptimization/nRandomConfigs") | |
534 | ✗ | .intValue()); | |
535 | |||
536 | ✗ | while (status != Solver_t::SUCCESS && nbTry < nRandomConfigs) { | |
537 | ✗ | d.waypoint.col(j) = problem()->configurationShooter()->shoot(); | |
538 | ✗ | status = solver.solve(d.waypoint.col(j), | |
539 | ✗ | constraints::solver::lineSearch::Backtracking()); | |
540 | ✗ | ++nbTry; | |
541 | } | ||
542 | ✗ | switch (status) { | |
543 | ✗ | case Solver_t::ERROR_INCREASED: | |
544 | hppDout(info, "error increased."); | ||
545 | ✗ | return false; | |
546 | ✗ | case Solver_t::MAX_ITERATION_REACHED: | |
547 | hppDout(info, "max iteration reached."); | ||
548 | ✗ | return false; | |
549 | ✗ | case Solver_t::INFEASIBLE: | |
550 | hppDout(info, "infeasible."); | ||
551 | ✗ | return false; | |
552 | ✗ | case Solver_t::SUCCESS: | |
553 | hppDout(info, "success."); | ||
554 | } | ||
555 | } | ||
556 | ✗ | return true; | |
557 | } | ||
558 | |||
559 | ✗ | core::PathVectorPtr_t CrossStateOptimization::buildPath( | |
560 | OptimizationData& d, const Edges_t& transitions) const { | ||
561 | using core::PathVector; | ||
562 | using core::PathVectorPtr_t; | ||
563 | |||
564 | ✗ | const core::DevicePtr_t& robot = problem()->robot(); | |
565 | PathVectorPtr_t pv = | ||
566 | ✗ | PathVector::create(robot->configSize(), robot->numberDof()); | |
567 | ✗ | core::PathPtr_t path; | |
568 | |||
569 | ✗ | std::size_t i = 0; | |
570 | ✗ | for (Edges_t::const_iterator _t = transitions.begin(); | |
571 | ✗ | _t != transitions.end(); ++_t) { | |
572 | ✗ | const EdgePtr_t& t = *_t; | |
573 | ✗ | bool first = (i == 0); | |
574 | ✗ | bool last = (i == d.N); | |
575 | |||
576 | bool status; | ||
577 | ✗ | if (first && last) | |
578 | ✗ | status = t->build(path, d.q1, d.q2); | |
579 | ✗ | else if (first) | |
580 | ✗ | status = t->build(path, d.q1, d.waypoint.col(i)); | |
581 | ✗ | else if (last) | |
582 | ✗ | status = t->build(path, d.waypoint.col(i - 1), d.q2); | |
583 | else { | ||
584 | ✗ | status = t->build(path, d.waypoint.col(i - 1), d.waypoint.col(i)); | |
585 | } | ||
586 | |||
587 | ✗ | if (!status || !path) { | |
588 | hppDout(warning, "Could not build path from solution "); | ||
589 | ✗ | return PathVectorPtr_t(); | |
590 | } | ||
591 | ✗ | pv->appendPath(path); | |
592 | |||
593 | ✗ | ++i; | |
594 | } | ||
595 | ✗ | return pv; | |
596 | } | ||
597 | |||
598 | ✗ | core::PathPtr_t CrossStateOptimization::impl_compute( | |
599 | ConfigurationIn_t q1, ConfigurationIn_t q2) const { | ||
600 | ✗ | const graph::GraphPtr_t& graph(problem_->constraintGraph()); | |
601 | ✗ | GraphSearchData d; | |
602 | ✗ | d.s1 = graph->getState(q1); | |
603 | ✗ | d.s2 = graph->getState(q2); | |
604 | // d.maxDepth = 2; | ||
605 | ✗ | d.maxDepth = | |
606 | ✗ | problem_->getParameter("CrossStateOptimization/maxDepth").intValue(); | |
607 | |||
608 | // Find | ||
609 | ✗ | d.queue1.push(d.addInitState()); | |
610 | ✗ | std::size_t idxSol = (d.s1 == d.s2 ? 1 : 0); | |
611 | ✗ | bool maxDepthReached = findTransitions(d); | |
612 | |||
613 | ✗ | while (!maxDepthReached) { | |
614 | ✗ | Edges_t transitions = getTransitionList(d, idxSol); | |
615 | ✗ | while (!transitions.empty()) { | |
616 | #ifdef HPP_DEBUG | ||
617 | std::ostringstream ss; | ||
618 | ss << "Trying solution " << idxSol << ": "; | ||
619 | for (std::size_t j = 0; j < transitions.size(); ++j) | ||
620 | ss << transitions[j]->name() << ", "; | ||
621 | hppDout(info, ss.str()); | ||
622 | #endif // HPP_DEBUG | ||
623 | |||
624 | ✗ | OptimizationData optData(problem(), q1, q2, transitions); | |
625 | ✗ | if (buildOptimizationProblem(optData, transitions)) { | |
626 | ✗ | if (solveOptimizationProblem(optData)) { | |
627 | ✗ | core::PathPtr_t path = buildPath(optData, transitions); | |
628 | ✗ | if (path) return path; | |
629 | hppDout(info, "Failed to build path from solution: "); | ||
630 | ✗ | } else { | |
631 | hppDout(info, "Failed to solve"); | ||
632 | } | ||
633 | } | ||
634 | ✗ | ++idxSol; | |
635 | ✗ | transitions = getTransitionList(d, idxSol); | |
636 | } | ||
637 | ✗ | maxDepthReached = findTransitions(d); | |
638 | } | ||
639 | |||
640 | ✗ | return core::PathPtr_t(); | |
641 | } | ||
642 | |||
643 | using core::Parameter; | ||
644 | using core::ParameterDescription; | ||
645 | |||
646 | 1 | HPP_START_PARAMETER_DECLARATION(CrossStateOptimization) | |
647 |
4/8✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
|
1 | core::Problem::declareParameter(ParameterDescription( |
648 | Parameter::INT, "CrossStateOptimization/maxDepth", | ||
649 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | "Maximum number of transitions to look for.", Parameter((size_type)2))); |
650 |
4/8✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
|
1 | core::Problem::declareParameter(ParameterDescription( |
651 | Parameter::INT, "CrossStateOptimization/maxIteration", | ||
652 | "Maximum number of iterations of the Newton Raphson algorithm.", | ||
653 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | Parameter((size_type)60))); |
654 |
4/8✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
|
1 | core::Problem::declareParameter(ParameterDescription( |
655 | Parameter::FLOAT, "CrossStateOptimization/errorThreshold", | ||
656 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | "Error threshold of the Newton Raphson algorithm.", Parameter(1e-4))); |
657 |
4/8✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
|
1 | core::Problem::declareParameter(ParameterDescription( |
658 | Parameter::INT, "CrossStateOptimization/nRandomConfigs", | ||
659 | "Number of random configurations to sample to initialize each " | ||
660 | "solver.", | ||
661 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | Parameter((size_type)0))); |
662 | 1 | HPP_END_PARAMETER_DECLARATION(CrossStateOptimization) | |
663 | } // namespace steeringMethod | ||
664 | } // namespace manipulation | ||
665 | } // namespace hpp | ||
666 |