blob: af310f091c9beeb59b177f00147cff91ab1c14b0 [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#include "y2014/control_loops/shooter/shooter.h"
2
3#include <stdio.h>
4
5#include <algorithm>
6#include <limits>
Austin Schuh214e9c12016-11-25 17:26:20 -08007#include <chrono>
Brian Silverman17f503e2015-08-02 18:17:18 -07008
9#include "aos/common/controls/control_loops.q.h"
10#include "aos/common/logging/logging.h"
11#include "aos/common/logging/queue_logging.h"
12
13#include "y2014/constants.h"
14#include "y2014/control_loops/shooter/shooter_motor_plant.h"
15
Austin Schuh24957102015-11-28 16:04:40 -080016namespace y2014 {
Brian Silverman17f503e2015-08-02 18:17:18 -070017namespace control_loops {
18
Austin Schuh214e9c12016-11-25 17:26:20 -080019namespace chrono = ::std::chrono;
20using ::aos::monotonic_clock;
21
Austin Schuh9d4aca82015-11-08 14:41:31 -080022using ::y2014::control_loops::shooter::kSpringConstant;
23using ::y2014::control_loops::shooter::kMaxExtension;
Austin Schuh0e997732015-11-08 15:14:53 -080024using ::y2014::control_loops::shooter::kDt;
Austin Schuh9d4aca82015-11-08 14:41:31 -080025using ::y2014::control_loops::shooter::MakeShooterLoop;
Brian Silverman17f503e2015-08-02 18:17:18 -070026
27void ZeroedStateFeedbackLoop::CapU() {
28 const double old_voltage = voltage_;
29 voltage_ += U(0, 0);
30
31 uncapped_voltage_ = voltage_;
32
33 // Make sure that reality and the observer can't get too far off. There is a
34 // delay by one cycle between the applied voltage and X_hat(2, 0), so compare
35 // against last cycle's voltage.
36 if (X_hat(2, 0) > last_voltage_ + 4.0) {
37 voltage_ -= X_hat(2, 0) - (last_voltage_ + 4.0);
38 LOG(DEBUG, "Capping due to runaway\n");
39 } else if (X_hat(2, 0) < last_voltage_ - 4.0) {
40 voltage_ += X_hat(2, 0) - (last_voltage_ - 4.0);
41 LOG(DEBUG, "Capping due to runaway\n");
42 }
43
44 voltage_ = std::min(max_voltage_, voltage_);
45 voltage_ = std::max(-max_voltage_, voltage_);
46 mutable_U(0, 0) = voltage_ - old_voltage;
47
Austin Schuh24957102015-11-28 16:04:40 -080048 LOG_STRUCT(
49 DEBUG, "shooter_output",
Brian Silvermanb601d892015-12-20 18:24:38 -050050 ::y2014::control_loops::ShooterVoltageToLog(X_hat(2, 0), voltage_));
Brian Silverman17f503e2015-08-02 18:17:18 -070051
52 last_voltage_ = voltage_;
53 capped_goal_ = false;
54}
55
56void ZeroedStateFeedbackLoop::CapGoal() {
57 if (uncapped_voltage() > max_voltage_) {
58 double dx;
59 if (controller_index() == 0) {
60 dx = (uncapped_voltage() - max_voltage_) /
61 (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
62 mutable_R(0, 0) -= dx;
63 mutable_R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
64 } else {
65 dx = (uncapped_voltage() - max_voltage_) / K(0, 0);
66 mutable_R(0, 0) -= dx;
67 }
68 capped_goal_ = true;
Austin Schuh24957102015-11-28 16:04:40 -080069 LOG_STRUCT(DEBUG, "to prevent windup",
Brian Silvermanb601d892015-12-20 18:24:38 -050070 ::y2014::control_loops::ShooterMovingGoal(dx));
Brian Silverman17f503e2015-08-02 18:17:18 -070071 } else if (uncapped_voltage() < -max_voltage_) {
72 double dx;
73 if (controller_index() == 0) {
74 dx = (uncapped_voltage() + max_voltage_) /
75 (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
76 mutable_R(0, 0) -= dx;
77 mutable_R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
78 } else {
79 dx = (uncapped_voltage() + max_voltage_) / K(0, 0);
80 mutable_R(0, 0) -= dx;
81 }
82 capped_goal_ = true;
Austin Schuh24957102015-11-28 16:04:40 -080083 LOG_STRUCT(DEBUG, "to prevent windup",
Brian Silvermanb601d892015-12-20 18:24:38 -050084 ::y2014::control_loops::ShooterMovingGoal(dx));
Brian Silverman17f503e2015-08-02 18:17:18 -070085 } else {
86 capped_goal_ = false;
87 }
88}
89
90void ZeroedStateFeedbackLoop::RecalculatePowerGoal() {
91 if (controller_index() == 0) {
92 mutable_R(2, 0) = (-A(1, 0) / A(1, 2) * R(0, 0) - A(1, 1) / A(1, 2) * R(1, 0));
93 } else {
94 mutable_R(2, 0) = -A(1, 1) / A(1, 2) * R(1, 0);
95 }
96}
97
98void ZeroedStateFeedbackLoop::SetCalibration(double encoder_val,
99 double known_position) {
100 double old_position = absolute_position();
101 double previous_offset = offset_;
102 offset_ = known_position - encoder_val;
103 double doffset = offset_ - previous_offset;
104 mutable_X_hat(0, 0) += doffset;
Brian Silverman17f503e2015-08-02 18:17:18 -0700105 // Offset the goal so we don't move.
106 mutable_R(0, 0) += doffset;
107 if (controller_index() == 0) {
108 mutable_R(2, 0) += -A(1, 0) / A(1, 2) * (doffset);
109 }
Austin Schuh24957102015-11-28 16:04:40 -0800110 LOG_STRUCT(DEBUG, "sensor edge (fake?)",
Brian Silvermanb601d892015-12-20 18:24:38 -0500111 ::y2014::control_loops::ShooterChangeCalibration(
Austin Schuh24957102015-11-28 16:04:40 -0800112 encoder_val, known_position, old_position, absolute_position(),
113 previous_offset, offset_));
Brian Silverman17f503e2015-08-02 18:17:18 -0700114}
115
Brian Silvermanb601d892015-12-20 18:24:38 -0500116ShooterMotor::ShooterMotor(::y2014::control_loops::ShooterQueue *my_shooter)
117 : aos::controls::ControlLoop<::y2014::control_loops::ShooterQueue>(
Austin Schuh24957102015-11-28 16:04:40 -0800118 my_shooter),
Brian Silverman17f503e2015-08-02 18:17:18 -0700119 shooter_(MakeShooterLoop()),
120 state_(STATE_INITIALIZE),
Brian Silverman17f503e2015-08-02 18:17:18 -0700121 cycles_not_moved_(0),
122 shot_count_(0),
123 zeroed_(false),
124 distal_posedge_validation_cycles_left_(0),
125 proximal_posedge_validation_cycles_left_(0),
126 last_distal_current_(true),
127 last_proximal_current_(true) {}
128
129double ShooterMotor::PowerToPosition(double power) {
Austin Schuh24957102015-11-28 16:04:40 -0800130 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700131 double maxpower = 0.5 * kSpringConstant *
132 (kMaxExtension * kMaxExtension -
133 (kMaxExtension - values.shooter.upper_limit) *
134 (kMaxExtension - values.shooter.upper_limit));
135 if (power < 0) {
Austin Schuh24957102015-11-28 16:04:40 -0800136 LOG_STRUCT(WARNING, "negative power",
Brian Silvermanb601d892015-12-20 18:24:38 -0500137 ::y2014::control_loops::PowerAdjustment(power, 0));
Brian Silverman17f503e2015-08-02 18:17:18 -0700138 power = 0;
139 } else if (power > maxpower) {
Austin Schuh24957102015-11-28 16:04:40 -0800140 LOG_STRUCT(WARNING, "power too high",
Brian Silvermanb601d892015-12-20 18:24:38 -0500141 ::y2014::control_loops::PowerAdjustment(power, maxpower));
Brian Silverman17f503e2015-08-02 18:17:18 -0700142 power = maxpower;
143 }
144
145 double mp = kMaxExtension * kMaxExtension - (power + power) / kSpringConstant;
146 double new_pos = 0.10;
147 if (mp < 0) {
148 LOG(ERROR,
149 "Power calculation has negative number before square root (%f).\n", mp);
150 } else {
151 new_pos = kMaxExtension - ::std::sqrt(mp);
152 }
153
154 new_pos = ::std::min(::std::max(new_pos, values.shooter.lower_limit),
155 values.shooter.upper_limit);
156 return new_pos;
157}
158
159double ShooterMotor::PositionToPower(double position) {
160 double power = kSpringConstant * position * (kMaxExtension - position / 2.0);
161 return power;
162}
163
164void ShooterMotor::CheckCalibrations(
Brian Silvermanb601d892015-12-20 18:24:38 -0500165 const ::y2014::control_loops::ShooterQueue::Position *position) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700166 CHECK_NOTNULL(position);
Austin Schuh24957102015-11-28 16:04:40 -0800167 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700168
169 // TODO(austin): Validate that this is the right edge.
170 // If we see a posedge on any of the hall effects,
171 if (position->pusher_proximal.posedge_count != last_proximal_posedge_count_ &&
172 !last_proximal_current_) {
173 proximal_posedge_validation_cycles_left_ = 2;
174 }
175 if (proximal_posedge_validation_cycles_left_ > 0) {
176 if (position->pusher_proximal.current) {
177 --proximal_posedge_validation_cycles_left_;
178 if (proximal_posedge_validation_cycles_left_ == 0) {
179 shooter_.SetCalibration(
180 position->pusher_proximal.posedge_value,
181 values.shooter.pusher_proximal.upper_angle);
182
183 LOG(DEBUG, "Setting calibration using proximal sensor\n");
184 zeroed_ = true;
185 }
186 } else {
187 proximal_posedge_validation_cycles_left_ = 0;
188 }
189 }
190
191 if (position->pusher_distal.posedge_count != last_distal_posedge_count_ &&
192 !last_distal_current_) {
193 distal_posedge_validation_cycles_left_ = 2;
194 }
195 if (distal_posedge_validation_cycles_left_ > 0) {
196 if (position->pusher_distal.current) {
197 --distal_posedge_validation_cycles_left_;
198 if (distal_posedge_validation_cycles_left_ == 0) {
199 shooter_.SetCalibration(
200 position->pusher_distal.posedge_value,
201 values.shooter.pusher_distal.upper_angle);
202
203 LOG(DEBUG, "Setting calibration using distal sensor\n");
204 zeroed_ = true;
205 }
206 } else {
207 distal_posedge_validation_cycles_left_ = 0;
208 }
209 }
210}
211
212// Positive is out, and positive power is out.
213void ShooterMotor::RunIteration(
Brian Silvermanb601d892015-12-20 18:24:38 -0500214 const ::y2014::control_loops::ShooterQueue::Goal *goal,
215 const ::y2014::control_loops::ShooterQueue::Position *position,
216 ::y2014::control_loops::ShooterQueue::Output *output,
217 ::y2014::control_loops::ShooterQueue::Status *status) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700218 if (goal && ::std::isnan(goal->shot_power)) {
219 state_ = STATE_ESTOP;
220 LOG(ERROR, "Estopping because got a shot power of NAN.\n");
221 }
222
223 // we must always have these or we have issues.
224 if (status == NULL) {
225 if (output) output->voltage = 0;
226 LOG(ERROR, "Thought I would just check for null and die.\n");
227 return;
228 }
229 status->ready = false;
230
231 if (WasReset()) {
232 state_ = STATE_INITIALIZE;
233 last_distal_current_ = position->pusher_distal.current;
234 last_proximal_current_ = position->pusher_proximal.current;
235 }
236 if (position) {
237 shooter_.CorrectPosition(position->position);
238 }
239
240 // Disable the motors now so that all early returns will return with the
241 // motors disabled.
242 if (output) output->voltage = 0;
243
Austin Schuh24957102015-11-28 16:04:40 -0800244 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700245
246 // Don't even let the control loops run.
247 bool shooter_loop_disable = false;
248
249 const bool disabled =
250 !::aos::joystick_state.get() || !::aos::joystick_state->enabled;
251
252 // If true, move the goal if we saturate.
253 bool cap_goal = false;
254
255 // TODO(austin): Move the offset if we see or don't see a hall effect when we
256 // expect to see one.
257 // Probably not needed yet.
258
259 if (position) {
260 int last_controller_index = shooter_.controller_index();
261 if (position->plunger && position->latch) {
262 // Use the controller without the spring if the latch is set and the
263 // plunger is back
264 shooter_.set_controller_index(1);
265 } else {
266 // Otherwise use the controller with the spring.
267 shooter_.set_controller_index(0);
268 }
269 if (shooter_.controller_index() != last_controller_index) {
270 shooter_.RecalculatePowerGoal();
271 }
272 }
273
274 switch (state_) {
275 case STATE_INITIALIZE:
276 if (position) {
277 // Reinitialize the internal filter state.
278 shooter_.InitializeState(position->position);
279
280 // Start off with the assumption that we are at the value
281 // futhest back given our sensors.
282 if (position->pusher_distal.current) {
283 shooter_.SetCalibration(position->position,
284 values.shooter.pusher_distal.lower_angle);
285 } else if (position->pusher_proximal.current) {
286 shooter_.SetCalibration(position->position,
287 values.shooter.pusher_proximal.upper_angle);
288 } else {
289 shooter_.SetCalibration(position->position,
290 values.shooter.upper_limit);
291 }
292
293 // Go to the current position.
294 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
295 // If the plunger is all the way back, we want to be latched.
296 latch_piston_ = position->plunger;
297 brake_piston_ = false;
298 if (position->latch == latch_piston_) {
299 state_ = STATE_REQUEST_LOAD;
300 } else {
301 shooter_loop_disable = true;
302 LOG(DEBUG,
303 "Not moving on until the latch has moved to avoid a crash\n");
304 }
305 } else {
306 // If we can't start yet because we don't know where we are, set the
307 // latch and brake to their defaults.
308 latch_piston_ = true;
309 brake_piston_ = true;
310 }
311 break;
312 case STATE_REQUEST_LOAD:
313 if (position) {
314 zeroed_ = false;
315 if (position->pusher_distal.current ||
316 position->pusher_proximal.current) {
317 // We started on the sensor, back up until we are found.
318 // If the plunger is all the way back and not latched, it won't be
319 // there for long.
320 state_ = STATE_LOAD_BACKTRACK;
321
322 // The plunger is already back and latched. Don't release it.
323 if (position->plunger && position->latch) {
324 latch_piston_ = true;
325 } else {
326 latch_piston_ = false;
327 }
328 } else if (position->plunger && position->latch) {
329 // The plunger is back and we are latched. We most likely got here
330 // from Initialize, in which case we want to 'load' again anyways to
331 // zero.
332 Load();
333 latch_piston_ = true;
334 } else {
335 // Off the sensor, start loading.
336 Load();
337 latch_piston_ = false;
338 }
339 }
340
341 // Hold our current position.
342 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
343 brake_piston_ = false;
344 break;
345 case STATE_LOAD_BACKTRACK:
346 // If we are here, then that means we started past the edge where we want
347 // to zero. Move backwards until we don't see the sensor anymore.
348 // The plunger is contacting the pusher (or will be shortly).
349
350 if (!disabled) {
351 shooter_.SetGoalPosition(
Austin Schuh0e997732015-11-08 15:14:53 -0800352 shooter_.goal_position() + values.shooter.zeroing_speed * kDt,
Brian Silverman17f503e2015-08-02 18:17:18 -0700353 values.shooter.zeroing_speed);
354 }
355 cap_goal = true;
356 shooter_.set_max_voltage(4.0);
357
358 if (position) {
359 if (!position->pusher_distal.current &&
360 !position->pusher_proximal.current) {
361 Load();
362 }
363 latch_piston_ = position->plunger;
364 }
365
366 brake_piston_ = false;
367 break;
368 case STATE_LOAD:
369 // If we are disabled right now, reset the timer.
370 if (disabled) {
371 Load();
372 // Latch defaults to true when disabled. Leave it latched until we have
373 // useful sensor data.
374 latch_piston_ = true;
375 }
376 if (output == nullptr) {
377 load_timeout_ += ::aos::controls::kLoopFrequency;
378 }
379 // Go to 0, which should be the latch position, or trigger a hall effect
380 // on the way. If we don't see edges where we are supposed to, the
381 // offset will be updated by code above.
382 shooter_.SetGoalPosition(0.0, 0.0);
383
384 if (position) {
385 CheckCalibrations(position);
386
387 // Latch if the plunger is far enough back to trigger the hall effect.
388 // This happens when the distal sensor is triggered.
389 latch_piston_ = position->pusher_distal.current || position->plunger;
390
391 // Check if we are latched and back. Make sure the plunger is all the
392 // way back as well.
393 if (position->plunger && position->latch &&
394 position->pusher_distal.current) {
395 if (!zeroed_) {
396 state_ = STATE_REQUEST_LOAD;
397 } else {
398 state_ = STATE_PREPARE_SHOT;
399 }
400 } else if (position->plunger &&
401 ::std::abs(shooter_.absolute_position() -
402 shooter_.goal_position()) < 0.001) {
403 // We are at the goal, but not latched.
404 state_ = STATE_LOADING_PROBLEM;
Austin Schuh214e9c12016-11-25 17:26:20 -0800405 loading_problem_end_time_ =
406 monotonic_clock::now() + kLoadProblemEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700407 }
408 }
Austin Schuh214e9c12016-11-25 17:26:20 -0800409 if (load_timeout_ < monotonic_clock::now()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700410 if (position) {
Austin Schuhadf2cde2015-11-08 20:35:16 -0800411 // If none of the sensors is triggered, estop.
412 // Otherwise, trigger anyways if it has been 0.5 seconds more.
413 if (!(position->pusher_distal.current ||
414 position->pusher_proximal.current) ||
Austin Schuh214e9c12016-11-25 17:26:20 -0800415 (load_timeout_ + chrono::milliseconds(500) <
416 monotonic_clock::now())) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700417 state_ = STATE_ESTOP;
418 LOG(ERROR, "Estopping because took too long to load.\n");
419 }
420 }
421 }
422 brake_piston_ = false;
423 break;
424 case STATE_LOADING_PROBLEM:
425 if (disabled) {
426 state_ = STATE_REQUEST_LOAD;
427 break;
428 }
429 // We got to the goal, but the latch hasn't registered as down. It might
430 // be stuck, or on it's way but not there yet.
Austin Schuh214e9c12016-11-25 17:26:20 -0800431 if (monotonic_clock::now() > loading_problem_end_time_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700432 // Timeout by unloading.
433 Unload();
434 } else if (position && position->plunger && position->latch) {
435 // If both trigger, we are latched.
436 state_ = STATE_PREPARE_SHOT;
437 }
438 // Move a bit further back to help it trigger.
439 // If the latch is slow due to the air flowing through the tubes or
440 // inertia, but is otherwise free, this won't have much time to do
441 // anything and is safe. Otherwise this gives us a bit more room to free
442 // up the latch.
443 shooter_.SetGoalPosition(values.shooter.lower_limit, 0.0);
444 if (position) {
445 LOG(DEBUG, "Waiting on latch: plunger %d, latch: %d\n",
446 position->plunger, position->latch);
447 }
448
449 latch_piston_ = true;
450 brake_piston_ = false;
451 break;
452 case STATE_PREPARE_SHOT:
453 // Move the shooter to the shot power set point and then lock the brake.
454 // TODO(austin): Timeout. Low priority.
455
456 if (goal) {
457 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
458 }
459
460 LOG(DEBUG, "PDIFF: absolute_position: %.2f, pow: %.2f\n",
461 shooter_.absolute_position(),
462 goal ? PowerToPosition(goal->shot_power)
463 : ::std::numeric_limits<double>::quiet_NaN());
464 if (goal &&
465 ::std::abs(shooter_.absolute_position() -
466 PowerToPosition(goal->shot_power)) < 0.001 &&
467 ::std::abs(shooter_.absolute_velocity()) < 0.005) {
468 // We are there, set the brake and move on.
469 latch_piston_ = true;
470 brake_piston_ = true;
Austin Schuh214e9c12016-11-25 17:26:20 -0800471 shooter_brake_set_time_ = monotonic_clock::now() + kShooterBrakeSetTime;
Brian Silverman17f503e2015-08-02 18:17:18 -0700472 state_ = STATE_READY;
473 } else {
474 latch_piston_ = true;
475 brake_piston_ = false;
476 }
477 if (goal && goal->unload_requested) {
478 Unload();
479 }
480 break;
481 case STATE_READY:
482 LOG(DEBUG, "In ready\n");
483 // Wait until the brake is set, and a shot is requested or the shot power
484 // is changed.
Austin Schuh214e9c12016-11-25 17:26:20 -0800485 if (monotonic_clock::now() > shooter_brake_set_time_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700486 status->ready = true;
487 // We have waited long enough for the brake to set, turn the shooter
488 // control loop off.
489 shooter_loop_disable = true;
490 LOG(DEBUG, "Brake is now set\n");
491 if (goal && goal->shot_requested && !disabled) {
492 LOG(DEBUG, "Shooting now\n");
493 shooter_loop_disable = true;
Austin Schuh214e9c12016-11-25 17:26:20 -0800494 shot_end_time_ = monotonic_clock::now() + kShotEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700495 firing_starting_position_ = shooter_.absolute_position();
496 state_ = STATE_FIRE;
497 }
498 }
499 if (state_ == STATE_READY && goal &&
500 ::std::abs(shooter_.absolute_position() -
501 PowerToPosition(goal->shot_power)) > 0.002) {
502 // TODO(austin): Add a state to release the brake.
503
504 // TODO(austin): Do we want to set the brake here or after shooting?
505 // Depends on air usage.
506 status->ready = false;
507 LOG(DEBUG, "Preparing shot again.\n");
508 state_ = STATE_PREPARE_SHOT;
509 }
510
511 if (goal) {
512 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
513 }
514
515 latch_piston_ = true;
516 brake_piston_ = true;
517
518 if (goal && goal->unload_requested) {
519 Unload();
520 }
521 break;
522
523 case STATE_FIRE:
524 if (disabled) {
525 if (position) {
526 if (position->plunger) {
527 // If disabled and the plunger is still back there, reset the
528 // timeout.
Austin Schuh214e9c12016-11-25 17:26:20 -0800529 shot_end_time_ = monotonic_clock::now() + kShotEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700530 }
531 }
532 }
533 shooter_loop_disable = true;
534 // Count the number of contiguous cycles during which we haven't moved.
535 if (::std::abs(last_position_.position - shooter_.absolute_position()) <
536 0.0005) {
537 ++cycles_not_moved_;
538 } else {
539 cycles_not_moved_ = 0;
540 }
541
542 // If we have moved any amount since the start and the shooter has now
543 // been still for a couple cycles, the shot finished.
544 // Also move on if it times out.
Austin Schuh829e6ad2015-11-08 14:10:37 -0800545 if (((::std::abs(firing_starting_position_ -
546 shooter_.absolute_position()) > 0.0005 &&
Austin Schuhadf2cde2015-11-08 20:35:16 -0800547 cycles_not_moved_ > 6) ||
Austin Schuh214e9c12016-11-25 17:26:20 -0800548 monotonic_clock::now() > shot_end_time_) &&
Austin Schuh829e6ad2015-11-08 14:10:37 -0800549 ::aos::robot_state->voltage_battery > 10.5) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700550 state_ = STATE_REQUEST_LOAD;
551 ++shot_count_;
552 }
553 latch_piston_ = false;
554 brake_piston_ = true;
555 break;
556 case STATE_UNLOAD:
557 // Reset the timeouts.
558 if (disabled) Unload();
559
560 // If it is latched and the plunger is back, move the pusher back to catch
561 // the plunger.
562 bool all_back;
563 if (position) {
564 all_back = position->plunger && position->latch;
565 } else {
566 all_back = last_position_.plunger && last_position_.latch;
567 }
568
569 if (all_back) {
570 // Pull back to 0, 0.
571 shooter_.SetGoalPosition(0.0, 0.0);
572 if (shooter_.absolute_position() < 0.005) {
573 // When we are close enough, 'fire'.
574 latch_piston_ = false;
575 } else {
576 latch_piston_ = true;
577
578 if (position) {
579 CheckCalibrations(position);
580 }
581 }
582 } else {
583 // The plunger isn't all the way back, or it is and it is unlatched, so
584 // we can now unload.
585 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
586 latch_piston_ = false;
587 state_ = STATE_UNLOAD_MOVE;
Austin Schuh214e9c12016-11-25 17:26:20 -0800588 unload_timeout_ = monotonic_clock::now() + kUnloadTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700589 }
590
Austin Schuh214e9c12016-11-25 17:26:20 -0800591 if (monotonic_clock::now() > unload_timeout_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700592 // We have been stuck trying to unload for way too long, give up and
593 // turn everything off.
594 state_ = STATE_ESTOP;
595 LOG(ERROR, "Estopping because took too long to unload.\n");
596 }
597
598 brake_piston_ = false;
599 break;
600 case STATE_UNLOAD_MOVE: {
601 if (disabled) {
Austin Schuh214e9c12016-11-25 17:26:20 -0800602 unload_timeout_ = monotonic_clock::now() + kUnloadTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700603 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
604 }
605 cap_goal = true;
606 shooter_.set_max_voltage(6.0);
607
608 // Slowly move back until we hit the upper limit.
609 // If we were at the limit last cycle, we are done unloading.
610 // This is because if we saturate, we might hit the limit before we are
611 // actually there.
612 if (shooter_.goal_position() >= values.shooter.upper_limit) {
613 shooter_.SetGoalPosition(values.shooter.upper_limit, 0.0);
614 // We don't want the loop fighting the spring when we are unloaded.
615 // Turn it off.
616 shooter_loop_disable = true;
617 state_ = STATE_READY_UNLOAD;
618 } else {
619 shooter_.SetGoalPosition(
620 ::std::min(
621 values.shooter.upper_limit,
Austin Schuh0e997732015-11-08 15:14:53 -0800622 shooter_.goal_position() + values.shooter.unload_speed * kDt),
Brian Silverman17f503e2015-08-02 18:17:18 -0700623 values.shooter.unload_speed);
624 }
625
626 latch_piston_ = false;
627 brake_piston_ = false;
628 } break;
629 case STATE_READY_UNLOAD:
630 if (goal && goal->load_requested) {
631 state_ = STATE_REQUEST_LOAD;
632 }
633 // If we are ready to load again,
634 shooter_loop_disable = true;
635
636 latch_piston_ = false;
637 brake_piston_ = false;
638 break;
639
640 case STATE_ESTOP:
641 LOG(WARNING, "estopped\n");
642 // Totally lost, go to a safe state.
643 shooter_loop_disable = true;
644 latch_piston_ = true;
645 brake_piston_ = true;
646 break;
647 }
648
649 if (!shooter_loop_disable) {
650 LOG_STRUCT(DEBUG, "running the loop",
Brian Silvermanb601d892015-12-20 18:24:38 -0500651 ::y2014::control_loops::ShooterStatusToLog(
Austin Schuh24957102015-11-28 16:04:40 -0800652 shooter_.goal_position(), shooter_.absolute_position()));
Brian Silverman17f503e2015-08-02 18:17:18 -0700653 if (!cap_goal) {
654 shooter_.set_max_voltage(12.0);
655 }
656 shooter_.Update(output == NULL);
657 if (cap_goal) {
658 shooter_.CapGoal();
659 }
660 // We don't really want to output anything if we went through everything
661 // assuming the motors weren't working.
662 if (output) output->voltage = shooter_.voltage();
663 } else {
664 shooter_.Update(true);
665 shooter_.ZeroPower();
666 if (output) output->voltage = 0.0;
667 }
668
669 status->hard_stop_power = PositionToPower(shooter_.absolute_position());
670
671 if (output) {
672 output->latch_piston = latch_piston_;
673 output->brake_piston = brake_piston_;
674 }
675
676 if (position) {
677 LOG_STRUCT(DEBUG, "internal state",
Brian Silvermanb601d892015-12-20 18:24:38 -0500678 ::y2014::control_loops::ShooterStateToLog(
Brian Silverman17f503e2015-08-02 18:17:18 -0700679 shooter_.absolute_position(), shooter_.absolute_velocity(),
680 state_, position->latch, position->pusher_proximal.current,
681 position->pusher_distal.current, position->plunger,
682 brake_piston_, latch_piston_));
683
684 last_position_ = *position;
685
686 last_distal_posedge_count_ = position->pusher_distal.posedge_count;
687 last_proximal_posedge_count_ = position->pusher_proximal.posedge_count;
688 last_distal_current_ = position->pusher_distal.current;
689 last_proximal_current_ = position->pusher_proximal.current;
690 }
691
Austin Schuhadf2cde2015-11-08 20:35:16 -0800692 status->absolute_position = shooter_.absolute_position();
693 status->absolute_velocity = shooter_.absolute_velocity();
694 status->state = state_;
695
Brian Silverman17f503e2015-08-02 18:17:18 -0700696 status->shots = shot_count_;
697}
698
699void ShooterMotor::ZeroOutputs() {
700 queue_group()->output.MakeWithBuilder()
701 .voltage(0)
702 .latch_piston(latch_piston_)
703 .brake_piston(brake_piston_)
704 .Send();
705}
706
707} // namespace control_loops
Austin Schuh24957102015-11-28 16:04:40 -0800708} // namespace y2014