Line |
Branch |
Exec |
Source |
1 |
|
|
// Copyright (c) 2014, LAAS-CNRS |
2 |
|
|
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr) |
3 |
|
|
// |
4 |
|
|
|
5 |
|
|
// Redistribution and use in source and binary forms, with or without |
6 |
|
|
// modification, are permitted provided that the following conditions are |
7 |
|
|
// met: |
8 |
|
|
// |
9 |
|
|
// 1. Redistributions of source code must retain the above copyright |
10 |
|
|
// notice, this list of conditions and the following disclaimer. |
11 |
|
|
// |
12 |
|
|
// 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
// notice, this list of conditions and the following disclaimer in the |
14 |
|
|
// documentation and/or other materials provided with the distribution. |
15 |
|
|
// |
16 |
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 |
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 |
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 |
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
20 |
|
|
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 |
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 |
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 |
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 |
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 |
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 |
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
27 |
|
|
// DAMAGE. |
28 |
|
|
|
29 |
|
|
#include "hpp/manipulation/graph-path-validation.hh" |
30 |
|
|
|
31 |
|
|
#include <hpp/core/path-vector.hh> |
32 |
|
|
#include <hpp/core/path.hh> |
33 |
|
|
#include <hpp/manipulation/constraint-set.hh> |
34 |
|
|
#include <hpp/manipulation/graph/edge.hh> |
35 |
|
|
#include <hpp/manipulation/graph/graph.hh> |
36 |
|
|
#include <hpp/manipulation/graph/state.hh> |
37 |
|
|
#include <hpp/pinocchio/configuration.hh> |
38 |
|
|
|
39 |
|
|
#ifdef HPP_DEBUG |
40 |
|
|
#include <hpp/manipulation/graph/state.hh> |
41 |
|
|
#endif |
42 |
|
|
|
43 |
|
|
namespace hpp { |
44 |
|
|
namespace manipulation { |
45 |
|
✗ |
GraphPathValidationPtr_t GraphPathValidation::create( |
46 |
|
|
const PathValidationPtr_t& pathValidation) { |
47 |
|
✗ |
GraphPathValidation* p = new GraphPathValidation(pathValidation); |
48 |
|
✗ |
return GraphPathValidationPtr_t(p); |
49 |
|
|
} |
50 |
|
|
|
51 |
|
✗ |
GraphPathValidation::GraphPathValidation( |
52 |
|
✗ |
const PathValidationPtr_t& pathValidation) |
53 |
|
✗ |
: pathValidation_(pathValidation), constraintGraph_() {} |
54 |
|
|
|
55 |
|
✗ |
bool GraphPathValidation::validate(const PathPtr_t& path, bool reverse, |
56 |
|
|
PathPtr_t& validPart, |
57 |
|
|
PathValidationReportPtr_t& report) { |
58 |
|
✗ |
assert(path); |
59 |
|
✗ |
bool success = impl_validate(path, reverse, validPart, report); |
60 |
|
✗ |
assert(constraintGraph_); |
61 |
|
✗ |
assert(constraintGraph_->getState(validPart->initial())); |
62 |
|
✗ |
assert(constraintGraph_->getState(validPart->end())); |
63 |
|
✗ |
return success; |
64 |
|
|
} |
65 |
|
|
|
66 |
|
✗ |
bool GraphPathValidation::impl_validate(const PathVectorPtr_t& path, |
67 |
|
|
bool reverse, PathPtr_t& validPart, |
68 |
|
|
PathValidationReportPtr_t& report) { |
69 |
|
✗ |
PathPtr_t validSubPart; |
70 |
|
✗ |
if (reverse) { |
71 |
|
|
// TODO: This has never been tested. |
72 |
|
✗ |
assert(!reverse && "This has never been tested with reverse path"); |
73 |
|
✗ |
for (long int i = path->numberPaths() - 1; i >= 0; i--) { |
74 |
|
|
// We should stop at the first non valid subpath. |
75 |
|
✗ |
if (!impl_validate(path->pathAtRank(i), true, validSubPart, report)) { |
76 |
|
✗ |
PathVectorPtr_t p = PathVector::create(path->outputSize(), |
77 |
|
✗ |
path->outputDerivativeSize()); |
78 |
|
✗ |
for (long int v = path->numberPaths() - 1; v > i; v--) |
79 |
|
✗ |
p->appendPath(path->pathAtRank(i)->copy()); |
80 |
|
|
// TODO: Make sure this subpart is generated by the steering method. |
81 |
|
✗ |
p->appendPath(validSubPart); |
82 |
|
✗ |
validPart = p; |
83 |
|
✗ |
return false; |
84 |
|
|
} |
85 |
|
|
} |
86 |
|
|
} else { |
87 |
|
✗ |
for (size_t i = 0; i != path->numberPaths(); i++) { |
88 |
|
|
// We should stop at the first non valid subpath. |
89 |
|
✗ |
if (!impl_validate(path->pathAtRank(i), false, validSubPart, report)) { |
90 |
|
✗ |
PathVectorPtr_t p = PathVector::create(path->outputSize(), |
91 |
|
✗ |
path->outputDerivativeSize()); |
92 |
|
✗ |
for (size_t v = 0; v < i; v++) |
93 |
|
✗ |
p->appendPath(path->pathAtRank(v)->copy()); |
94 |
|
|
// TODO: Make sure this subpart is generated by the steering method. |
95 |
|
✗ |
p->appendPath(validSubPart); |
96 |
|
✗ |
validPart = p; |
97 |
|
✗ |
return false; |
98 |
|
|
} |
99 |
|
|
} |
100 |
|
|
} |
101 |
|
|
// Here, every subpath is valid. |
102 |
|
✗ |
validPart = path; |
103 |
|
✗ |
return true; |
104 |
|
|
} |
105 |
|
|
|
106 |
|
✗ |
bool GraphPathValidation::impl_validate(const PathPtr_t& path, bool reverse, |
107 |
|
|
PathPtr_t& validPart, |
108 |
|
|
PathValidationReportPtr_t& report) { |
109 |
|
|
#ifndef NDEBUG |
110 |
|
|
bool success; |
111 |
|
✗ |
Configuration_t q0 = path->eval(path->timeRange().second, success); |
112 |
|
✗ |
assert(success); |
113 |
|
✗ |
assert(!path->constraints() || path->constraints()->isSatisfied(q0)); |
114 |
|
|
#endif |
115 |
|
|
using pinocchio::displayConfig; |
116 |
|
✗ |
PathVectorPtr_t pathVector = HPP_DYNAMIC_PTR_CAST(PathVector, path); |
117 |
|
✗ |
if (pathVector) return impl_validate(pathVector, reverse, validPart, report); |
118 |
|
|
|
119 |
|
✗ |
PathPtr_t pathNoCollision; |
120 |
|
|
ConstraintSetPtr_t c = |
121 |
|
✗ |
HPP_DYNAMIC_PTR_CAST(ConstraintSet, path->constraints()); |
122 |
|
|
hppDout(info, |
123 |
|
|
(c ? "Using edge path validation" : "Using default path validation")); |
124 |
|
✗ |
PathValidationPtr_t validation(c ? c->edge()->pathValidation() |
125 |
|
✗ |
: pathValidation_); |
126 |
|
|
|
127 |
|
✗ |
if (validation->validate(path, reverse, pathNoCollision, report)) { |
128 |
|
✗ |
validPart = path; |
129 |
|
✗ |
return true; |
130 |
|
|
} |
131 |
|
✗ |
const Path& newPath(*pathNoCollision); |
132 |
|
✗ |
const Path& oldPath(*path); |
133 |
|
✗ |
const core::interval_t &newTR = newPath.timeRange(), |
134 |
|
✗ |
oldTR = oldPath.timeRange(); |
135 |
|
✗ |
Configuration_t q(newPath.outputSize()); |
136 |
|
✗ |
if (!newPath.eval(q, newTR.first)) |
137 |
|
✗ |
throw std::logic_error( |
138 |
|
✗ |
"Initial configuration of the valid part cannot be projected."); |
139 |
|
✗ |
const graph::StatePtr_t& origState = constraintGraph_->getState(q); |
140 |
|
✗ |
if (!newPath.eval(q, newTR.second)) |
141 |
|
✗ |
throw std::logic_error( |
142 |
|
✗ |
"End configuration of the valid part cannot be projected."); |
143 |
|
|
// This may throw in the following case: |
144 |
|
|
// - state constraints: object_placement + other_function |
145 |
|
|
// - path constraints: other_function, object_lock |
146 |
|
|
// This is semantically correct but for a path going from q0 to q1, |
147 |
|
|
// we ensure that |
148 |
|
|
// - object_placement (q0) = eps_place0 |
149 |
|
|
// - other_function (q0) = eps_other0 |
150 |
|
|
// - eps_place0 + eps_other0 < eps |
151 |
|
|
// - other_function (q(s)) < eps |
152 |
|
|
// - object_placement (q(s)) = object_placement (q0) # thanks to the |
153 |
|
|
// object_lock So we only have: |
154 |
|
|
// - other_function (q(s)) + object_placement (q(s)) < eps + eps_place0 |
155 |
|
|
// And not: |
156 |
|
|
// - other_function (q(s)) + object_placement (q(s)) < eps |
157 |
|
|
// In this case, there is no good way to recover. Just return failure. |
158 |
|
✗ |
graph::StatePtr_t destState; |
159 |
|
|
try { |
160 |
|
✗ |
destState = constraintGraph_->getState(q); |
161 |
|
✗ |
} catch (const std::logic_error& e) { |
162 |
|
|
ConstraintSetPtr_t c = |
163 |
|
✗ |
HPP_DYNAMIC_PTR_CAST(ConstraintSet, path->constraints()); |
164 |
|
|
hppDout(error, "Edge " << c->edge()->name() |
165 |
|
|
<< " generated an error: " << e.what()); |
166 |
|
|
hppDout(error, |
167 |
|
|
"Likely, the constraints for paths are relaxed. If " |
168 |
|
|
"this problem occurs often, you may want to use the same " |
169 |
|
|
"constraints for state and paths in " |
170 |
|
|
<< c->edge()->state()->name()); |
171 |
|
✗ |
validPart = path->extract(std::make_pair(oldTR.first, oldTR.first)); |
172 |
|
✗ |
return false; |
173 |
|
|
} |
174 |
|
✗ |
if (!oldPath.eval(q, oldTR.first)) { |
175 |
|
✗ |
std::stringstream oss; |
176 |
|
|
oss << "Initial configuration of the path to be validated failed to" |
177 |
|
|
" be projected. After maximal number of iterations, q=" |
178 |
|
✗ |
<< displayConfig(q) << "; error="; |
179 |
|
✗ |
vector_t error; |
180 |
|
✗ |
oldPath.constraints()->isSatisfied(q, error); |
181 |
|
✗ |
oss << displayConfig(error) << "."; |
182 |
|
✗ |
throw std::logic_error(oss.str().c_str()); |
183 |
|
|
} |
184 |
|
✗ |
const graph::StatePtr_t& oldOstate = constraintGraph_->getState(q); |
185 |
|
✗ |
if (!oldPath.eval(q, oldTR.second)) { |
186 |
|
✗ |
std::stringstream oss; |
187 |
|
|
oss << "End configuration of the path to be validated failed to" |
188 |
|
|
" be projected. After maximal number of iterations, q=" |
189 |
|
✗ |
<< displayConfig(q) << "; error="; |
190 |
|
✗ |
vector_t error; |
191 |
|
✗ |
oldPath.constraints()->isSatisfied(q, error); |
192 |
|
✗ |
oss << displayConfig(error) << "."; |
193 |
|
✗ |
throw std::logic_error(oss.str().c_str()); |
194 |
|
|
} |
195 |
|
✗ |
const graph::StatePtr_t& oldDstate = constraintGraph_->getState(q); |
196 |
|
|
|
197 |
|
✗ |
if (origState == oldOstate && destState == oldDstate) { |
198 |
|
✗ |
validPart = pathNoCollision; |
199 |
|
✗ |
return false; |
200 |
|
|
} |
201 |
|
|
|
202 |
|
|
// Here, the full path is not valid. Moreover, it does not correspond to the |
203 |
|
|
// same edge of the constraint graph. Two option are possible: |
204 |
|
|
// - Use the steering method to create a new path and validate it. |
205 |
|
|
// - Return a null path. |
206 |
|
|
// validPart = path->extract (std::make_pair (oldTR.first,oldTR.first)); |
207 |
|
✗ |
validPart = pathNoCollision; |
208 |
|
✗ |
return false; |
209 |
|
|
} |
210 |
|
|
} // namespace manipulation |
211 |
|
|
} // namespace hpp |
212 |
|
|
|