blob: 243cb2d1d99189a4bd2005bce935bc480ea65706 [file] [log] [blame]
#include "y2022/control_loops/superstructure/catapult/catapult.h"
#include "Eigen/Dense"
#include "Eigen/Sparse"
#include "aos/realtime.h"
#include "aos/time/time.h"
#include "glog/logging.h"
#include "osqp++.h"
#include "osqp.h"
#include "y2022/control_loops/superstructure/catapult/catapult_plant.h"
namespace y2022 {
namespace control_loops {
namespace superstructure {
namespace catapult {
namespace chrono = std::chrono;
namespace {
osqp::OsqpInstance MakeInstance(
size_t horizon, Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> P) {
osqp::OsqpInstance instance;
instance.objective_matrix = P.cast<c_float>().sparseView();
instance.constraint_matrix =
Eigen::SparseMatrix<c_float, Eigen::ColMajor, osqp::c_int>(horizon,
horizon);
instance.constraint_matrix.setIdentity();
instance.lower_bounds =
Eigen::Matrix<c_float, Eigen::Dynamic, 1>::Zero(horizon, 1);
instance.upper_bounds =
Eigen::Matrix<c_float, Eigen::Dynamic, 1>::Ones(horizon, 1) * 12.0;
return instance;
}
} // namespace
MPCProblem::MPCProblem(size_t horizon,
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> P,
Eigen::Matrix<double, Eigen::Dynamic, 1> accel_q,
Eigen::Matrix<double, 2, 2> Af,
Eigen::Matrix<double, Eigen::Dynamic, 2> final_q)
: horizon_(horizon),
accel_q_(std::move(accel_q.cast<c_float>())),
Af_(std::move(Af.cast<c_float>())),
final_q_(std::move(final_q.cast<c_float>())),
instance_(MakeInstance(horizon, std::move(P))) {
instance_.objective_vector =
Eigen::Matrix<c_float, Eigen::Dynamic, 1>(horizon, 1);
settings_.max_iter = 25;
settings_.check_termination = 25;
settings_.warm_start = 0;
auto status = solver_.Init(instance_, settings_);
CHECK(status.ok()) << status;
}
void MPCProblem::SetState(Eigen::Matrix<c_float, 2, 1> X_initial,
Eigen::Matrix<c_float, 2, 1> X_final) {
X_initial_ = X_initial;
X_final_ = X_final;
instance_.objective_vector =
X_initial(1, 0) * accel_q_ + final_q_ * (Af_ * X_initial - X_final);
auto status = solver_.SetObjectiveVector(instance_.objective_vector);
CHECK(status.ok()) << status;
}
bool MPCProblem::Solve() {
const aos::monotonic_clock::time_point start_time =
aos::monotonic_clock::now();
osqp::OsqpExitCode exit_code = solver_.Solve();
const aos::monotonic_clock::time_point end_time = aos::monotonic_clock::now();
VLOG(1) << "OSQP solved in "
<< std::chrono::duration<double>(end_time - start_time).count();
solve_time_ = std::chrono::duration<double>(end_time - start_time).count();
// TODO(austin): Dump the exit codes out as an enum for logging.
//
// TODO(austin): The dual problem doesn't appear to be converging on all
// problems. Are we phrasing something wrong?
// TODO(austin): Set a time limit so we can't run forever, and signal back
// when we hit our limit.
return exit_code == osqp::OsqpExitCode::kOptimal;
}
void MPCProblem::WarmStart(const MPCProblem &p) {
CHECK_GE(p.horizon(), horizon())
<< ": Can only copy a bigger problem's solution into a smaller problem.";
auto status = solver_.SetPrimalWarmStart(p.solver_.primal_solution().block(
p.horizon() - horizon(), 0, horizon(), 1));
CHECK(status.ok()) << status;
status = solver_.SetDualWarmStart(p.solver_.dual_solution().block(
p.horizon() - horizon(), 0, horizon(), 1));
CHECK(status.ok()) << status;
}
CatapultProblemGenerator::CatapultProblemGenerator(size_t horizon)
: plant_(MakeCatapultPlant()),
horizon_(horizon),
Q_final_(
(Eigen::DiagonalMatrix<double, 2>().diagonal() << 10000.0, 10000.0)
.finished()),
As_(MakeAs()),
Bs_(MakeBs()),
m_(Makem()),
M_(MakeM()),
W_(MakeW()),
w_(Makew()),
Pi_(MakePi()),
WM_(W_ * M_),
Wmpw_(W_ * m_ + w_) {}
std::unique_ptr<MPCProblem> CatapultProblemGenerator::MakeProblem(
size_t horizon) {
return std::make_unique<MPCProblem>(
horizon, P(horizon), accel_q(horizon), Af(horizon),
(2.0 * Q_final_ * Bf(horizon)).transpose());
}
const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>
CatapultProblemGenerator::P(size_t horizon) {
CHECK_GT(horizon, 0u);
CHECK_LE(horizon, horizon_);
return 2.0 * (WM_.block(0, 0, horizon, horizon).transpose() * Pi(horizon) *
WM_.block(0, 0, horizon, horizon) +
Bf(horizon).transpose() * Q_final_ * Bf(horizon));
}
const Eigen::Matrix<double, Eigen::Dynamic, 1> CatapultProblemGenerator::q(
size_t horizon, Eigen::Matrix<c_float, 2, 1> X_initial,
Eigen::Matrix<c_float, 2, 1> X_final) {
CHECK_GT(horizon, 0u);
CHECK_LE(horizon, horizon_);
return 2.0 * X_initial(1, 0) * accel_q(horizon) +
2.0 * ((Af(horizon) * X_initial - X_final).transpose() * Q_final_ *
Bf(horizon))
.transpose();
}
const Eigen::Matrix<double, Eigen::Dynamic, 1>
CatapultProblemGenerator::accel_q(size_t horizon) {
return 2.0 * ((Wmpw_.block(0, 0, horizon, 1)).transpose() * Pi(horizon) *
WM_.block(0, 0, horizon, horizon))
.transpose();
}
const Eigen::Matrix<double, 2, 2> CatapultProblemGenerator::Af(size_t horizon) {
CHECK_GT(horizon, 0u);
CHECK_LE(horizon, horizon_);
return As_.block<2, 2>(2 * (horizon - 1), 0);
}
const Eigen::Matrix<double, 2, Eigen::Dynamic> CatapultProblemGenerator::Bf(
size_t horizon) {
CHECK_GT(horizon, 0u);
CHECK_LE(horizon, horizon_);
return Bs_.block(2 * (horizon - 1), 0, 2, horizon);
}
const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>
CatapultProblemGenerator::Pi(size_t horizon) {
CHECK_GT(horizon, 0u);
CHECK_LE(horizon, horizon_);
return Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>(Pi_).block(
horizon_ - horizon, horizon_ - horizon, horizon, horizon);
}
Eigen::Matrix<double, Eigen::Dynamic, 2> CatapultProblemGenerator::MakeAs() {
Eigen::Matrix<double, Eigen::Dynamic, 2> As =
Eigen::Matrix<double, Eigen::Dynamic, 2>::Zero(horizon_ * 2, 2);
for (size_t i = 0; i < horizon_; ++i) {
if (i == 0) {
As.block<2, 2>(0, 0) = plant_.A();
} else {
As.block<2, 2>(i * 2, 0) = plant_.A() * As.block<2, 2>((i - 1) * 2, 0);
}
}
return As;
}
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>
CatapultProblemGenerator::MakeBs() {
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> Bs =
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>::Zero(horizon_ * 2,
horizon_);
for (size_t i = 0; i < horizon_; ++i) {
for (size_t j = 0; j < i + 1; ++j) {
if (i == j) {
Bs.block<2, 1>(i * 2, j) = plant_.B();
} else {
Bs.block<2, 1>(i * 2, j) =
As_.block<2, 2>((i - j - 1) * 2, 0) * plant_.B();
}
}
}
return Bs;
}
Eigen::Matrix<double, Eigen::Dynamic, 1> CatapultProblemGenerator::Makem() {
Eigen::Matrix<double, Eigen::Dynamic, 1> m =
Eigen::Matrix<double, Eigen::Dynamic, 1>::Zero(horizon_, 1);
for (size_t i = 0; i < horizon_; ++i) {
m(i, 0) = As_(1 + 2 * i, 1);
}
return m;
}
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>
CatapultProblemGenerator::MakeM() {
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> M =
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>::Zero(horizon_,
horizon_);
for (size_t i = 0; i < horizon_; ++i) {
for (size_t j = 0; j < horizon_; ++j) {
M(i, j) = Bs_(2 * i + 1, j);
}
}
return M;
}
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>
CatapultProblemGenerator::MakeW() {
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> W =
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>::Identity(horizon_,
horizon_);
for (size_t i = 0; i < horizon_ - 1; ++i) {
W(i + 1, i) = -1.0;
}
W /= std::chrono::duration<double>(plant_.dt()).count();
return W;
}
Eigen::Matrix<double, Eigen::Dynamic, 1> CatapultProblemGenerator::Makew() {
Eigen::Matrix<double, Eigen::Dynamic, 1> w =
Eigen::Matrix<double, Eigen::Dynamic, 1>::Zero(horizon_, 1);
w(0, 0) = -1.0 / std::chrono::duration<double>(plant_.dt()).count();
return w;
}
Eigen::DiagonalMatrix<double, Eigen::Dynamic>
CatapultProblemGenerator::MakePi() {
Eigen::DiagonalMatrix<double, Eigen::Dynamic> Pi(horizon_);
for (size_t i = 0; i < horizon_; ++i) {
Pi.diagonal()(i) =
std::pow(0.01, 2.0) +
std::pow(0.02 * std::max(0.0, (20 - ((int)horizon_ - (int)i)) / 20.),
2.0);
}
return Pi;
}
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>
CatapultProblemGenerator::MakeP() {
return 2.0 * (M_.transpose() * W_.transpose() * Pi_ * W_ * M_ +
Bf(horizon_).transpose() * Q_final_ * Bf(horizon_));
}
} // namespace catapult
} // namespace superstructure
} // namespace control_loops
} // namespace y2022