Austin Schuh | 7400da0 | 2018-01-28 19:54:58 -0800 | [diff] [blame^] | 1 | |
| 2 | #include <ct/optcon/optcon.h> |
| 3 | #include "exampleDir.h" |
| 4 | |
| 5 | using namespace ct::core; |
| 6 | using namespace ct::optcon; |
| 7 | |
| 8 | /*! |
| 9 | * This tutorial example shows how to use the MPC class. In the CT, every optimal control solver can be wrapped into the MPC-class, |
| 10 | * allowing for very rapid prototyping of different MPC applications. |
| 11 | * In this example, we apply iLQR-MPC to a simple second order system, a damped oscillator. |
| 12 | * This tutorial builds up on the example NLOC.cpp, please consider this one as well. |
| 13 | * |
| 14 | * \example NLOC_MPC.cpp |
| 15 | */ |
| 16 | int main(int argc, char **argv) |
| 17 | { |
| 18 | /* PRELIMINIARIES, see example NLOC.cpp */ |
| 19 | |
| 20 | const size_t state_dim = ct::core::SecondOrderSystem::STATE_DIM; |
| 21 | const size_t control_dim = ct::core::SecondOrderSystem::CONTROL_DIM; |
| 22 | |
| 23 | double w_n = 0.1; |
| 24 | double zeta = 5.0; |
| 25 | std::shared_ptr<ct::core::ControlledSystem<state_dim, control_dim>> oscillatorDynamics( |
| 26 | new ct::core::SecondOrderSystem(w_n, zeta)); |
| 27 | |
| 28 | std::shared_ptr<ct::core::SystemLinearizer<state_dim, control_dim>> adLinearizer( |
| 29 | new ct::core::SystemLinearizer<state_dim, control_dim>(oscillatorDynamics)); |
| 30 | |
| 31 | std::shared_ptr<ct::optcon::TermQuadratic<state_dim, control_dim>> intermediateCost( |
| 32 | new ct::optcon::TermQuadratic<state_dim, control_dim>()); |
| 33 | std::shared_ptr<ct::optcon::TermQuadratic<state_dim, control_dim>> finalCost( |
| 34 | new ct::optcon::TermQuadratic<state_dim, control_dim>()); |
| 35 | bool verbose = true; |
| 36 | intermediateCost->loadConfigFile(ct::optcon::exampleDir + "/mpcCost.info", "intermediateCost", verbose); |
| 37 | finalCost->loadConfigFile(ct::optcon::exampleDir + "/mpcCost.info", "finalCost", verbose); |
| 38 | |
| 39 | std::shared_ptr<CostFunctionQuadratic<state_dim, control_dim>> costFunction( |
| 40 | new CostFunctionAnalytical<state_dim, control_dim>()); |
| 41 | costFunction->addIntermediateTerm(intermediateCost); |
| 42 | costFunction->addFinalTerm(finalCost); |
| 43 | |
| 44 | StateVector<state_dim> x0; |
| 45 | x0.setRandom(); |
| 46 | |
| 47 | ct::core::Time timeHorizon = 3.0; |
| 48 | |
| 49 | OptConProblem<state_dim, control_dim> optConProblem( |
| 50 | timeHorizon, x0, oscillatorDynamics, costFunction, adLinearizer); |
| 51 | |
| 52 | |
| 53 | NLOptConSettings ilqr_settings; |
| 54 | ilqr_settings.dt = 0.01; // the control discretization in [sec] |
| 55 | ilqr_settings.integrator = ct::core::IntegrationType::EULERCT; |
| 56 | ilqr_settings.discretization = NLOptConSettings::APPROXIMATION::FORWARD_EULER; |
| 57 | ilqr_settings.max_iterations = 10; |
| 58 | ilqr_settings.nlocp_algorithm = NLOptConSettings::NLOCP_ALGORITHM::ILQR; |
| 59 | ilqr_settings.lqocp_solver = NLOptConSettings::LQOCP_SOLVER:: |
| 60 | GNRICCATI_SOLVER; // the LQ-problems are solved using a custom Gauss-Newton Riccati solver |
| 61 | ilqr_settings.printSummary = true; |
| 62 | |
| 63 | size_t K = ilqr_settings.computeK(timeHorizon); |
| 64 | |
| 65 | FeedbackArray<state_dim, control_dim> u0_fb(K, FeedbackMatrix<state_dim, control_dim>::Zero()); |
| 66 | ControlVectorArray<control_dim> u0_ff(K, ControlVector<control_dim>::Zero()); |
| 67 | StateVectorArray<state_dim> x_ref_init(K + 1, x0); |
| 68 | NLOptConSolver<state_dim, control_dim>::Policy_t initController(x_ref_init, u0_ff, u0_fb, ilqr_settings.dt); |
| 69 | |
| 70 | |
| 71 | // STEP 2-C: create an NLOptConSolver instance |
| 72 | NLOptConSolver<state_dim, control_dim> iLQR(optConProblem, ilqr_settings); |
| 73 | |
| 74 | // set the initial guess |
| 75 | iLQR.setInitialGuess(initController); |
| 76 | |
| 77 | |
| 78 | // we solve the optimal control problem and retrieve the solution |
| 79 | iLQR.solve(); |
| 80 | ct::core::StateFeedbackController<state_dim, control_dim> initialSolution = iLQR.getSolution(); |
| 81 | |
| 82 | |
| 83 | /* MPC-EXAMPLE |
| 84 | * we store the initial solution obtained from solving the initial optimal control problem, |
| 85 | * and re-use it to initialize the MPC solver in the following. */ |
| 86 | |
| 87 | /* STEP 1: first, we set up an MPC instance for the iLQR solver and configure it. Since the MPC |
| 88 | * class is wrapped around normal Optimal Control Solvers, we need to different kind of settings, |
| 89 | * those for the optimal control solver, and those specific to MPC: */ |
| 90 | |
| 91 | // 1) settings for the iLQR instance used in MPC. Of course, we use the same settings |
| 92 | // as for solving the initial problem ... |
| 93 | NLOptConSettings ilqr_settings_mpc = ilqr_settings; |
| 94 | // ... however, in MPC-mode, it makes sense to limit the overall number of iLQR iterations (real-time iteration scheme) |
| 95 | ilqr_settings_mpc.max_iterations = 1; |
| 96 | // and we limited the printouts, too. |
| 97 | ilqr_settings_mpc.printSummary = false; |
| 98 | |
| 99 | |
| 100 | // 2) settings specific to model predictive control. For a more detailed description of those, visit ct/optcon/mpc/MpcSettings.h |
| 101 | ct::optcon::mpc_settings mpc_settings; |
| 102 | mpc_settings.stateForwardIntegration_ = true; |
| 103 | mpc_settings.postTruncation_ = true; |
| 104 | mpc_settings.measureDelay_ = true; |
| 105 | mpc_settings.delayMeasurementMultiplier_ = 1.0; |
| 106 | mpc_settings.mpc_mode = ct::optcon::MPC_MODE::FIXED_FINAL_TIME; |
| 107 | mpc_settings.coldStart_ = false; |
| 108 | |
| 109 | |
| 110 | // STEP 2 : Create the iLQR-MPC object, based on the optimal control problem and the selected settings. |
| 111 | MPC<NLOptConSolver<state_dim, control_dim>> ilqr_mpc(optConProblem, ilqr_settings_mpc, mpc_settings); |
| 112 | |
| 113 | // initialize it using the previously computed initial controller |
| 114 | ilqr_mpc.setInitialGuess(initialSolution); |
| 115 | |
| 116 | |
| 117 | /* STEP 3: running MPC |
| 118 | * Here, we run the MPC loop. Note that the general underlying idea is that you receive a state-estimate |
| 119 | * together with a time-stamp from your robot or system. MPC needs to receive both that time information and |
| 120 | * the state from your control system. Here, "simulate" the time measurement using std::chrono and wrap |
| 121 | * everything into a for-loop. |
| 122 | * The basic idea of operation is that after receiving time and state information, one executes the finishIteration() method of MPC. |
| 123 | */ |
| 124 | auto start_time = std::chrono::high_resolution_clock::now(); |
| 125 | |
| 126 | |
| 127 | // limit the maximum number of runs in this example |
| 128 | size_t maxNumRuns = 100; |
| 129 | |
| 130 | std::cout << "Starting to run MPC" << std::endl; |
| 131 | |
| 132 | for (size_t i = 0; i < maxNumRuns; i++) |
| 133 | { |
| 134 | // let's for simplicity, assume that the "measured" state is the first state from the optimal trajectory plus some noise |
| 135 | if (i > 0) |
| 136 | x0 = 0.1 * StateVector<state_dim>::Random(); |
| 137 | |
| 138 | // time which has passed since start of MPC |
| 139 | auto current_time = std::chrono::high_resolution_clock::now(); |
| 140 | ct::core::Time t = |
| 141 | 1e-6 * std::chrono::duration_cast<std::chrono::microseconds>(current_time - start_time).count(); |
| 142 | |
| 143 | // prepare mpc iteration |
| 144 | ilqr_mpc.prepareIteration(t); |
| 145 | |
| 146 | // new optimal policy |
| 147 | ct::core::StateFeedbackController<state_dim, control_dim> newPolicy; |
| 148 | |
| 149 | // timestamp of the new optimal policy |
| 150 | ct::core::Time ts_newPolicy; |
| 151 | |
| 152 | current_time = std::chrono::high_resolution_clock::now(); |
| 153 | t = 1e-6 * std::chrono::duration_cast<std::chrono::microseconds>(current_time - start_time).count(); |
| 154 | bool success = ilqr_mpc.finishIteration(x0, t, newPolicy, ts_newPolicy); |
| 155 | |
| 156 | // we break the loop in case the time horizon is reached or solve() failed |
| 157 | if (ilqr_mpc.timeHorizonReached() | !success) |
| 158 | break; |
| 159 | } |
| 160 | |
| 161 | |
| 162 | // the summary contains some statistical data about time delays, etc. |
| 163 | ilqr_mpc.printMpcSummary(); |
| 164 | } |