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