Re-enable polytopes for closed loop driving
I reproduced the original bug which resulted in this being disabled in a
test and fixed it already (I99a1299e538a395685c2e7e555dfccab48055349).
This commit fixes another issue observed in testing and re-enables the
polytopes.
Change-Id: Ic27e0ab8b2ea6dd480d7998c44530918443874db
diff --git a/frc971/control_loops/drivetrain/ssdrivetrain.cc b/frc971/control_loops/drivetrain/ssdrivetrain.cc
index 049b576..9fdfe1c 100644
--- a/frc971/control_loops/drivetrain/ssdrivetrain.cc
+++ b/frc971/control_loops/drivetrain/ssdrivetrain.cc
@@ -18,98 +18,95 @@
DrivetrainMotorsSS::LimitedDrivetrainLoop::LimitedDrivetrainLoop(
StateFeedbackLoop<4, 2, 2> &&loop)
: StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
- U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0, -1, 0, 0, 1, 0, -1)
+ U_poly_((Eigen::Matrix<double, 4, 2>() << 1, 0, -1, 0, 0, 1, 0, -1)
.finished(),
(Eigen::Matrix<double, 4, 1>() << 12.0, 12.0, 12.0, 12.0)
.finished()) {
::aos::controls::HPolytope<0>::Init();
- T << 1, -1, 1, 1;
- T_inverse = T.inverse();
+ T_ << 1, 1, 1, -1;
+ T_inverse_ = T_.inverse();
}
+// This intentionally runs the U-capping code even when it's unnecessary to help
+// make it more deterministic. Only running it when one or both sides want
+// out-of-range voltages could lead to things like running out of CPU under
+// certain situations, which would be bad.
void DrivetrainMotorsSS::LimitedDrivetrainLoop::CapU() {
+ output_was_capped_ = ::std::abs(U(0, 0)) > 12.0 || ::std::abs(U(1, 0)) > 12.0;
+
const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
- if (::std::abs(U(0, 0)) > 12.0 || ::std::abs(U(1, 0)) > 12.0) {
- mutable_U() =
- U() * 12.0 / ::std::max(::std::abs(U(0, 0)), ::std::abs(U(1, 0)));
- LOG_MATRIX(DEBUG, "U is now", U());
- // TODO(Austin): Figure out why the polytope stuff wasn't working and
- // remove this hack.
- output_was_capped_ = true;
- return;
+ LOG_MATRIX(DEBUG, "U at start", U());
+ LOG_MATRIX(DEBUG, "R at start", R());
+ LOG_MATRIX(DEBUG, "Xhat at start", X_hat());
- LOG_MATRIX(DEBUG, "U at start", U());
- LOG_MATRIX(DEBUG, "R at start", R());
- LOG_MATRIX(DEBUG, "Xhat at start", X_hat());
+ Eigen::Matrix<double, 2, 2> position_K;
+ position_K << K(0, 0), K(0, 2), K(1, 0), K(1, 2);
+ Eigen::Matrix<double, 2, 2> velocity_K;
+ velocity_K << K(0, 1), K(0, 3), K(1, 1), K(1, 3);
- Eigen::Matrix<double, 2, 2> position_K;
- position_K << K(0, 0), K(0, 2), K(1, 0), K(1, 2);
- Eigen::Matrix<double, 2, 2> velocity_K;
- velocity_K << K(0, 1), K(0, 3), K(1, 1), K(1, 3);
+ Eigen::Matrix<double, 2, 1> position_error;
+ position_error << error(0, 0), error(2, 0);
+ // drive_error = [total_distance_error, left_error - right_error]
+ const auto drive_error = T_inverse_ * position_error;
+ Eigen::Matrix<double, 2, 1> velocity_error;
+ velocity_error << error(1, 0), error(3, 0);
+ LOG_MATRIX(DEBUG, "error", error);
- Eigen::Matrix<double, 2, 1> position_error;
- position_error << error(0, 0), error(2, 0);
- const auto drive_error = T_inverse * position_error;
- Eigen::Matrix<double, 2, 1> velocity_error;
- velocity_error << error(1, 0), error(3, 0);
- LOG_MATRIX(DEBUG, "error", error);
+ const ::aos::controls::HPolytope<2> pos_poly(U_poly_, position_K * T_,
+ -velocity_K * velocity_error);
- const auto &poly = U_Poly_;
- const Eigen::Matrix<double, 4, 2> pos_poly_H = poly.H() * position_K * T;
- const Eigen::Matrix<double, 4, 1> pos_poly_k =
- poly.k() - poly.H() * velocity_K * velocity_error;
- const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
+ Eigen::Matrix<double, 2, 1> adjusted_pos_error;
+ {
+ const auto &P = drive_error;
- Eigen::Matrix<double, 2, 1> adjusted_pos_error;
- {
- const auto &P = drive_error;
+ Eigen::Matrix<double, 1, 2> L45;
+ L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
+ const double w45 = 0;
- Eigen::Matrix<double, 1, 2> L45;
- L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
- const double w45 = 0;
+ Eigen::Matrix<double, 1, 2> LH;
+ if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
+ LH << 0, 1;
+ } else {
+ LH << 1, 0;
+ }
+ const double wh = LH.dot(P);
- Eigen::Matrix<double, 1, 2> LH;
- if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
- LH << 0, 1;
- } else {
- LH << 1, 0;
- }
- const double wh = LH.dot(P);
+ Eigen::Matrix<double, 2, 2> standard;
+ standard << L45, LH;
+ Eigen::Matrix<double, 2, 1> W;
+ W << w45, wh;
+ const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
- Eigen::Matrix<double, 2, 2> standard;
- standard << L45, LH;
- Eigen::Matrix<double, 2, 1> W;
- W << w45, wh;
- const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
-
- bool is_inside_h;
- const auto adjusted_pos_error_h =
- DoCoerceGoal(pos_poly, LH, wh, drive_error, &is_inside_h);
- const auto adjusted_pos_error_45 =
- DoCoerceGoal(pos_poly, L45, w45, intersection, nullptr);
- if (pos_poly.IsInside(intersection)) {
- adjusted_pos_error = adjusted_pos_error_h;
- } else {
- if (is_inside_h) {
- if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm() ||
- adjusted_pos_error_45.norm() > intersection.norm()) {
- adjusted_pos_error = adjusted_pos_error_h;
- } else {
- adjusted_pos_error = adjusted_pos_error_45;
- }
+ bool is_inside_h, is_inside_45;
+ const auto adjusted_pos_error_h =
+ DoCoerceGoal(pos_poly, LH, wh, drive_error, &is_inside_h);
+ const auto adjusted_pos_error_45 =
+ DoCoerceGoal(pos_poly, L45, w45, intersection, &is_inside_45);
+ if (pos_poly.IsInside(intersection)) {
+ adjusted_pos_error = adjusted_pos_error_h;
+ } else {
+ if (is_inside_h) {
+ if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm() ||
+ adjusted_pos_error_45.norm() > intersection.norm()) {
+ adjusted_pos_error = adjusted_pos_error_h;
} else {
adjusted_pos_error = adjusted_pos_error_45;
}
+ } else {
+ adjusted_pos_error = adjusted_pos_error_45;
}
}
+ }
- LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
- mutable_U() =
- velocity_K * velocity_error + position_K * T * adjusted_pos_error;
- LOG_MATRIX(DEBUG, "U is now", U());
- } else {
- output_was_capped_ = false;
+ mutable_U() =
+ velocity_K * velocity_error + position_K * T_ * adjusted_pos_error;
+ LOG_MATRIX(DEBUG, "U is now", U());
+
+ if (!output_was_capped_) {
+ if ((U() - U_uncapped()).norm() > 0.0001) {
+ LOG(FATAL, "U unnecessarily capped\n");
+ }
}
}