Line |
Branch |
Exec |
Source |
1 |
|
|
// Copyright (c) 2017, Joseph Mirabel |
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/core/path-optimization/linear-constraint.hh> |
30 |
|
|
#include <hpp/manipulation/constraint-set.hh> |
31 |
|
|
#include <hpp/manipulation/graph/edge.hh> |
32 |
|
|
#include <hpp/manipulation/graph/state.hh> |
33 |
|
|
#include <hpp/manipulation/path-optimization/spline-gradient-based.hh> |
34 |
|
|
|
35 |
|
|
namespace hpp { |
36 |
|
|
namespace manipulation { |
37 |
|
|
namespace pathOptimization { |
38 |
|
|
|
39 |
|
|
template <int _PB, int _SO> |
40 |
|
✗ |
SplineGradientBased<_PB, _SO>::SplineGradientBased( |
41 |
|
|
const ProblemConstPtr_t& problem) |
42 |
|
✗ |
: Parent_t(problem) { |
43 |
|
✗ |
this->checkOptimum_ = true; |
44 |
|
|
} |
45 |
|
|
|
46 |
|
|
// ----------- Convenience class -------------------------------------- // |
47 |
|
|
|
48 |
|
|
// ----------- Resolution steps --------------------------------------- // |
49 |
|
|
|
50 |
|
|
template <int _PB, int _SO> |
51 |
|
|
typename SplineGradientBased<_PB, _SO>::Ptr_t |
52 |
|
✗ |
SplineGradientBased<_PB, _SO>::create(const ProblemConstPtr_t& problem) { |
53 |
|
✗ |
SplineGradientBased* ptr = new SplineGradientBased(problem); |
54 |
|
✗ |
Ptr_t shPtr(ptr); |
55 |
|
✗ |
return shPtr; |
56 |
|
|
} |
57 |
|
|
|
58 |
|
|
template <int _PB, int _SO> |
59 |
|
|
typename SplineGradientBased<_PB, _SO>::Ptr_t |
60 |
|
✗ |
SplineGradientBased<_PB, _SO>::createFromCore( |
61 |
|
|
const core::ProblemConstPtr_t& problem) { |
62 |
|
✗ |
if (!HPP_DYNAMIC_PTR_CAST(const Problem, problem)) |
63 |
|
✗ |
throw std::invalid_argument("This is not a manipulation problem."); |
64 |
|
✗ |
return create(HPP_STATIC_PTR_CAST(const Problem, problem)); |
65 |
|
|
} |
66 |
|
|
|
67 |
|
|
template <int _PB, int _SO> |
68 |
|
✗ |
void SplineGradientBased<_PB, _SO>::initializePathValidation( |
69 |
|
|
const Splines_t& splines) { |
70 |
|
✗ |
this->validations_.resize(splines.size()); |
71 |
|
✗ |
for (std::size_t i = 0; i < splines.size(); ++i) { |
72 |
|
✗ |
ConstraintSetPtr_t set = |
73 |
|
✗ |
HPP_STATIC_PTR_CAST(ConstraintSet, splines[i]->constraints()); |
74 |
|
✗ |
if (set && set->edge()) |
75 |
|
✗ |
this->validations_[i] = set->edge()->pathValidation(); |
76 |
|
|
else |
77 |
|
✗ |
this->validations_[i] = this->problem()->pathValidation(); |
78 |
|
|
} |
79 |
|
|
} |
80 |
|
|
|
81 |
|
|
template <int _PB, int _SO> |
82 |
|
✗ |
void SplineGradientBased<_PB, _SO>::addProblemConstraints( |
83 |
|
|
const core::PathVectorPtr_t& init, const Splines_t& splines, |
84 |
|
|
LinearConstraint& lc, SplineOptimizationDatas_t& sods) const { |
85 |
|
✗ |
assert(init->numberPaths() == splines.size() && |
86 |
|
|
sods.size() == splines.size()); |
87 |
|
|
|
88 |
|
|
bool zeroDerivative = |
89 |
|
|
this->problem() |
90 |
|
✗ |
->getParameter( |
91 |
|
|
"SplineGradientBased/zeroDerivativesAtStateIntersection") |
92 |
|
✗ |
.boolValue(); |
93 |
|
|
|
94 |
|
✗ |
const std::size_t last = splines.size() - 1; |
95 |
|
✗ |
graph::StatePtr_t stateOfStart; |
96 |
|
✗ |
for (std::size_t i = 0; i < last; ++i) { |
97 |
|
✗ |
core::PathPtr_t path = init->pathAtRank(i); |
98 |
|
✗ |
ConstraintSetPtr_t set = |
99 |
|
✗ |
HPP_STATIC_PTR_CAST(ConstraintSet, splines[i]->constraints()); |
100 |
|
✗ |
if (!set || !set->edge()) |
101 |
|
✗ |
std::invalid_argument( |
102 |
|
|
"Cannot optimize a path that has not been " |
103 |
|
|
"generated with a graph."); |
104 |
|
✗ |
graph::EdgePtr_t transition = set->edge(); |
105 |
|
|
|
106 |
|
✗ |
this->addProblemConstraintOnPath(path, i, splines[i], lc, sods[i]); |
107 |
|
|
|
108 |
|
|
// The path should always go through the start and end states of the |
109 |
|
|
// transition. |
110 |
|
✗ |
assert(!HPP_DYNAMIC_PTR_CAST(graph::WaypointEdge, transition)); |
111 |
|
✗ |
graph::StatePtr_t from = transition->stateFrom(); |
112 |
|
✗ |
graph::StatePtr_t to = transition->stateTo(); |
113 |
|
✗ |
graph::StatePtr_t from2 = from, to2 = to; |
114 |
|
|
|
115 |
|
✗ |
Configuration_t q0 = path->initial(), q1 = path->end(); |
116 |
|
✗ |
const bool src_contains_q0 = from->contains(q0); |
117 |
|
✗ |
const bool dst_contains_q0 = to->contains(q0); |
118 |
|
✗ |
const bool src_contains_q1 = from->contains(q1); |
119 |
|
✗ |
const bool dst_contains_q1 = to->contains(q1); |
120 |
|
|
|
121 |
|
✗ |
bool use_direct = src_contains_q0 && dst_contains_q1; |
122 |
|
✗ |
bool use_reverse = src_contains_q1 && dst_contains_q0; |
123 |
|
✗ |
if (use_direct && use_reverse) { |
124 |
|
✗ |
if (i == 0 || stateOfStart == from) |
125 |
|
✗ |
use_reverse = false; |
126 |
|
✗ |
else if (stateOfStart == to) |
127 |
|
✗ |
use_direct = false; |
128 |
|
✗ |
else if (stateOfStart) { |
129 |
|
✗ |
if (stateOfStart->contains(q0)) |
130 |
|
✗ |
use_reverse = false; |
131 |
|
|
else |
132 |
|
✗ |
use_direct = false; // assumes stateOfStart->contains(q0) |
133 |
|
|
} else |
134 |
|
✗ |
use_reverse = false; // default if we don't know what to do... |
135 |
|
|
} |
136 |
|
✗ |
if (use_direct) { |
137 |
|
|
// Nominal case |
138 |
|
✗ |
if (transition->state() != to) { |
139 |
|
✗ |
constrainEndIntoState(path, i, splines[i], transition->stateTo(), lc); |
140 |
|
|
} |
141 |
|
✗ |
stateOfStart = to; |
142 |
|
✗ |
} else if (use_reverse) { |
143 |
|
|
// Reversed nominal case |
144 |
|
✗ |
if (transition->state() != from) { |
145 |
|
✗ |
constrainEndIntoState(path, i, splines[i], from, lc); |
146 |
|
|
} |
147 |
|
✗ |
stateOfStart = from; |
148 |
|
|
} else { |
149 |
|
✗ |
if (src_contains_q0) { // q1 must stay in state |
150 |
|
✗ |
to2 = transition->state(); |
151 |
|
✗ |
stateOfStart = to; |
152 |
|
✗ |
} else if (dst_contains_q0) { // q1 must stay in state |
153 |
|
✗ |
from2 = transition->state(); |
154 |
|
✗ |
stateOfStart = from; |
155 |
|
✗ |
} else if (src_contains_q1) { // q1 must stay in src |
156 |
|
✗ |
to2 = transition->state(); |
157 |
|
✗ |
stateOfStart = from; |
158 |
|
✗ |
if (transition->state() != from) { |
159 |
|
✗ |
constrainEndIntoState(path, i, splines[i], from, lc); |
160 |
|
|
} |
161 |
|
✗ |
} else if (dst_contains_q1) { // q1 must stay in dst |
162 |
|
✗ |
from2 = transition->state(); |
163 |
|
✗ |
stateOfStart = to; |
164 |
|
✗ |
if (transition->state() != to) { |
165 |
|
✗ |
constrainEndIntoState(path, i, splines[i], to, lc); |
166 |
|
|
} |
167 |
|
|
} else { |
168 |
|
|
// q0 and q1 are in state. We add no constraint. |
169 |
|
|
hppDout(warning, "Add no constraint for this path."); |
170 |
|
✗ |
from2 = transition->state(); |
171 |
|
✗ |
to2 = transition->state(); |
172 |
|
✗ |
stateOfStart.reset(); |
173 |
|
|
} |
174 |
|
|
} |
175 |
|
✗ |
if (zeroDerivative) { |
176 |
|
✗ |
if (!(use_reverse && src_contains_q0 && src_contains_q1) && |
177 |
|
✗ |
!(use_direct && dst_contains_q0 && dst_contains_q1) && from2 != to2) { |
178 |
|
✗ |
constraintDerivativesAtEndOfSpline(i, splines[i], lc); |
179 |
|
|
} |
180 |
|
|
} |
181 |
|
|
} |
182 |
|
✗ |
this->addProblemConstraintOnPath(init->pathAtRank(last), last, splines[last], |
183 |
|
✗ |
lc, sods[last]); |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
template <int _PB, int _SO> |
187 |
|
✗ |
void SplineGradientBased<_PB, _SO>::constrainEndIntoState( |
188 |
|
|
const core::PathPtr_t& path, const size_type& idxSpline, |
189 |
|
|
const SplinePtr_t& spline, const graph::StatePtr_t state, |
190 |
|
|
LinearConstraint& lc) const { |
191 |
|
✗ |
typename Spline::BasisFunctionVector_t B1; |
192 |
|
✗ |
spline->basisFunctionDerivative(0, 1, B1); |
193 |
|
|
// TODO Should we add zero velocity sometimes ? |
194 |
|
|
|
195 |
|
✗ |
ConstraintSetPtr_t set = state->configConstraint(); |
196 |
|
✗ |
value_type guessThreshold = |
197 |
|
|
this->problem() |
198 |
|
✗ |
->getParameter("SplineGradientBased/guessThreshold") |
199 |
|
✗ |
.floatValue(); |
200 |
|
✗ |
Eigen::RowBlockIndices select = this->computeActiveParameters( |
201 |
|
✗ |
path, set->configProjector()->solver(), guessThreshold, true); |
202 |
|
|
hppDout(info, |
203 |
|
|
"End of path " << idxSpline << ": do not change this dof " << select); |
204 |
|
|
hppDout(info, "End of path " << idxSpline << ": state " << state->name()); |
205 |
|
|
|
206 |
|
✗ |
const size_type rDof = this->robot_->numberDof(), |
207 |
|
✗ |
col = idxSpline * Spline::NbCoeffs * rDof, row = lc.J.rows(), |
208 |
|
✗ |
nOutVar = select.nbIndices(); |
209 |
|
|
|
210 |
|
|
// Add nOutVar constraints |
211 |
|
✗ |
lc.addRows(nOutVar); |
212 |
|
✗ |
matrix_t I = select.rview(matrix_t::Identity(rDof, rDof)); |
213 |
|
✗ |
for (size_type k = 0; k < Spline::NbCoeffs; ++k) |
214 |
|
✗ |
lc.J.block(row, col + k * rDof, nOutVar, rDof) = B1(k) * I; |
215 |
|
✗ |
lc.b.segment(row, nOutVar) = |
216 |
|
✗ |
select.rview(spline->parameters().transpose() * B1); |
217 |
|
|
|
218 |
|
✗ |
assert((lc.J.block(row, col, nOutVar, rDof * Spline::NbCoeffs) * |
219 |
|
|
spline->rowParameters()) |
220 |
|
|
.isApprox(lc.b.segment(row, nOutVar))); |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
template <int _PB, int _SO> |
224 |
|
✗ |
void SplineGradientBased<_PB, _SO>::constraintDerivativesAtEndOfSpline( |
225 |
|
|
const size_type& idxSpline, const SplinePtr_t& spline, |
226 |
|
|
LinearConstraint& lc) const { |
227 |
|
✗ |
typename Spline::BasisFunctionVector_t B1; |
228 |
|
✗ |
spline->basisFunctionDerivative(1, 1, B1); |
229 |
|
|
|
230 |
|
|
// ConstraintSetPtr_t set = state->configConstraint(); |
231 |
|
|
// value_type guessThreshold = this->problem().getParameter |
232 |
|
|
// ("SplineGradientBased/guessThreshold").floatValue(); Eigen::RowBlockIndices |
233 |
|
|
// select = this->computeActiveParameters (path, |
234 |
|
|
// set->configProjector()->solver(), guessThreshold); |
235 |
|
|
|
236 |
|
✗ |
const size_type rDof = this->robot_->numberDof(), |
237 |
|
✗ |
col = idxSpline * Spline::NbCoeffs * rDof, row = lc.J.rows(), |
238 |
|
|
// nOutVar = select.nbIndices(); |
239 |
|
✗ |
nOutVar = rDof; |
240 |
|
|
|
241 |
|
|
// Add nOutVar constraints |
242 |
|
✗ |
lc.addRows(nOutVar); |
243 |
|
|
// matrix_t I = select.rview(matrix_t::Identity(rDof, rDof)); |
244 |
|
✗ |
matrix_t I(matrix_t::Identity(rDof, rDof)); |
245 |
|
✗ |
for (size_type k = 0; k < Spline::NbCoeffs; ++k) |
246 |
|
✗ |
lc.J.block(row, col + k * rDof, nOutVar, rDof) = B1(k) * I; |
247 |
|
✗ |
lc.b.segment(row, nOutVar).setZero(); |
248 |
|
|
|
249 |
|
✗ |
if (!(lc.J.block(row, col, nOutVar, rDof * Spline::NbCoeffs) * |
250 |
|
|
spline->rowParameters()) |
251 |
|
✗ |
.isApprox(lc.b.segment(row, nOutVar))) { |
252 |
|
|
hppDout(error, |
253 |
|
|
"The velocity should already be zero:\n" |
254 |
|
|
<< (lc.J.block(row, col, nOutVar, rDof * Spline::NbCoeffs) * |
255 |
|
|
spline->rowParameters()) |
256 |
|
|
.transpose()); |
257 |
|
|
} |
258 |
|
|
} |
259 |
|
|
|
260 |
|
|
// ----------- Optimize ----------------------------------------------- // |
261 |
|
|
|
262 |
|
|
// ----------- Convenience functions ---------------------------------- // |
263 |
|
|
|
264 |
|
|
// ----------- Instanciate -------------------------------------------- // |
265 |
|
|
|
266 |
|
|
// template class SplineGradientBased<core::path::CanonicalPolynomeBasis, 1>; // |
267 |
|
|
// equivalent to StraightPath template class |
268 |
|
|
// SplineGradientBased<core::path::CanonicalPolynomeBasis, 2>; template class |
269 |
|
|
// SplineGradientBased<core::path::CanonicalPolynomeBasis, 3>; |
270 |
|
|
template class SplineGradientBased<core::path::BernsteinBasis, |
271 |
|
|
1>; // equivalent to StraightPath |
272 |
|
|
// template class SplineGradientBased<core::path::BernsteinBasis, 2>; |
273 |
|
|
template class SplineGradientBased<core::path::BernsteinBasis, 3>; |
274 |
|
|
|
275 |
|
|
using core::Parameter; |
276 |
|
|
using core::ParameterDescription; |
277 |
|
|
|
278 |
|
✗ |
HPP_START_PARAMETER_DECLARATION(SplineGradientBased) |
279 |
|
✗ |
core::Problem::declareParameter(ParameterDescription( |
280 |
|
|
Parameter::BOOL, "SplineGradientBased/zeroDerivativesAtStateIntersection", |
281 |
|
|
"Whether we should enforce a null velocity at each control point where the " |
282 |
|
|
"state before and after is different.", |
283 |
|
✗ |
Parameter(false))); |
284 |
|
✗ |
HPP_END_PARAMETER_DECLARATION(SplineGradientBased) |
285 |
|
|
} // namespace pathOptimization |
286 |
|
|
} // namespace manipulation |
287 |
|
|
} // namespace hpp |
288 |
|
|
|