- 3.0.2 optimal control module.
MPC-impl.h
Go to the documentation of this file.
1 /**********************************************************************************************************************
2 This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich
3 Licensed under the BSD-2 license (see LICENSE file in main directory)
4  **********************************************************************************************************************/
5 
6 #pragma once
7 
8 namespace ct {
9 namespace optcon {
10 
11 template <typename OPTCON_SOLVER>
13  const typename OPTCON_SOLVER::Settings_t& solverSettings,
14  const mpc_settings& mpcsettings,
15  std::shared_ptr<PolicyHandler<Policy_t, STATE_DIM, CONTROL_DIM, Scalar_t>> customPolicyHandler,
16  std::shared_ptr<tpl::MpcTimeHorizon<Scalar_t>> customTimeHorizon)
17  :
18 
19  solver_(problem, solverSettings),
20  mpc_settings_(mpcsettings),
21  dynamics_(problem.getNonlinearSystem()->clone()),
22  forwardIntegrator_(dynamics_, mpcsettings.stateForwardIntegratorType_),
23  firstRun_(true),
24  runCallCounter_(0),
25  policyHandler_(new PolicyHandler<Policy_t, STATE_DIM, CONTROL_DIM, Scalar_t>())
26 {
27  checkSettings(mpcsettings);
28 
29  // =========== INIT WARM START STRATEGY =============
30 
31  if (mpc_settings_.coldStart_ == false)
32  {
33  if (customPolicyHandler)
34  {
35  std::cout << "Initializing MPC with a custom policy handler (warmstarter) provided by the user."
36  << std::endl;
37  policyHandler_ = customPolicyHandler;
38  }
39  else
40  {
41  if (std::is_base_of<NLOptConSolver<STATE_DIM, CONTROL_DIM, P_DIM, V_DIM, Scalar_t>, OPTCON_SOLVER>::value)
42  {
43  // default policy handler for standard discrete-time iLQG implementation
44  policyHandler_ = std::shared_ptr<PolicyHandler<Policy_t, STATE_DIM, CONTROL_DIM, Scalar_t>>(
46  }
47  else
48  {
49  throw std::runtime_error(
50  "ERROR in MPC Constructor -- no default warm start strategy available for the selected "
51  "controller.");
52  }
53  }
54  }
55 
56 
57  // ============== INIT MPC TIME HORIZON STRATEGY ==============
58  if (customTimeHorizon)
59  {
60  std::cout << "Initializing MPC with a custom time-horizon strategy provided by the user." << std::endl;
61  timeHorizonStrategy_ = customTimeHorizon;
62  }
63  else
64  {
65  const Scalar_t initTimeHorizon = solver_.getTimeHorizon();
66 
67  timeHorizonStrategy_ = std::shared_ptr<tpl::MpcTimeHorizon<Scalar_t>>(
68  new tpl::MpcTimeHorizon<Scalar_t>(mpc_settings_, initTimeHorizon));
69  }
70 
71 
72  // ============== INIT MPC TIME KEEPER ==============
73  timeKeeper_ = tpl::MpcTimeKeeper<Scalar_t>(timeHorizonStrategy_, mpc_settings_);
74 }
75 
76 
77 template <typename OPTCON_SOLVER>
79 {
80  return solver_;
81 }
82 
83 
84 template <typename OPTCON_SOLVER>
86 {
87  timeHorizonStrategy_ = timeHorizonStrategy;
88 }
89 
90 
91 template <typename OPTCON_SOLVER>
93 {
94  solver_.setInitialGuess(initGuess);
95  policyHandler_->setPolicy(initGuess);
96  currentPolicy_ = initGuess;
97 }
98 
99 
100 template <typename OPTCON_SOLVER>
102 {
103  return timeKeeper_.finalPointReached();
104 }
105 
106 
107 template <typename OPTCON_SOLVER>
109 {
110  return timeKeeper_.timeSinceFirstSuccessfulSolve(extTime);
111 }
112 
113 
114 template <typename OPTCON_SOLVER>
116  const Scalar_t& t_forward_stop,
118  const std::shared_ptr<core::Controller<STATE_DIM, CONTROL_DIM, Scalar_t>> forwardIntegrationController)
119 {
120  if (mpc_settings_.stateForwardIntegration_ == true)
121  {
122  if (forwardIntegrationController)
123  {
124  // ... either with a given third-party controller
125  integrateForward(t_forward_start, t_forward_stop, x_start, forwardIntegrationController);
126  }
127  else
128  {
129  // ... or with the controller obtained from the solver (solution of last mpc-run).
130  std::shared_ptr<Policy_t> prevController(new Policy_t(currentPolicy_));
131  integrateForward(t_forward_start, t_forward_stop, x_start, prevController);
132  }
133  }
134 }
135 
136 
137 template <typename OPTCON_SOLVER>
139 {
140 #ifdef DEBUG_PRINT_MPC
141  std::cout << "DEBUG_PRINT_MPC: started to prepare MPC iteration() " << std::endl;
142 #endif //DEBUG_PRINT_MPC
143 
144  runCallCounter_++;
145 
146  const Scalar_t currTimeHorizon = solver_.getTimeHorizon();
147  Scalar_t newTimeHorizon;
148 
149 
150  if (firstRun_)
151  timeKeeper_.initialize();
152 
153  timeKeeper_.computeNewTimings(extTime, currTimeHorizon, newTimeHorizon, t_forward_start_, t_forward_stop_);
154 
155  // update the Optimal Control Solver with new time horizon and state information
156  solver_.changeTimeHorizon(newTimeHorizon);
157 
158 
159  // Calculate new initial guess / warm-starting policy
160  policyHandler_->designWarmStartingPolicy(t_forward_stop_, newTimeHorizon, currentPolicy_);
161 
162  // todo: remove this after through testing
163  if (t_forward_stop_ < t_forward_start_)
164  throw std::runtime_error("ERROR: t_forward_stop < t_forward_start is impossible.");
165 
166 
170  solver_.setInitialGuess(currentPolicy_);
171 
172  solver_.prepareMPCIteration();
173 }
174 
175 
176 template <typename OPTCON_SOLVER>
178  const Scalar_t x_ts,
179  Policy_t& newPolicy,
180  Scalar_t& newPolicy_ts,
181  const std::shared_ptr<core::Controller<STATE_DIM, CONTROL_DIM, Scalar_t>> forwardIntegrationController)
182 {
183 #ifdef DEBUG_PRINT_MPC
184  std::cout << "DEBUG_PRINT_MPC: started mpc finish Iteration() with state-timestamp " << x_ts << std::endl;
185 #endif //DEBUG_PRINT_MPC
186 
187  timeKeeper_.startDelayMeasurement(x_ts);
188 
189  // initialize the time-stamp for policy which is to be designed
190  newPolicy_ts = x_ts;
191 
193 
194  if (!firstRun_)
195  doForwardIntegration(t_forward_start_, t_forward_stop_, x_start, forwardIntegrationController);
196 
197 
198  solver_.changeInitialState(x_start);
199 
200  bool solveSuccessful = solver_.finishMPCIteration();
201 
202  if (solveSuccessful)
203  {
204  newPolicy_ts = newPolicy_ts + (t_forward_stop_ - t_forward_start_);
205 
206  // get optimized policy and state trajectory from OptConSolver
207  currentPolicy_ = solver_.getSolution();
208 
209  // obtain the time which passed since the previous successful solve
210  Scalar_t dtp = timeKeeper_.timeSincePreviousSuccessfulSolve(x_ts);
211 
212  // post-truncation may be an option of the solve-call took longer than the estimated delay
213  if (mpc_settings_.postTruncation_)
214  {
215  if (dtp > t_forward_stop_ && !firstRun_)
216  {
217  // the time-difference to be account for by post-truncation
218  Scalar_t dt_post_truncation = dtp - t_forward_stop_;
219 
220 #ifdef DEBUG_PRINT_MPC
221  std::cout << "DEBUG_PRINT_MPC: additional post-truncation about " << dt_post_truncation << " [sec]."
222  << std::endl;
223 #endif //DEBUG_PRINT_MPC
224 
225  // the time which was effectively truncated away (e.g. discrete-time case)
226  Scalar_t dt_truncated_eff;
227 
228  policyHandler_->truncateSolutionFront(dt_post_truncation, currentPolicy_, dt_truncated_eff);
229 
230  // update policy timestamp with the truncated time
231  newPolicy_ts += dt_truncated_eff;
232  }
233  else if (t_forward_stop_ >= dtp && !firstRun_)
234  {
235 #ifdef DEBUG_PRINT_MPC
236  std::cout << "DEBUG_PRINT_MPC: controller opt faster than pre-integration horizon. Consider tuning "
237  "pre-integration. "
238  << std::endl;
239 #endif //DEBUG_PRINT_MPC
240  }
241 
242  } // post truncation
243 
244  } // solve successful
245 
246 
247 #ifdef DEBUG_PRINT_MPC
248  std::cout << "DEBUG_PRINT_MPC: start timestamp outgoing policy: " << newPolicy_ts << std::endl;
249  std::cout << "DEBUG_PRINT_MPC: ended finishIteration() " << std::endl << std::endl;
250 #endif //DEBUG_PRINT_MPC
251 
252  // update policy result
253  newPolicy = currentPolicy_;
254 
255  // stop the delay measurement. This needs to be the last method called in finishIteration().
256  timeKeeper_.stopDelayMeasurement(x_ts);
257 
258  // in the first run, the policy time-stamp needs to be shifted about the solving time
259  if (firstRun_)
260  {
261  newPolicy_ts = newPolicy_ts + timeKeeper_.getMeasuredDelay();
262  firstRun_ = false;
263  }
264 
265  return solveSuccessful;
266 }
267 
268 
269 template <typename OPTCON_SOLVER>
270 void MPC<OPTCON_SOLVER>::resetMpc(const Scalar_t& newTimeHorizon)
271 {
272  firstRun_ = true;
273 
274  runCallCounter_ = 0;
275 
276  // reset the time horizon of the strategy
277  timeHorizonStrategy_->updateInitialTimeHorizon(newTimeHorizon);
278 
279  solver_.changeTimeHorizon(newTimeHorizon);
280 
281  timeKeeper_.initialize();
282 }
283 
284 
285 template <typename OPTCON_SOLVER>
287 {
288  checkSettings(settings);
289 
290  if (settings.stateForwardIntegratorType_ != mpc_settings_.stateForwardIntegratorType_)
291  forwardIntegrator_ = ct::core::Integrator<STATE_DIM, Scalar_t>(dynamics_, settings.stateForwardIntegratorType_);
292 
293  mpc_settings_ = settings;
294  timeKeeper_.updateSettings(settings);
295  timeHorizonStrategy_->updateSettings(settings);
296 }
297 
298 
299 template <typename OPTCON_SOLVER>
301 {
302  std::cout << std::endl;
303  std::cout << "================ MPC Summary ================" << std::endl;
304  std::cout << "Number of MPC calls:\t\t\t" << runCallCounter_ << std::endl;
305 
306  if (mpc_settings_.measureDelay_)
307  {
308  std::cout << "Max measured solving time [sec]:\t" << timeKeeper_.getMaxMeasuredDelay() << std::endl;
309  std::cout << "Min measured solving time [sec]:\t" << timeKeeper_.getMinMeasuredDelay() << std::endl;
310  std::cout << "Total sum of meas. delay [sec]: \t" << timeKeeper_.getSummedDelay() << std::endl;
311  std::cout << "Average measured delay [sec]: \t" << timeKeeper_.getSummedDelay() / runCallCounter_
312  << std::endl;
313  }
314  else
315  {
316  std::cout << "Used fixed delay[sec]: \t" << 0.000001 * mpc_settings_.fixedDelayUs_ << std::endl;
317  }
318 
319  std::cout << "================ End Summary ================" << std::endl;
320  std::cout << std::endl;
321 }
322 
323 
324 template <typename OPTCON_SOLVER>
326  const Scalar_t stopTime,
328  const std::shared_ptr<core::Controller<STATE_DIM, CONTROL_DIM, Scalar_t>>& controller)
329 {
330  dynamics_->setController(controller);
331 
332  int nSteps = std::max(0, (int)std::lround((stopTime - startTime) / mpc_settings_.stateForwardIntegration_dt_));
333 
334  // adaptive pre-integration
335  forwardIntegrator_.integrate_n_steps(state, startTime, nSteps, mpc_settings_.stateForwardIntegration_dt_);
336 }
337 
338 
339 template <typename OPTCON_SOLVER>
341 {
342  // if external timing is active, internal delay measurement must be turned off
343  if (settings.useExternalTiming_ && settings.measureDelay_)
344  throw std::runtime_error(
345  "MPC: measuring delay internally contradicts using external Timing. Switch off one of those options.");
346 }
347 
348 } //namespace optcon
349 } //namespace ct
void computeNewTimings(const SCALAR externalTime, const SCALAR current_T, SCALAR &new_T, SCALAR &t_forw_start, SCALAR &t_forw_stop)
compute new mpc timings, based on current time horizon and the given time horizon strategy ...
Definition: MpcTimeKeeper.h:98
void printMpcSummary()
printout simple statistical data
Definition: MPC-impl.h:300
bool measureDelay_
Definition: MpcSettings.h:71
const bool finalPointReached() const
query this in order to find out if the final time horizon has been reached.
Definition: MpcTimeKeeper.h:192
void updateSettings(const mpc_settings &settings)
update mpc settings
Definition: MpcTimeKeeper.h:198
Definition: PolicyHandler.h:12
void integrate_n_steps(StateVector< STATE_DIM, SCALAR > &state, const SCALAR &startTime, size_t numSteps, SCALAR dt, StateVectorArray< STATE_DIM, SCALAR > &stateTrajectory, tpl::TimeArray< SCALAR > &timeTrajectory)
const Scalar_t timeSinceFirstSuccessfulSolve(const Scalar_t &extTime)
retrieve the time that elapsed since the first successful solve() call to an Optimal Control Problem ...
Definition: MPC-impl.h:108
bool finishIteration(const core::StateVector< STATE_DIM, Scalar_t > &x, const Scalar_t x_ts, Policy_t &newPolicy, Scalar_t &newPolicy_ts, const std::shared_ptr< core::Controller< STATE_DIM, CONTROL_DIM, Scalar_t >> forwardIntegrationController=nullptr)
finish MPC iteration
Definition: MPC-impl.h:177
SCALAR timeSincePreviousSuccessfulSolve(const SCALAR &externalTime)
retrieve the time that elapsed since the last successful solve() call to an Optimal Control Problem ...
Definition: MpcTimeKeeper.h:265
bool timeHorizonReached()
Check if final time horizon for this task was reached.
Definition: MPC-impl.h:101
MPC(const OptConProblem_t &problem, const typename OPTCON_SOLVER::Settings_t &solverSettings, const mpc_settings &mpcsettings=mpc_settings(), std::shared_ptr< PolicyHandler< Policy_t, STATE_DIM, CONTROL_DIM, Scalar_t >> customPolicyHandler=nullptr, std::shared_ptr< tpl::MpcTimeHorizon< Scalar_t >> customTimeHorizon=nullptr)
MPC solver constructor.
Definition: MPC-impl.h:12
void prepareIteration(const Scalar_t &ext_ts)
Definition: MPC-impl.h:138
const SCALAR & getMeasuredDelay() const
obtain the delay which was measured during solving the optimal control problem
Definition: MpcTimeKeeper.h:310
double stateForwardIntegration_dt_
Definition: MpcSettings.h:63
the default policy handler for iLQR
Definition: StateFeedbackPolicyHandler.h:13
bool coldStart_
Definition: MpcSettings.h:111
const SCALAR & getMaxMeasuredDelay() const
get the maximum measured delay (maximum over all cycles)
Definition: MpcTimeKeeper.h:312
const SCALAR & getSummedDelay() const
get the sum of all measured delays
Definition: MpcTimeKeeper.h:316
typename OPTCON_SOLVER::Policy_t Policy_t
Definition: MPC.h:67
bool postTruncation_
Definition: MpcSettings.h:94
const SCALAR & getMinMeasuredDelay() const
get the smallest measured delay (minimum over all cycles)
Definition: MpcTimeKeeper.h:314
void doForwardIntegration(const Scalar_t &t_forward_start, const Scalar_t &t_forward_stop, core::StateVector< STATE_DIM, Scalar_t > &x_start, const std::shared_ptr< core::Controller< STATE_DIM, CONTROL_DIM, Scalar_t >> forwardIntegrationController=nullptr)
perform forward integration of the measured system state, to compensate for expected or already occur...
Definition: MPC-impl.h:115
MPC Settings struct.
Definition: MpcSettings.h:45
Main MPC class.
Definition: MPC.h:55
void setTimeHorizonStrategy(std::shared_ptr< tpl::MpcTimeHorizon< Scalar_t >> timeHorizonStrategy)
Additional method to insert a custom time horizon strategy, independent from the constructor.
Definition: MPC-impl.h:85
Definition: MpcTimeHorizon.h:20
typename OPTCON_SOLVER::Scalar_t Scalar_t
Definition: MPC.h:66
const SCALAR timeSinceFirstSuccessfulSolve(const SCALAR &externalTime)
retrieve the time that elapsed since the first successful solve() call to an Optimal Control Problem ...
Definition: MpcTimeKeeper.h:290
ct::core::StateVector< state_dim > x
Definition: LoadFromFileTest.cpp:20
bool useExternalTiming_
Definition: MpcSettings.h:118
bool stateForwardIntegration_
Definition: MpcSettings.h:53
ct::core::IntegrationType stateForwardIntegratorType_
Definition: MpcSettings.h:58
void resetMpc(const Scalar_t &newTimeHorizon)
reset the mpc problem and provide new problem time horizon (mandatory)
Definition: MPC-impl.h:270
Definition: NLOptConSolver.hpp:29
int fixedDelayUs_
Definition: MpcSettings.h:84
void startDelayMeasurement(const SCALAR &externalTime)
start measuring time elapsed during planning / solving the optimal control problem ...
Definition: MpcTimeKeeper.h:200
void setInitialGuess(const Policy_t &initGuess)
set a new initial guess for the policy
Definition: MPC-impl.h:92
void initialize()
initialize the Mpc Time Keeper (mandatory)
Definition: MpcTimeKeeper.h:64
void stopDelayMeasurement(const SCALAR &externalTime)
stop measuring time elapsed during solving the optimal control problem
Definition: MpcTimeKeeper.h:213
Definition: OptConProblemBase.h:40
void updateSettings(const mpc_settings &settings)
update the mpc settings in all instances (main class, time keeper class, etc)
Definition: MPC-impl.h:286
OPTCON_SOLVER & getSolver()
Allows access to the solver member, required mainly for unit testing.
Definition: MPC-impl.h:78