blob: 7f3a3b02e06a5de776ffd506cd05343b563c4043 [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
John Park33858a32018-09-28 23:05:48 -07009#include "aos/controls/control_loops.q.h"
10#include "aos/logging/logging.h"
11#include "aos/logging/queue_logging.h"
Brian Silverman17f503e2015-08-02 18:17:18 -070012
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;
Austin Schuhc5fceb82017-02-25 16:24:12 -080059 if (index() == 0) {
Brian Silverman17f503e2015-08-02 18:17:18 -070060 dx = (uncapped_voltage() - max_voltage_) /
Austin Schuh32501832017-02-25 18:32:56 -080061 (controller().K(0, 0) -
62 plant().A(1, 0) * controller().K(0, 2) / plant().A(1, 2));
Brian Silverman17f503e2015-08-02 18:17:18 -070063 mutable_R(0, 0) -= dx;
Austin Schuhc5fceb82017-02-25 16:24:12 -080064 mutable_R(2, 0) -= -plant().A(1, 0) / plant().A(1, 2) * dx;
Brian Silverman17f503e2015-08-02 18:17:18 -070065 } else {
Austin Schuh32501832017-02-25 18:32:56 -080066 dx = (uncapped_voltage() - max_voltage_) / controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -070067 mutable_R(0, 0) -= dx;
68 }
69 capped_goal_ = true;
Austin Schuh24957102015-11-28 16:04:40 -080070 LOG_STRUCT(DEBUG, "to prevent windup",
Brian Silvermanb601d892015-12-20 18:24:38 -050071 ::y2014::control_loops::ShooterMovingGoal(dx));
Brian Silverman17f503e2015-08-02 18:17:18 -070072 } else if (uncapped_voltage() < -max_voltage_) {
73 double dx;
Austin Schuhc5fceb82017-02-25 16:24:12 -080074 if (index() == 0) {
Brian Silverman17f503e2015-08-02 18:17:18 -070075 dx = (uncapped_voltage() + max_voltage_) /
Austin Schuh32501832017-02-25 18:32:56 -080076 (controller().K(0, 0) -
77 plant().A(1, 0) * controller().K(0, 2) / plant().A(1, 2));
Brian Silverman17f503e2015-08-02 18:17:18 -070078 mutable_R(0, 0) -= dx;
Austin Schuhc5fceb82017-02-25 16:24:12 -080079 mutable_R(2, 0) -= -plant().A(1, 0) / plant().A(1, 2) * dx;
Brian Silverman17f503e2015-08-02 18:17:18 -070080 } else {
Austin Schuh32501832017-02-25 18:32:56 -080081 dx = (uncapped_voltage() + max_voltage_) / controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -070082 mutable_R(0, 0) -= dx;
83 }
84 capped_goal_ = true;
Austin Schuh24957102015-11-28 16:04:40 -080085 LOG_STRUCT(DEBUG, "to prevent windup",
Brian Silvermanb601d892015-12-20 18:24:38 -050086 ::y2014::control_loops::ShooterMovingGoal(dx));
Brian Silverman17f503e2015-08-02 18:17:18 -070087 } else {
88 capped_goal_ = false;
89 }
90}
91
92void ZeroedStateFeedbackLoop::RecalculatePowerGoal() {
Austin Schuhc5fceb82017-02-25 16:24:12 -080093 if (index() == 0) {
94 mutable_R(2, 0) = (-plant().A(1, 0) / plant().A(1, 2) * R(0, 0) -
95 plant().A(1, 1) / plant().A(1, 2) * R(1, 0));
Brian Silverman17f503e2015-08-02 18:17:18 -070096 } else {
Austin Schuhc5fceb82017-02-25 16:24:12 -080097 mutable_R(2, 0) = -plant().A(1, 1) / plant().A(1, 2) * R(1, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -070098 }
99}
100
101void ZeroedStateFeedbackLoop::SetCalibration(double encoder_val,
102 double known_position) {
103 double old_position = absolute_position();
104 double previous_offset = offset_;
105 offset_ = known_position - encoder_val;
106 double doffset = offset_ - previous_offset;
107 mutable_X_hat(0, 0) += doffset;
Brian Silverman17f503e2015-08-02 18:17:18 -0700108 // Offset the goal so we don't move.
109 mutable_R(0, 0) += doffset;
Austin Schuhc5fceb82017-02-25 16:24:12 -0800110 if (index() == 0) {
111 mutable_R(2, 0) += -plant().A(1, 0) / plant().A(1, 2) * (doffset);
Brian Silverman17f503e2015-08-02 18:17:18 -0700112 }
Austin Schuh24957102015-11-28 16:04:40 -0800113 LOG_STRUCT(DEBUG, "sensor edge (fake?)",
Brian Silvermanb601d892015-12-20 18:24:38 -0500114 ::y2014::control_loops::ShooterChangeCalibration(
Austin Schuh24957102015-11-28 16:04:40 -0800115 encoder_val, known_position, old_position, absolute_position(),
116 previous_offset, offset_));
Brian Silverman17f503e2015-08-02 18:17:18 -0700117}
118
Brian Silvermanb601d892015-12-20 18:24:38 -0500119ShooterMotor::ShooterMotor(::y2014::control_loops::ShooterQueue *my_shooter)
120 : aos::controls::ControlLoop<::y2014::control_loops::ShooterQueue>(
Austin Schuh24957102015-11-28 16:04:40 -0800121 my_shooter),
Brian Silverman17f503e2015-08-02 18:17:18 -0700122 shooter_(MakeShooterLoop()),
123 state_(STATE_INITIALIZE),
Brian Silverman17f503e2015-08-02 18:17:18 -0700124 cycles_not_moved_(0),
125 shot_count_(0),
126 zeroed_(false),
127 distal_posedge_validation_cycles_left_(0),
128 proximal_posedge_validation_cycles_left_(0),
129 last_distal_current_(true),
130 last_proximal_current_(true) {}
131
132double ShooterMotor::PowerToPosition(double power) {
Austin Schuh24957102015-11-28 16:04:40 -0800133 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700134 double maxpower = 0.5 * kSpringConstant *
135 (kMaxExtension * kMaxExtension -
136 (kMaxExtension - values.shooter.upper_limit) *
137 (kMaxExtension - values.shooter.upper_limit));
138 if (power < 0) {
Austin Schuh24957102015-11-28 16:04:40 -0800139 LOG_STRUCT(WARNING, "negative power",
Brian Silvermanb601d892015-12-20 18:24:38 -0500140 ::y2014::control_loops::PowerAdjustment(power, 0));
Brian Silverman17f503e2015-08-02 18:17:18 -0700141 power = 0;
142 } else if (power > maxpower) {
Austin Schuh24957102015-11-28 16:04:40 -0800143 LOG_STRUCT(WARNING, "power too high",
Brian Silvermanb601d892015-12-20 18:24:38 -0500144 ::y2014::control_loops::PowerAdjustment(power, maxpower));
Brian Silverman17f503e2015-08-02 18:17:18 -0700145 power = maxpower;
146 }
147
148 double mp = kMaxExtension * kMaxExtension - (power + power) / kSpringConstant;
149 double new_pos = 0.10;
150 if (mp < 0) {
151 LOG(ERROR,
152 "Power calculation has negative number before square root (%f).\n", mp);
153 } else {
154 new_pos = kMaxExtension - ::std::sqrt(mp);
155 }
156
157 new_pos = ::std::min(::std::max(new_pos, values.shooter.lower_limit),
158 values.shooter.upper_limit);
159 return new_pos;
160}
161
162double ShooterMotor::PositionToPower(double position) {
163 double power = kSpringConstant * position * (kMaxExtension - position / 2.0);
164 return power;
165}
166
167void ShooterMotor::CheckCalibrations(
Brian Silvermanb601d892015-12-20 18:24:38 -0500168 const ::y2014::control_loops::ShooterQueue::Position *position) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700169 CHECK_NOTNULL(position);
Austin Schuh24957102015-11-28 16:04:40 -0800170 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700171
172 // TODO(austin): Validate that this is the right edge.
173 // If we see a posedge on any of the hall effects,
174 if (position->pusher_proximal.posedge_count != last_proximal_posedge_count_ &&
175 !last_proximal_current_) {
176 proximal_posedge_validation_cycles_left_ = 2;
177 }
178 if (proximal_posedge_validation_cycles_left_ > 0) {
179 if (position->pusher_proximal.current) {
180 --proximal_posedge_validation_cycles_left_;
181 if (proximal_posedge_validation_cycles_left_ == 0) {
182 shooter_.SetCalibration(
183 position->pusher_proximal.posedge_value,
184 values.shooter.pusher_proximal.upper_angle);
185
186 LOG(DEBUG, "Setting calibration using proximal sensor\n");
187 zeroed_ = true;
188 }
189 } else {
190 proximal_posedge_validation_cycles_left_ = 0;
191 }
192 }
193
194 if (position->pusher_distal.posedge_count != last_distal_posedge_count_ &&
195 !last_distal_current_) {
196 distal_posedge_validation_cycles_left_ = 2;
197 }
198 if (distal_posedge_validation_cycles_left_ > 0) {
199 if (position->pusher_distal.current) {
200 --distal_posedge_validation_cycles_left_;
201 if (distal_posedge_validation_cycles_left_ == 0) {
202 shooter_.SetCalibration(
203 position->pusher_distal.posedge_value,
204 values.shooter.pusher_distal.upper_angle);
205
206 LOG(DEBUG, "Setting calibration using distal sensor\n");
207 zeroed_ = true;
208 }
209 } else {
210 distal_posedge_validation_cycles_left_ = 0;
211 }
212 }
213}
214
215// Positive is out, and positive power is out.
216void ShooterMotor::RunIteration(
Brian Silvermanb601d892015-12-20 18:24:38 -0500217 const ::y2014::control_loops::ShooterQueue::Goal *goal,
218 const ::y2014::control_loops::ShooterQueue::Position *position,
219 ::y2014::control_loops::ShooterQueue::Output *output,
220 ::y2014::control_loops::ShooterQueue::Status *status) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700221 if (goal && ::std::isnan(goal->shot_power)) {
222 state_ = STATE_ESTOP;
223 LOG(ERROR, "Estopping because got a shot power of NAN.\n");
224 }
225
226 // we must always have these or we have issues.
227 if (status == NULL) {
228 if (output) output->voltage = 0;
229 LOG(ERROR, "Thought I would just check for null and die.\n");
230 return;
231 }
232 status->ready = false;
233
234 if (WasReset()) {
235 state_ = STATE_INITIALIZE;
236 last_distal_current_ = position->pusher_distal.current;
237 last_proximal_current_ = position->pusher_proximal.current;
238 }
239 if (position) {
240 shooter_.CorrectPosition(position->position);
241 }
242
243 // Disable the motors now so that all early returns will return with the
244 // motors disabled.
245 if (output) output->voltage = 0;
246
Austin Schuh24957102015-11-28 16:04:40 -0800247 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700248
249 // Don't even let the control loops run.
250 bool shooter_loop_disable = false;
251
Austin Schuheeec74a2019-01-27 20:58:59 -0800252 const bool disabled = !has_joystick_state() || !joystick_state().enabled;
Brian Silverman17f503e2015-08-02 18:17:18 -0700253
254 // If true, move the goal if we saturate.
255 bool cap_goal = false;
256
257 // TODO(austin): Move the offset if we see or don't see a hall effect when we
258 // expect to see one.
259 // Probably not needed yet.
260
261 if (position) {
Austin Schuhc5fceb82017-02-25 16:24:12 -0800262 int last_index = shooter_.index();
Brian Silverman17f503e2015-08-02 18:17:18 -0700263 if (position->plunger && position->latch) {
264 // Use the controller without the spring if the latch is set and the
265 // plunger is back
Austin Schuhc5fceb82017-02-25 16:24:12 -0800266 shooter_.set_index(1);
Brian Silverman17f503e2015-08-02 18:17:18 -0700267 } else {
268 // Otherwise use the controller with the spring.
Austin Schuhc5fceb82017-02-25 16:24:12 -0800269 shooter_.set_index(0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700270 }
Austin Schuhc5fceb82017-02-25 16:24:12 -0800271 if (shooter_.index() != last_index) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700272 shooter_.RecalculatePowerGoal();
273 }
274 }
275
276 switch (state_) {
277 case STATE_INITIALIZE:
278 if (position) {
279 // Reinitialize the internal filter state.
280 shooter_.InitializeState(position->position);
281
282 // Start off with the assumption that we are at the value
283 // futhest back given our sensors.
284 if (position->pusher_distal.current) {
285 shooter_.SetCalibration(position->position,
286 values.shooter.pusher_distal.lower_angle);
287 } else if (position->pusher_proximal.current) {
288 shooter_.SetCalibration(position->position,
289 values.shooter.pusher_proximal.upper_angle);
290 } else {
291 shooter_.SetCalibration(position->position,
292 values.shooter.upper_limit);
293 }
294
295 // Go to the current position.
296 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
297 // If the plunger is all the way back, we want to be latched.
298 latch_piston_ = position->plunger;
299 brake_piston_ = false;
300 if (position->latch == latch_piston_) {
301 state_ = STATE_REQUEST_LOAD;
302 } else {
303 shooter_loop_disable = true;
304 LOG(DEBUG,
305 "Not moving on until the latch has moved to avoid a crash\n");
306 }
307 } else {
308 // If we can't start yet because we don't know where we are, set the
309 // latch and brake to their defaults.
310 latch_piston_ = true;
311 brake_piston_ = true;
312 }
313 break;
314 case STATE_REQUEST_LOAD:
315 if (position) {
316 zeroed_ = false;
317 if (position->pusher_distal.current ||
318 position->pusher_proximal.current) {
319 // We started on the sensor, back up until we are found.
320 // If the plunger is all the way back and not latched, it won't be
321 // there for long.
322 state_ = STATE_LOAD_BACKTRACK;
323
324 // The plunger is already back and latched. Don't release it.
325 if (position->plunger && position->latch) {
326 latch_piston_ = true;
327 } else {
328 latch_piston_ = false;
329 }
330 } else if (position->plunger && position->latch) {
331 // The plunger is back and we are latched. We most likely got here
332 // from Initialize, in which case we want to 'load' again anyways to
333 // zero.
334 Load();
335 latch_piston_ = true;
336 } else {
337 // Off the sensor, start loading.
338 Load();
339 latch_piston_ = false;
340 }
341 }
342
343 // Hold our current position.
344 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
345 brake_piston_ = false;
346 break;
347 case STATE_LOAD_BACKTRACK:
348 // If we are here, then that means we started past the edge where we want
349 // to zero. Move backwards until we don't see the sensor anymore.
350 // The plunger is contacting the pusher (or will be shortly).
351
352 if (!disabled) {
353 shooter_.SetGoalPosition(
Austin Schuh0e997732015-11-08 15:14:53 -0800354 shooter_.goal_position() + values.shooter.zeroing_speed * kDt,
Brian Silverman17f503e2015-08-02 18:17:18 -0700355 values.shooter.zeroing_speed);
356 }
357 cap_goal = true;
358 shooter_.set_max_voltage(4.0);
359
360 if (position) {
361 if (!position->pusher_distal.current &&
362 !position->pusher_proximal.current) {
363 Load();
364 }
365 latch_piston_ = position->plunger;
366 }
367
368 brake_piston_ = false;
369 break;
370 case STATE_LOAD:
371 // If we are disabled right now, reset the timer.
372 if (disabled) {
373 Load();
374 // Latch defaults to true when disabled. Leave it latched until we have
375 // useful sensor data.
376 latch_piston_ = true;
377 }
378 if (output == nullptr) {
379 load_timeout_ += ::aos::controls::kLoopFrequency;
380 }
381 // Go to 0, which should be the latch position, or trigger a hall effect
382 // on the way. If we don't see edges where we are supposed to, the
383 // offset will be updated by code above.
384 shooter_.SetGoalPosition(0.0, 0.0);
385
386 if (position) {
387 CheckCalibrations(position);
388
389 // Latch if the plunger is far enough back to trigger the hall effect.
390 // This happens when the distal sensor is triggered.
391 latch_piston_ = position->pusher_distal.current || position->plunger;
392
393 // Check if we are latched and back. Make sure the plunger is all the
394 // way back as well.
395 if (position->plunger && position->latch &&
396 position->pusher_distal.current) {
397 if (!zeroed_) {
398 state_ = STATE_REQUEST_LOAD;
399 } else {
400 state_ = STATE_PREPARE_SHOT;
401 }
402 } else if (position->plunger &&
403 ::std::abs(shooter_.absolute_position() -
404 shooter_.goal_position()) < 0.001) {
405 // We are at the goal, but not latched.
406 state_ = STATE_LOADING_PROBLEM;
Austin Schuh214e9c12016-11-25 17:26:20 -0800407 loading_problem_end_time_ =
408 monotonic_clock::now() + kLoadProblemEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700409 }
410 }
Austin Schuh214e9c12016-11-25 17:26:20 -0800411 if (load_timeout_ < monotonic_clock::now()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700412 if (position) {
Austin Schuhadf2cde2015-11-08 20:35:16 -0800413 // If none of the sensors is triggered, estop.
414 // Otherwise, trigger anyways if it has been 0.5 seconds more.
415 if (!(position->pusher_distal.current ||
416 position->pusher_proximal.current) ||
Austin Schuh214e9c12016-11-25 17:26:20 -0800417 (load_timeout_ + chrono::milliseconds(500) <
418 monotonic_clock::now())) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700419 state_ = STATE_ESTOP;
420 LOG(ERROR, "Estopping because took too long to load.\n");
421 }
422 }
423 }
424 brake_piston_ = false;
425 break;
426 case STATE_LOADING_PROBLEM:
427 if (disabled) {
428 state_ = STATE_REQUEST_LOAD;
429 break;
430 }
431 // We got to the goal, but the latch hasn't registered as down. It might
432 // be stuck, or on it's way but not there yet.
Austin Schuh214e9c12016-11-25 17:26:20 -0800433 if (monotonic_clock::now() > loading_problem_end_time_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700434 // Timeout by unloading.
435 Unload();
436 } else if (position && position->plunger && position->latch) {
437 // If both trigger, we are latched.
438 state_ = STATE_PREPARE_SHOT;
439 }
440 // Move a bit further back to help it trigger.
441 // If the latch is slow due to the air flowing through the tubes or
442 // inertia, but is otherwise free, this won't have much time to do
443 // anything and is safe. Otherwise this gives us a bit more room to free
444 // up the latch.
445 shooter_.SetGoalPosition(values.shooter.lower_limit, 0.0);
446 if (position) {
447 LOG(DEBUG, "Waiting on latch: plunger %d, latch: %d\n",
448 position->plunger, position->latch);
449 }
450
451 latch_piston_ = true;
452 brake_piston_ = false;
453 break;
454 case STATE_PREPARE_SHOT:
455 // Move the shooter to the shot power set point and then lock the brake.
456 // TODO(austin): Timeout. Low priority.
457
458 if (goal) {
459 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
460 }
461
462 LOG(DEBUG, "PDIFF: absolute_position: %.2f, pow: %.2f\n",
463 shooter_.absolute_position(),
464 goal ? PowerToPosition(goal->shot_power)
465 : ::std::numeric_limits<double>::quiet_NaN());
466 if (goal &&
467 ::std::abs(shooter_.absolute_position() -
468 PowerToPosition(goal->shot_power)) < 0.001 &&
469 ::std::abs(shooter_.absolute_velocity()) < 0.005) {
470 // We are there, set the brake and move on.
471 latch_piston_ = true;
472 brake_piston_ = true;
Austin Schuh214e9c12016-11-25 17:26:20 -0800473 shooter_brake_set_time_ = monotonic_clock::now() + kShooterBrakeSetTime;
Brian Silverman17f503e2015-08-02 18:17:18 -0700474 state_ = STATE_READY;
475 } else {
476 latch_piston_ = true;
477 brake_piston_ = false;
478 }
479 if (goal && goal->unload_requested) {
480 Unload();
481 }
482 break;
483 case STATE_READY:
484 LOG(DEBUG, "In ready\n");
485 // Wait until the brake is set, and a shot is requested or the shot power
486 // is changed.
Austin Schuh214e9c12016-11-25 17:26:20 -0800487 if (monotonic_clock::now() > shooter_brake_set_time_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700488 status->ready = true;
489 // We have waited long enough for the brake to set, turn the shooter
490 // control loop off.
491 shooter_loop_disable = true;
492 LOG(DEBUG, "Brake is now set\n");
493 if (goal && goal->shot_requested && !disabled) {
494 LOG(DEBUG, "Shooting now\n");
495 shooter_loop_disable = true;
Austin Schuh214e9c12016-11-25 17:26:20 -0800496 shot_end_time_ = monotonic_clock::now() + kShotEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700497 firing_starting_position_ = shooter_.absolute_position();
498 state_ = STATE_FIRE;
499 }
500 }
501 if (state_ == STATE_READY && goal &&
502 ::std::abs(shooter_.absolute_position() -
503 PowerToPosition(goal->shot_power)) > 0.002) {
504 // TODO(austin): Add a state to release the brake.
505
506 // TODO(austin): Do we want to set the brake here or after shooting?
507 // Depends on air usage.
508 status->ready = false;
509 LOG(DEBUG, "Preparing shot again.\n");
510 state_ = STATE_PREPARE_SHOT;
511 }
512
513 if (goal) {
514 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
515 }
516
517 latch_piston_ = true;
518 brake_piston_ = true;
519
520 if (goal && goal->unload_requested) {
521 Unload();
522 }
523 break;
524
525 case STATE_FIRE:
526 if (disabled) {
527 if (position) {
528 if (position->plunger) {
529 // If disabled and the plunger is still back there, reset the
530 // timeout.
Austin Schuh214e9c12016-11-25 17:26:20 -0800531 shot_end_time_ = monotonic_clock::now() + kShotEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700532 }
533 }
534 }
535 shooter_loop_disable = true;
536 // Count the number of contiguous cycles during which we haven't moved.
537 if (::std::abs(last_position_.position - shooter_.absolute_position()) <
538 0.0005) {
539 ++cycles_not_moved_;
540 } else {
541 cycles_not_moved_ = 0;
542 }
543
544 // If we have moved any amount since the start and the shooter has now
545 // been still for a couple cycles, the shot finished.
546 // Also move on if it times out.
Austin Schuh829e6ad2015-11-08 14:10:37 -0800547 if (((::std::abs(firing_starting_position_ -
548 shooter_.absolute_position()) > 0.0005 &&
Austin Schuhadf2cde2015-11-08 20:35:16 -0800549 cycles_not_moved_ > 6) ||
Austin Schuh214e9c12016-11-25 17:26:20 -0800550 monotonic_clock::now() > shot_end_time_) &&
Austin Schuheeec74a2019-01-27 20:58:59 -0800551 robot_state().voltage_battery > 10.5) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700552 state_ = STATE_REQUEST_LOAD;
553 ++shot_count_;
554 }
555 latch_piston_ = false;
556 brake_piston_ = true;
557 break;
558 case STATE_UNLOAD:
559 // Reset the timeouts.
560 if (disabled) Unload();
561
562 // If it is latched and the plunger is back, move the pusher back to catch
563 // the plunger.
564 bool all_back;
565 if (position) {
566 all_back = position->plunger && position->latch;
567 } else {
568 all_back = last_position_.plunger && last_position_.latch;
569 }
570
571 if (all_back) {
572 // Pull back to 0, 0.
573 shooter_.SetGoalPosition(0.0, 0.0);
574 if (shooter_.absolute_position() < 0.005) {
575 // When we are close enough, 'fire'.
576 latch_piston_ = false;
577 } else {
578 latch_piston_ = true;
579
580 if (position) {
581 CheckCalibrations(position);
582 }
583 }
584 } else {
585 // The plunger isn't all the way back, or it is and it is unlatched, so
586 // we can now unload.
587 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
588 latch_piston_ = false;
589 state_ = STATE_UNLOAD_MOVE;
Austin Schuh214e9c12016-11-25 17:26:20 -0800590 unload_timeout_ = monotonic_clock::now() + kUnloadTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700591 }
592
Austin Schuh214e9c12016-11-25 17:26:20 -0800593 if (monotonic_clock::now() > unload_timeout_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700594 // We have been stuck trying to unload for way too long, give up and
595 // turn everything off.
596 state_ = STATE_ESTOP;
597 LOG(ERROR, "Estopping because took too long to unload.\n");
598 }
599
600 brake_piston_ = false;
601 break;
602 case STATE_UNLOAD_MOVE: {
603 if (disabled) {
Austin Schuh214e9c12016-11-25 17:26:20 -0800604 unload_timeout_ = monotonic_clock::now() + kUnloadTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700605 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
606 }
607 cap_goal = true;
608 shooter_.set_max_voltage(6.0);
609
610 // Slowly move back until we hit the upper limit.
611 // If we were at the limit last cycle, we are done unloading.
612 // This is because if we saturate, we might hit the limit before we are
613 // actually there.
614 if (shooter_.goal_position() >= values.shooter.upper_limit) {
615 shooter_.SetGoalPosition(values.shooter.upper_limit, 0.0);
616 // We don't want the loop fighting the spring when we are unloaded.
617 // Turn it off.
618 shooter_loop_disable = true;
619 state_ = STATE_READY_UNLOAD;
620 } else {
621 shooter_.SetGoalPosition(
622 ::std::min(
623 values.shooter.upper_limit,
Austin Schuh0e997732015-11-08 15:14:53 -0800624 shooter_.goal_position() + values.shooter.unload_speed * kDt),
Brian Silverman17f503e2015-08-02 18:17:18 -0700625 values.shooter.unload_speed);
626 }
627
628 latch_piston_ = false;
629 brake_piston_ = false;
630 } break;
631 case STATE_READY_UNLOAD:
632 if (goal && goal->load_requested) {
633 state_ = STATE_REQUEST_LOAD;
634 }
635 // If we are ready to load again,
636 shooter_loop_disable = true;
637
638 latch_piston_ = false;
639 brake_piston_ = false;
640 break;
641
642 case STATE_ESTOP:
643 LOG(WARNING, "estopped\n");
644 // Totally lost, go to a safe state.
645 shooter_loop_disable = true;
646 latch_piston_ = true;
647 brake_piston_ = true;
648 break;
649 }
650
651 if (!shooter_loop_disable) {
652 LOG_STRUCT(DEBUG, "running the loop",
Brian Silvermanb601d892015-12-20 18:24:38 -0500653 ::y2014::control_loops::ShooterStatusToLog(
Austin Schuh24957102015-11-28 16:04:40 -0800654 shooter_.goal_position(), shooter_.absolute_position()));
Brian Silverman17f503e2015-08-02 18:17:18 -0700655 if (!cap_goal) {
656 shooter_.set_max_voltage(12.0);
657 }
658 shooter_.Update(output == NULL);
659 if (cap_goal) {
660 shooter_.CapGoal();
661 }
662 // We don't really want to output anything if we went through everything
663 // assuming the motors weren't working.
664 if (output) output->voltage = shooter_.voltage();
665 } else {
666 shooter_.Update(true);
667 shooter_.ZeroPower();
668 if (output) output->voltage = 0.0;
669 }
670
671 status->hard_stop_power = PositionToPower(shooter_.absolute_position());
672
673 if (output) {
674 output->latch_piston = latch_piston_;
675 output->brake_piston = brake_piston_;
676 }
677
678 if (position) {
679 LOG_STRUCT(DEBUG, "internal state",
Brian Silvermanb601d892015-12-20 18:24:38 -0500680 ::y2014::control_loops::ShooterStateToLog(
Brian Silverman17f503e2015-08-02 18:17:18 -0700681 shooter_.absolute_position(), shooter_.absolute_velocity(),
682 state_, position->latch, position->pusher_proximal.current,
683 position->pusher_distal.current, position->plunger,
684 brake_piston_, latch_piston_));
685
686 last_position_ = *position;
687
688 last_distal_posedge_count_ = position->pusher_distal.posedge_count;
689 last_proximal_posedge_count_ = position->pusher_proximal.posedge_count;
690 last_distal_current_ = position->pusher_distal.current;
691 last_proximal_current_ = position->pusher_proximal.current;
692 }
693
Austin Schuhadf2cde2015-11-08 20:35:16 -0800694 status->absolute_position = shooter_.absolute_position();
695 status->absolute_velocity = shooter_.absolute_velocity();
696 status->state = state_;
697
Brian Silverman17f503e2015-08-02 18:17:18 -0700698 status->shots = shot_count_;
699}
700
Austin Schuhaebfb4f2019-01-27 13:26:38 -0800701void ShooterMotor::Zero(::y2014::control_loops::ShooterQueue::Output *output) {
702 output->voltage = 0.0;
703 output->latch_piston = latch_piston_;
704 output->brake_piston = brake_piston_;
Brian Silverman17f503e2015-08-02 18:17:18 -0700705}
706
707} // namespace control_loops
Austin Schuh24957102015-11-28 16:04:40 -0800708} // namespace y2014