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