Aikido
Spline-impl.hpp
Go to the documentation of this file.
1 #include <algorithm>
2 #include <cmath>
3 #include <iostream>
4 #include <stdexcept>
5 
6 namespace aikido {
7 namespace common {
8 
9 template <
10  class Scalar,
11  class Index,
12  Index _NumCoefficients,
13  Index _NumOutputs,
14  Index _NumKnots>
16  const TimeVector& _times,
17  const std::vector<
19  Eigen::aligned_allocator<SolutionMatrix> >& _solution)
20  : mTimes(_times), mSolution(_solution)
21 {
22  if (static_cast<std::size_t>(mTimes.size()) != mSolution.size() + 1u)
23  throw std::invalid_argument("Mismatch in argument length.");
24 
25  if (!std::is_sorted(mTimes.data(), mTimes.data() + mTimes.size()))
26  throw std::invalid_argument("Times are not monotonically increasing.");
27 }
28 
29 template <
30  class Scalar,
31  class Index,
32  Index _NumCoefficients,
33  Index _NumOutputs,
34  Index _NumKnots>
36  Index _index, Scalar _t)
37 {
38  mTimes[_index] = _t;
39 }
40 
41 template <
42  class Scalar,
43  class Index,
44  Index _NumCoefficients,
45  Index _NumOutputs,
46  Index _NumKnots>
49 {
50  mTimes = _t;
51 }
52 
53 template <
54  class Scalar,
55  class Index,
56  Index _NumCoefficients,
57  Index _NumOutputs,
58  Index _NumKnots>
61 {
62  mTimes = _t;
63 }
64 
65 template <
66  class Scalar,
67  class Index,
68  Index _NumCoefficients,
69  Index _NumOutputs,
70  Index _NumKnots>
72  getTimes() const -> const TimeVector&
73 {
74  return mTimes;
75 }
76 
77 template <
78  class Scalar,
79  class Index,
80  Index _NumCoefficients,
81  Index _NumOutputs,
82  Index _NumKnots>
85 {
86  return mSolution;
87 }
88 
89 template <
90  class Scalar,
91  class Index,
92  Index _NumCoefficients,
93  Index _NumOutputs,
94  Index _NumKnots>
96  getNumKnots() const
97 {
98  return mTimes.size();
99 }
100 
101 template <
102  class Scalar,
103  class Index,
104  Index _NumCoefficients,
105  Index _NumOutputs,
106  Index _NumKnots>
109 {
110  if (!mSolution.empty())
111  return mSolution.front().rows();
112  else if (NumOutputsAtCompileTime != Eigen::Dynamic)
113  return NumOutputsAtCompileTime;
114  else
115  return 0;
116 }
117 
118 template <
119  class Scalar,
120  class Index,
121  Index _NumCoefficients,
122  Index _NumOutputs,
123  Index _NumKnots>
126 {
127  if (!mSolution.empty())
128  return mSolution.front().cols();
129  else if (NumCoefficientsAtCompileTime != Eigen::Dynamic)
130  return NumCoefficientsAtCompileTime;
131  else
132  return 0;
133 }
134 
135 template <
136  class Scalar,
137  class Index,
138  Index _NumCoefficients,
139  Index _NumOutputs,
140  Index _NumKnots>
143 {
144  return getNumCoefficients() - 1;
145 }
146 
147 template <
148  class Scalar,
149  class Index,
150  Index _NumCoefficients,
151  Index _NumOutputs,
152  Index _NumKnots>
153 Scalar
155  const
156 {
157  if (mTimes.size() > 0)
158  return mTimes[mTimes.size() - 1];
159  else
160  return 0;
161 }
162 
163 template <
164  class Scalar,
165  class Index,
166  Index _NumCoefficients,
167  Index _NumOutputs,
168  Index _NumKnots>
171 {
172  const Index numKnots = getNumKnots();
173 
174  if (_t <= mTimes[0])
175  {
176  return 0;
177  }
178  else if (_t >= mTimes[numKnots - 1])
179  {
180  return numKnots - 2;
181  }
182  else
183  {
184  auto it
185  = std::lower_bound(mTimes.data(), mTimes.data() + mTimes.size(), _t);
186  return it - mTimes.data() - 1;
187  }
188 }
189 
190 template <
191  class Scalar,
192  class Index,
193  Index _NumCoefficients,
194  Index _NumOutputs,
195  Index _NumKnots>
197  evaluate(Scalar _t, Index _derivative) const -> OutputVector
198 {
199  using SplineProblem
201 
202  const Index numOutputs = getNumOutputs();
203  const Index numCoeffs = getNumCoefficients();
204 
205  // All higher-order derivatives are zero.
206  if (_derivative >= numCoeffs)
207  {
208  return OutputVector::Zero(numOutputs);
209  }
210 
211  const CoefficientMatrix coefficientMatrix
213 
214  const CoefficientVector timeVector
215  = SplineProblem::createTimeVector(_t, _derivative, numCoeffs);
216  const CoefficientVector derivativeVector = coefficientMatrix.row(_derivative);
217  const CoefficientVector evaluationVector
218  = derivativeVector.cwiseProduct(timeVector);
219  const Index segmentIndex = getSegmentIndex(_t);
220  const SolutionMatrix& solutionMatrix = mSolution[segmentIndex];
221 
222  OutputVector output(numOutputs);
223 
224  for (Index ioutput = 0; ioutput < numOutputs; ++ioutput)
225  {
226  const CoefficientVector solutionVector = solutionMatrix.row(ioutput);
227  output[ioutput] = evaluationVector.dot(solutionVector);
228  }
229 
230  return output;
231 }
232 
233 // --
234 
235 template <
236  class Scalar,
237  class Index,
238  Index _NumCoefficients,
239  Index _NumOutputs,
240  Index _NumKnots>
243  : SplineProblem(_times, NumCoefficientsAtCompileTime, NumOutputsAtCompileTime)
244 {
245  static_assert(
246  NumCoefficientsAtCompileTime != Eigen::Dynamic,
247  "NumCoefficientsAtCompileTime must be static to use this constructor.");
248  static_assert(
249  NumOutputsAtCompileTime != Eigen::Dynamic,
250  "NumOutputsAtCompileTime must be static to use this constructor.");
251 }
252 
253 template <
254  class Scalar,
255  class Index,
256  Index _NumCoefficients,
257  Index _NumOutputs,
258  Index _NumKnots>
261  const TimeVector& _times, Index _numCoefficients, Index _numOutputs)
262  : mNumKnots(_times.size())
263  , mNumSegments(std::max<Index>(mNumKnots - 1, 0))
264  , mNumCoefficients(_numCoefficients)
265  , mNumOutputs(_numOutputs)
266  , mDimension(mNumSegments * _numCoefficients)
267  , mCoefficientMatrix(createCoefficientMatrix(mNumCoefficients))
268  , mRowIndex(0)
269  , mTimes(_times)
270  , mA(mDimension, mDimension)
271  , mB(mDimension, _numOutputs)
272  , mSolution(mNumSegments, SolutionMatrix(_numOutputs, _numCoefficients))
273 {
274  mA.setZero();
275  mB.setZero();
276 
277  if (!std::is_sorted(mTimes.data(), mTimes.data() + mTimes.size()))
278  {
279  throw std::invalid_argument("Times are not monotonically increasing.");
280  }
281 }
282 
283 template <
284  class Scalar,
285  class Index,
286  Index _NumCoefficients,
287  Index _NumOutputs,
288  Index _NumKnots>
291  Index _knot, Index _derivative, const OutputVector& _value)
292 {
293  assert(0 <= _knot && _knot < mNumKnots);
294  assert(0 <= _derivative && _derivative < mNumCoefficients);
295  assert(_value.size() == mNumOutputs);
296 
297  const CoefficientVector timeVector
298  = createTimeVector(mTimes[_knot], _derivative, mNumCoefficients);
299  const CoefficientVector derivativeVector
300  = mCoefficientMatrix.row(_derivative);
301  const CoefficientVector coeffVector
302  = derivativeVector.cwiseProduct(timeVector);
303 
304  // Position constraint on segment before this knot.
305  if (_knot > 0)
306  {
307  assert(mRowIndex < mDimension);
308 
309  Index const colOffset = (_knot - 1) * mNumCoefficients;
310  for (Index i = 0; i < mNumCoefficients; ++i)
311  {
312  mA.coeffRef(mRowIndex, colOffset + i) = coeffVector[i];
313  }
314 
315  mB.row(mRowIndex) = _value.transpose();
316 
317  ++mRowIndex;
318  }
319 
320  // Position constraint on segment after this knot.
321  if (_knot + 1 < mNumKnots)
322  {
323  assert(mRowIndex < mDimension);
324 
325  Index const colOffset = _knot * mNumCoefficients;
326  for (Index i = 0; i < mNumCoefficients; ++i)
327  {
328  mA.coeffRef(mRowIndex, colOffset + i) = coeffVector[i];
329  }
330 
331  mB.row(mRowIndex) = _value.transpose();
332 
333  ++mRowIndex;
334  }
335 }
336 
337 template <
338  class Scalar,
339  class Index,
340  Index _NumCoefficients,
341  Index _NumOutputs,
342  Index _NumKnots>
345 {
346  assert(0 <= _knot && _knot < mNumKnots);
347  assert(_knot != 0 && _knot + 1 != mNumKnots);
348  assert(0 <= _derivative && _derivative < mNumCoefficients);
349  assert(mRowIndex < mDimension);
350 
351  const CoefficientVector derivativeVector
352  = mCoefficientMatrix.row(_derivative);
353  const CoefficientVector timeVector
354  = createTimeVector(mTimes[_knot], _derivative, mNumCoefficients);
355  const CoefficientVector coeffVector
356  = derivativeVector.cwiseProduct(timeVector);
357 
358  const Index colOffset1 = (_knot - 1) * mNumCoefficients;
359  for (Index i = 0; i < mNumCoefficients; ++i)
360  {
361  mA.coeffRef(mRowIndex, colOffset1 + i) = coeffVector[i];
362  }
363 
364  const Index colOffset2 = _knot * mNumCoefficients;
365  for (Index i = 0; i < mNumCoefficients; ++i)
366  {
367  mA.coeffRef(mRowIndex, colOffset2 + i) = -coeffVector[i];
368  }
369 
370  mB.row(mRowIndex).setZero();
371 
372  ++mRowIndex;
373 }
374 
375 template <
376  class Scalar,
377  class Index,
378  Index _NumCoefficients,
379  Index _NumOutputs,
380  Index _NumKnots>
383 {
384  CoefficientVector exponents(_n);
385 
386  for (Index j = 0; j < _n; ++j)
387  {
388  if (j > _i)
389  {
390  exponents[j] = std::pow(_t, j - _i);
391  }
392  else if (j == _i)
393  {
394  exponents[j] = 1;
395  }
396  else
397  {
398  exponents[j] = 0;
399  }
400  }
401 
402  return exponents;
403 }
404 
405 template <
406  class Scalar,
407  class Index,
408  Index _NumCoefficients,
409  Index _NumOutputs,
410  Index _NumKnots>
413 {
414  assert(mRowIndex == mDimension);
415 
416  // SparseQR requires the matrix to be compressed.
417  mA.finalize();
418  mA.makeCompressed();
419 
420  // Perform the QR decomposition once.
421  Eigen::SparseQR<ProblemMatrix, Eigen::COLAMDOrdering<Index> > solver(mA);
422 
423  for (Index ioutput = 0; ioutput < mNumOutputs; ++ioutput)
424  {
425  // Solve for the spline coefficients for each output dimension.
426  //
427  // TODO: As of Eigen 3.3.5, the output type of SparseQR::solve() is not
428  // assignable to a fixed size vector, so we use Eigen::Dynamic here instead
429  // of DimensionAtCompileTime.
430  Eigen::Matrix<Scalar, Eigen::Dynamic, 1> solutionVector
431  = solver.solve(mB.col(ioutput));
432 
433  // Split the coefficients by segment.
434  for (Index isegment = 0; isegment < mNumSegments; ++isegment)
435  {
436  SolutionMatrix& solutionMatrix = mSolution[isegment];
437  solutionMatrix.row(ioutput) = solutionVector.segment(
438  isegment * mNumCoefficients, mNumCoefficients);
439  }
440  }
441 
442  return Spline(mTimes, mSolution);
443 }
444 
445 template <
446  class Scalar,
447  class Index,
448  Index _NumCoefficients,
449  Index _NumOutputs,
450  Index _NumKnots>
453 {
454  CoefficientMatrix coefficients(_n, _n);
455  coefficients.setZero();
456 
457  if (_n > 0)
458  {
459  coefficients.row(0).setOnes();
460  }
461 
462  for (Index i = 1; i < _n; ++i)
463  {
464  for (Index j = i; j < _n; ++j)
465  {
466  coefficients(i, j) = (j - i + 1) * coefficients(i - 1, j);
467  }
468  }
469  return coefficients;
470 }
471 
472 template <
473  class Scalar,
474  class Index,
475  Index _NumCoefficients,
476  Index _NumOutputs,
477  Index _NumKnots>
480 {
481  return mNumKnots;
482 }
483 
484 template <
485  class Scalar,
486  class Index,
487  Index _NumCoefficients,
488  Index _NumOutputs,
489  Index _NumKnots>
492 {
493  return mNumOutputs;
494 }
495 
496 template <
497  class Scalar,
498  class Index,
499  Index _NumCoefficients,
500  Index _NumOutputs,
501  Index _NumKnots>
504 {
505  if (mTimes.size() > 0)
506  {
507  return mTimes[mTimes.size() - 1];
508  }
509  else
510  {
511  return 0;
512  }
513 }
514 
515 } // namespace common
516 } // namespace aikido
aikido::common::SplineND::getCoefficients
const SolutionMatrices & getCoefficients() const
Gets polynomial coefficients for all segments.
Definition: Spline-impl.hpp:84
aikido::common::SplineProblem::mB
ProblemVector mB
Definition: Spline.hpp:323
aikido::common::SplineProblem::createTimeVector
static CoefficientVector createTimeVector(Scalar _t, Index _i, Index _n)
Creates a vector of the form [ 1, t, t^2, ... t^_n ].
Definition: Spline-impl.hpp:382
aikido::common::SplineND::CoefficientVector
Eigen::Matrix< Scalar, NumCoefficientsAtCompileTime, 1 > CoefficientVector
Definition: Spline.hpp:157
aikido::common::SplineND::OutputVector
Eigen::Matrix< Scalar, NumOutputsAtCompileTime, 1 > OutputVector
Definition: Spline.hpp:59
aikido::common::SplineProblem::fit
Spline fit()
Fit a spline given the constraints on added to this object.
Definition: Spline-impl.hpp:412
aikido::common::SplineProblem::TimeVector
Eigen::Matrix< Scalar, NumKnotsAtCompileTime, 1 > TimeVector
Definition: Spline.hpp:209
aikido::common::SplineND::getDuration
Scalar getDuration() const
Gets the duration of the spline.
Definition: Spline-impl.hpp:154
aikido::common::SplineND::getNumCoefficients
Index getNumCoefficients() const
Gets the number of polynomial coefficients in each segment.
Definition: Spline-impl.hpp:125
aikido::common::SplineND::SplineND
SplineND()=default
Constructs an empty spline.
aikido::common::SplineProblem::CoefficientVector
Eigen::Matrix< Scalar, NumCoefficientsAtCompileTime, 1 > CoefficientVector
Definition: Spline.hpp:214
aikido
Format of serialized trajectory in YAML.
Definition: algorithm.hpp:4
aikido::common::SplineND::TimeVector
Eigen::Matrix< Scalar, NumKnotsAtCompileTime, 1 > TimeVector
Definition: Spline.hpp:56
aikido::common::SplineProblem::SplineProblem
SplineProblem(const TimeVector &_times)
Constructs a spline fitting problem with the knot points at the specified times.
Definition: Spline-impl.hpp:242
aikido::common::SplineProblem::NumOutputsAtCompileTime
static constexpr Index NumOutputsAtCompileTime
Definition: Spline.hpp:198
aikido::common::SplineND::setTimes
void setTimes(TimeVector &&_t)
Sets the times of all knot points.
Definition: Spline-impl.hpp:48
aikido::common::SplineProblem::Index
_Index Index
Definition: Spline.hpp:195
aikido::common::SplineProblem::getDuration
Scalar getDuration() const
Gets the duration of the spline.
Definition: Spline-impl.hpp:503
aikido::common::SplineProblem::Scalar
_Scalar Scalar
Definition: Spline.hpp:194
aikido::common::SplineND::Index
_Index Index
Definition: Spline.hpp:42
aikido::common::SplineND::getNumKnots
Index getNumKnots() const
Gets the number of knot points.
Definition: Spline-impl.hpp:96
aikido::common::SplineProblem::addConstantConstraint
void addConstantConstraint(Index _knot, Index _derivative, const OutputVector &_value)
Adds a constraint that the _derivative-th order derivative of knot point _knot should equal _value.
Definition: Spline-impl.hpp:290
aikido::common::SplineND::CoefficientMatrix
Eigen::Matrix< Scalar, NumCoefficientsAtCompileTime, NumCoefficientsAtCompileTime > CoefficientMatrix
Definition: Spline.hpp:161
aikido::common::SplineProblem::getNumKnots
Index getNumKnots() const
Gets the number of knot points.
Definition: Spline-impl.hpp:479
aikido::common::SplineND::SolutionMatrix
Eigen::Matrix< Scalar, NumOutputsAtCompileTime, NumCoefficientsAtCompileTime > SolutionMatrix
Definition: Spline.hpp:58
aikido::common::SplineProblem::SolutionMatrix
Eigen::Matrix< Scalar, NumOutputsAtCompileTime, NumCoefficientsAtCompileTime > SolutionMatrix
Definition: Spline.hpp:223
aikido::common::SplineND::mSolution
SolutionMatrices mSolution
Definition: Spline.hpp:164
aikido::common::SplineProblem::mTimes
TimeVector mTimes
Definition: Spline.hpp:321
aikido::common::SplineND::getNumDerivatives
Index getNumDerivatives() const
Gets an upperbound on the number of non-zero derivatives.
Definition: Spline-impl.hpp:142
aikido::common::SplineProblem::OutputVector
Eigen::Matrix< Scalar, NumOutputsAtCompileTime, 1 > OutputVector
Definition: Spline.hpp:210
aikido::common::SplineND::setTime
void setTime(Index _index, Scalar _t)
Sets the time of the _index-th knot point.
Definition: Spline-impl.hpp:35
aikido::common::SplineProblem::NumCoefficientsAtCompileTime
static constexpr Index NumCoefficientsAtCompileTime
Definition: Spline.hpp:197
aikido::common::SplineProblem::createCoefficientMatrix
static CoefficientMatrix createCoefficientMatrix(Index _n)
Creates the _n by _n matrix of derivative coefficients for a polynomial with _n coefficients.
Definition: Spline-impl.hpp:452
aikido::common::SplineND::getSegmentIndex
Index getSegmentIndex(Scalar _t) const
Gets the index of the segment that contains time _t.
Definition: Spline-impl.hpp:170
aikido::common::SplineND
An arbitrary dimensional polynomial spline.
Definition: Spline.hpp:38
aikido::common::SplineProblem::addContinuityConstraint
void addContinuityConstraint(Index _knot, Index _derivative)
Adds a continuity constraint on the _derivative-th order derivative at knot point _knot.
Definition: Spline-impl.hpp:344
aikido::common::SplineND::SolutionMatrices
std::vector< SolutionMatrix, Eigen::aligned_allocator< SolutionMatrix > > SolutionMatrices
Definition: Spline.hpp:61
aikido::common::SplineProblem::getNumOutputs
Index getNumOutputs() const
Gets the number of outputs points.
Definition: Spline-impl.hpp:491
aikido::common::SplineProblem
Utility for fitting splines given constraints on function value, derivative value,...
Definition: Spline.hpp:191
aikido::common::SplineND::evaluate
OutputVector evaluate(Scalar _t, Index _derivative=0) const
Evaluate the _derivative-th order of the spline at time _t.
Definition: Spline-impl.hpp:197
aikido::common::SplineProblem::CoefficientMatrix
Eigen::Matrix< Scalar, NumCoefficientsAtCompileTime, NumCoefficientsAtCompileTime > CoefficientMatrix
Definition: Spline.hpp:218
aikido::common::SplineND::getTimes
const TimeVector & getTimes() const
Gets times of all knot points.
Definition: Spline-impl.hpp:72
aikido::common::SplineND::Scalar
_Scalar Scalar
Definition: Spline.hpp:41
aikido::common::SplineND::getNumOutputs
Index getNumOutputs() const
Gets the number of outputs points.
Definition: Spline-impl.hpp:108
aikido::common::SplineND::mTimes
TimeVector mTimes
Definition: Spline.hpp:163
aikido::common::SplineProblem::mA
ProblemMatrix mA
Definition: Spline.hpp:322