blob: 9a3aff1ee26fecf59baaaf3c4608c4357477d98f [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#include "y2014/control_loops/drivetrain/drivetrain.h"
2
3#include <stdio.h>
4#include <sched.h>
5#include <cmath>
6#include <memory>
7#include "Eigen/Dense"
8
9#include "aos/common/logging/logging.h"
10#include "aos/common/controls/polytope.h"
11#include "aos/common/commonmath.h"
12#include "aos/common/logging/queue_logging.h"
13#include "aos/common/logging/matrix_logging.h"
14
15#include "y2014/constants.h"
16#include "frc971/control_loops/state_feedback_loop.h"
17#include "frc971/control_loops/coerce_goal.h"
Brian Silverman17f503e2015-08-02 18:17:18 -070018#include "y2014/control_loops/drivetrain/drivetrain.q.h"
Austin Schuh0e997732015-11-08 15:14:53 -080019#include "y2014/control_loops/drivetrain/drivetrain_dog_motor_plant.h"
Austin Schuh96ce8ae2015-11-26 12:46:02 -080020#include "y2014/control_loops/drivetrain/polydrivetrain.h"
Brian Silverman17f503e2015-08-02 18:17:18 -070021#include "frc971/queues/gyro.q.h"
22#include "frc971/shifter_hall_effect.h"
23
24// A consistent way to mark code that goes away without shifters. It's still
25// here because we will have shifters again in the future.
26#define HAVE_SHIFTERS 1
27
28using frc971::sensors::gyro_reading;
29
30namespace frc971 {
31namespace control_loops {
32
Austin Schuh0e997732015-11-08 15:14:53 -080033using ::y2014::control_loops::drivetrain::kDt;
34
Brian Silverman17f503e2015-08-02 18:17:18 -070035class DrivetrainMotorsSS {
36 public:
37 class LimitedDrivetrainLoop : public StateFeedbackLoop<4, 2, 2> {
38 public:
39 LimitedDrivetrainLoop(StateFeedbackLoop<4, 2, 2> &&loop)
40 : StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
41 U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0,
42 -1, 0,
43 0, 1,
44 0, -1).finished(),
45 (Eigen::Matrix<double, 4, 1>() << 12.0, 12.0,
46 12.0, 12.0).finished()) {
47 ::aos::controls::HPolytope<0>::Init();
48 T << 1, -1, 1, 1;
49 T_inverse = T.inverse();
50 }
51
52 bool output_was_capped() const {
53 return output_was_capped_;
54 }
55
56 private:
57 virtual void CapU() {
58 const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
59
60 if (::std::abs(U(0, 0)) > 12.0 || ::std::abs(U(1, 0)) > 12.0) {
61 mutable_U() =
62 U() * 12.0 / ::std::max(::std::abs(U(0, 0)), ::std::abs(U(1, 0)));
63 LOG_MATRIX(DEBUG, "U is now", U());
64 // TODO(Austin): Figure out why the polytope stuff wasn't working and
65 // remove this hack.
66 output_was_capped_ = true;
67 return;
68
69 LOG_MATRIX(DEBUG, "U at start", U());
70 LOG_MATRIX(DEBUG, "R at start", R());
71 LOG_MATRIX(DEBUG, "Xhat at start", X_hat());
72
73 Eigen::Matrix<double, 2, 2> position_K;
74 position_K << K(0, 0), K(0, 2),
75 K(1, 0), K(1, 2);
76 Eigen::Matrix<double, 2, 2> velocity_K;
77 velocity_K << K(0, 1), K(0, 3),
78 K(1, 1), K(1, 3);
79
80 Eigen::Matrix<double, 2, 1> position_error;
81 position_error << error(0, 0), error(2, 0);
82 const auto drive_error = T_inverse * position_error;
83 Eigen::Matrix<double, 2, 1> velocity_error;
84 velocity_error << error(1, 0), error(3, 0);
85 LOG_MATRIX(DEBUG, "error", error);
86
87 const auto &poly = U_Poly_;
88 const Eigen::Matrix<double, 4, 2> pos_poly_H =
89 poly.H() * position_K * T;
90 const Eigen::Matrix<double, 4, 1> pos_poly_k =
91 poly.k() - poly.H() * velocity_K * velocity_error;
92 const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
93
94 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
95 {
96 const auto &P = drive_error;
97
98 Eigen::Matrix<double, 1, 2> L45;
99 L45 << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
100 const double w45 = 0;
101
102 Eigen::Matrix<double, 1, 2> LH;
103 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
104 LH << 0, 1;
105 } else {
106 LH << 1, 0;
107 }
108 const double wh = LH.dot(P);
109
110 Eigen::Matrix<double, 2, 2> standard;
111 standard << L45, LH;
112 Eigen::Matrix<double, 2, 1> W;
113 W << w45, wh;
114 const Eigen::Matrix<double, 2, 1> intersection =
115 standard.inverse() * W;
116
117 bool is_inside_h;
118 const auto adjusted_pos_error_h =
119 DoCoerceGoal(pos_poly, LH, wh, drive_error, &is_inside_h);
120 const auto adjusted_pos_error_45 =
121 DoCoerceGoal(pos_poly, L45, w45, intersection, nullptr);
122 if (pos_poly.IsInside(intersection)) {
123 adjusted_pos_error = adjusted_pos_error_h;
124 } else {
125 if (is_inside_h) {
126 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
127 adjusted_pos_error = adjusted_pos_error_h;
128 } else {
129 adjusted_pos_error = adjusted_pos_error_45;
130 }
131 } else {
132 adjusted_pos_error = adjusted_pos_error_45;
133 }
134 }
135 }
136
137 LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
138 mutable_U() =
139 velocity_K * velocity_error + position_K * T * adjusted_pos_error;
140 LOG_MATRIX(DEBUG, "U is now", U());
141 } else {
142 output_was_capped_ = false;
143 }
144 }
145
146 const ::aos::controls::HPolytope<2> U_Poly_;
147 Eigen::Matrix<double, 2, 2> T, T_inverse;
148 bool output_was_capped_ = false;;
149 };
150
151 DrivetrainMotorsSS()
152 : loop_(new LimitedDrivetrainLoop(
153 constants::GetValues().make_drivetrain_loop())),
154 filtered_offset_(0.0),
155 gyro_(0.0),
156 left_goal_(0.0),
157 right_goal_(0.0),
158 raw_left_(0.0),
159 raw_right_(0.0) {
Austin Schuh86f895e2015-11-08 13:40:51 -0800160 // High gear on both.
161 loop_->set_controller_index(3);
Brian Silverman17f503e2015-08-02 18:17:18 -0700162 }
163
164 void SetGoal(double left, double left_velocity, double right,
165 double right_velocity) {
166 left_goal_ = left;
167 right_goal_ = right;
168 loop_->mutable_R() << left, left_velocity, right, right_velocity;
169 }
170 void SetRawPosition(double left, double right) {
171 raw_right_ = right;
172 raw_left_ = left;
173 Eigen::Matrix<double, 2, 1> Y;
174 Y << left + filtered_offset_, right - filtered_offset_;
175 loop_->Correct(Y);
176 }
177 void SetPosition(double left, double right, double gyro) {
178 // Decay the offset quickly because this gyro is great.
179 const double offset =
180 (right - left - gyro * constants::GetValues().turn_width) / 2.0;
181 filtered_offset_ = 0.25 * offset + 0.75 * filtered_offset_;
182 gyro_ = gyro;
183 SetRawPosition(left, right);
184 }
185
186 void SetExternalMotors(double left_voltage, double right_voltage) {
187 loop_->mutable_U() << left_voltage, right_voltage;
188 }
189
190 void Update(bool stop_motors, bool enable_control_loop) {
191 if (enable_control_loop) {
192 loop_->Update(stop_motors);
193 } else {
194 if (stop_motors) {
195 loop_->mutable_U().setZero();
196 loop_->mutable_U_uncapped().setZero();
197 }
198 loop_->UpdateObserver();
199 }
200 ::Eigen::Matrix<double, 4, 1> E = loop_->R() - loop_->X_hat();
201 LOG_MATRIX(DEBUG, "E", E);
202 }
203
204 double GetEstimatedRobotSpeed() const {
205 // lets just call the average of left and right velocities close enough
206 return (loop_->X_hat(1, 0) + loop_->X_hat(3, 0)) / 2;
207 }
208
209 double GetEstimatedLeftEncoder() const {
210 return loop_->X_hat(0, 0);
211 }
212
213 double GetEstimatedRightEncoder() const {
214 return loop_->X_hat(2, 0);
215 }
216
217 bool OutputWasCapped() const {
218 return loop_->output_was_capped();
219 }
220
221 void SendMotors(DrivetrainQueue::Output *output) const {
222 if (output) {
223 output->left_voltage = loop_->U(0, 0);
224 output->right_voltage = loop_->U(1, 0);
Austin Schuh86f895e2015-11-08 13:40:51 -0800225 output->left_high = true;
226 output->right_high = true;
Brian Silverman17f503e2015-08-02 18:17:18 -0700227 }
228 }
229
230 const LimitedDrivetrainLoop &loop() const { return *loop_; }
231
232 private:
233 ::std::unique_ptr<LimitedDrivetrainLoop> loop_;
234
235 double filtered_offset_;
236 double gyro_;
237 double left_goal_;
238 double right_goal_;
239 double raw_left_;
240 double raw_right_;
241};
242
Brian Silverman17f503e2015-08-02 18:17:18 -0700243
244void DrivetrainLoop::RunIteration(const DrivetrainQueue::Goal *goal,
245 const DrivetrainQueue::Position *position,
246 DrivetrainQueue::Output *output,
247 DrivetrainQueue::Status * status) {
248 // TODO(aschuh): These should be members of the class.
249 static DrivetrainMotorsSS dt_closedloop;
250 static PolyDrivetrain dt_openloop;
251
252 bool bad_pos = false;
253 if (position == nullptr) {
254 LOG_INTERVAL(no_position_);
255 bad_pos = true;
256 }
257 no_position_.Print();
258
259 bool control_loop_driving = false;
260 if (goal) {
261 double wheel = goal->steering;
262 double throttle = goal->throttle;
263 bool quickturn = goal->quickturn;
264#if HAVE_SHIFTERS
265 bool highgear = goal->highgear;
266#endif
267
268 control_loop_driving = goal->control_loop_driving;
269 double left_goal = goal->left_goal;
270 double right_goal = goal->right_goal;
271
272 dt_closedloop.SetGoal(left_goal, goal->left_velocity_goal, right_goal,
273 goal->right_velocity_goal);
274#if HAVE_SHIFTERS
275 dt_openloop.SetGoal(wheel, throttle, quickturn, highgear);
276#else
277 dt_openloop.SetGoal(wheel, throttle, quickturn, false);
278#endif
279 }
280
281 if (!bad_pos) {
282 const double left_encoder = position->left_encoder;
283 const double right_encoder = position->right_encoder;
284 if (gyro_reading.FetchLatest()) {
285 LOG_STRUCT(DEBUG, "using", *gyro_reading.get());
286 dt_closedloop.SetPosition(left_encoder, right_encoder,
287 gyro_reading->angle);
288 } else {
289 dt_closedloop.SetRawPosition(left_encoder, right_encoder);
290 }
291 }
292 dt_openloop.SetPosition(position);
293 dt_openloop.Update();
294
295 if (control_loop_driving) {
296 dt_closedloop.Update(output == NULL, true);
297 dt_closedloop.SendMotors(output);
298 } else {
299 dt_openloop.SendMotors(output);
300 if (output) {
301 dt_closedloop.SetExternalMotors(output->left_voltage,
302 output->right_voltage);
303 }
304 dt_closedloop.Update(output == NULL, false);
305 }
306
307 // set the output status of the control loop state
308 if (status) {
309 bool done = false;
310 if (goal) {
311 done = ((::std::abs(goal->left_goal -
312 dt_closedloop.GetEstimatedLeftEncoder()) <
313 constants::GetValues().drivetrain_done_distance) &&
314 (::std::abs(goal->right_goal -
315 dt_closedloop.GetEstimatedRightEncoder()) <
316 constants::GetValues().drivetrain_done_distance));
317 }
318 status->is_done = done;
319 status->robot_speed = dt_closedloop.GetEstimatedRobotSpeed();
320 status->filtered_left_position = dt_closedloop.GetEstimatedLeftEncoder();
321 status->filtered_right_position = dt_closedloop.GetEstimatedRightEncoder();
322
323 status->filtered_left_velocity = dt_closedloop.loop().X_hat(1, 0);
324 status->filtered_right_velocity = dt_closedloop.loop().X_hat(3, 0);
325 status->output_was_capped = dt_closedloop.OutputWasCapped();
326 status->uncapped_left_voltage = dt_closedloop.loop().U_uncapped(0, 0);
327 status->uncapped_right_voltage = dt_closedloop.loop().U_uncapped(1, 0);
328 }
329}
330
331} // namespace control_loops
332} // namespace frc971