blob: 53cc2dd07bcd21618878b16444eb362cf0e643be [file] [log] [blame]
Austin Schuh64ebab22015-11-26 13:28:30 -08001#include "y2014/control_loops/drivetrain/ssdrivetrain.h"
2
3#include "aos/common/controls/polytope.h"
4#include "aos/common/commonmath.h"
5#include "aos/common/logging/matrix_logging.h"
6
7#include "frc971/control_loops/state_feedback_loop.h"
8#include "frc971/control_loops/coerce_goal.h"
9#include "y2014/constants.h"
10#include "y2014/control_loops/drivetrain/drivetrain.q.h"
11
12namespace frc971 {
13namespace control_loops {
14
15DrivetrainMotorsSS::LimitedDrivetrainLoop::LimitedDrivetrainLoop(
16 StateFeedbackLoop<4, 2, 2> &&loop)
17 : StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
18 U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0, -1, 0, 0, 1, 0, -1)
19 .finished(),
20 (Eigen::Matrix<double, 4, 1>() << 12.0, 12.0, 12.0, 12.0)
21 .finished()) {
22 ::aos::controls::HPolytope<0>::Init();
23 T << 1, -1, 1, 1;
24 T_inverse = T.inverse();
25}
26
27void DrivetrainMotorsSS::LimitedDrivetrainLoop::CapU() {
28 const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
29
30 if (::std::abs(U(0, 0)) > 12.0 || ::std::abs(U(1, 0)) > 12.0) {
31 mutable_U() =
32 U() * 12.0 / ::std::max(::std::abs(U(0, 0)), ::std::abs(U(1, 0)));
33 LOG_MATRIX(DEBUG, "U is now", U());
34 // TODO(Austin): Figure out why the polytope stuff wasn't working and
35 // remove this hack.
36 output_was_capped_ = true;
37 return;
38
39 LOG_MATRIX(DEBUG, "U at start", U());
40 LOG_MATRIX(DEBUG, "R at start", R());
41 LOG_MATRIX(DEBUG, "Xhat at start", X_hat());
42
43 Eigen::Matrix<double, 2, 2> position_K;
44 position_K << K(0, 0), K(0, 2), K(1, 0), K(1, 2);
45 Eigen::Matrix<double, 2, 2> velocity_K;
46 velocity_K << K(0, 1), K(0, 3), K(1, 1), K(1, 3);
47
48 Eigen::Matrix<double, 2, 1> position_error;
49 position_error << error(0, 0), error(2, 0);
50 const auto drive_error = T_inverse * position_error;
51 Eigen::Matrix<double, 2, 1> velocity_error;
52 velocity_error << error(1, 0), error(3, 0);
53 LOG_MATRIX(DEBUG, "error", error);
54
55 const auto &poly = U_Poly_;
56 const Eigen::Matrix<double, 4, 2> pos_poly_H = poly.H() * position_K * T;
57 const Eigen::Matrix<double, 4, 1> pos_poly_k =
58 poly.k() - poly.H() * velocity_K * velocity_error;
59 const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
60
61 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
62 {
63 const auto &P = drive_error;
64
65 Eigen::Matrix<double, 1, 2> L45;
66 L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
67 const double w45 = 0;
68
69 Eigen::Matrix<double, 1, 2> LH;
70 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
71 LH << 0, 1;
72 } else {
73 LH << 1, 0;
74 }
75 const double wh = LH.dot(P);
76
77 Eigen::Matrix<double, 2, 2> standard;
78 standard << L45, LH;
79 Eigen::Matrix<double, 2, 1> W;
80 W << w45, wh;
81 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
82
83 bool is_inside_h;
84 const auto adjusted_pos_error_h =
85 DoCoerceGoal(pos_poly, LH, wh, drive_error, &is_inside_h);
86 const auto adjusted_pos_error_45 =
87 DoCoerceGoal(pos_poly, L45, w45, intersection, nullptr);
88 if (pos_poly.IsInside(intersection)) {
89 adjusted_pos_error = adjusted_pos_error_h;
90 } else {
91 if (is_inside_h) {
92 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
93 adjusted_pos_error = adjusted_pos_error_h;
94 } else {
95 adjusted_pos_error = adjusted_pos_error_45;
96 }
97 } else {
98 adjusted_pos_error = adjusted_pos_error_45;
99 }
100 }
101 }
102
103 LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
104 mutable_U() =
105 velocity_K * velocity_error + position_K * T * adjusted_pos_error;
106 LOG_MATRIX(DEBUG, "U is now", U());
107 } else {
108 output_was_capped_ = false;
109 }
110}
111
112DrivetrainMotorsSS::DrivetrainMotorsSS()
113 : loop_(new LimitedDrivetrainLoop(
114 constants::GetValues().make_drivetrain_loop())),
115 filtered_offset_(0.0),
116 gyro_(0.0),
117 left_goal_(0.0),
118 right_goal_(0.0),
119 raw_left_(0.0),
120 raw_right_(0.0) {
121 // High gear on both.
122 loop_->set_controller_index(3);
123}
124
125void DrivetrainMotorsSS::SetGoal(double left, double left_velocity,
126 double right, double right_velocity) {
127 left_goal_ = left;
128 right_goal_ = right;
129 loop_->mutable_R() << left, left_velocity, right, right_velocity;
130}
131void DrivetrainMotorsSS::SetRawPosition(double left, double right) {
132 raw_right_ = right;
133 raw_left_ = left;
134 Eigen::Matrix<double, 2, 1> Y;
135 Y << left + filtered_offset_, right - filtered_offset_;
136 loop_->Correct(Y);
137}
138void DrivetrainMotorsSS::SetPosition(double left, double right, double gyro) {
139 // Decay the offset quickly because this gyro is great.
140 const double offset =
141 (right - left - gyro * constants::GetValues().turn_width) / 2.0;
142 filtered_offset_ = 0.25 * offset + 0.75 * filtered_offset_;
143 gyro_ = gyro;
144 SetRawPosition(left, right);
145}
146
147void DrivetrainMotorsSS::SetExternalMotors(double left_voltage,
148 double right_voltage) {
149 loop_->mutable_U() << left_voltage, right_voltage;
150}
151
152void DrivetrainMotorsSS::Update(bool stop_motors, bool enable_control_loop) {
153 if (enable_control_loop) {
154 loop_->Update(stop_motors);
155 } else {
156 if (stop_motors) {
157 loop_->mutable_U().setZero();
158 loop_->mutable_U_uncapped().setZero();
159 }
160 loop_->UpdateObserver();
161 }
162 ::Eigen::Matrix<double, 4, 1> E = loop_->R() - loop_->X_hat();
163 LOG_MATRIX(DEBUG, "E", E);
164}
165
166double DrivetrainMotorsSS::GetEstimatedRobotSpeed() const {
167 // lets just call the average of left and right velocities close enough
168 return (loop_->X_hat(1, 0) + loop_->X_hat(3, 0)) / 2;
169}
170
171void DrivetrainMotorsSS::SendMotors(DrivetrainQueue::Output *output) const {
172 if (output) {
173 output->left_voltage = loop_->U(0, 0);
174 output->right_voltage = loop_->U(1, 0);
175 output->left_high = true;
176 output->right_high = true;
177 }
178}
179
180} // namespace control_loops
181} // namespace frc971