Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 1 | #include <chrono> |
| 2 | #include <cmath> |
| 3 | #include <thread> |
| 4 | |
| 5 | #include <ct/optcon/optcon.h> |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 6 | #include <Eigen/Eigenvalues> |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 7 | |
| 8 | #include "third_party/gflags/include/gflags/gflags.h" |
| 9 | #include "third_party/matplotlib-cpp/matplotlibcpp.h" |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 10 | #include "y2018/control_loops/python/arm_bounds.h" |
| 11 | #include "y2018/control_loops/python/dlqr.h" |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 12 | |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 13 | DEFINE_double(boundary_scalar, 1500.0, "Test command-line flag"); |
| 14 | DEFINE_double(velocity_boundary_scalar, 10.0, "Test command-line flag"); |
| 15 | DEFINE_double(boundary_rate, 20.0, "Sigmoid rate"); |
| 16 | DEFINE_bool(linear, false, "If true, linear, else see sigmoid."); |
| 17 | DEFINE_bool(sigmoid, false, "If true, sigmoid, else exponential."); |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 18 | DEFINE_double(round_corner, 0.0, "Corner radius of the constraint box."); |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 19 | DEFINE_double(convergance, 1e-12, "Residual before finishing the solver."); |
| 20 | DEFINE_double(position_allowance, 5.0, |
| 21 | "Distance to Velocity at which we have 0 penalty conversion."); |
| 22 | DEFINE_double(bounds_offset, 0.02, "Offset the quadratic boundary in by this"); |
| 23 | DEFINE_double(linear_bounds_offset, 0.00, |
| 24 | "Offset the linear boundary in by this"); |
| 25 | DEFINE_double(yrange, 1.0, |
| 26 | "+- y max for saturating out the state for the cost function."); |
| 27 | DEFINE_bool(debug_print, false, "Print the debugging print from the solver."); |
| 28 | DEFINE_bool(print_starting_summary, true, |
| 29 | "Print the summary on the pre-solution."); |
| 30 | DEFINE_bool(print_summary, false, "Print the summary on each iteration."); |
| 31 | DEFINE_bool(quadratic, true, "If true, quadratic bounds penalty."); |
| 32 | |
| 33 | DEFINE_bool(reset_every_cycle, false, |
| 34 | "If true, reset the initial guess every cycle."); |
| 35 | |
| 36 | DEFINE_double(seconds, 1.5, "The number of seconds to simulate."); |
| 37 | |
| 38 | DEFINE_double(theta0, 1.0, "Starting theta0"); |
| 39 | DEFINE_double(theta1, 0.9, "Starting theta1"); |
| 40 | |
| 41 | DEFINE_double(goal_theta0, -0.5, "Starting theta0"); |
| 42 | DEFINE_double(goal_theta1, -0.5, "Starting theta1"); |
| 43 | |
| 44 | DEFINE_double(qpos1, 0.2, "qpos1"); |
| 45 | DEFINE_double(qvel1, 4.0, "qvel1"); |
| 46 | DEFINE_double(qpos2, 0.2, "qpos2"); |
| 47 | DEFINE_double(qvel2, 4.0, "qvel2"); |
| 48 | |
| 49 | DEFINE_double(u_over_linear, 0.0, "Linear penalty for too much U."); |
| 50 | DEFINE_double(u_over_quadratic, 4.0, "Quadratic penalty for too much U."); |
| 51 | |
| 52 | DEFINE_double(time_horizon, 0.75, "MPC time horizon"); |
| 53 | |
| 54 | DEFINE_bool(only_print_eigenvalues, false, |
| 55 | "If true, stop after computing the final eigenvalues"); |
| 56 | |
| 57 | DEFINE_bool(plot_xy, false, "If true, plot the xy trajectory of the end of the arm."); |
| 58 | DEFINE_bool(plot_cost, false, "If true, plot the cost function."); |
| 59 | DEFINE_bool(plot_state_cost, false, |
| 60 | "If true, plot the state portion of the cost function."); |
| 61 | DEFINE_bool(plot_states, false, "If true, plot the states."); |
| 62 | DEFINE_bool(plot_u, false, "If true, plot the control signal."); |
| 63 | |
| 64 | static constexpr double kDt = 0.00505; |
| 65 | |
| 66 | namespace y2018 { |
| 67 | namespace 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 112 | |
| 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. |
| 119 | template <typename SCALAR> |
| 120 | class 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 139 | 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 162 | // 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 173 | derivative = Dynamics(state, control); |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 174 | } |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 175 | |
| 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 | |
| 231 | template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR_EVAL = double, |
| 232 | typename SCALAR = SCALAR_EVAL> |
| 233 | class 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 479 | }; |
| 480 | |
| 481 | template <size_t STATE_DIM, size_t CONTROL_DIM, typename SCALAR_EVAL = double, |
| 482 | typename SCALAR = SCALAR_EVAL> |
| 483 | class 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 500 | MyTermStateBarrier(BoundsCheck *bounds_check) : bounds_check_(bounds_check) {} |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 501 | |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 502 | MyTermStateBarrier(const MyTermStateBarrier &arg) |
| 503 | : bounds_check_(arg.bounds_check_) {} |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 504 | |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 505 | static constexpr double kEpsilon = 5.0e-6; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 506 | |
| 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 514 | 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 520 | virtual SCALAR evaluate(const Eigen::Matrix<SCALAR, STATE_DIM, 1> &x, |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 521 | 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 525 | |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 526 | // 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 531 | |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 532 | 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) { |
| 545 | result = |
| 546 | FLAGS_boundary_scalar * ::std::max(0.0, min_distance) + |
| 547 | FLAGS_velocity_boundary_scalar * ::std::max(0.0, -velocity_penalty); |
| 548 | } else if (FLAGS_sigmoid) { |
| 549 | result = FLAGS_boundary_scalar / |
| 550 | (1.0 + ::std::exp(-min_distance * FLAGS_boundary_rate)) + |
| 551 | FLAGS_velocity_boundary_scalar / |
| 552 | (1.0 + ::std::exp(-velocity_penalty * FLAGS_boundary_rate)); |
| 553 | } else { |
| 554 | // Values of 4 and 15 work semi resonably. |
| 555 | result = FLAGS_boundary_scalar * |
| 556 | ::std::exp(min_distance * FLAGS_boundary_rate) + |
| 557 | FLAGS_velocity_boundary_scalar * |
| 558 | ::std::exp(velocity_penalty * FLAGS_boundary_rate); |
| 559 | } |
| 560 | if (result < 0.0) { |
| 561 | printf("Result negative %f\n", result); |
| 562 | } |
| 563 | */ |
| 564 | return result; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 565 | } |
| 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 599 | for (size_t i = 0; i < STATE_DIM; i += 1) { |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 600 | ::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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 649 | |
| 650 | BoundsCheck *bounds_check_; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 651 | }; |
| 652 | |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 653 | int Main() { |
| 654 | // PRELIMINIARIES |
| 655 | BoundsCheck arm_space = MakeClippedArmSpace(); |
| 656 | |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 657 | 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 667 | 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 672 | ::Eigen::Matrix<double, 4, 4> Q_step; |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 673 | 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 676 | ::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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 678 | ::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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 685 | |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 686 | ::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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 710 | ::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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 713 | if (FLAGS_only_print_eigenvalues) { |
| 714 | return 0; |
| 715 | } |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 716 | |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 717 | ::std::shared_ptr<MyTermStateBarrier<state_dim, control_dim>> bounds_cost( |
| 718 | new MyTermStateBarrier<4, 2>(&arm_space)); |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 719 | |
| 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 724 | //cost_function->addIntermediateTerm(quadratic_intermediate_cost); |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 725 | 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 736 | //::std::cout << "uub " << u_ub << ::std::endl; |
| 737 | //::std::cout << "ulb " << u_lb << ::std::endl; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 738 | |
| 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 757 | x0 << FLAGS_theta0, 0.0, FLAGS_theta1, 0.0; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 758 | |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 759 | const ::ct::core::Time kTimeHorizon = FLAGS_time_horizon; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 760 | ::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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 763 | ilqr_settings.nThreads = 4; |
| 764 | ilqr_settings.dt = kDt; // the control discretization in [sec] |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 765 | ilqr_settings.integrator = ::ct::core::IntegrationType::RK4; |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 766 | ilqr_settings.debugPrint = FLAGS_debug_print; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 767 | ilqr_settings.discretization = |
| 768 | ::ct::optcon::NLOptConSettings::APPROXIMATION::FORWARD_EULER; |
| 769 | // ilqr_settings.discretization = |
| 770 | // NLOptConSettings::APPROXIMATION::MATRIX_EXPONENTIAL; |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 771 | ilqr_settings.max_iterations = 40; |
| 772 | ilqr_settings.min_cost_improvement = FLAGS_convergance; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 773 | ilqr_settings.nlocp_algorithm = |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 774 | //::ct::optcon::NLOptConSettings::NLOCP_ALGORITHM::ILQR; |
| 775 | ::ct::optcon::NLOptConSettings::NLOCP_ALGORITHM::GNMS; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 776 | // the LQ-problems are solved using a custom Gauss-Newton Riccati solver |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 777 | ilqr_settings.lqocp_solver = |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 778 | ::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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 782 | if (ilqr_settings.lqocp_solver == |
| 783 | ::ct::optcon::NLOptConSettings::LQOCP_SOLVER::HPIPM_SOLVER) { |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 784 | //opt_con_problem.setBoxConstraints(box_constraints); |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 785 | } |
| 786 | |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 787 | const size_t num_steps = ilqr_settings.computeK(kTimeHorizon); |
| 788 | printf("Using %d steps\n", static_cast<int>(num_steps)); |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 789 | |
| 790 | // Vector of feeback matricies. |
| 791 | ::ct::core::FeedbackArray<state_dim, control_dim> u0_fb( |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 792 | num_steps, ::ct::core::FeedbackMatrix<state_dim, control_dim>::Zero()); |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 793 | ::ct::core::ControlVectorArray<control_dim> u0_ff( |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 794 | num_steps, ::ct::core::ControlVector<control_dim>::Zero()); |
| 795 | ::ct::core::StateVectorArray<state_dim> x_ref_init(num_steps + 1, x0); |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 796 | ::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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 821 | ilqr_settings_mpc.max_iterations = 40; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 822 | // and we limited the printouts, too. |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 823 | ilqr_settings_mpc.printSummary = FLAGS_print_summary; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 824 | // 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 853 | size_t maxNumRuns = FLAGS_seconds / kDt; |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 854 | ::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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 865 | ::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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 870 | 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 879 | { |
| 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 898 | // 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 911 | // 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 914 | 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 939 | 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 944 | // 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 Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 949 | 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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 1007 | |
| 1008 | matplotlibcpp::figure(); |
| 1009 | matplotlibcpp::plot(theta1_array, theta2_array, {{"label", "trajectory"}}); |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 1010 | ::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 Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 1017 | matplotlibcpp::legend(); |
| 1018 | |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 1019 | matplotlibcpp::show(); |
Austin Schuh | ad59622 | 2018-01-31 23:34:03 -0800 | [diff] [blame^] | 1020 | |
| 1021 | return 0; |
| 1022 | } |
| 1023 | |
| 1024 | } // namespace control_loops |
| 1025 | } // namespace y2018 |
| 1026 | |
| 1027 | int main(int argc, char **argv) { |
| 1028 | gflags::ParseCommandLineFlags(&argc, &argv, false); |
| 1029 | return ::y2018::control_loops::Main(); |
Austin Schuh | f8f9502 | 2018-01-28 20:01:10 -0800 | [diff] [blame] | 1030 | } |