blob: a032cae2f78bd3f3fce6c35009cda963e3ecd4f8 [file] [log] [blame]
Austin Schuhf8f95022018-01-28 20:01:10 -08001#include <chrono>
2#include <cmath>
3#include <thread>
4
5#include <ct/optcon/optcon.h>
Austin Schuhad596222018-01-31 23:34:03 -08006#include <Eigen/Eigenvalues>
Austin Schuhf8f95022018-01-28 20:01:10 -08007
8#include "third_party/gflags/include/gflags/gflags.h"
9#include "third_party/matplotlib-cpp/matplotlibcpp.h"
Austin Schuhad596222018-01-31 23:34:03 -080010#include "y2018/control_loops/python/arm_bounds.h"
11#include "y2018/control_loops/python/dlqr.h"
Austin Schuhf8f95022018-01-28 20:01:10 -080012
Austin Schuhad596222018-01-31 23:34:03 -080013DEFINE_double(boundary_scalar, 1500.0, "Test command-line flag");
14DEFINE_double(velocity_boundary_scalar, 10.0, "Test command-line flag");
15DEFINE_double(boundary_rate, 20.0, "Sigmoid rate");
16DEFINE_bool(linear, false, "If true, linear, else see sigmoid.");
17DEFINE_bool(sigmoid, false, "If true, sigmoid, else exponential.");
Austin Schuhf8f95022018-01-28 20:01:10 -080018DEFINE_double(round_corner, 0.0, "Corner radius of the constraint box.");
Austin Schuhad596222018-01-31 23:34:03 -080019DEFINE_double(convergance, 1e-12, "Residual before finishing the solver.");
20DEFINE_double(position_allowance, 5.0,
21 "Distance to Velocity at which we have 0 penalty conversion.");
22DEFINE_double(bounds_offset, 0.02, "Offset the quadratic boundary in by this");
23DEFINE_double(linear_bounds_offset, 0.00,
24 "Offset the linear boundary in by this");
25DEFINE_double(yrange, 1.0,
26 "+- y max for saturating out the state for the cost function.");
27DEFINE_bool(debug_print, false, "Print the debugging print from the solver.");
28DEFINE_bool(print_starting_summary, true,
29 "Print the summary on the pre-solution.");
30DEFINE_bool(print_summary, false, "Print the summary on each iteration.");
31DEFINE_bool(quadratic, true, "If true, quadratic bounds penalty.");
32
33DEFINE_bool(reset_every_cycle, false,
34 "If true, reset the initial guess every cycle.");
35
36DEFINE_double(seconds, 1.5, "The number of seconds to simulate.");
37
38DEFINE_double(theta0, 1.0, "Starting theta0");
39DEFINE_double(theta1, 0.9, "Starting theta1");
40
41DEFINE_double(goal_theta0, -0.5, "Starting theta0");
42DEFINE_double(goal_theta1, -0.5, "Starting theta1");
43
44DEFINE_double(qpos1, 0.2, "qpos1");
45DEFINE_double(qvel1, 4.0, "qvel1");
46DEFINE_double(qpos2, 0.2, "qpos2");
47DEFINE_double(qvel2, 4.0, "qvel2");
48
49DEFINE_double(u_over_linear, 0.0, "Linear penalty for too much U.");
50DEFINE_double(u_over_quadratic, 4.0, "Quadratic penalty for too much U.");
51
52DEFINE_double(time_horizon, 0.75, "MPC time horizon");
53
54DEFINE_bool(only_print_eigenvalues, false,
55 "If true, stop after computing the final eigenvalues");
56
57DEFINE_bool(plot_xy, false, "If true, plot the xy trajectory of the end of the arm.");
58DEFINE_bool(plot_cost, false, "If true, plot the cost function.");
59DEFINE_bool(plot_state_cost, false,
60 "If true, plot the state portion of the cost function.");
61DEFINE_bool(plot_states, false, "If true, plot the states.");
62DEFINE_bool(plot_u, false, "If true, plot the control signal.");
63
64static constexpr double kDt = 0.00505;
65
66namespace y2018 {
67namespace control_loops {
68
69::Eigen::Matrix<double, 4, 4> NumericalJacobianX(
70 ::Eigen::Matrix<double, 4, 1> (*fn)(
71 ::Eigen::Ref<::Eigen::Matrix<double, 4, 1>> X,
72 ::Eigen::Ref<::Eigen::Matrix<double, 2, 1>> U, double dt),
73 ::Eigen::Matrix<double, 4, 1> X, ::Eigen::Matrix<double, 2, 1> U, double dt,
74 const double kEpsilon = 1e-4) {
75 constexpr int num_states = 4;
76 ::Eigen::Matrix<double, 4, 4> answer = ::Eigen::Matrix<double, 4, 4>::Zero();
77
78 // It's more expensive, but +- epsilon will be more reliable
79 for (int i = 0; i < num_states; ++i) {
80 ::Eigen::Matrix<double, 4, 1> dX_plus = X;
81 dX_plus(i, 0) += kEpsilon;
82 ::Eigen::Matrix<double, 4, 1> dX_minus = X;
83 dX_minus(i, 0) -= kEpsilon;
84 answer.block<4, 1>(0, i) =
85 (fn(dX_plus, U, dt) - fn(dX_minus, U, dt)) / kEpsilon / 2.0;
86 }
87 return answer;
88}
89
90::Eigen::Matrix<double, 4, 2> NumericalJacobianU(
91 ::Eigen::Matrix<double, 4, 1> (*fn)(
92 ::Eigen::Ref<::Eigen::Matrix<double, 4, 1>> X,
93 ::Eigen::Ref<::Eigen::Matrix<double, 2, 1>> U, double dt),
94 ::Eigen::Matrix<double, 4, 1> X, ::Eigen::Matrix<double, 2, 1> U, double dt,
95 const double kEpsilon = 1e-4) {
96 constexpr int num_states = 4;
97 constexpr int num_inputs = 2;
98 ::Eigen::Matrix<double, num_states, num_inputs> answer =
99 ::Eigen::Matrix<double, num_states, num_inputs>::Zero();
100
101 // It's more expensive, but +- epsilon will be more reliable
102 for (int i = 0; i < num_inputs; ++i) {
103 ::Eigen::Matrix<double, 2, 1> dU_plus = U;
104 dU_plus(i, 0) += kEpsilon;
105 ::Eigen::Matrix<double, 2, 1> dU_minus = U;
106 dU_minus(i, 0) -= kEpsilon;
107 answer.block<4, 1>(0, i) =
108 (fn(X, dU_plus, dt) - fn(X, dU_minus, dt)) / kEpsilon / 2.0;
109 }
110 return answer;
111}
Austin Schuhf8f95022018-01-28 20:01:10 -0800112
113// This code is for analysis and simulation of a double jointed arm. It is an
114// attempt to see if a MPC could work for arm control under constraints.
115
116// Describes a double jointed arm.
117// A large chunk of this code comes from demos. Most of the raw pointer,
118// shared_ptr, and non-const &'s come from the library's conventions.
119template <typename SCALAR>
120class MySecondOrderSystem : public ::ct::core::ControlledSystem<4, 2, SCALAR> {
121 public:
122 static const size_t STATE_DIM = 4;
123 static const size_t CONTROL_DIM = 2;
124
125 MySecondOrderSystem(::std::shared_ptr<::ct::core::Controller<4, 2, SCALAR>>
126 controller = nullptr)
127 : ::ct::core::ControlledSystem<4, 2, SCALAR>(
128 controller, ::ct::core::SYSTEM_TYPE::GENERAL) {}
129
130 MySecondOrderSystem(const MySecondOrderSystem &arg)
131 : ::ct::core::ControlledSystem<4, 2, SCALAR>(arg) {}
132
133 // Deep copy
134 MySecondOrderSystem *clone() const override {
135 return new MySecondOrderSystem(*this);
136 }
137 virtual ~MySecondOrderSystem() {}
138
Austin Schuhad596222018-01-31 23:34:03 -0800139 static constexpr SCALAR l1 = 46.25 * 0.0254;
140 static constexpr SCALAR l2 = 41.80 * 0.0254;
141
142 static constexpr SCALAR m1 = 9.34 / 2.2;
143 static constexpr SCALAR m2 = 9.77 / 2.2;
144
145 static constexpr SCALAR J1 = 2957.05 * 0.0002932545454545454;
146 static constexpr SCALAR J2 = 2824.70 * 0.0002932545454545454;
147
148 static constexpr SCALAR r1 = 21.64 * 0.0254;
149 static constexpr SCALAR r2 = 26.70 * 0.0254;
150
151 static constexpr SCALAR G1 = 140.0;
152 static constexpr SCALAR G2 = 90.0;
153
154 static constexpr SCALAR stall_torque = 1.41;
155 static constexpr SCALAR free_speed = (5840.0 / 60.0) * 2.0 * M_PI;
156 static constexpr SCALAR stall_current = 89.0;
157 static constexpr SCALAR R = 12.0 / stall_current;
158
159 static constexpr SCALAR Kv = free_speed / 12.0;
160 static constexpr SCALAR Kt = stall_torque / stall_current;
161
Austin Schuhf8f95022018-01-28 20:01:10 -0800162 // Evaluate the system dynamics.
163 //
164 // Args:
165 // state: current state (position, velocity)
166 // t: current time (gets ignored)
167 // control: control action
168 // derivative: (velocity, acceleration)
169 virtual void computeControlledDynamics(
170 const ::ct::core::StateVector<4, SCALAR> &state, const SCALAR & /*t*/,
171 const ::ct::core::ControlVector<2, SCALAR> &control,
172 ::ct::core::StateVector<4, SCALAR> &derivative) override {
Austin Schuhad596222018-01-31 23:34:03 -0800173 derivative = Dynamics(state, control);
Austin Schuhf8f95022018-01-28 20:01:10 -0800174 }
Austin Schuhad596222018-01-31 23:34:03 -0800175
176 static ::Eigen::Matrix<double, 4, 1> Dynamics(
177 const ::ct::core::StateVector<4, SCALAR> &X,
178 const ::ct::core::ControlVector<2, SCALAR> &U) {
179 ::ct::core::StateVector<4, SCALAR> derivative;
180 const SCALAR alpha = J1 + r1 * r1 * m1 + l1 * l1 * m2;
181 const SCALAR beta = l1 * r2 * m2;
182 const SCALAR gamma = J2 + r2 * r2 * m2;
183
184 const SCALAR s = sin(X(0) - X(2));
185 const SCALAR c = cos(X(0) - X(2));
186
187 // K1 * d^2 theta/dt^2 + K2 * d theta/dt = torque
188 ::Eigen::Matrix<SCALAR, 2, 2> K1;
189 K1(0, 0) = alpha;
190 K1(1, 0) = K1(0, 1) = c * beta;
191 K1(1, 1) = gamma;
192
193 ::Eigen::Matrix<SCALAR, 2, 2> K2 = ::Eigen::Matrix<SCALAR, 2, 2>::Zero();
194 K2(0, 1) = s * beta * X(3);
195 K2(1, 0) = -s * beta * X(1);
196
197 const SCALAR kNumDistalMotors = 2.0;
198 ::Eigen::Matrix<SCALAR, 2, 1> torque;
199 torque(0, 0) = G1 * (U(0) * Kt / R - X(1) * G1 * Kt / (Kv * R));
200 torque(1, 0) = G2 * (U(1) * kNumDistalMotors * Kt / R -
201 X(3) * G2 * Kt * kNumDistalMotors / (Kv * R));
202
203 ::Eigen::Matrix<SCALAR, 2, 1> velocity;
204 velocity(0, 0) = X(0);
205 velocity(1, 0) = X(2);
206
207 const ::Eigen::Matrix<SCALAR, 2, 1> accel =
208 K1.inverse() * (torque - K2 * velocity);
209
210 derivative(0) = X(1);
211 derivative(1) = accel(0);
212 derivative(2) = X(3);
213 derivative(3) = accel(1);
214
215 return derivative;
216 }
217
218 // Runge-Kutta.
219 static ::Eigen::Matrix<double, 4, 1> DiscreteDynamics(
220 ::Eigen::Ref<::Eigen::Matrix<double, 4, 1>> X,
221 ::Eigen::Ref<::Eigen::Matrix<double, 2, 1>> U, double dt) {
222 const double half_dt = dt * 0.5;
223 ::Eigen::Matrix<double, 4, 1> k1 = Dynamics(X, U);
224 ::Eigen::Matrix<double, 4, 1> k2 = Dynamics(X + half_dt * k1, U);
225 ::Eigen::Matrix<double, 4, 1> k3 = Dynamics(X + half_dt * k2, U);
226 ::Eigen::Matrix<double, 4, 1> k4 = Dynamics(X + dt * k3, U);
227 return X + dt / 6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4);
228 }
229};
230
231template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR_EVAL = double,
232 typename SCALAR = SCALAR_EVAL>
233class ObstacleAwareQuadraticCost
234 : public ::ct::optcon::TermBase<STATE_DIM, CONTROL_DIM, SCALAR_EVAL,
235 SCALAR> {
236 public:
237 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
238
239 typedef Eigen::Matrix<SCALAR_EVAL, STATE_DIM, 1> state_vector_t;
240 typedef Eigen::Matrix<SCALAR_EVAL, STATE_DIM, STATE_DIM> state_matrix_t;
241 typedef Eigen::Matrix<SCALAR_EVAL, CONTROL_DIM, CONTROL_DIM> control_matrix_t;
242 typedef Eigen::Matrix<SCALAR_EVAL, CONTROL_DIM, STATE_DIM>
243 control_state_matrix_t;
244 typedef Eigen::Matrix<SCALAR_EVAL, STATE_DIM, STATE_DIM>
245 state_matrix_double_t;
246 typedef Eigen::Matrix<SCALAR_EVAL, CONTROL_DIM, CONTROL_DIM>
247 control_matrix_double_t;
248 typedef Eigen::Matrix<SCALAR_EVAL, CONTROL_DIM, STATE_DIM>
249 control_state_matrix_double_t;
250
251 ObstacleAwareQuadraticCost(const ::Eigen::Matrix<double, 2, 2> &R,
252 const ::Eigen::Matrix<double, 4, 4> &Q)
253 : R_(R), Q_(Q) {}
254
255 ObstacleAwareQuadraticCost(const ObstacleAwareQuadraticCost &arg)
256 : R_(arg.R_), Q_(arg.Q_) {}
257 static constexpr double kEpsilon = 1.0e-5;
258
259 virtual ~ObstacleAwareQuadraticCost() {}
260
261 ObstacleAwareQuadraticCost<STATE_DIM, CONTROL_DIM, SCALAR_EVAL, SCALAR>
262 *clone() const override {
263 return new ObstacleAwareQuadraticCost(*this);
264 }
265
266 double SaturateX(double x, double yrange) {
267 return 2.0 * ((1.0 / (1.0 + ::std::exp(-x * 2.0 / yrange)) - 0.5)) * yrange;
268 }
269
270 SCALAR distance(const Eigen::Matrix<SCALAR, STATE_DIM, 1> &x,
271 const Eigen::Matrix<SCALAR, CONTROL_DIM, 1> & /*u*/) {
272 constexpr double kCornerNewUpper0 = 0.35;
273 // constexpr double kCornerUpper1 = 3.13;
274 // Push it up a bit further (non-real) until we have an actual path cost.
275 constexpr double kCornerNewUpper1 = 3.39;
276 constexpr double kCornerNewUpper0_far = 10.0;
277
278 // Push it up a bit further (non-real) until we have an actual path cost.
279 // constexpr double kCornerUpper0 = 0.315;
280 constexpr double kCornerUpper0 = 0.310;
281 // constexpr double kCornerUpper1 = 3.13;
282 constexpr double kCornerUpper1 = 3.25;
283 constexpr double kCornerUpper0_far = 10.0;
284
285 constexpr double kCornerLower0 = 0.023;
286 constexpr double kCornerLower1 = 1.57;
287 constexpr double kCornerLower0_far = 10.0;
288
289 const Segment new_upper_segment(
290 Point(kCornerNewUpper0, kCornerNewUpper1),
291 Point(kCornerNewUpper0_far, kCornerNewUpper1));
292 const Segment upper_segment(Point(kCornerUpper0, kCornerUpper1),
293 Point(kCornerUpper0_far, kCornerUpper1));
294 const Segment lower_segment(Point(kCornerLower0, kCornerLower1),
295 Point(kCornerLower0_far, kCornerLower1));
296
297 Point current_point(x(0, 0), x(2, 0));
298
299 SCALAR result = 0.0;
300 if (intersects(new_upper_segment,
301 Segment(current_point,
302 Point(FLAGS_goal_theta0, FLAGS_goal_theta1)))) {
303 result += hypot(current_point.x() - kCornerNewUpper0,
304 current_point.y() - kCornerNewUpper1);
305 current_point = Point(kCornerNewUpper0, kCornerNewUpper1);
306 }
307
308 if (intersects(upper_segment,
309 Segment(current_point,
310 Point(FLAGS_goal_theta0, FLAGS_goal_theta1)))) {
311 result += hypot(current_point.x() - kCornerUpper0,
312 current_point.y() - kCornerUpper1);
313 current_point = Point(kCornerUpper0, kCornerUpper1);
314 }
315
316 if (intersects(lower_segment,
317 Segment(current_point,
318 Point(FLAGS_goal_theta0, FLAGS_goal_theta1)))) {
319 result += hypot(current_point.x() - kCornerLower0,
320 current_point.y() - kCornerLower1);
321 current_point = Point(kCornerLower0, kCornerLower1);
322 }
323 result += hypot(current_point.x() - FLAGS_goal_theta0,
324 current_point.y() - FLAGS_goal_theta1);
325 return result;
326 }
327
328 virtual SCALAR evaluate(const Eigen::Matrix<SCALAR, STATE_DIM, 1> &x,
329 const Eigen::Matrix<SCALAR, CONTROL_DIM, 1> &u,
330 const SCALAR & /*t*/) override {
331 // Positive means violation.
332 Eigen::Matrix<SCALAR, STATE_DIM, 1> saturated_x = x;
333 SCALAR d = distance(x, u);
334 saturated_x(0, 0) = d;
335 saturated_x(2, 0) = 0.0;
336
337 saturated_x(0, 0) = SaturateX(saturated_x(0, 0), FLAGS_yrange);
338 saturated_x(2, 0) = 0.0;
339
340 //SCALAR saturation_scalar = saturated_x(0, 0) / d;
341 //saturated_x(1, 0) *= saturation_scalar;
342 //saturated_x(3, 0) *= saturation_scalar;
343
344 SCALAR result = (saturated_x.transpose() * Q_ * saturated_x +
345 u.transpose() * R_ * u)(0, 0);
346
347 if (::std::abs(u(0, 0)) > 11.0) {
348 result += (::std::abs(u(0, 0)) - 11.0) * FLAGS_u_over_linear;
349 result += (::std::abs(u(0, 0)) - 11.0) * (::std::abs(u(0, 0)) - 11.0) *
350 FLAGS_u_over_quadratic;
351 }
352 if (::std::abs(u(1, 0)) > 11.0) {
353 result += (::std::abs(u(1, 0)) - 11.0) * FLAGS_u_over_linear;
354 result += (::std::abs(u(1, 0)) - 11.0) * (::std::abs(u(1, 0)) - 11.0) *
355 FLAGS_u_over_quadratic;
356 }
357 return result;
358 }
359
360 ct::core::StateVector<STATE_DIM, SCALAR_EVAL> stateDerivative(
361 const ct::core::StateVector<STATE_DIM, SCALAR_EVAL> &x,
362 const ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> &u,
363 const SCALAR_EVAL &t) override {
364 SCALAR epsilon = SCALAR(kEpsilon);
365
366 ct::core::StateVector<STATE_DIM, SCALAR_EVAL> result =
367 ct::core::StateVector<STATE_DIM, SCALAR_EVAL>::Zero();
368
369 // Perterb x for both position axis and return the result.
370 for (size_t i = 0; i < STATE_DIM; i += 1) {
371 ct::core::StateVector<STATE_DIM, SCALAR_EVAL> plus_perterbed_x = x;
372 ct::core::StateVector<STATE_DIM, SCALAR_EVAL> minus_perterbed_x = x;
373 plus_perterbed_x[i] += epsilon;
374 minus_perterbed_x[i] -= epsilon;
375 result[i] = (evaluate(plus_perterbed_x, u, t) -
376 evaluate(minus_perterbed_x, u, t)) /
377 (epsilon * 2.0);
378 }
379 return result;
380 }
381
382 // Compute second order derivative of this cost term w.r.t. the state
383 state_matrix_t stateSecondDerivative(
384 const ct::core::StateVector<STATE_DIM, SCALAR_EVAL> &x,
385 const ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> &u,
386 const SCALAR_EVAL &t) override {
387 state_matrix_t result = state_matrix_t::Zero();
388
389 SCALAR epsilon = SCALAR(kEpsilon);
390
391 // Perterb x a second time.
392 for (size_t i = 0; i < STATE_DIM; i += 1) {
393 ct::core::StateVector<STATE_DIM, SCALAR_EVAL> plus_perterbed_x = x;
394 ct::core::StateVector<STATE_DIM, SCALAR_EVAL> minus_perterbed_x = x;
395 plus_perterbed_x[i] += epsilon;
396 minus_perterbed_x[i] -= epsilon;
397 state_vector_t delta = (stateDerivative(plus_perterbed_x, u, t) -
398 stateDerivative(minus_perterbed_x, u, t)) /
399 (epsilon * 2.0);
400
401 result.col(i) = delta;
402 }
403 //::std::cout << "Q_numeric " << result << " endQ" << ::std::endl;
404 return result;
405 }
406
407 ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> controlDerivative(
408 const ct::core::StateVector<STATE_DIM, SCALAR_EVAL> &x,
409 const ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> &u,
410 const SCALAR_EVAL &t) override {
411 ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> result =
412 ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL>::Zero();
413
414 SCALAR epsilon = SCALAR(kEpsilon);
415
416 // Perterb x a second time.
417 for (size_t i = 0; i < CONTROL_DIM; i += 1) {
418 ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> plus_perterbed_u = u;
419 ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> minus_perterbed_u = u;
420 plus_perterbed_u[i] += epsilon;
421 minus_perterbed_u[i] -= epsilon;
422 SCALAR delta = (evaluate(x, plus_perterbed_u, t) -
423 evaluate(x, minus_perterbed_u, t)) /
424 (epsilon * 2.0);
425
426 result[i] = delta;
427 }
428 //::std::cout << "cd " << result(0, 0) << " " << result(1, 0) << " endcd"
429 //<< ::std::endl;
430
431 return result;
432 }
433
434 control_state_matrix_t stateControlDerivative(
435 const ct::core::StateVector<STATE_DIM, SCALAR_EVAL> & /*x*/,
436 const ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> & /*u*/,
437 const SCALAR_EVAL & /*t*/) override {
438 // No coupling here, so let's not bother to calculate it.
439 control_state_matrix_t result = control_state_matrix_t::Zero();
440 return result;
441 }
442
443 control_matrix_t controlSecondDerivative(
444 const ct::core::StateVector<STATE_DIM, SCALAR_EVAL> &x,
445 const ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> &u,
446 const SCALAR_EVAL &t) override {
447 control_matrix_t result = control_matrix_t::Zero();
448
449 SCALAR epsilon = SCALAR(kEpsilon);
450
451 //static int j = 0;
452 //::std::this_thread::sleep_for(::std::chrono::milliseconds(j % 10));
453 //int k = ++j;
454 // Perterb x a second time.
455 for (size_t i = 0; i < CONTROL_DIM; i += 1) {
456 ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> plus_perterbed_u = u;
457 ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> minus_perterbed_u = u;
458 plus_perterbed_u[i] += epsilon;
459 minus_perterbed_u[i] -= epsilon;
460 ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> delta =
461 (controlDerivative(x, plus_perterbed_u, t) -
462 controlDerivative(x, minus_perterbed_u, t)) /
463 (epsilon * 2.0);
464
465 //::std::cout << "delta: " << delta(0, 0) << " " << delta(1, 0) << " k "
466 //<< k << ::std::endl;
467 result.col(i) = delta;
468 }
469 //::std::cout << "R_numeric " << result << " endR 0.013888888888888888 k:" << k
470 //<< ::std::endl;
471 //::std::cout << "x " << x << " u " << u << " k " << k << ::std::endl;
472
473 return result;
474 }
475
476 private:
477 const ::Eigen::Matrix<double, 2, 2> R_;
478 const ::Eigen::Matrix<double, 4, 4> Q_;
Austin Schuhf8f95022018-01-28 20:01:10 -0800479};
480
481template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR_EVAL = double,
482 typename SCALAR = SCALAR_EVAL>
483class MyTermStateBarrier : public ::ct::optcon::TermBase<STATE_DIM, CONTROL_DIM,
484 SCALAR_EVAL, SCALAR> {
485 public:
486 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
487
488 typedef Eigen::Matrix<SCALAR_EVAL, STATE_DIM, 1> state_vector_t;
489 typedef Eigen::Matrix<SCALAR_EVAL, STATE_DIM, STATE_DIM> state_matrix_t;
490 typedef Eigen::Matrix<SCALAR_EVAL, CONTROL_DIM, CONTROL_DIM> control_matrix_t;
491 typedef Eigen::Matrix<SCALAR_EVAL, CONTROL_DIM, STATE_DIM>
492 control_state_matrix_t;
493 typedef Eigen::Matrix<SCALAR_EVAL, STATE_DIM, STATE_DIM>
494 state_matrix_double_t;
495 typedef Eigen::Matrix<SCALAR_EVAL, CONTROL_DIM, CONTROL_DIM>
496 control_matrix_double_t;
497 typedef Eigen::Matrix<SCALAR_EVAL, CONTROL_DIM, STATE_DIM>
498 control_state_matrix_double_t;
499
Austin Schuhad596222018-01-31 23:34:03 -0800500 MyTermStateBarrier(BoundsCheck *bounds_check) : bounds_check_(bounds_check) {}
Austin Schuhf8f95022018-01-28 20:01:10 -0800501
Austin Schuhad596222018-01-31 23:34:03 -0800502 MyTermStateBarrier(const MyTermStateBarrier &arg)
503 : bounds_check_(arg.bounds_check_) {}
Austin Schuhf8f95022018-01-28 20:01:10 -0800504
Austin Schuhad596222018-01-31 23:34:03 -0800505 static constexpr double kEpsilon = 5.0e-6;
Austin Schuhf8f95022018-01-28 20:01:10 -0800506
507 virtual ~MyTermStateBarrier() {}
508
509 MyTermStateBarrier<STATE_DIM, CONTROL_DIM, SCALAR_EVAL, SCALAR> *clone()
510 const override {
511 return new MyTermStateBarrier(*this);
512 }
513
Austin Schuhad596222018-01-31 23:34:03 -0800514 SCALAR distance(const Eigen::Matrix<SCALAR, STATE_DIM, 1> &x,
515 const Eigen::Matrix<SCALAR, CONTROL_DIM, 1> & /*u*/,
516 const SCALAR & /*t*/, Eigen::Matrix<SCALAR, 2, 1> *norm) {
517 return bounds_check_->min_distance(Point(x(0, 0), x(2, 0)), norm);
518 }
519
Austin Schuhf8f95022018-01-28 20:01:10 -0800520 virtual SCALAR evaluate(const Eigen::Matrix<SCALAR, STATE_DIM, 1> &x,
Austin Schuhad596222018-01-31 23:34:03 -0800521 const Eigen::Matrix<SCALAR, CONTROL_DIM, 1> & u,
522 const SCALAR & t) override {
523 Eigen::Matrix<SCALAR, 2, 1> norm = Eigen::Matrix<SCALAR, 2, 1>::Zero();
524 SCALAR min_distance = distance(x, u, t, &norm);
Austin Schuhf8f95022018-01-28 20:01:10 -0800525
Austin Schuhad596222018-01-31 23:34:03 -0800526 // Velocity component (+) towards the wall.
527 SCALAR velocity_penalty = -(x(1, 0) * norm(0, 0) + x(3, 0) * norm(1, 0));
528 if (min_distance + FLAGS_bounds_offset < 0.0) {
529 velocity_penalty = 0.0;
530 }
Austin Schuhf8f95022018-01-28 20:01:10 -0800531
Austin Schuhad596222018-01-31 23:34:03 -0800532 SCALAR result;
533 //if (FLAGS_quadratic) {
534 result = FLAGS_boundary_scalar *
535 ::std::max(0.0, min_distance + FLAGS_bounds_offset) *
536 ::std::max(0.0, min_distance + FLAGS_bounds_offset) +
537 FLAGS_boundary_rate *
538 ::std::max(0.0, min_distance + FLAGS_linear_bounds_offset) +
539 FLAGS_velocity_boundary_scalar *
540 ::std::max(0.0, min_distance + FLAGS_linear_bounds_offset) *
541 ::std::max(0.0, velocity_penalty) *
542 ::std::max(0.0, velocity_penalty);
543 /*
544} else if (FLAGS_linear) {
545result =
546FLAGS_boundary_scalar * ::std::max(0.0, min_distance) +
547FLAGS_velocity_boundary_scalar * ::std::max(0.0, -velocity_penalty);
548} else if (FLAGS_sigmoid) {
549result = FLAGS_boundary_scalar /
550 (1.0 + ::std::exp(-min_distance * FLAGS_boundary_rate)) +
551FLAGS_velocity_boundary_scalar /
552 (1.0 + ::std::exp(-velocity_penalty * FLAGS_boundary_rate));
553} else {
554// Values of 4 and 15 work semi resonably.
555result = FLAGS_boundary_scalar *
556 ::std::exp(min_distance * FLAGS_boundary_rate) +
557FLAGS_velocity_boundary_scalar *
558 ::std::exp(velocity_penalty * FLAGS_boundary_rate);
559}
560if (result < 0.0) {
561printf("Result negative %f\n", result);
562}
563*/
564 return result;
Austin Schuhf8f95022018-01-28 20:01:10 -0800565 }
566
567 ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> stateDerivative(
568 const ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> &x,
569 const ::ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> &u,
570 const SCALAR_EVAL &t) override {
571 SCALAR epsilon = SCALAR(kEpsilon);
572
573 ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> result =
574 ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL>::Zero();
575
576 // Perturb x for both position axis and return the result.
577 for (size_t i = 0; i < STATE_DIM; i += 2) {
578 ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> plus_perterbed_x = x;
579 ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> minus_perterbed_x = x;
580 plus_perterbed_x[i] += epsilon;
581 minus_perterbed_x[i] -= epsilon;
582 result[i] = (evaluate(plus_perterbed_x, u, t) -
583 evaluate(minus_perterbed_x, u, t)) /
584 (epsilon * 2.0);
585 }
586 return result;
587 }
588
589 // Compute second order derivative of this cost term w.r.t. the state
590 state_matrix_t stateSecondDerivative(
591 const ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> &x,
592 const ::ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> &u,
593 const SCALAR_EVAL &t) override {
594 state_matrix_t result = state_matrix_t::Zero();
595
596 SCALAR epsilon = SCALAR(kEpsilon);
597
598 // Perturb x a second time.
Austin Schuhad596222018-01-31 23:34:03 -0800599 for (size_t i = 0; i < STATE_DIM; i += 1) {
Austin Schuhf8f95022018-01-28 20:01:10 -0800600 ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> plus_perterbed_x = x;
601 ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> minus_perterbed_x = x;
602 plus_perterbed_x[i] += epsilon;
603 minus_perterbed_x[i] -= epsilon;
604 state_vector_t delta = (stateDerivative(plus_perterbed_x, u, t) -
605 stateDerivative(minus_perterbed_x, u, t)) /
606 (epsilon * 2.0);
607
608 result.col(i) = delta;
609 }
610 return result;
611 }
612
613 ::ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> controlDerivative(
614 const ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> & /*x*/,
615 const ::ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> & /*u*/,
616 const SCALAR_EVAL & /*t*/) override {
617 return ::ct::core::StateVector<CONTROL_DIM, SCALAR_EVAL>::Zero();
618 }
619
620 control_state_matrix_t stateControlDerivative(
621 const ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> & /*x*/,
622 const ::ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> & /*u*/,
623 const SCALAR_EVAL & /*t*/) override {
624 control_state_matrix_t result = control_state_matrix_t::Zero();
625
626 return result;
627 }
628
629 control_matrix_t controlSecondDerivative(
630 const ::ct::core::StateVector<STATE_DIM, SCALAR_EVAL> & /*x*/,
631 const ::ct::core::ControlVector<CONTROL_DIM, SCALAR_EVAL> & /*u*/,
632 const SCALAR_EVAL & /*t*/) override {
633 control_matrix_t result = control_matrix_t::Zero();
634 return result;
635 }
636
637 /*
638 // TODO(austin): Implement this for the automatic differentiation.
639 virtual ::ct::core::ADCGScalar evaluateCppadCg(
640 const ::ct::core::StateVector<STATE_DIM, ::ct::core::ADCGScalar> &x,
641 const ::ct::core::ControlVector<CONTROL_DIM, ::ct::core::ADCGScalar> &u,
642 ::ct::core::ADCGScalar t) override {
643 ::ct::core::ADCGScalar c = ::ct::core::ADCGScalar(0.0);
644 for (size_t i = 0; i < STATE_DIM; i++)
645 c += barriers_[i].computeActivation(x(i));
646 return c;
647 }
648 */
Austin Schuhad596222018-01-31 23:34:03 -0800649
650 BoundsCheck *bounds_check_;
Austin Schuhf8f95022018-01-28 20:01:10 -0800651};
652
Austin Schuhad596222018-01-31 23:34:03 -0800653int Main() {
654 // PRELIMINIARIES
655 BoundsCheck arm_space = MakeClippedArmSpace();
656
Austin Schuhf8f95022018-01-28 20:01:10 -0800657 constexpr size_t state_dim = MySecondOrderSystem<double>::STATE_DIM;
658 constexpr size_t control_dim = MySecondOrderSystem<double>::CONTROL_DIM;
659
660 ::std::shared_ptr<ct::core::ControlledSystem<state_dim, control_dim>>
661 oscillator_dynamics(new MySecondOrderSystem<double>());
662
663 ::std::shared_ptr<ct::core::SystemLinearizer<state_dim, control_dim>>
664 ad_linearizer(new ::ct::core::SystemLinearizer<state_dim, control_dim>(
665 oscillator_dynamics));
666
Austin Schuhad596222018-01-31 23:34:03 -0800667 const double kQPos1 = FLAGS_qpos1;
668 const double kQVel1 = FLAGS_qvel1;
669 const double kQPos2 = FLAGS_qpos2;
670 const double kQVel2 = FLAGS_qvel2;
671
Austin Schuhf8f95022018-01-28 20:01:10 -0800672 ::Eigen::Matrix<double, 4, 4> Q_step;
Austin Schuhad596222018-01-31 23:34:03 -0800673 Q_step << 1.0 / (kQPos1 * kQPos1), 0.0, 0.0, 0.0, 0.0,
674 1.0 / (kQVel1 * kQVel1), 0.0, 0.0, 0.0, 0.0, 1.0 / (kQPos2 * kQPos2), 0.0,
675 0.0, 0.0, 0.0, 1.0 / (kQVel2 * kQVel2);
Austin Schuhf8f95022018-01-28 20:01:10 -0800676 ::Eigen::Matrix<double, 2, 2> R_step;
677 R_step << 1.0 / (12.0 * 12.0), 0.0, 0.0, 1.0 / (12.0 * 12.0);
Austin Schuhad596222018-01-31 23:34:03 -0800678 ::std::shared_ptr<::ct::optcon::TermQuadratic<state_dim, control_dim>>
679 quadratic_intermediate_cost(
680 new ::ct::optcon::TermQuadratic<state_dim, control_dim>(Q_step,
681 R_step));
682 // TODO(austin): Move back to this with the new Q and R
683 ::std::shared_ptr<ObstacleAwareQuadraticCost<state_dim, control_dim>>
684 intermediate_cost(new ObstacleAwareQuadraticCost<4, 2>(R_step, Q_step));
Austin Schuhf8f95022018-01-28 20:01:10 -0800685
Austin Schuhad596222018-01-31 23:34:03 -0800686 ::Eigen::Matrix<double, 4, 4> final_A =
687 NumericalJacobianX(MySecondOrderSystem<double>::DiscreteDynamics,
688 Eigen::Matrix<double, 4, 1>::Zero(),
689 Eigen::Matrix<double, 2, 1>::Zero(), kDt);
690
691 ::Eigen::Matrix<double, 4, 2> final_B =
692 NumericalJacobianU(MySecondOrderSystem<double>::DiscreteDynamics,
693 Eigen::Matrix<double, 4, 1>::Zero(),
694 Eigen::Matrix<double, 2, 1>::Zero(), kDt);
695
696 ::Eigen::Matrix<double, 4, 4> S_lqr;
697 ::Eigen::Matrix<double, 2, 4> K_lqr;
698 ::frc971::controls::dlqr(final_A, final_B, Q_step, R_step, &K_lqr, &S_lqr);
699 ::std::cout << "A -> " << ::std::endl << final_A << ::std::endl;
700 ::std::cout << "B -> " << ::std::endl << final_B << ::std::endl;
701 ::std::cout << "K -> " << ::std::endl << K_lqr << ::std::endl;
702 ::std::cout << "S -> " << ::std::endl << S_lqr << ::std::endl;
703 ::std::cout << "Q -> " << ::std::endl << Q_step << ::std::endl;
704 ::std::cout << "R -> " << ::std::endl << R_step << ::std::endl;
705 ::std::cout << "Eigenvalues: " << (final_A - final_B * K_lqr).eigenvalues()
706 << ::std::endl;
707
708 ::Eigen::Matrix<double, 4, 4> Q_final = 0.5 * S_lqr;
709 ::Eigen::Matrix<double, 2, 2> R_final = ::Eigen::Matrix<double, 2, 2>::Zero();
Austin Schuhf8f95022018-01-28 20:01:10 -0800710 ::std::shared_ptr<ct::optcon::TermQuadratic<state_dim, control_dim>>
711 final_cost(new ::ct::optcon::TermQuadratic<state_dim, control_dim>(
712 Q_final, R_final));
Austin Schuhad596222018-01-31 23:34:03 -0800713 if (FLAGS_only_print_eigenvalues) {
714 return 0;
715 }
Austin Schuhf8f95022018-01-28 20:01:10 -0800716
Austin Schuhad596222018-01-31 23:34:03 -0800717 ::std::shared_ptr<MyTermStateBarrier<state_dim, control_dim>> bounds_cost(
718 new MyTermStateBarrier<4, 2>(&arm_space));
Austin Schuhf8f95022018-01-28 20:01:10 -0800719
720 // TODO(austin): Cost function needs constraints.
721 ::std::shared_ptr<::ct::optcon::CostFunctionQuadratic<state_dim, control_dim>>
722 cost_function(
723 new ::ct::optcon::CostFunctionAnalytical<state_dim, control_dim>());
Austin Schuhad596222018-01-31 23:34:03 -0800724 //cost_function->addIntermediateTerm(quadratic_intermediate_cost);
Austin Schuhf8f95022018-01-28 20:01:10 -0800725 cost_function->addIntermediateTerm(intermediate_cost);
726 cost_function->addIntermediateTerm(bounds_cost);
727 cost_function->addFinalTerm(final_cost);
728
729 // STEP 1-D: set up the box constraints for the control input
730 // input box constraint boundaries with sparsities in constraint toolbox
731 // format
732 Eigen::VectorXd u_lb(control_dim);
733 Eigen::VectorXd u_ub(control_dim);
734 u_ub.setConstant(12.0);
735 u_lb = -u_ub;
Austin Schuhad596222018-01-31 23:34:03 -0800736 //::std::cout << "uub " << u_ub << ::std::endl;
737 //::std::cout << "ulb " << u_lb << ::std::endl;
Austin Schuhf8f95022018-01-28 20:01:10 -0800738
739 // constraint terms
740 std::shared_ptr<::ct::optcon::ControlInputConstraint<state_dim, control_dim>>
741 controlConstraint(
742 new ::ct::optcon::ControlInputConstraint<state_dim, control_dim>(
743 u_lb, u_ub));
744 controlConstraint->setName("ControlInputConstraint");
745 // create constraint container
746 std::shared_ptr<
747 ::ct::optcon::ConstraintContainerAnalytical<state_dim, control_dim>>
748 box_constraints(
749 new ::ct::optcon::ConstraintContainerAnalytical<state_dim,
750 control_dim>());
751 // add and initialize constraint terms
752 box_constraints->addIntermediateConstraint(controlConstraint, true);
753 box_constraints->initialize();
754
755 // Starting point.
756 ::ct::core::StateVector<state_dim> x0;
Austin Schuhad596222018-01-31 23:34:03 -0800757 x0 << FLAGS_theta0, 0.0, FLAGS_theta1, 0.0;
Austin Schuhf8f95022018-01-28 20:01:10 -0800758
Austin Schuhad596222018-01-31 23:34:03 -0800759 const ::ct::core::Time kTimeHorizon = FLAGS_time_horizon;
Austin Schuhf8f95022018-01-28 20:01:10 -0800760 ::ct::optcon::OptConProblem<state_dim, control_dim> opt_con_problem(
761 kTimeHorizon, x0, oscillator_dynamics, cost_function, ad_linearizer);
762 ::ct::optcon::NLOptConSettings ilqr_settings;
Austin Schuhad596222018-01-31 23:34:03 -0800763 ilqr_settings.nThreads = 4;
764 ilqr_settings.dt = kDt; // the control discretization in [sec]
Austin Schuhf8f95022018-01-28 20:01:10 -0800765 ilqr_settings.integrator = ::ct::core::IntegrationType::RK4;
Austin Schuhad596222018-01-31 23:34:03 -0800766 ilqr_settings.debugPrint = FLAGS_debug_print;
Austin Schuhf8f95022018-01-28 20:01:10 -0800767 ilqr_settings.discretization =
768 ::ct::optcon::NLOptConSettings::APPROXIMATION::FORWARD_EULER;
769 // ilqr_settings.discretization =
770 // NLOptConSettings::APPROXIMATION::MATRIX_EXPONENTIAL;
Austin Schuhad596222018-01-31 23:34:03 -0800771 ilqr_settings.max_iterations = 40;
772 ilqr_settings.min_cost_improvement = FLAGS_convergance;
Austin Schuhf8f95022018-01-28 20:01:10 -0800773 ilqr_settings.nlocp_algorithm =
Austin Schuhad596222018-01-31 23:34:03 -0800774 //::ct::optcon::NLOptConSettings::NLOCP_ALGORITHM::ILQR;
775 ::ct::optcon::NLOptConSettings::NLOCP_ALGORITHM::GNMS;
Austin Schuhf8f95022018-01-28 20:01:10 -0800776 // the LQ-problems are solved using a custom Gauss-Newton Riccati solver
Austin Schuhf8f95022018-01-28 20:01:10 -0800777 ilqr_settings.lqocp_solver =
Austin Schuhad596222018-01-31 23:34:03 -0800778 ::ct::optcon::NLOptConSettings::LQOCP_SOLVER::GNRICCATI_SOLVER;
779 //ilqr_settings.lqocp_solver =
780 //::ct::optcon::NLOptConSettings::LQOCP_SOLVER::HPIPM_SOLVER;
781 ilqr_settings.printSummary = FLAGS_print_starting_summary;
Austin Schuhf8f95022018-01-28 20:01:10 -0800782 if (ilqr_settings.lqocp_solver ==
783 ::ct::optcon::NLOptConSettings::LQOCP_SOLVER::HPIPM_SOLVER) {
Austin Schuhad596222018-01-31 23:34:03 -0800784 //opt_con_problem.setBoxConstraints(box_constraints);
Austin Schuhf8f95022018-01-28 20:01:10 -0800785 }
786
Austin Schuhad596222018-01-31 23:34:03 -0800787 const size_t num_steps = ilqr_settings.computeK(kTimeHorizon);
788 printf("Using %d steps\n", static_cast<int>(num_steps));
Austin Schuhf8f95022018-01-28 20:01:10 -0800789
790 // Vector of feeback matricies.
791 ::ct::core::FeedbackArray<state_dim, control_dim> u0_fb(
Austin Schuhad596222018-01-31 23:34:03 -0800792 num_steps, ::ct::core::FeedbackMatrix<state_dim, control_dim>::Zero());
Austin Schuhf8f95022018-01-28 20:01:10 -0800793 ::ct::core::ControlVectorArray<control_dim> u0_ff(
Austin Schuhad596222018-01-31 23:34:03 -0800794 num_steps, ::ct::core::ControlVector<control_dim>::Zero());
795 ::ct::core::StateVectorArray<state_dim> x_ref_init(num_steps + 1, x0);
Austin Schuhf8f95022018-01-28 20:01:10 -0800796 ::ct::core::StateFeedbackController<state_dim, control_dim>
797 initial_controller(x_ref_init, u0_ff, u0_fb, ilqr_settings.dt);
798
799 // STEP 2-C: create an NLOptConSolver instance
800 ::ct::optcon::NLOptConSolver<state_dim, control_dim> iLQR(opt_con_problem,
801 ilqr_settings);
802 // Seed it with the initial guess
803 iLQR.setInitialGuess(initial_controller);
804 // we solve the optimal control problem and retrieve the solution
805 iLQR.solve();
806 ::ct::core::StateFeedbackController<state_dim, control_dim> initial_solution =
807 iLQR.getSolution();
808 // MPC-EXAMPLE
809 // we store the initial solution obtained from solving the initial optimal
810 // control problem, and re-use it to initialize the MPC solver in the
811 // following.
812
813 // STEP 1: first, we set up an MPC instance for the iLQR solver and configure
814 // it. Since the MPC class is wrapped around normal Optimal Control Solvers,
815 // we need to different kind of settings, those for the optimal control
816 // solver, and those specific to MPC:
817
818 // 1) settings for the iLQR instance used in MPC. Of course, we use the same
819 // settings as for solving the initial problem ...
820 ::ct::optcon::NLOptConSettings ilqr_settings_mpc = ilqr_settings;
Austin Schuhad596222018-01-31 23:34:03 -0800821 ilqr_settings_mpc.max_iterations = 40;
Austin Schuhf8f95022018-01-28 20:01:10 -0800822 // and we limited the printouts, too.
Austin Schuhad596222018-01-31 23:34:03 -0800823 ilqr_settings_mpc.printSummary = FLAGS_print_summary;
Austin Schuhf8f95022018-01-28 20:01:10 -0800824 // 2) settings specific to model predictive control. For a more detailed
825 // description of those, visit ct/optcon/mpc/MpcSettings.h
826 ::ct::optcon::mpc_settings mpc_settings;
827 mpc_settings.stateForwardIntegration_ = true;
828 mpc_settings.postTruncation_ = false;
829 mpc_settings.measureDelay_ = false;
830 mpc_settings.fixedDelayUs_ = 5000 * 0; // Ignore the delay for now.
831 mpc_settings.delayMeasurementMultiplier_ = 1.0;
832 // mpc_settings.mpc_mode = ::ct::optcon::MPC_MODE::FIXED_FINAL_TIME;
833 mpc_settings.mpc_mode = ::ct::optcon::MPC_MODE::CONSTANT_RECEDING_HORIZON;
834 mpc_settings.coldStart_ = false;
835
836 // STEP 2 : Create the iLQR-MPC object, based on the optimal control problem
837 // and the selected settings.
838 ::ct::optcon::MPC<::ct::optcon::NLOptConSolver<state_dim, control_dim>>
839 ilqr_mpc(opt_con_problem, ilqr_settings_mpc, mpc_settings);
840 // initialize it using the previously computed initial controller
841 ilqr_mpc.setInitialGuess(initial_solution);
842 // STEP 3: running MPC
843 // Here, we run the MPC loop. Note that the general underlying idea is that
844 // you receive a state-estimate together with a time-stamp from your robot or
845 // system. MPC needs to receive both that time information and the state from
846 // your control system. Here, "simulate" the time measurement using
847 // ::std::chrono and wrap everything into a for-loop.
848 // The basic idea of operation is that after receiving time and state
849 // information, one executes the finishIteration() method of MPC.
850 ///
851 auto start_time = ::std::chrono::high_resolution_clock::now();
852 // limit the maximum number of runs in this example
Austin Schuhad596222018-01-31 23:34:03 -0800853 size_t maxNumRuns = FLAGS_seconds / kDt;
Austin Schuhf8f95022018-01-28 20:01:10 -0800854 ::std::cout << "Starting to run MPC" << ::std::endl;
855
856 ::std::vector<double> time_array;
857 ::std::vector<double> theta1_array;
858 ::std::vector<double> omega1_array;
859 ::std::vector<double> theta2_array;
860 ::std::vector<double> omega2_array;
861
862 ::std::vector<double> u0_array;
863 ::std::vector<double> u1_array;
864
Austin Schuhad596222018-01-31 23:34:03 -0800865 ::std::vector<double> x_array;
866 ::std::vector<double> y_array;
867
868 // TODO(austin): Plot x, y of the end of the arm.
869
Austin Schuhf8f95022018-01-28 20:01:10 -0800870 for (size_t i = 0; i < maxNumRuns; i++) {
871 ::std::cout << "Solving iteration " << i << ::std::endl;
872 // Time which has passed since start of MPC
873 auto current_time = ::std::chrono::high_resolution_clock::now();
874 ::ct::core::Time t =
875 1e-6 *
876 ::std::chrono::duration_cast<::std::chrono::microseconds>(current_time -
877 start_time)
878 .count();
Austin Schuhad596222018-01-31 23:34:03 -0800879 {
880 if (FLAGS_reset_every_cycle) {
881 ::ct::core::FeedbackArray<state_dim, control_dim> u0_fb(
882 num_steps,
883 ::ct::core::FeedbackMatrix<state_dim, control_dim>::Zero());
884 ::ct::core::ControlVectorArray<control_dim> u0_ff(
885 num_steps, ::ct::core::ControlVector<control_dim>::Zero());
886 ::ct::core::StateVectorArray<state_dim> x_ref_init(num_steps + 1, x0);
887 ::ct::core::StateFeedbackController<state_dim, control_dim>
888 resolved_controller(x_ref_init, u0_ff, u0_fb, ilqr_settings.dt);
889
890 iLQR.setInitialGuess(initial_controller);
891 // we solve the optimal control problem and retrieve the solution
892 iLQR.solve();
893 resolved_controller = iLQR.getSolution();
894 ilqr_mpc.setInitialGuess(resolved_controller);
895 }
896 }
897
Austin Schuhf8f95022018-01-28 20:01:10 -0800898 // prepare mpc iteration
899 ilqr_mpc.prepareIteration(t);
900 // new optimal policy
901 ::std::shared_ptr<ct::core::StateFeedbackController<state_dim, control_dim>>
902 newPolicy(
903 new ::ct::core::StateFeedbackController<state_dim, control_dim>());
904 // timestamp of the new optimal policy
905 ::ct::core::Time ts_newPolicy;
906 current_time = ::std::chrono::high_resolution_clock::now();
907 t = 1e-6 *
908 ::std::chrono::duration_cast<::std::chrono::microseconds>(current_time -
909 start_time)
910 .count();
Austin Schuhad596222018-01-31 23:34:03 -0800911 // TODO(austin): This is only iterating once... I need to fix that...
912 // NLOptConSolver::solve() runs for upto N iterations. This call runs
913 // runIteration() effectively once. (nlocAlgorithm_ is iLQR)
Austin Schuhf8f95022018-01-28 20:01:10 -0800914 bool success = ilqr_mpc.finishIteration(x0, t, *newPolicy, ts_newPolicy);
915 // we break the loop in case the time horizon is reached or solve() failed
916 if (ilqr_mpc.timeHorizonReached() | !success) break;
917
918 ::std::cout << "Solved for time " << newPolicy->time()[0] << " state "
919 << x0.transpose() << " next time " << newPolicy->time()[1]
920 << ::std::endl;
921 ::std::cout << " Solution: Uff " << newPolicy->uff()[0].transpose()
922 << " x_ref_ " << newPolicy->x_ref()[0].transpose()
923 << ::std::endl;
924
925 time_array.push_back(ilqr_settings.dt * i);
926 theta1_array.push_back(x0(0));
927 omega1_array.push_back(x0(1));
928 theta2_array.push_back(x0(2));
929 omega2_array.push_back(x0(3));
930
931 u0_array.push_back(newPolicy->uff()[0](0, 0));
932 u1_array.push_back(newPolicy->uff()[0](1, 0));
933
934 ::std::cout << "xref[1] " << newPolicy->x_ref()[1].transpose()
935 << ::std::endl;
936 ilqr_mpc.doForwardIntegration(0.0, ilqr_settings.dt, x0, newPolicy);
937 ::std::cout << "Next X: " << x0.transpose() << ::std::endl;
938
Austin Schuhad596222018-01-31 23:34:03 -0800939 x_array.push_back(MySecondOrderSystem<double>::l1 * sin(x0(0)) +
940 MySecondOrderSystem<double>::r2 * sin(x0(2)));
941 y_array.push_back(MySecondOrderSystem<double>::l1 * cos(x0(0)) +
942 MySecondOrderSystem<double>::r2 * cos(x0(2)));
943
Austin Schuhf8f95022018-01-28 20:01:10 -0800944 // TODO(austin): Re-use the policy. Maybe? Or maybe mpc already does that.
945 }
946 // The summary contains some statistical data about time delays, etc.
947 ilqr_mpc.printMpcSummary();
948
Austin Schuhad596222018-01-31 23:34:03 -0800949 if (FLAGS_plot_states) {
950 // Now plot our simulation.
951 matplotlibcpp::plot(time_array, theta1_array, {{"label", "theta1"}});
952 matplotlibcpp::plot(time_array, omega1_array, {{"label", "omega1"}});
953 matplotlibcpp::plot(time_array, theta2_array, {{"label", "theta2"}});
954 matplotlibcpp::plot(time_array, omega2_array, {{"label", "omega2"}});
955 matplotlibcpp::legend();
956 }
957
958 if (FLAGS_plot_xy) {
959 matplotlibcpp::figure();
960 matplotlibcpp::plot(x_array, y_array, {{"label", "xy trajectory"}});
961 matplotlibcpp::legend();
962 }
963
964 if (FLAGS_plot_u) {
965 matplotlibcpp::figure();
966 matplotlibcpp::plot(time_array, u0_array, {{"label", "u0"}});
967 matplotlibcpp::plot(time_array, u1_array, {{"label", "u1"}});
968 matplotlibcpp::legend();
969 }
970
971 ::std::vector<::std::vector<double>> cost_x;
972 ::std::vector<::std::vector<double>> cost_y;
973 ::std::vector<::std::vector<double>> cost_z;
974 ::std::vector<::std::vector<double>> cost_state_z;
975
976 for (double x_coordinate = -0.5; x_coordinate < 1.2; x_coordinate += 0.05) {
977 ::std::vector<double> cost_x_row;
978 ::std::vector<double> cost_y_row;
979 ::std::vector<double> cost_z_row;
980 ::std::vector<double> cost_state_z_row;
981
982 for (double y_coordinate = -1.0; y_coordinate < 6.0; y_coordinate += 0.05) {
983 cost_x_row.push_back(x_coordinate);
984 cost_y_row.push_back(y_coordinate);
985 Eigen::Matrix<double, 4, 1> state_matrix;
986 state_matrix << x_coordinate, 0.0, y_coordinate, 0.0;
987 Eigen::Matrix<double, 2, 1> u_matrix =
988 Eigen::Matrix<double, 2, 1>::Zero();
989 cost_state_z_row.push_back(
990 intermediate_cost->distance(state_matrix, u_matrix));
991 cost_z_row.push_back(
992 ::std::min(bounds_cost->evaluate(state_matrix, u_matrix, 0.0), 50.0));
993 }
994 cost_x.push_back(cost_x_row);
995 cost_y.push_back(cost_y_row);
996 cost_z.push_back(cost_z_row);
997 cost_state_z.push_back(cost_state_z_row);
998 }
999
1000 if (FLAGS_plot_cost) {
1001 matplotlibcpp::plot_surface(cost_x, cost_y, cost_z);
1002 }
1003
1004 if (FLAGS_plot_state_cost) {
1005 matplotlibcpp::plot_surface(cost_x, cost_y, cost_state_z);
1006 }
Austin Schuhf8f95022018-01-28 20:01:10 -08001007
1008 matplotlibcpp::figure();
1009 matplotlibcpp::plot(theta1_array, theta2_array, {{"label", "trajectory"}});
Austin Schuhad596222018-01-31 23:34:03 -08001010 ::std::vector<double> bounds_x;
1011 ::std::vector<double> bounds_y;
1012 for (const Point p : arm_space.points()) {
1013 bounds_x.push_back(p.x());
1014 bounds_y.push_back(p.y());
1015 }
1016 matplotlibcpp::plot(bounds_x, bounds_y, {{"label", "allowed region"}});
Austin Schuhf8f95022018-01-28 20:01:10 -08001017 matplotlibcpp::legend();
1018
Austin Schuhf8f95022018-01-28 20:01:10 -08001019 matplotlibcpp::show();
Austin Schuhad596222018-01-31 23:34:03 -08001020
1021 return 0;
1022}
1023
1024} // namespace control_loops
1025} // namespace y2018
1026
1027int main(int argc, char **argv) {
1028 gflags::ParseCommandLineFlags(&argc, &argv, false);
1029 return ::y2018::control_loops::Main();
Austin Schuhf8f95022018-01-28 20:01:10 -08001030}