blob: 2a35c6230b5c589465656b2e0c0ccdc8201b358a [file] [log] [blame]
joe93778a62014-02-15 13:22:14 -08001#include "frc971/control_loops/shooter/shooter.h"
joe2d92e852014-01-25 14:31:24 -08002
3#include <stdio.h>
4
5#include <algorithm>
6
Briana6553ed2014-04-02 21:26:46 -07007#include "aos/common/controls/control_loops.q.h"
joe2d92e852014-01-25 14:31:24 -08008#include "aos/common/logging/logging.h"
Brian Silvermanf48fab32014-03-09 14:32:24 -07009#include "aos/common/logging/queue_logging.h"
joe2d92e852014-01-25 14:31:24 -080010
11#include "frc971/constants.h"
joe93778a62014-02-15 13:22:14 -080012#include "frc971/control_loops/shooter/shooter_motor_plant.h"
joe2d92e852014-01-25 14:31:24 -080013
14namespace frc971 {
15namespace control_loops {
16
Ben Fredricksonedf0e092014-02-16 10:46:50 +000017using ::aos::time::Time;
Ben Fredrickson22c93322014-02-17 05:56:33 +000018
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000019void ZeroedStateFeedbackLoop::CapU() {
20 const double old_voltage = voltage_;
Brian Silvermana21c3a22014-06-12 21:49:15 -070021 voltage_ += U(0, 0);
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000022
23 uncapped_voltage_ = voltage_;
24
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000025 // Make sure that reality and the observer can't get too far off. There is a
Brian Silvermana21c3a22014-06-12 21:49:15 -070026 // delay by one cycle between the applied voltage and X_hat(2, 0), so compare
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000027 // against last cycle's voltage.
Brian Silvermana21c3a22014-06-12 21:49:15 -070028 if (X_hat(2, 0) > last_voltage_ + 4.0) {
29 voltage_ -= X_hat(2, 0) - (last_voltage_ + 4.0);
Brian Silverman101b9642014-03-08 12:45:16 -080030 LOG(DEBUG, "Capping due to runaway\n");
Brian Silvermana21c3a22014-06-12 21:49:15 -070031 } else if (X_hat(2, 0) < last_voltage_ - 4.0) {
32 voltage_ += X_hat(2, 0) - (last_voltage_ - 4.0);
Brian Silverman101b9642014-03-08 12:45:16 -080033 LOG(DEBUG, "Capping due to runaway\n");
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000034 }
35
Austin Schuhd34569d2014-02-18 20:26:38 -080036 voltage_ = std::min(max_voltage_, voltage_);
37 voltage_ = std::max(-max_voltage_, voltage_);
Brian Silvermana21c3a22014-06-12 21:49:15 -070038 mutable_U(0, 0) = voltage_ - old_voltage;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000039
Brian Silvermana21c3a22014-06-12 21:49:15 -070040 LOG_STRUCT(DEBUG, "output", ShooterVoltageToLog(X_hat(2, 0), voltage_));
Austin Schuhbe1401f2014-02-18 03:18:41 -080041
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000042 last_voltage_ = voltage_;
Austin Schuhd34569d2014-02-18 20:26:38 -080043 capped_goal_ = false;
44}
45
46void ZeroedStateFeedbackLoop::CapGoal() {
47 if (uncapped_voltage() > max_voltage_) {
48 double dx;
49 if (controller_index() == 0) {
50 dx = (uncapped_voltage() - max_voltage_) /
51 (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
Brian Silvermana21c3a22014-06-12 21:49:15 -070052 mutable_R(0, 0) -= dx;
53 mutable_R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
Austin Schuhd34569d2014-02-18 20:26:38 -080054 } else {
55 dx = (uncapped_voltage() - max_voltage_) / K(0, 0);
Brian Silvermana21c3a22014-06-12 21:49:15 -070056 mutable_R(0, 0) -= dx;
Austin Schuhd34569d2014-02-18 20:26:38 -080057 }
58 capped_goal_ = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -070059 LOG_STRUCT(DEBUG, "to prevent windup", ShooterMovingGoal(dx));
Austin Schuhd34569d2014-02-18 20:26:38 -080060 } else if (uncapped_voltage() < -max_voltage_) {
61 double dx;
62 if (controller_index() == 0) {
63 dx = (uncapped_voltage() + max_voltage_) /
64 (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
Brian Silvermana21c3a22014-06-12 21:49:15 -070065 mutable_R(0, 0) -= dx;
66 mutable_R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
Austin Schuhd34569d2014-02-18 20:26:38 -080067 } else {
68 dx = (uncapped_voltage() + max_voltage_) / K(0, 0);
Brian Silvermana21c3a22014-06-12 21:49:15 -070069 mutable_R(0, 0) -= dx;
Austin Schuhd34569d2014-02-18 20:26:38 -080070 }
71 capped_goal_ = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -070072 LOG_STRUCT(DEBUG, "to prevent windup", ShooterMovingGoal(dx));
Austin Schuhd34569d2014-02-18 20:26:38 -080073 } else {
74 capped_goal_ = false;
75 }
76}
77
78void ZeroedStateFeedbackLoop::RecalculatePowerGoal() {
79 if (controller_index() == 0) {
Brian Silvermana21c3a22014-06-12 21:49:15 -070080 mutable_R(2, 0) = (-A(1, 0) / A(1, 2) * R(0, 0) - A(1, 1) / A(1, 2) * R(1, 0));
Austin Schuhd34569d2014-02-18 20:26:38 -080081 } else {
Brian Silvermana21c3a22014-06-12 21:49:15 -070082 mutable_R(2, 0) = -A(1, 1) / A(1, 2) * R(1, 0);
Austin Schuhd34569d2014-02-18 20:26:38 -080083 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000084}
85
Austin Schuh30537882014-02-18 01:07:23 -080086void ZeroedStateFeedbackLoop::SetCalibration(double encoder_val,
87 double known_position) {
Brian Silvermanf48fab32014-03-09 14:32:24 -070088 double old_position = absolute_position();
Austin Schuh30537882014-02-18 01:07:23 -080089 double previous_offset = offset_;
90 offset_ = known_position - encoder_val;
91 double doffset = offset_ - previous_offset;
Brian Silvermana21c3a22014-06-12 21:49:15 -070092 mutable_X_hat(0, 0) += doffset;
Austin Schuh30537882014-02-18 01:07:23 -080093 // Offset our measurements because the offset is baked into them.
Brian Silverman273d8a32014-05-10 22:19:09 -070094 // This is safe because if we got here, it means position != nullptr, which
95 // means we already set Y to something and it won't just get overwritten.
Brian Silvermana21c3a22014-06-12 21:49:15 -070096 mutable_Y(0, 0) += doffset;
Austin Schuh30537882014-02-18 01:07:23 -080097 // Offset the goal so we don't move.
Brian Silvermana21c3a22014-06-12 21:49:15 -070098 mutable_R(0, 0) += doffset;
Austin Schuhd34569d2014-02-18 20:26:38 -080099 if (controller_index() == 0) {
Brian Silvermana21c3a22014-06-12 21:49:15 -0700100 mutable_R(2, 0) += -A(1, 0) / A(1, 2) * (doffset);
Austin Schuhd34569d2014-02-18 20:26:38 -0800101 }
Brian Silvermanf48fab32014-03-09 14:32:24 -0700102 LOG_STRUCT(
Brian Silvermand6337412014-03-14 18:51:26 -0700103 DEBUG, "sensor edge (fake?)",
Brian Silvermanf48fab32014-03-09 14:32:24 -0700104 ShooterChangeCalibration(encoder_val, known_position, old_position,
105 absolute_position(), previous_offset, offset_));
Austin Schuh30537882014-02-18 01:07:23 -0800106}
107
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000108ShooterMotor::ShooterMotor(control_loops::ShooterGroup *my_shooter)
Brian Silverman38111502014-04-10 12:36:26 -0700109 : aos::controls::ControlLoop<control_loops::ShooterGroup>(my_shooter),
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000110 shooter_(MakeShooterLoop()),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000111 state_(STATE_INITIALIZE),
112 loading_problem_end_time_(0, 0),
Austin Schuh30537882014-02-18 01:07:23 -0800113 load_timeout_(0, 0),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000114 shooter_brake_set_time_(0, 0),
Austin Schuh30537882014-02-18 01:07:23 -0800115 unload_timeout_(0, 0),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000116 shot_end_time_(0, 0),
Austin Schuh4edad872014-03-02 11:56:12 -0800117 cycles_not_moved_(0),
Austin Schuhd30d7382014-03-02 21:15:38 -0800118 shot_count_(0),
119 zeroed_(false),
120 distal_posedge_validation_cycles_left_(0),
Austin Schuh2e976812014-03-05 19:56:58 -0800121 proximal_posedge_validation_cycles_left_(0),
122 last_distal_current_(true),
123 last_proximal_current_(true) {}
Austin Schuh30537882014-02-18 01:07:23 -0800124
125double ShooterMotor::PowerToPosition(double power) {
Austin Schuh30537882014-02-18 01:07:23 -0800126 const frc971::constants::Values &values = constants::GetValues();
Ben Fredricksonda334d12014-02-23 09:09:23 +0000127 double maxpower = 0.5 * kSpringConstant *
128 (kMaxExtension * kMaxExtension -
129 (kMaxExtension - values.shooter.upper_limit) *
130 (kMaxExtension - values.shooter.upper_limit));
131 if (power < 0) {
Brian Silverman0ebe9442014-03-22 13:57:35 -0700132 LOG_STRUCT(WARNING, "negative power", PowerAdjustment(power, 0));
Ben Fredricksonda334d12014-02-23 09:09:23 +0000133 power = 0;
134 } else if (power > maxpower) {
Brian Silverman0ebe9442014-03-22 13:57:35 -0700135 LOG_STRUCT(WARNING, "power too high", PowerAdjustment(power, maxpower));
Ben Fredricksonda334d12014-02-23 09:09:23 +0000136 power = maxpower;
137 }
138
139 double mp = kMaxExtension * kMaxExtension - (power + power) / kSpringConstant;
140 double new_pos = 0.10;
141 if (mp < 0) {
142 LOG(ERROR,
143 "Power calculation has negative number before square root (%f).\n", mp);
144 } else {
145 new_pos = kMaxExtension - ::std::sqrt(mp);
146 }
147
148 new_pos = ::std::min(::std::max(new_pos, values.shooter.lower_limit),
Austin Schuh30537882014-02-18 01:07:23 -0800149 values.shooter.upper_limit);
150 return new_pos;
151}
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000152
James Kuszmauld29689f2014-03-02 13:06:54 -0800153double ShooterMotor::PositionToPower(double position) {
154 double power = kSpringConstant * position * (kMaxExtension - position / 2.0);
155 return power;
156}
157
Brian Silverman07202632014-08-09 21:53:07 -0700158void ShooterMotor::CheckCalibrations(
159 const control_loops::ShooterGroup::Position *position) {
160 CHECK_NOTNULL(position);
161 const frc971::constants::Values &values = constants::GetValues();
162
163 // TODO(austin): Validate that this is the right edge.
164 // If we see a posedge on any of the hall effects,
165 if (position->pusher_proximal.posedge_count != last_proximal_posedge_count_ &&
166 !last_proximal_current_) {
167 proximal_posedge_validation_cycles_left_ = 2;
168 }
169 if (proximal_posedge_validation_cycles_left_ > 0) {
170 if (position->pusher_proximal.current) {
171 --proximal_posedge_validation_cycles_left_;
172 if (proximal_posedge_validation_cycles_left_ == 0) {
173 shooter_.SetCalibration(
174 position->pusher_proximal.posedge_value,
175 values.shooter.pusher_proximal.upper_angle);
176
177 LOG(DEBUG, "Setting calibration using proximal sensor\n");
178 zeroed_ = true;
179 }
180 } else {
181 proximal_posedge_validation_cycles_left_ = 0;
182 }
183 }
184
185 if (position->pusher_distal.posedge_count != last_distal_posedge_count_ &&
186 !last_distal_current_) {
187 distal_posedge_validation_cycles_left_ = 2;
188 }
189 if (distal_posedge_validation_cycles_left_ > 0) {
190 if (position->pusher_distal.current) {
191 --distal_posedge_validation_cycles_left_;
192 if (distal_posedge_validation_cycles_left_ == 0) {
193 shooter_.SetCalibration(
194 position->pusher_distal.posedge_value,
195 values.shooter.pusher_distal.upper_angle);
196
197 LOG(DEBUG, "Setting calibration using distal sensor\n");
198 zeroed_ = true;
199 }
200 } else {
201 distal_posedge_validation_cycles_left_ = 0;
202 }
203 }
204}
205
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000206// Positive is out, and positive power is out.
joe2d92e852014-01-25 14:31:24 -0800207void ShooterMotor::RunIteration(
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000208 const control_loops::ShooterGroup::Goal *goal,
209 const control_loops::ShooterGroup::Position *position,
210 control_loops::ShooterGroup::Output *output,
Ben Fredrickson22c93322014-02-17 05:56:33 +0000211 control_loops::ShooterGroup::Status *status) {
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000212 constexpr double dt = 0.01;
213
Ben Fredrickson61893d52014-03-02 09:43:23 +0000214 if (::std::isnan(goal->shot_power)) {
215 state_ = STATE_ESTOP;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700216 LOG(ERROR, "Estopping because got a shot power of NAN.\n");
Ben Fredrickson61893d52014-03-02 09:43:23 +0000217 }
218
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000219 // we must always have these or we have issues.
220 if (goal == NULL || status == NULL) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000221 if (output) output->voltage = 0;
222 LOG(ERROR, "Thought I would just check for null and die.\n");
223 return;
224 }
James Kuszmaul4abaf482014-02-26 21:16:35 -0800225 status->ready = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000226
Austin Schuh30537882014-02-18 01:07:23 -0800227 if (reset()) {
228 state_ = STATE_INITIALIZE;
Austin Schuh2e976812014-03-05 19:56:58 -0800229 last_distal_current_ = position->pusher_distal.current;
230 last_proximal_current_ = position->pusher_proximal.current;
Austin Schuh30537882014-02-18 01:07:23 -0800231 }
232 if (position) {
233 shooter_.CorrectPosition(position->position);
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000234 }
joe2d92e852014-01-25 14:31:24 -0800235
236 // Disable the motors now so that all early returns will return with the
237 // motors disabled.
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000238 if (output) output->voltage = 0;
joe2d92e852014-01-25 14:31:24 -0800239
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000240 const frc971::constants::Values &values = constants::GetValues();
241
Brian Silvermanaae236a2014-02-17 01:49:39 -0800242 // Don't even let the control loops run.
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000243 bool shooter_loop_disable = false;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000244
Brian Silvermand8a7cf02014-03-22 17:15:23 -0700245 const bool disabled =
246 !::aos::robot_state.get() || !::aos::robot_state->enabled;
Brian Silverman36407c92014-04-01 15:54:02 -0700247
Austin Schuhd34569d2014-02-18 20:26:38 -0800248 // If true, move the goal if we saturate.
249 bool cap_goal = false;
250
251 // TODO(austin): Move the offset if we see or don't see a hall effect when we
252 // expect to see one.
253 // Probably not needed yet.
254
255 if (position) {
256 int last_controller_index = shooter_.controller_index();
257 if (position->plunger && position->latch) {
258 // Use the controller without the spring if the latch is set and the
259 // plunger is back
260 shooter_.set_controller_index(1);
261 } else {
262 // Otherwise use the controller with the spring.
263 shooter_.set_controller_index(0);
264 }
265 if (shooter_.controller_index() != last_controller_index) {
266 shooter_.RecalculatePowerGoal();
267 }
268 }
Austin Schuh30537882014-02-18 01:07:23 -0800269
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000270 switch (state_) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000271 case STATE_INITIALIZE:
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000272 if (position) {
Austin Schuh30537882014-02-18 01:07:23 -0800273 // Reinitialize the internal filter state.
274 shooter_.InitializeState(position->position);
Austin Schuh30537882014-02-18 01:07:23 -0800275
276 // Start off with the assumption that we are at the value
277 // futhest back given our sensors.
278 if (position->pusher_distal.current) {
279 shooter_.SetCalibration(position->position,
280 values.shooter.pusher_distal.lower_angle);
281 } else if (position->pusher_proximal.current) {
282 shooter_.SetCalibration(position->position,
Austin Schuhd34569d2014-02-18 20:26:38 -0800283 values.shooter.pusher_proximal.upper_angle);
Austin Schuh30537882014-02-18 01:07:23 -0800284 } else {
285 shooter_.SetCalibration(position->position,
286 values.shooter.upper_limit);
287 }
288
Austin Schuh30537882014-02-18 01:07:23 -0800289 // Go to the current position.
290 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
291 // If the plunger is all the way back, we want to be latched.
292 latch_piston_ = position->plunger;
293 brake_piston_ = false;
Austin Schuh1a08bd82014-03-09 00:47:11 -0800294 if (position->latch == latch_piston_) {
295 state_ = STATE_REQUEST_LOAD;
296 } else {
297 shooter_loop_disable = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700298 LOG(DEBUG,
299 "Not moving on until the latch has moved to avoid a crash\n");
Austin Schuh1a08bd82014-03-09 00:47:11 -0800300 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000301 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800302 // If we can't start yet because we don't know where we are, set the
303 // latch and brake to their defaults.
304 latch_piston_ = true;
305 brake_piston_ = true;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000306 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000307 break;
308 case STATE_REQUEST_LOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800309 if (position) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800310 zeroed_ = false;
Ben Fredrickson26a0dee2014-02-23 04:38:23 +0000311 if (position->pusher_distal.current ||
312 position->pusher_proximal.current) {
Austin Schuh30537882014-02-18 01:07:23 -0800313 // We started on the sensor, back up until we are found.
314 // If the plunger is all the way back and not latched, it won't be
315 // there for long.
316 state_ = STATE_LOAD_BACKTRACK;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800317
318 // The plunger is already back and latched. Don't release it.
319 if (position->plunger && position->latch) {
320 latch_piston_ = true;
321 } else {
322 latch_piston_ = false;
323 }
324 } else if (position->plunger && position->latch) {
325 // The plunger is back and we are latched. We most likely got here
326 // from Initialize, in which case we want to 'load' again anyways to
327 // zero.
328 Load();
329 latch_piston_ = true;
Austin Schuh30537882014-02-18 01:07:23 -0800330 } else {
331 // Off the sensor, start loading.
332 Load();
333 latch_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000334 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000335 }
336
Austin Schuh30537882014-02-18 01:07:23 -0800337 // Hold our current position.
338 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
339 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000340 break;
341 case STATE_LOAD_BACKTRACK:
Austin Schuh30537882014-02-18 01:07:23 -0800342 // If we are here, then that means we started past the edge where we want
343 // to zero. Move backwards until we don't see the sensor anymore.
344 // The plunger is contacting the pusher (or will be shortly).
345
Austin Schuh30537882014-02-18 01:07:23 -0800346 if (!disabled) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000347 shooter_.SetGoalPosition(
Austin Schuhfaeee632014-02-18 01:24:05 -0800348 shooter_.goal_position() + values.shooter.zeroing_speed * dt,
349 values.shooter.zeroing_speed);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000350 }
Austin Schuhd34569d2014-02-18 20:26:38 -0800351 cap_goal = true;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800352 shooter_.set_max_voltage(4.0);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000353
Austin Schuh30537882014-02-18 01:07:23 -0800354 if (position) {
Ben Fredrickson26a0dee2014-02-23 04:38:23 +0000355 if (!position->pusher_distal.current &&
356 !position->pusher_proximal.current) {
Austin Schuh30537882014-02-18 01:07:23 -0800357 Load();
358 }
Austin Schuh6b428602014-02-22 21:02:00 -0800359 latch_piston_ = position->plunger;
Austin Schuh30537882014-02-18 01:07:23 -0800360 }
361
Austin Schuh30537882014-02-18 01:07:23 -0800362 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000363 break;
364 case STATE_LOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800365 // If we are disabled right now, reset the timer.
366 if (disabled) {
367 Load();
368 // Latch defaults to true when disabled. Leave it latched until we have
369 // useful sensor data.
370 latch_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000371 }
Brian Silverman2704ecf2014-04-09 20:24:03 -0700372 if (output == nullptr) {
Brian Silverman38111502014-04-10 12:36:26 -0700373 load_timeout_ += ::aos::controls::kLoopFrequency;
Brian Silverman36407c92014-04-01 15:54:02 -0700374 }
Austin Schuh30537882014-02-18 01:07:23 -0800375 // Go to 0, which should be the latch position, or trigger a hall effect
376 // on the way. If we don't see edges where we are supposed to, the
377 // offset will be updated by code above.
378 shooter_.SetGoalPosition(0.0, 0.0);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000379
Austin Schuh30537882014-02-18 01:07:23 -0800380 if (position) {
Brian Silverman07202632014-08-09 21:53:07 -0700381 CheckCalibrations(position);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000382
Austin Schuh30537882014-02-18 01:07:23 -0800383 // Latch if the plunger is far enough back to trigger the hall effect.
384 // This happens when the distal sensor is triggered.
Austin Schuh06cbbf12014-02-22 02:07:31 -0800385 latch_piston_ = position->pusher_distal.current || position->plunger;
Austin Schuh30537882014-02-18 01:07:23 -0800386
Austin Schuh06cbbf12014-02-22 02:07:31 -0800387 // Check if we are latched and back. Make sure the plunger is all the
388 // way back as well.
389 if (position->plunger && position->latch &&
390 position->pusher_distal.current) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800391 if (!zeroed_) {
392 state_ = STATE_REQUEST_LOAD;
393 } else {
394 state_ = STATE_PREPARE_SHOT;
395 }
Austin Schuh30537882014-02-18 01:07:23 -0800396 } else if (position->plunger &&
397 ::std::abs(shooter_.absolute_position() -
398 shooter_.goal_position()) < 0.001) {
399 // We are at the goal, but not latched.
400 state_ = STATE_LOADING_PROBLEM;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800401 loading_problem_end_time_ = Time::Now() + kLoadProblemEndTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800402 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000403 }
Austin Schuh30537882014-02-18 01:07:23 -0800404 if (load_timeout_ < Time::Now()) {
405 if (position) {
406 if (!position->pusher_distal.current ||
407 !position->pusher_proximal.current) {
408 state_ = STATE_ESTOP;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700409 LOG(ERROR, "Estopping because took too long to load.\n");
Austin Schuh30537882014-02-18 01:07:23 -0800410 }
411 }
Austin Schuh30537882014-02-18 01:07:23 -0800412 }
413 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000414 break;
415 case STATE_LOADING_PROBLEM:
Austin Schuh30537882014-02-18 01:07:23 -0800416 if (disabled) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800417 state_ = STATE_REQUEST_LOAD;
418 break;
Austin Schuh30537882014-02-18 01:07:23 -0800419 }
420 // We got to the goal, but the latch hasn't registered as down. It might
421 // be stuck, or on it's way but not there yet.
Ben Fredrickson22c93322014-02-17 05:56:33 +0000422 if (Time::Now() > loading_problem_end_time_) {
Austin Schuh30537882014-02-18 01:07:23 -0800423 // Timeout by unloading.
424 Unload();
425 } else if (position && position->plunger && position->latch) {
426 // If both trigger, we are latched.
Ben Fredrickson22c93322014-02-17 05:56:33 +0000427 state_ = STATE_PREPARE_SHOT;
428 }
Austin Schuh30537882014-02-18 01:07:23 -0800429 // Move a bit further back to help it trigger.
430 // If the latch is slow due to the air flowing through the tubes or
431 // inertia, but is otherwise free, this won't have much time to do
432 // anything and is safe. Otherwise this gives us a bit more room to free
433 // up the latch.
434 shooter_.SetGoalPosition(values.shooter.lower_limit, 0.0);
Brian Silverman3693f9a2014-03-30 17:54:15 -0700435 if (position) {
436 LOG(DEBUG, "Waiting on latch: plunger %d, latch: %d\n",
437 position->plunger, position->latch);
438 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000439
Austin Schuh30537882014-02-18 01:07:23 -0800440 latch_piston_ = true;
441 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000442 break;
443 case STATE_PREPARE_SHOT:
Austin Schuh30537882014-02-18 01:07:23 -0800444 // Move the shooter to the shot power set point and then lock the brake.
445 // TODO(austin): Timeout. Low priority.
446
Ben Fredrickson22c93322014-02-17 05:56:33 +0000447 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
Austin Schuh30537882014-02-18 01:07:23 -0800448
449 LOG(DEBUG, "PDIFF: absolute_position: %.2f, pow: %.2f\n",
Austin Schuhbe1401f2014-02-18 03:18:41 -0800450 shooter_.absolute_position(), PowerToPosition(goal->shot_power));
Austin Schuh30537882014-02-18 01:07:23 -0800451 if (::std::abs(shooter_.absolute_position() -
Austin Schuh6e4b5fa2014-12-14 14:20:10 -0800452 PowerToPosition(goal->shot_power)) < 0.001 &&
453 ::std::abs(shooter_.absolute_velocity()) < 0.005) {
Austin Schuh30537882014-02-18 01:07:23 -0800454 // We are there, set the brake and move on.
455 latch_piston_ = true;
456 brake_piston_ = true;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800457 shooter_brake_set_time_ = Time::Now() + kShooterBrakeSetTime;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000458 state_ = STATE_READY;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000459 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800460 latch_piston_ = true;
461 brake_piston_ = false;
462 }
463 if (goal->unload_requested) {
464 Unload();
Ben Fredrickson22c93322014-02-17 05:56:33 +0000465 }
466 break;
467 case STATE_READY:
Austin Schuh30537882014-02-18 01:07:23 -0800468 LOG(DEBUG, "In ready\n");
Austin Schuhbe1401f2014-02-18 03:18:41 -0800469 // Wait until the brake is set, and a shot is requested or the shot power
470 // is changed.
Austin Schuh6b428602014-02-22 21:02:00 -0800471 if (Time::Now() > shooter_brake_set_time_) {
James Kuszmaul4abaf482014-02-26 21:16:35 -0800472 status->ready = true;
Austin Schuhbe1401f2014-02-18 03:18:41 -0800473 // We have waited long enough for the brake to set, turn the shooter
474 // control loop off.
Ben Fredrickson22c93322014-02-17 05:56:33 +0000475 shooter_loop_disable = true;
Austin Schuh30537882014-02-18 01:07:23 -0800476 LOG(DEBUG, "Brake is now set\n");
477 if (goal->shot_requested && !disabled) {
478 LOG(DEBUG, "Shooting now\n");
479 shooter_loop_disable = true;
James Kuszmaul7290a942014-02-26 20:04:13 -0800480 shot_end_time_ = Time::Now() + kShotEndTimeout;
481 firing_starting_position_ = shooter_.absolute_position();
482 state_ = STATE_FIRE;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000483 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000484 }
Austin Schuh6b428602014-02-22 21:02:00 -0800485 if (state_ == STATE_READY &&
486 ::std::abs(shooter_.absolute_position() -
487 PowerToPosition(goal->shot_power)) > 0.002) {
488 // TODO(austin): Add a state to release the brake.
489
490 // TODO(austin): Do we want to set the brake here or after shooting?
491 // Depends on air usage.
James Kuszmaul4abaf482014-02-26 21:16:35 -0800492 status->ready = false;
Austin Schuh6b428602014-02-22 21:02:00 -0800493 LOG(DEBUG, "Preparing shot again.\n");
494 state_ = STATE_PREPARE_SHOT;
495 }
496
Ben Fredricksona6d77542014-02-17 07:54:43 +0000497 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
498
Austin Schuh30537882014-02-18 01:07:23 -0800499 latch_piston_ = true;
500 brake_piston_ = true;
501
502 if (goal->unload_requested) {
503 Unload();
504 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000505 break;
Austin Schuh30537882014-02-18 01:07:23 -0800506
Ben Fredrickson22c93322014-02-17 05:56:33 +0000507 case STATE_FIRE:
Austin Schuh30537882014-02-18 01:07:23 -0800508 if (disabled) {
509 if (position) {
510 if (position->plunger) {
511 // If disabled and the plunger is still back there, reset the
512 // timeout.
Austin Schuh06cbbf12014-02-22 02:07:31 -0800513 shot_end_time_ = Time::Now() + kShotEndTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800514 }
515 }
516 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000517 shooter_loop_disable = true;
Austin Schuh30537882014-02-18 01:07:23 -0800518 // Count the number of contiguous cycles during which we haven't moved.
519 if (::std::abs(last_position_.position - shooter_.absolute_position()) <
520 0.0005) {
521 ++cycles_not_moved_;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000522 } else {
523 cycles_not_moved_ = 0;
524 }
Austin Schuh30537882014-02-18 01:07:23 -0800525
526 // If we have moved any amount since the start and the shooter has now
527 // been still for a couple cycles, the shot finished.
528 // Also move on if it times out.
529 if ((::std::abs(firing_starting_position_ -
530 shooter_.absolute_position()) > 0.0005 &&
531 cycles_not_moved_ > 3) ||
532 Time::Now() > shot_end_time_) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000533 state_ = STATE_REQUEST_LOAD;
Austin Schuh4edad872014-03-02 11:56:12 -0800534 ++shot_count_;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000535 }
Austin Schuh30537882014-02-18 01:07:23 -0800536 latch_piston_ = false;
537 brake_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000538 break;
539 case STATE_UNLOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800540 // Reset the timeouts.
541 if (disabled) Unload();
542
543 // If it is latched and the plunger is back, move the pusher back to catch
544 // the plunger.
Austin Schuhf84a1302014-02-19 00:23:30 -0800545 bool all_back;
546 if (position) {
547 all_back = position->plunger && position->latch;
548 } else {
549 all_back = last_position_.plunger && last_position_.latch;
550 }
551
552 if (all_back) {
Austin Schuh30537882014-02-18 01:07:23 -0800553 // Pull back to 0, 0.
554 shooter_.SetGoalPosition(0.0, 0.0);
555 if (shooter_.absolute_position() < 0.005) {
556 // When we are close enough, 'fire'.
557 latch_piston_ = false;
558 } else {
559 latch_piston_ = true;
Brian Silverman07202632014-08-09 21:53:07 -0700560
561 if (position) {
562 CheckCalibrations(position);
563 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000564 }
565 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800566 // The plunger isn't all the way back, or it is and it is unlatched, so
567 // we can now unload.
568 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
569 latch_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000570 state_ = STATE_UNLOAD_MOVE;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800571 unload_timeout_ = Time::Now() + kUnloadTimeout;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000572 }
573
Austin Schuh30537882014-02-18 01:07:23 -0800574 if (Time::Now() > unload_timeout_) {
575 // We have been stuck trying to unload for way too long, give up and
576 // turn everything off.
577 state_ = STATE_ESTOP;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700578 LOG(ERROR, "Estopping because took too long to unload.\n");
Austin Schuh30537882014-02-18 01:07:23 -0800579 }
580
581 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000582 break;
Austin Schuh30537882014-02-18 01:07:23 -0800583 case STATE_UNLOAD_MOVE: {
584 if (disabled) {
Austin Schuh06cbbf12014-02-22 02:07:31 -0800585 unload_timeout_ = Time::Now() + kUnloadTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800586 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
587 }
Austin Schuhd34569d2014-02-18 20:26:38 -0800588 cap_goal = true;
Austin Schuh6b428602014-02-22 21:02:00 -0800589 shooter_.set_max_voltage(6.0);
Austin Schuh30537882014-02-18 01:07:23 -0800590
591 // Slowly move back until we hit the upper limit.
Austin Schuhd34569d2014-02-18 20:26:38 -0800592 // If we were at the limit last cycle, we are done unloading.
593 // This is because if we saturate, we might hit the limit before we are
594 // actually there.
595 if (shooter_.goal_position() >= values.shooter.upper_limit) {
Austin Schuh30537882014-02-18 01:07:23 -0800596 shooter_.SetGoalPosition(values.shooter.upper_limit, 0.0);
597 // We don't want the loop fighting the spring when we are unloaded.
598 // Turn it off.
599 shooter_loop_disable = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000600 state_ = STATE_READY_UNLOAD;
601 } else {
Austin Schuhd34569d2014-02-18 20:26:38 -0800602 shooter_.SetGoalPosition(
603 ::std::min(
604 values.shooter.upper_limit,
Austin Schuh6b428602014-02-22 21:02:00 -0800605 shooter_.goal_position() + values.shooter.unload_speed * dt),
606 values.shooter.unload_speed);
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000607 }
608
Austin Schuh30537882014-02-18 01:07:23 -0800609 latch_piston_ = false;
610 brake_piston_ = false;
611 } break;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000612 case STATE_READY_UNLOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800613 if (goal->load_requested) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000614 state_ = STATE_REQUEST_LOAD;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000615 }
James Kuszmaul7290a942014-02-26 20:04:13 -0800616 // If we are ready to load again,
Austin Schuh30537882014-02-18 01:07:23 -0800617 shooter_loop_disable = true;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000618
Austin Schuh30537882014-02-18 01:07:23 -0800619 latch_piston_ = false;
620 brake_piston_ = false;
621 break;
622
623 case STATE_ESTOP:
Brian Silverman0ebe9442014-03-22 13:57:35 -0700624 LOG(WARNING, "estopped\n");
Austin Schuh30537882014-02-18 01:07:23 -0800625 // Totally lost, go to a safe state.
626 shooter_loop_disable = true;
627 latch_piston_ = true;
628 brake_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000629 break;
joe2d92e852014-01-25 14:31:24 -0800630 }
631
James Kuszmaul7290a942014-02-26 20:04:13 -0800632 if (!shooter_loop_disable) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700633 LOG_STRUCT(DEBUG, "running the loop",
634 ShooterStatusToLog(shooter_.goal_position(),
635 shooter_.absolute_position()));
Austin Schuhd34569d2014-02-18 20:26:38 -0800636 if (!cap_goal) {
637 shooter_.set_max_voltage(12.0);
638 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000639 shooter_.Update(output == NULL);
Austin Schuhd34569d2014-02-18 20:26:38 -0800640 if (cap_goal) {
641 shooter_.CapGoal();
642 }
Brian Silverman36407c92014-04-01 15:54:02 -0700643 // We don't really want to output anything if we went through everything
644 // assuming the motors weren't working.
Brian Silverman2704ecf2014-04-09 20:24:03 -0700645 if (output) output->voltage = shooter_.voltage();
Ben Fredrickson7d980c22014-02-16 21:39:02 +0000646 } else {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000647 shooter_.Update(true);
Austin Schuhd34569d2014-02-18 20:26:38 -0800648 shooter_.ZeroPower();
Ben Fredrickson22c93322014-02-17 05:56:33 +0000649 if (output) output->voltage = 0.0;
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000650 }
Ben Fredrickson7d980c22014-02-16 21:39:02 +0000651
Austin Schuhf4392d42014-03-02 14:00:37 -0800652 status->hard_stop_power = PositionToPower(shooter_.absolute_position());
James Kuszmauld29689f2014-03-02 13:06:54 -0800653
Austin Schuh30537882014-02-18 01:07:23 -0800654 if (output) {
655 output->latch_piston = latch_piston_;
656 output->brake_piston = brake_piston_;
657 }
658
Austin Schuh30537882014-02-18 01:07:23 -0800659 if (position) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700660 LOG_STRUCT(DEBUG, "internal state",
661 ShooterStateToLog(
662 shooter_.absolute_position(), shooter_.absolute_velocity(),
663 state_, position->latch, position->pusher_proximal.current,
664 position->pusher_distal.current, position->plunger,
665 brake_piston_, latch_piston_));
666
Austin Schuh30537882014-02-18 01:07:23 -0800667 last_position_ = *position;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700668
Austin Schuh30537882014-02-18 01:07:23 -0800669 last_distal_posedge_count_ = position->pusher_distal.posedge_count;
670 last_proximal_posedge_count_ = position->pusher_proximal.posedge_count;
Austin Schuh2e976812014-03-05 19:56:58 -0800671 last_distal_current_ = position->pusher_distal.current;
672 last_proximal_current_ = position->pusher_proximal.current;
Austin Schuh30537882014-02-18 01:07:23 -0800673 }
Austin Schuh4edad872014-03-02 11:56:12 -0800674
675 status->shots = shot_count_;
joe2d92e852014-01-25 14:31:24 -0800676}
677
Brian Silvermand1e65b92014-03-08 17:07:14 -0800678void ShooterMotor::ZeroOutputs() {
679 queue_group()->output.MakeWithBuilder()
680 .voltage(0)
681 .latch_piston(latch_piston_)
682 .brake_piston(brake_piston_)
683 .Send();
684}
685
joe2d92e852014-01-25 14:31:24 -0800686} // namespace control_loops
687} // namespace frc971