full_discretization_grid.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement
4  *
5  * Copyright (c) 2018,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * This software is currently not released.
10  * Redistribution and use in source and binary forms,
11  * with or without modification, are prohibited.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
16  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
17  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  *
26  * Authors: Christoph Rösmann
27  *********************************************************************/
28 
29 #include <corbo-optimal-control/structured_ocp/discretization_grids/full_discretization_grid.h>
30 
31 #include <corbo-core/console.h>
32 
33 #include <memory>
34 
35 namespace corbo {
36 
37 bool FullDiscretizationGrid::initialize(const Eigen::VectorXd& x0, const Eigen::VectorXd& u0, DynamicsEvalInterface::Ptr dynamics_eval)
38 {
39  return initialize(x0, x0, u0, dynamics_eval);
40 }
41 
42 bool FullDiscretizationGrid::initialize(const Eigen::VectorXd& x0, const Eigen::VectorXd& xf, const Eigen::VectorXd& u0,
43  DynamicsEvalInterface::Ptr /*dynamics_eval*/)
44 {
45  clear();
46 
47  DiscretizationGrid::initialize(x0.size(), u0.size());
48 
49  if (_num_desired_states < 2)
50  {
51  PRINT_ERROR("FullDiscretizationGrid::initialize(): number of desired states > 1 required. Please call setHorizon() first.");
52  return false;
53  }
54 
55  int num_intervals = _num_desired_states - 1;
56 
57  // we initialize the state trajectory linearly
58  Eigen::VectorXd dir = xf - x0;
59  double dist = dir.norm();
60  if (dist != 0) dir /= dist;
61  double step = dist / num_intervals;
62 
63  for (int k = 0; k < num_intervals; ++k)
64  {
65  // add new shooting interval
66  appendShootingInterval(x0 + (double)k * step * dir, u0, _dt);
67  }
68 
69  // fix start state for optimization
70  getShootingIntervalsRaw().front().shooting_node->setFixed(true);
71 
72  PartiallyFixedVectorVertex::Ptr xf_node = std::make_shared<PartiallyFixedVectorVertex>(xf, _x_lower, _x_upper);
73  if (_xf_fixed.size() == 0)
74  xf_node->setFixed(false);
75  else
76  xf_node->setFixed(_xf_fixed);
77  getFinalStateRaw() = xf_node;
78 
79  // updateBounds(); // TODO(roesmann): more efficienct
80 
81  return true;
82 }
83 
84 bool FullDiscretizationGrid::initialize(const Eigen::VectorXd& x0, ReferenceTrajectoryInterface& xref, ReferenceTrajectoryInterface& uref,
85  const Time& t, DynamicsEvalInterface::Ptr dynamics_eval)
86 {
87  DiscretizationGrid::initialize(x0.size(), uref.getDimension());
88 
89  clear();
90 
91  if (xref.isStatic() && uref.isStatic())
92  {
93  return initialize(x0, xref.getReferenceCached(0), uref.getReferenceCached(0), dynamics_eval);
94  }
95 
96  else
97  {
98  if (_num_desired_states < 2)
99  {
100  PRINT_ERROR("FullDiscretizationGrid::initialize(): number of desired states > 1 required. Please call setHorizon() first.");
101  return false;
102  }
103 
104  if (xref.isStatic()) // xref.isStatic && !uref.isStatic
105  {
106  uref.precompute(_dt, _num_desired_states, t);
107 
108  clear();
109 
110  DiscretizationGrid::initialize(xref.getDimension(), uref.getDimension());
111 
112  int num_intervals = _num_desired_states - 1;
113 
114  // we initialize the state trajectory linearly
115  Eigen::VectorXd dir = xref.getReferenceCached(num_intervals) - x0;
116  double dist = dir.norm();
117  if (dist != 0) dir /= dist;
118  double step = dist / num_intervals;
119 
120  for (int k = 0; k < num_intervals; ++k)
121  {
122  // add new shooting interval
123  appendShootingInterval(x0 + (double)k * step * dir, uref.getReferenceCached(k), _dt);
124  }
125 
126  // fix start state for optimization
127  getShootingIntervalsRaw().front().shooting_node->setFixed(true);
128 
130  std::make_shared<PartiallyFixedVectorVertex>(xref.getReferenceCached(num_intervals), _x_lower, _x_upper);
131  if (_xf_fixed.size() == 0)
132  xf_node->setFixed(false);
133  else
134  xf_node->setFixed(_xf_fixed);
135  getFinalStateRaw() = xf_node;
136  }
137 
138  else // !xref.isStatic && !uref.isStatic || !xref.isStatic && uref.isStatic
139  {
140  xref.precompute(_dt, _num_desired_states, t);
141  uref.precompute(_dt, _num_desired_states, t);
142 
143  if ((x0 - xref.getReferenceCached(0)).norm() > _non_static_ref_dist_threshold)
144  return initialize(x0, xref.getReferenceCached(_num_desired_states - 1), uref.getReferenceCached(_num_desired_states - 1),
145  dynamics_eval);
146  else
147  {
148  clear();
149 
150  DiscretizationGrid::initialize(xref.getDimension(), uref.getDimension());
151 
152  int num_intervals = _num_desired_states - 1;
153 
154  appendShootingInterval(x0, uref.getReferenceCached(0), _dt);
155 
156  for (int k = 1; k < num_intervals; ++k)
157  {
158  // add new shooting interval
159  appendShootingInterval(xref.getReferenceCached(k), uref.getReferenceCached(k), _dt);
160  }
161 
162  // fix start state for optimization
163  getShootingIntervalsRaw().front().shooting_node->setFixed(true);
164 
166  std::make_shared<PartiallyFixedVectorVertex>(xref.getReferenceCached(num_intervals), _x_lower, _x_upper);
167  if (_xf_fixed.size() == 0)
168  xf_node->setFixed(false);
169  else
170  xf_node->setFixed(_xf_fixed);
171  getFinalStateRaw() = xf_node;
172  }
173  }
174  }
175 
176  return true;
177 }
178 
179 bool FullDiscretizationGrid::update(const Eigen::VectorXd& x0, ReferenceTrajectoryInterface& xref, ReferenceTrajectoryInterface& uref, bool new_run,
180  const Time& t)
181 {
182  if (_shooting_intervals.empty() || !_warm_start)
183  {
184  if (!new_run)
185  {
186  resizeTrajectory(); // TODO(roesman)
187  setN(getN());
188  }
189  initialize(x0, xref, uref, t);
190  }
191  else if (new_run)
192  {
193  // get current goal
194  Eigen::VectorXd xf(x0.rows());
195  xref.getReference(Time(getFinalTime()), xf);
196 
197  // reinit if goal is far away
198  if (_current_xf_values.rows() > 0 && (xf - _current_xf_values).norm() > _warm_start_goal_dist_reinit)
199  {
200  initialize(x0, xref, uref, t); // initialize should also clear everything first
201  _current_xf_values = xf;
202  }
203  else
204  {
205  // warm-start!
206 
207  // update goal
208  // _xf->values() = xf;
209 
210  if (!_prune_trajectory)
211  {
212  // shift values and extrapolate
213  if (!shiftGridValues(x0)) PRINT_WARNING_NAMED("shifting of GridValues failed!");
214  }
215  else
216  {
217  pruneTrajectory(x0);
218  }
219 
220  if (_shooting_intervals.empty())
221  {
222  PRINT_ERROR_NAMED("warm-start failed. No grid intervals found. Reinitializing...");
223  initialize(x0, xref, uref, t);
224  }
225 
226  // update start
227  _shooting_intervals.front().shooting_node->values() = x0;
228  // int num_erased = pruneTrajectory(x0);
229 
230  // recreate erased shooting nodes at the end of the horizon if prune_trajectory is turned off)
231  // if (!_prune_trajectory) extrapolateTrajectory(num_erased);
232 
233  // store xf values, we might decide later whether to reinitialize (even in case we have a moving horizon controller)
234  if (_current_xf_values.rows() == 0) _current_xf_values = xf;
235  }
236 
237  if (!new_run) resizeTrajectory();
238  }
239 
240  return isModified();
241 }
242 
243 int FullDiscretizationGrid::pruneTrajectory(const Eigen::VectorXd& x0)
244 {
245  int nearest_idx = findNewInitialShootingInterval(x0);
246 
247  // prune trajectory at the beginning
248  if (nearest_idx > 0)
249  {
250  // nearest_idx is equal to the number of samples to be removed (since it counts from 0 ;-) )
251  _shooting_intervals.erase(_shooting_intervals.begin(),
252  _shooting_intervals.begin() + nearest_idx); // delete first states such that the closest state is the new first one
253 
254  // fix new start state for optimization
255  getShootingIntervalsRaw().front().shooting_node->setFixed(true);
256 
257  setModified(true);
258  }
259 
260  // update start
261  _shooting_intervals.front().shooting_node->values() = x0;
262 
263  return nearest_idx;
264 }
265 
266 void FullDiscretizationGrid::appendShootingInterval(const Eigen::VectorXd& x, const Eigen::VectorXd& u, double dt_if_not_single)
267 {
268  getShootingIntervalsRaw().emplace_back();
269  ShootingInterval& interval = getShootingIntervalsRaw().back();
270 
271  // initialize x
272  ShootingVertex::Ptr xk_node = std::make_shared<ShootingVertex>(x, _x_lower, _x_upper); // set first state (k == 0) to fixed
273  interval.shooting_node = xk_node;
274 
275  // in full discretization we always have a single control and dt per interval
276  interval.num_controls = 1;
277 
278  // initialize u
279  interval.controls.push_back(std::make_shared<ControlVertex>(u, _u_lower, _u_upper));
280 
281  // initialize dt
282  if (_single_dt)
283  {
284  if (_shooting_intervals.size() == 1) // this is the first interval
285  interval.dts.push_back(std::make_shared<DtVertex>(_dt, _dt_lower, _dt_upper, _dt_fixed));
286  else
287  interval.dts.push_back(getFirstDtVertex());
288  }
289  else
290  {
291  interval.dts.push_back(std::make_shared<DtVertex>(dt_if_not_single, _dt_lower, _dt_upper, _dt_fixed));
292  }
293 
294  setModified(true);
295 }
296 
297 void FullDiscretizationGrid::insertShootingInterval(int k, const Eigen::VectorXd& x, const Eigen::VectorXd& u, double dt_if_not_single)
298 {
299  assert(k > 0);
300  assert(k <= (int)_shooting_intervals.size());
301 
302  if (k == _shooting_intervals.size())
303  {
304  appendShootingInterval(x, u, dt_if_not_single);
305  return;
306  }
307 
308  auto new_interv_it = _shooting_intervals.insert(_shooting_intervals.begin() + k, ShootingInterval());
309  ShootingInterval& interval = *new_interv_it;
310 
311  // initialize x
312  ShootingVertex::Ptr xk_node = std::make_shared<ShootingVertex>(x, _x_lower, _x_upper); // set first state (k == 0) to fixed
313  interval.shooting_node = xk_node;
314 
315  // in full discretization we always have a single control and dt per interval
316  interval.num_controls = 1;
317 
318  // initialize u
319  interval.controls.push_back(std::make_shared<ControlVertex>(u, _u_lower, _u_upper));
320 
321  // initialize dt
322  if (_single_dt)
323  {
324  if (_shooting_intervals.size() == 1) // this is the first interval
325  interval.dts.push_back(std::make_shared<DtVertex>(_dt, _dt_lower, _dt_upper, _dt_fixed));
326  else
327  interval.dts.push_back(getFirstDtVertex());
328  }
329  else
330  {
331  interval.dts.push_back(std::make_shared<DtVertex>(dt_if_not_single, _dt_lower, _dt_upper, _dt_fixed));
332  }
333 
334  setModified(true);
335 }
336 
337 void FullDiscretizationGrid::eraseShootingInterval(int k)
338 {
339  assert(k > 0);
340  assert(k < (int)_shooting_intervals.size());
341  _shooting_intervals.erase(_shooting_intervals.begin() + k);
342 
343  if (k == 0 && !_shooting_intervals.empty())
344  {
345  // fix new start state for optimization
346  getShootingIntervalsRaw().front().shooting_node->setFixed(true);
347  }
348  setModified(true);
349 }
350 
351 void FullDiscretizationGrid::extrapolateTrajectory(int num_new_intervals)
352 {
353  if (num_new_intervals < 1) return;
354 
355  if (_shooting_intervals.empty())
356  {
357  PRINT_ERROR("FullDiscretizationGrid::extrapolateTrajectory(): cannot extrapolate with less than states"); // at least one interval + xf
358  return;
359  }
360 
361  // add current final state as new shooting interval
362  appendShootingInterval(_xf->values(), _shooting_intervals.back().controls.front()->values(), _dt);
363 
364  // now add further intervals
365  for (int i = 0; i < num_new_intervals; ++i)
366  {
367  // add new shooting interval
368  // TODO(roesmann): switch to zero-order hold close to goal
369  auto it_back = std::prev(_shooting_intervals.end());
370  Eigen::VectorXd xi = it_back->shooting_node->values() +
371  (it_back->shooting_node->values() -
372  std::prev(it_back)->shooting_node->values()); // TODO(roesmann) multiply by fraction of last dt and _dt
373 
374  if (i < num_new_intervals - 1)
375  {
376  appendShootingInterval(xi, it_back->controls.front()->values(), _dt);
377  }
378  else
379  {
380  // update xf
381  _xf->values() = xi;
382  }
383  }
384 }
385 
386 bool FullDiscretizationGrid::shiftGridValues(const Eigen::VectorXd& x0)
387 {
388  int nearest_idx = findNewInitialShootingInterval(x0);
389  return shiftGridValues(nearest_idx);
390 }
391 
392 bool FullDiscretizationGrid::shiftGridValues(int num_shift)
393 {
394  if (num_shift == 0)
395  return true;
396  else if (num_shift < 0)
397  return false;
398 
399  if (num_shift > getN() - 2)
400  {
401  PRINT_ERROR_NAMED("Cannot shift if num_shift > N-2");
402  return false;
403  }
404 
405  // shift from end to front:
406  for (int i = 0; i < getN() - num_shift; ++i)
407  {
408  int idx = i + num_shift;
409  if (idx == getN() - 1)
410  {
411  assert(_xf);
412  // final state reached
413  _shooting_intervals[i].shooting_node->values() = _xf->values();
414  }
415  else
416  {
417  _shooting_intervals[i].shooting_node->values() = _shooting_intervals[idx].shooting_node->values();
418 
419  for (int j = 0; j < _shooting_intervals[i].controls.size(); ++j)
420  {
421  assert(_shooting_intervals[i].controls.size() == _shooting_intervals[idx].controls.size());
422  _shooting_intervals[i].controls[j]->values() = _shooting_intervals[idx].controls[j]->values();
423  }
424 
425  for (int j = 0; j < _shooting_intervals[i].dts.size(); ++j)
426  {
427  assert(_shooting_intervals[i].dts.size() == _shooting_intervals[idx].dts.size());
428  _shooting_intervals[i].dts[j]->values() = _shooting_intervals[idx].dts[j]->values();
429  }
430 
431  assert(_shooting_intervals[i].additional_states.empty() && "No additional states allowed in full discretization grid.");
432 
433  // _shooting_intervals[i].dt_complete_interval = _shooting_intervals[idx].dt_complete_interval; // should be always the same here
434  // _shooting_intervals[i].num_controls = _shooting_intervals[idx].num_controls; // should be always 1
435  }
436  }
437 
438  int idx = getN() - num_shift;
439  if (idx < 0)
440  {
441  PRINT_ERROR_NAMED("idx < 0...");
442  return false;
443  }
444  for (int i = 0; i < num_shift; ++i, ++idx)
445  {
446  // now extrapolate
447  assert(idx > 1);
448 
449  // linearly extrapolate states
450  if (i == num_shift - 1) // consider xf
451  {
452  _xf->values() = _shooting_intervals[idx - 2].shooting_node->values() +
453  2.0 * (_shooting_intervals[idx - 1].shooting_node->values() - _shooting_intervals[idx - 2].shooting_node->values());
454  }
455  else
456  {
457  _shooting_intervals[idx].shooting_node->values() =
458  _shooting_intervals[idx - 2].shooting_node->values() +
459  2.0 * (_shooting_intervals[idx - 1].shooting_node->values() - _shooting_intervals[idx - 2].shooting_node->values());
460  // TODO(roesmann) multiply by fraction of last dt and _dt
461  }
462 
463  for (int j = 0; j < _shooting_intervals[idx - 1].controls.size(); ++j)
464  {
465  assert(_shooting_intervals[idx - 1].controls.size() == _shooting_intervals[idx - 2].controls.size());
466  _shooting_intervals[idx - 1].controls[j]->values() = _shooting_intervals[idx - 2].controls[j]->values();
467  }
468 
469  for (int j = 0; j < _shooting_intervals[idx - 1].dts.size(); ++j)
470  {
471  assert(_shooting_intervals[idx - 1].dts.size() == _shooting_intervals[idx - 2].dts.size());
472  _shooting_intervals[idx - 1].dts[j]->values() = _shooting_intervals[idx - 2].dts[j]->values();
473  }
474 
475  assert(_shooting_intervals[i].additional_states.empty() && "No additional states allowed in full discretization grid.");
476  }
477 
478  return true;
479 }
480 
481 int FullDiscretizationGrid::findNewInitialShootingInterval(const Eigen::VectorXd& x0)
482 {
483  assert(!_shooting_intervals.empty());
484  assert(_shooting_intervals.front().shooting_node);
485 
486  // same start as before
487  if (x0 == _shooting_intervals.front().shooting_node->values()) return 0;
488 
489  // find nearest state (using l2-norm) in order to prune the trajectory
490  // (remove already passed states, should be 1 if the sampling times of the planning and controller call match, but could be higher if rates
491  // differ)
492 
493  int num_interv = (int)_shooting_intervals.size();
494 
495  double dist_cache = (x0 - _shooting_intervals.front().shooting_node->values()).norm();
496  double dist;
497  int num_keep_interv = std::max(1, _num_states_min - 1); // we need to keep at least this number of intervals in the trajectory
498  int lookahead = std::min(num_interv - num_keep_interv, 20); // max 20 samples
499 
500  int nearest_idx = 0;
501 
502  for (int i = 1; i <= lookahead; ++i)
503  {
504  dist = (x0 - _shooting_intervals[i].shooting_node->values()).norm();
505  if (dist < dist_cache)
506  {
507  dist_cache = dist;
508  nearest_idx = i;
509  }
510  else
511  break;
512  }
513 
514  return nearest_idx;
515 }
516 
517 bool FullDiscretizationGrid::resizeTrajectory()
518 {
519  bool success = true;
520  switch (_auto_resize)
521  {
522  case AutoResizeStrategy::NoAutoResize:
523  {
524  break;
525  }
526  case AutoResizeStrategy::TimeBased:
527  {
528  success = resizeTrajectoryTimeBased();
529  break;
530  }
531  case AutoResizeStrategy::RedundantControls:
532  {
533  success = resizeTrajectoryRedundantControls();
534  break;
535  }
536  default:
537  {
538  PRINT_ERROR("FullDiscretizationGrid::resizeTrajectory(): selected auto resize strategy not implemented.");
539  success = false;
540  }
541  }
542  return success;
543 }
544 
545 bool FullDiscretizationGrid::resizeTrajectoryTimeBased()
546 {
547  if (isDtFixed())
548  {
549  PRINT_WARNING("FullDiscretizationGrid::resizeTrajectoryTimeBased(): time based resize might only be used with an unfixed dt.");
550  }
551 
552  int n = getN();
553 
554  if (hasSingleDt())
555  {
556  assert(getFirstDtVertexRaw());
557  double dt = getFirstDtVertexRaw()->value();
558  // if (cfg->ctrl.estimate_dt)
559  // {
560  // // estimate number of samples based on the fraction dt/dt_ref.
561  // // dt is the time difference obtained in a previous solution (with a coarser resp. finer trajectory resolution)
562  // int new_n = n * (int)std::round(_dt.dt() / _dt_ref);
563  // new_n = bound(cfg->ctrl.n_min, new_n, cfg->ctrl.n_max);
564  // resampleTrajectory(new_n);
565  // PRINT_INFO("new n: " << new_n << " old n: " << n);
566  // }
567  // else // use a simple linear search
568  //{
569  if (dt > _dt * (1.0 + _dt_hyst_ratio) && n < _num_states_max)
570  {
571  resampleTrajectory(n + 1);
572  }
573  else if (dt < _dt * (1.0 - _dt_hyst_ratio) && n > _num_states_min)
574  {
575  resampleTrajectory(n - 1);
576  }
577  //}
578  }
579  else
580  {
581  // iterate sequence once and check time differences
582  bool changed = false;
583 
584  for (int i = 0; i < (int)_shooting_intervals.size() - 1; ++i)
585  {
586  ShootingInterval& interv = _shooting_intervals[i];
587  ShootingInterval& interv_next = _shooting_intervals[i + 1];
588 
589  double dt = interv.dts.front()->value();
590 
591  if (dt > _dt * (1.0 + _dt_hyst_ratio) && n < _num_states_max)
592  {
593  double new_dt = 0.5 * dt;
594 
595  insertShootingInterval(i + 1, 0.5 * (interv.shooting_node->values() + interv_next.shooting_node->values()),
596  interv.controls.front()->values(), new_dt);
597  changed = true;
598 
599  break; // TODO(roesmann): better strategy, instead of just searching the first dt?
600 
601  ++i; // skip newly inserted node
602  }
603  else if (dt < _dt * (1.0 - _dt_hyst_ratio) && n > _num_states_min)
604  {
605  // if (i < (int)_dt_seq.size()-2)
606  {
607  interv_next.dts.front()->value() += dt;
608  eraseShootingInterval(i);
609 
610  changed = true;
611 
612  break; // TODO(roesmann): better strategy, instead of just searching the first dt?
613  }
614  }
615  }
616  }
617  return true;
618 }
619 
620 bool FullDiscretizationGrid::resizeTrajectoryRedundantControls()
621 {
622  // find approx(u_k, u_{k+1})
623  std::vector<std::size_t> non_unique_indices;
624  for (std::size_t idx = 0; idx < (int)_shooting_intervals.size() - 1; ++idx) // never delete the last control
625  {
626  ShootingInterval& interv = _shooting_intervals[idx];
627  ShootingInterval& interv_next = _shooting_intervals[idx + 1];
628 
629  // if ( _ctrl_seq[idx-1].controls().isApprox(_ctrl_seq[idx].controls(), 1e-1 ) )
630  // also delete if the time diff of the interval is sufficiently small
631  if (interv.dts.front()->value() < 1e-6)
632  {
633  non_unique_indices.emplace_back(idx);
634  // non_unique_indices.emplace_back(idx+1); // since we have zero dt, at least two samples are obsolete
635  // ++idx;
636  continue;
637  }
638  // PRINT_INFO("control threshold: " << _controls_similar_threshold);
639  // if ( (_ctrl_seq[idx+1].controls()-_ctrl_seq[idx].controls()).isZero(_controls_similar_threshold) )
640  // non_unique_indices.emplace_back(idx);
641  Eigen::VectorXd& u_k = interv.controls.front()->values();
642  Eigen::VectorXd& u_kp1 = interv_next.controls.front()->values();
643  if (((u_kp1 - u_k).cwiseAbs().array() <= _redundant_ctrl_epsilon).all()) non_unique_indices.emplace_back(idx);
644  }
645 
646  // PRINT_INFO("number non-unique: " << non_unique_indices.size());
647 
648  bool changed = false;
649 
650  int backup_diff = (int)non_unique_indices.size() - _redundant_ctrl_backup;
651 
652  if (backup_diff < 0)
653  {
654  changed = true;
655  backup_diff = std::abs(backup_diff);
656  for (int i = 0; i < backup_diff && getN() < _num_states_max; ++i)
657  {
658  // add new sample
659  int dt_max_idx = 0;
660  if (getN() > 2)
661  {
662  // insert inbetween largest gap (largest DT)
663  auto end_it = _shooting_intervals.end();
664  std::advance(end_it, -1); // we do not want to a new point to the goal (therefore -2)
665  auto max_elem = std::max_element(_shooting_intervals.begin(), end_it, [](const ShootingInterval& si1, const ShootingInterval& si2) {
666  return si1.dts.front()->value() < si2.dts.front()->value();
667  });
668  if (max_elem == end_it)
669  {
670  PRINT_INFO("Invalid time max element in resizeTrajectoryRedundantControls(). break...");
671  break;
672  }
673  dt_max_idx = std::distance(_shooting_intervals.begin(), max_elem);
674  }
675  assert(dt_max_idx + 1 < (int)_shooting_intervals.size());
676  ShootingInterval& interv = _shooting_intervals[dt_max_idx];
677  ShootingInterval& interv_next = _shooting_intervals[dt_max_idx + 1];
678 
679  double new_dt = 0.5 * interv.dts.front()->value();
680  interv.dts.front()->value() = new_dt;
681  insertShootingInterval(dt_max_idx + 1, 0.5 * (interv.shooting_node->values() + interv_next.shooting_node->values()),
682  interv.controls.front()->values(), new_dt);
683  // PRINT_INFO("inserted");
684  }
685  }
686  else if (backup_diff > 0)
687  {
688  changed = true;
689  auto idx_it = non_unique_indices.rbegin(); // erase starting from the last one (reverse iterator)
690  for (int i = 0; i < backup_diff && getN() > _num_states_min; ++i)
691  {
692  int k = (int)*idx_it;
693  if (k >= getN() - 2) --k;
694 
695  assert(k + 1 < (int)_shooting_intervals.size());
696  ShootingInterval& interv = _shooting_intervals[k];
697  ShootingInterval& interv_next = _shooting_intervals[k + 1];
698 
699  interv.dts.front()->value() += interv_next.dts.front()->value();
700  eraseShootingInterval(k + 1);
701  ++idx_it;
702  // PRINT_INFO("removed");
703  }
704  }
705  return true;
706 }
707 
708 void FullDiscretizationGrid::resampleTrajectory(int n_new)
709 {
710  int n = getN();
711  if (n == n_new) return;
712 
713  // copy vertices (vertices itself are shared pointers)
716  TimeSeries::Ptr ts_states_old = std::make_shared<TimeSeries>();
717  TimeSeries::Ptr ts_controls_old = std::make_shared<TimeSeries>();
718  getShootingNodeTimeSeries(ts_states_old);
719  getControlInputTimeSeries(ts_controls_old);
720 
721  TimeSeries::ValuesMatMap states_old = ts_states_old->getValuesMatrixView();
722  TimeSeries::ValuesMatMap controls_old = ts_controls_old->getValuesMatrixView();
723 
724  int num_interv = n - 1;
725 
726  if (hasSingleDt())
727  {
728  assert(getFirstDtVertexRaw() != nullptr);
729  double dt_old = getFirstDtVertexRaw()->value();
730  // compute new time diff
731  double dt_new = dt_old * double(n - 1) / double(n_new - 1);
732 
733  double t_new;
734  int idx_old = 1;
735  double t_old_p1 = dt_old; // time for old sample with index idx_old (acutally its the subsequent time step w.r.t t_new)
736 
737  for (int idx_new = 1; idx_new < n_new - 1; ++idx_new) // n_new-1 since last state is identical and copied later. idx_new=1, since start
738  // sample is already valid (we do not touch it)
739  // we allow a small mismatch for the control u_1 and let the optimizer correct it later
740  {
741  t_new = dt_new * double(idx_new);
742  while (t_new > double(idx_old) * dt_old && idx_old < n)
743  {
744  ++idx_old;
745  }; // find idx_old that represents the state subsequent to the new one (w.r.t. time)
746  t_old_p1 = double(idx_old) * dt_old;
747 
748  const Eigen::VectorXd& x_prev = states_old.col(idx_old - 1);
749  const Eigen::VectorXd& x_cur = (idx_old < n - 1) ? states_old.col(idx_old) : _xf->values();
750 
751  if (idx_new < num_interv)
752  {
753  // states / shooting node
754  _shooting_intervals[idx_new].shooting_node->values() = x_prev + (t_new - (t_old_p1 - dt_old)) / dt_old * (x_cur - x_prev);
755 
756  // controls
757  // TODO(roesmann): we do not have a valid control (u_f), so hold last
758  _shooting_intervals[idx_new].controls.front()->values() = controls_old.col(idx_old - 1);
759  }
760  else
761  {
762  // add new shooting interval
763  appendShootingInterval(x_prev + (t_new - (t_old_p1 - dt_old)) / dt_old * (x_cur - x_prev), controls_old.col(idx_old - 1), _dt);
764  }
765  }
766 
767  // clear invalid states
768  if (n_new < n)
769  {
770  _shooting_intervals.resize(n_new - 1);
771  }
772 
773  // save new dt
774  getFirstDtVertexRaw()->value() = dt_new;
775 
776  // set first vertex always to fixed
777  // _shooting_intervals.front().shooting_node->setFixed(true); // should not be necessary here (we just change values in the first node)
778  }
779  else
780  {
781  PRINT_ERROR("FullDiscretizationGrid::resampleTrajectory(): not yet implemented for multiple dts");
782  }
783 
784  // notify graph that vertices are changed
785  setModified(true);
786 }
787 
788 double FullDiscretizationGrid::getFinalTime() const
789 {
790  if (_dt_fixed && _single_dt) return (double)_shooting_intervals.size() * _dt;
791  if (_single_dt) return (double)_shooting_intervals.size() * getFirstDt();
792 
793  return DiscretizationGrid::getFinalTime();
794 }
795 
796 void FullDiscretizationGrid::setGridResizeTimeBased(int num_states_max, double dt_hyst_ratio)
797 {
798  _auto_resize = AutoResizeStrategy::TimeBased;
799  _num_states_max = num_states_max;
800  _dt_hyst_ratio = dt_hyst_ratio;
801 }
802 
803 void FullDiscretizationGrid::setGridResizeRedundControls(int num_states_max, int num_backup_nodes, double epsilon)
804 {
805  _auto_resize = AutoResizeStrategy::RedundantControls;
806  _num_states_max = num_states_max;
807  _redundant_ctrl_backup = num_backup_nodes;
808  _redundant_ctrl_epsilon = epsilon;
809 }
810 
811 void FullDiscretizationGrid::computeActiveVertices()
812 {
813  _active_vertices.clear();
814  if (hasSingleDt())
815  {
816  for (ShootingInterval& interv : _shooting_intervals)
817  {
818  if (!interv.shooting_node->isFixed()) _active_vertices.push_back(interv.shooting_node.get());
819  if (!interv.controls.front()->isFixed()) _active_vertices.push_back(interv.controls.front().get());
820  }
821  if (!_xf->isFixed()) _active_vertices.push_back(_xf.get());
822  if (!getFirstDtVertexRaw()->isFixed()) _active_vertices.push_back(getFirstDtVertexRaw());
823  }
824  else
825  {
826  for (ShootingInterval& interv : _shooting_intervals)
827  {
828  if (!interv.shooting_node->isFixed()) _active_vertices.push_back(interv.shooting_node.get());
829  if (!interv.controls.front()->isFixed()) _active_vertices.push_back(interv.controls.front().get());
830  if (!interv.dts.front()->isFixed()) _active_vertices.push_back(interv.dts.front().get());
831  }
832  if (!_xf->isFixed()) _active_vertices.push_back(_xf.get());
833  }
834 }
835 
836 void FullDiscretizationGrid::updateDtsFixedFlag()
837 {
838  if (_single_dt && !_shooting_intervals.empty() && !_shooting_intervals.front().dts.empty())
839  {
840  getFirstDtVertexRaw()->setFixed(_dt_fixed);
841  }
842  else
843  {
844  for (ShootingInterval& interv : _shooting_intervals)
845  {
846  for (ScalarVertex::Ptr& dt_vtx : interv.dts)
847  {
848  dt_vtx->setFixed(_dt_fixed);
849  }
850  }
851  }
852 }
853 
854 void FullDiscretizationGrid::updateDts()
855 {
856  // compute average dt
857  double dt = 0;
858 
859  for (const ShootingInterval& interv : _shooting_intervals) dt += interv.getTotalTimeInterval();
860 
861  if (!_shooting_intervals.empty()) dt /= (double)_shooting_intervals.size();
862 
863  if (_single_dt)
864  {
865  ScalarVertex::Ptr single_dt = std::make_shared<ScalarVertex>(dt, _dt_fixed);
866  for (ShootingInterval& interv : _shooting_intervals)
867  {
868  for (ScalarVertex::Ptr& dt_vtx : interv.dts)
869  {
870  dt_vtx = single_dt;
871  }
872  }
873  }
874  else
875  {
876  for (ShootingInterval& interv : _shooting_intervals)
877  {
878  if (interv.dts.empty()) continue;
879  double local_dt = dt / (double)interv.dts.size();
880  for (ScalarVertex::Ptr& dt_vtx : interv.dts)
881  {
882  dt_vtx = std::make_shared<ScalarVertex>(local_dt, _dt_fixed);
883  }
884  }
885  }
886 }
887 
888 #ifdef MESSAGE_SUPPORT
889 void FullDiscretizationGrid::fromMessage(const messages::FullDiscretizationGrid& message, std::stringstream* issues)
890 {
891  if (message.n() < 2 && issues) *issues << "FullDiscretizationGrid: Number of states must be greater than or equal 2.\n";
892 
893  if (message.dt() <= 0 && issues) *issues << "FullDiscretizationGrid: Dt must be greater than 0.0.\n";
894 
895  setN(message.n());
896  setDt(message.dt());
897 
898  // dt
899  setSingleDt(message.single_dt());
900  setDtFixed(message.fixed_dt());
901 
902  int dim_states = 0;
903  int dim_controls = 0;
904 
905  // xf fixed states
906  // if (grid_msg.xf_fixed_size() != _p && issues) *issues << "FullDiscretizationGrid: xf_fixed size does not match state dimension " << _p <<
907  // ".\n";
908  _xf_fixed = Eigen::Map<const Eigen::Matrix<bool, -1, 1>>(message.xf_fixed().data(), message.xf_fixed_size());
909  dim_states = _xf_fixed.rows();
910 
911  // bounds
912  if (message.x_min_size() != dim_states && issues)
913  {
914  *issues << "FullDiscretizationGrid: x_min size does not match dimension of xf_fixed " << dim_states << ".\n";
915  }
916  else
917  {
918  setLowerStateBounds(Eigen::Map<const Eigen::VectorXd>(message.x_min().data(), message.x_min_size()));
919  }
920 
921  if (message.x_max_size() != dim_states && issues)
922  {
923  *issues << "FullDiscretizationGrid: x_max size does not match dimension of xf_fixed " << dim_states << ".\n";
924  }
925  else
926  {
927  setUpperStateBounds(Eigen::Map<const Eigen::VectorXd>(message.x_max().data(), message.x_max_size()));
928  }
929 
930  // if (message.u_min_size() != dim_controls && issues)
931  // *issues << "FullDiscretizationGrid: u_min size does not match control input dimension " << dim_controls << ".\n";
932  // else
933  setLowerControlBounds(Eigen::Map<const Eigen::VectorXd>(message.u_min().data(), message.u_min_size()));
934  dim_controls = message.u_min_size();
935  if (message.u_max_size() != dim_controls && issues)
936  *issues << "FullDiscretizationGrid: u_max size does not match dimension of u_min " << dim_controls << ".\n";
937  // else
938  setUpperControlBounds(Eigen::Map<const Eigen::VectorXd>(message.u_max().data(), message.u_max_size()));
939 
940  // dt bounds
941  setDtBounds(message.dt_min(), message.dt_max());
942 
943  // auto resize
944  if (message.has_resize_strategy())
945  {
946  if (message.resize_strategy().has_no_auto_resize())
947  {
948  disableGridResize();
949  }
950  else if (message.resize_strategy().has_time_based())
951  {
952  setGridResizeTimeBased(message.resize_strategy().time_based().n_max(), message.resize_strategy().time_based().dt_hyst_ratio());
953  }
954  else if (message.resize_strategy().has_redundant_controls())
955  {
956  setGridResizeRedundControls(message.resize_strategy().redundant_controls().n_max(),
957  message.resize_strategy().redundant_controls().num_backup(),
958  message.resize_strategy().redundant_controls().epsilon());
959  }
960  }
961 
962  // others
963  _warm_start = message.warm_start();
964  _prune_trajectory = message.prune_trajectory();
965  _num_states_min = message.n_min();
966 
967  _include_intermediate_constr = message.intermediate_collocation_constr();
968 
969  DiscretizationGrid::initialize(dim_states, dim_controls);
970 }
971 
972 void FullDiscretizationGrid::toMessage(messages::FullDiscretizationGrid& message) const
973 {
974  message.set_n(_num_desired_states);
975  message.set_dt(_dt);
976 
977  // dt
978  message.set_fixed_dt(_dt_fixed);
979  message.set_single_dt(_single_dt);
980 
981  // xf fixed states
982  if (_xf_fixed.size() > 0)
983  {
984  message.mutable_xf_fixed()->Resize(_xf_fixed.size(), false);
985  Eigen::Map<Eigen::Matrix<bool, -1, 1>>(message.mutable_xf_fixed()->mutable_data(), _xf_fixed.size()) = _xf_fixed;
986  }
987 
988  // bounds
989  message.mutable_x_min()->Resize(_x_lower.size(), 0);
990  Eigen::Map<Eigen::VectorXd>(message.mutable_x_min()->mutable_data(), _x_lower.size()) = _x_lower;
991  message.mutable_x_max()->Resize(_x_upper.size(), 0);
992  Eigen::Map<Eigen::VectorXd>(message.mutable_x_max()->mutable_data(), _x_upper.size()) = _x_upper;
993  message.mutable_u_min()->Resize(_u_lower.size(), 0);
994  Eigen::Map<Eigen::VectorXd>(message.mutable_u_min()->mutable_data(), _u_lower.size()) = _u_lower;
995  message.mutable_u_max()->Resize(_u_upper.size(), 0);
996  Eigen::Map<Eigen::VectorXd>(message.mutable_u_max()->mutable_data(), _u_upper.size()) = _u_upper;
997 
998  // dt bounds
999  message.set_dt_min(_dt_lower);
1000  message.set_dt_max(_dt_upper);
1001 
1002  // auto resize
1003  switch (_auto_resize)
1004  {
1005  case AutoResizeStrategy::NoAutoResize:
1006  {
1007  message.mutable_resize_strategy()->mutable_no_auto_resize();
1008  break;
1009  }
1010  case AutoResizeStrategy::TimeBased:
1011  {
1012  message.mutable_resize_strategy()->mutable_time_based()->set_n_max(_num_states_max);
1013  message.mutable_resize_strategy()->mutable_time_based()->set_dt_hyst_ratio(_dt_hyst_ratio);
1014  break;
1015  }
1016  case AutoResizeStrategy::RedundantControls:
1017  {
1018  message.mutable_resize_strategy()->mutable_redundant_controls()->set_n_max(_num_states_max);
1019  message.mutable_resize_strategy()->mutable_redundant_controls()->set_num_backup(_redundant_ctrl_backup);
1020  message.mutable_resize_strategy()->mutable_redundant_controls()->set_epsilon(_redundant_ctrl_epsilon);
1021  break;
1022  }
1023  default:
1024  {
1025  PRINT_ERROR("FullDiscretizationGrid::toMessage(): exporting of the selected auto resize strategy not implemented.");
1026  }
1027  }
1028 
1029  // others
1030  message.set_n_min(_num_states_min);
1031  message.set_warm_start(_warm_start);
1032  message.set_prune_trajectory(_prune_trajectory);
1033  message.set_intermediate_collocation_constr(_include_intermediate_constr);
1034 }
1035 #endif
1036 
1037 } // namespace corbo
Eigen::Map< Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > > ValuesMatMap
Definition: time_series.h:66
#define PRINT_ERROR_NAMED(msg)
Definition: console.h:260
std::shared_ptr< ScalarVertex > Ptr
Definition: scalar_vertex.h:53
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index rows() const
#define max(a, b)
Definition: datatypes.h:20
Scalar * x
#define PRINT_WARNING_NAMED(msg)
Definition: console.h:255
def update(text)
Definition: relicense.py:46
return int(ret)+1
std::shared_ptr< PartiallyFixedVectorVertex > Ptr
#define PRINT_WARNING(msg)
Print msg-stream.
Definition: console.h:145
#define min(a, b)
Definition: datatypes.h:19
A matrix or vector expression mapping an existing array of data.
Definition: Map.h:94
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const AbsReturnType abs() const
std::shared_ptr< DynamicsEvalInterface > Ptr
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const CwiseAbsReturnType cwiseAbs() const
std::shared_ptr< TimeSeries > Ptr
Definition: time_series.h:64
The matrix class, also used for vectors and row-vectors.
Definition: Matrix.h:178
PlainMatrixType mat * n
Definition: eigenvalues.cpp:41
#define PRINT_INFO(msg)
Print msg-stream.
Definition: console.h:117
#define PRINT_ERROR(msg)
Print msg-stream as error msg.
Definition: console.h:173


control_box_rst
Author(s): Christoph Rösmann
autogenerated on Mon Feb 28 2022 22:06:52