blob: bf14aadc0863251c313cf85b385ea1473621ea21 [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);
Austin Schuhf257f3c2019-10-27 21:00:43 -070038 AOS_LOG(DEBUG, "Capping due to runaway\n");
Brian Silverman17f503e2015-08-02 18:17:18 -070039 } else if (X_hat(2, 0) < last_voltage_ - 4.0) {
40 voltage_ += X_hat(2, 0) - (last_voltage_ - 4.0);
Austin Schuhf257f3c2019-10-27 21:00:43 -070041 AOS_LOG(DEBUG, "Capping due to runaway\n");
Brian Silverman17f503e2015-08-02 18:17:18 -070042 }
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 Schuhf257f3c2019-10-27 21:00:43 -070048 AOS_LOG_STRUCT(
Austin Schuh24957102015-11-28 16:04:40 -080049 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 Schuhf257f3c2019-10-27 21:00:43 -070070 AOS_LOG_STRUCT(DEBUG, "to prevent windup",
71 ::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 Schuhf257f3c2019-10-27 21:00:43 -070085 AOS_LOG_STRUCT(DEBUG, "to prevent windup",
86 ::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 Schuhf257f3c2019-10-27 21:00:43 -0700113 AOS_LOG_STRUCT(DEBUG, "sensor edge (fake?)",
114 ::y2014::control_loops::ShooterChangeCalibration(
115 encoder_val, known_position, old_position,
116 absolute_position(), previous_offset, offset_));
Brian Silverman17f503e2015-08-02 18:17:18 -0700117}
118
Austin Schuh55a13dc2019-01-27 22:39:03 -0800119ShooterMotor::ShooterMotor(::aos::EventLoop *event_loop,
120 const ::std::string &name)
Brian Silvermanb601d892015-12-20 18:24:38 -0500121 : aos::controls::ControlLoop<::y2014::control_loops::ShooterQueue>(
Austin Schuh55a13dc2019-01-27 22:39:03 -0800122 event_loop, name),
Brian Silverman17f503e2015-08-02 18:17:18 -0700123 shooter_(MakeShooterLoop()),
124 state_(STATE_INITIALIZE),
Brian Silverman17f503e2015-08-02 18:17:18 -0700125 cycles_not_moved_(0),
126 shot_count_(0),
127 zeroed_(false),
128 distal_posedge_validation_cycles_left_(0),
129 proximal_posedge_validation_cycles_left_(0),
130 last_distal_current_(true),
131 last_proximal_current_(true) {}
132
133double ShooterMotor::PowerToPosition(double power) {
Austin Schuh24957102015-11-28 16:04:40 -0800134 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700135 double maxpower = 0.5 * kSpringConstant *
136 (kMaxExtension * kMaxExtension -
137 (kMaxExtension - values.shooter.upper_limit) *
138 (kMaxExtension - values.shooter.upper_limit));
139 if (power < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700140 AOS_LOG_STRUCT(WARNING, "negative power",
141 ::y2014::control_loops::PowerAdjustment(power, 0));
Brian Silverman17f503e2015-08-02 18:17:18 -0700142 power = 0;
143 } else if (power > maxpower) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700144 AOS_LOG_STRUCT(WARNING, "power too high",
145 ::y2014::control_loops::PowerAdjustment(power, maxpower));
Brian Silverman17f503e2015-08-02 18:17:18 -0700146 power = maxpower;
147 }
148
149 double mp = kMaxExtension * kMaxExtension - (power + power) / kSpringConstant;
150 double new_pos = 0.10;
151 if (mp < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700152 AOS_LOG(ERROR,
153 "Power calculation has negative number before square root (%f).\n",
154 mp);
Brian Silverman17f503e2015-08-02 18:17:18 -0700155 } else {
156 new_pos = kMaxExtension - ::std::sqrt(mp);
157 }
158
159 new_pos = ::std::min(::std::max(new_pos, values.shooter.lower_limit),
160 values.shooter.upper_limit);
161 return new_pos;
162}
163
164double ShooterMotor::PositionToPower(double position) {
165 double power = kSpringConstant * position * (kMaxExtension - position / 2.0);
166 return power;
167}
168
169void ShooterMotor::CheckCalibrations(
Brian Silvermanb601d892015-12-20 18:24:38 -0500170 const ::y2014::control_loops::ShooterQueue::Position *position) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700171 AOS_CHECK_NOTNULL(position);
Austin Schuh24957102015-11-28 16:04:40 -0800172 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700173
174 // TODO(austin): Validate that this is the right edge.
175 // If we see a posedge on any of the hall effects,
176 if (position->pusher_proximal.posedge_count != last_proximal_posedge_count_ &&
177 !last_proximal_current_) {
178 proximal_posedge_validation_cycles_left_ = 2;
179 }
180 if (proximal_posedge_validation_cycles_left_ > 0) {
181 if (position->pusher_proximal.current) {
182 --proximal_posedge_validation_cycles_left_;
183 if (proximal_posedge_validation_cycles_left_ == 0) {
184 shooter_.SetCalibration(
185 position->pusher_proximal.posedge_value,
186 values.shooter.pusher_proximal.upper_angle);
187
Austin Schuhf257f3c2019-10-27 21:00:43 -0700188 AOS_LOG(DEBUG, "Setting calibration using proximal sensor\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700189 zeroed_ = true;
190 }
191 } else {
192 proximal_posedge_validation_cycles_left_ = 0;
193 }
194 }
195
196 if (position->pusher_distal.posedge_count != last_distal_posedge_count_ &&
197 !last_distal_current_) {
198 distal_posedge_validation_cycles_left_ = 2;
199 }
200 if (distal_posedge_validation_cycles_left_ > 0) {
201 if (position->pusher_distal.current) {
202 --distal_posedge_validation_cycles_left_;
203 if (distal_posedge_validation_cycles_left_ == 0) {
204 shooter_.SetCalibration(
205 position->pusher_distal.posedge_value,
206 values.shooter.pusher_distal.upper_angle);
207
Austin Schuhf257f3c2019-10-27 21:00:43 -0700208 AOS_LOG(DEBUG, "Setting calibration using distal sensor\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700209 zeroed_ = true;
210 }
211 } else {
212 distal_posedge_validation_cycles_left_ = 0;
213 }
214 }
215}
216
217// Positive is out, and positive power is out.
218void ShooterMotor::RunIteration(
Brian Silvermanb601d892015-12-20 18:24:38 -0500219 const ::y2014::control_loops::ShooterQueue::Goal *goal,
220 const ::y2014::control_loops::ShooterQueue::Position *position,
221 ::y2014::control_loops::ShooterQueue::Output *output,
222 ::y2014::control_loops::ShooterQueue::Status *status) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700223 const monotonic_clock::time_point monotonic_now = event_loop()->monotonic_now();
Brian Silverman17f503e2015-08-02 18:17:18 -0700224 if (goal && ::std::isnan(goal->shot_power)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700225 state_ = STATE_ESTOP;
226 AOS_LOG(ERROR, "Estopping because got a shot power of NAN.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700227 }
228
229 // we must always have these or we have issues.
230 if (status == NULL) {
231 if (output) output->voltage = 0;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700232 AOS_LOG(ERROR, "Thought I would just check for null and die.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700233 return;
234 }
235 status->ready = false;
236
237 if (WasReset()) {
238 state_ = STATE_INITIALIZE;
239 last_distal_current_ = position->pusher_distal.current;
240 last_proximal_current_ = position->pusher_proximal.current;
241 }
242 if (position) {
243 shooter_.CorrectPosition(position->position);
244 }
245
246 // Disable the motors now so that all early returns will return with the
247 // motors disabled.
248 if (output) output->voltage = 0;
249
Austin Schuh24957102015-11-28 16:04:40 -0800250 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700251
252 // Don't even let the control loops run.
253 bool shooter_loop_disable = false;
254
Austin Schuheeec74a2019-01-27 20:58:59 -0800255 const bool disabled = !has_joystick_state() || !joystick_state().enabled;
Brian Silverman17f503e2015-08-02 18:17:18 -0700256
257 // If true, move the goal if we saturate.
258 bool cap_goal = false;
259
260 // TODO(austin): Move the offset if we see or don't see a hall effect when we
261 // expect to see one.
262 // Probably not needed yet.
263
264 if (position) {
Austin Schuhc5fceb82017-02-25 16:24:12 -0800265 int last_index = shooter_.index();
Brian Silverman17f503e2015-08-02 18:17:18 -0700266 if (position->plunger && position->latch) {
267 // Use the controller without the spring if the latch is set and the
268 // plunger is back
Austin Schuhc5fceb82017-02-25 16:24:12 -0800269 shooter_.set_index(1);
Brian Silverman17f503e2015-08-02 18:17:18 -0700270 } else {
271 // Otherwise use the controller with the spring.
Austin Schuhc5fceb82017-02-25 16:24:12 -0800272 shooter_.set_index(0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700273 }
Austin Schuhc5fceb82017-02-25 16:24:12 -0800274 if (shooter_.index() != last_index) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700275 shooter_.RecalculatePowerGoal();
276 }
277 }
278
279 switch (state_) {
280 case STATE_INITIALIZE:
281 if (position) {
282 // Reinitialize the internal filter state.
283 shooter_.InitializeState(position->position);
284
285 // Start off with the assumption that we are at the value
286 // futhest back given our sensors.
287 if (position->pusher_distal.current) {
288 shooter_.SetCalibration(position->position,
289 values.shooter.pusher_distal.lower_angle);
290 } else if (position->pusher_proximal.current) {
291 shooter_.SetCalibration(position->position,
292 values.shooter.pusher_proximal.upper_angle);
293 } else {
294 shooter_.SetCalibration(position->position,
295 values.shooter.upper_limit);
296 }
297
298 // Go to the current position.
299 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
300 // If the plunger is all the way back, we want to be latched.
301 latch_piston_ = position->plunger;
302 brake_piston_ = false;
303 if (position->latch == latch_piston_) {
304 state_ = STATE_REQUEST_LOAD;
305 } else {
306 shooter_loop_disable = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700307 AOS_LOG(DEBUG,
308 "Not moving on until the latch has moved to avoid a crash\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700309 }
310 } else {
311 // If we can't start yet because we don't know where we are, set the
312 // latch and brake to their defaults.
313 latch_piston_ = true;
314 brake_piston_ = true;
315 }
316 break;
317 case STATE_REQUEST_LOAD:
318 if (position) {
319 zeroed_ = false;
320 if (position->pusher_distal.current ||
321 position->pusher_proximal.current) {
322 // We started on the sensor, back up until we are found.
323 // If the plunger is all the way back and not latched, it won't be
324 // there for long.
325 state_ = STATE_LOAD_BACKTRACK;
326
327 // The plunger is already back and latched. Don't release it.
328 if (position->plunger && position->latch) {
329 latch_piston_ = true;
330 } else {
331 latch_piston_ = false;
332 }
333 } else if (position->plunger && position->latch) {
334 // The plunger is back and we are latched. We most likely got here
335 // from Initialize, in which case we want to 'load' again anyways to
336 // zero.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700337 Load(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700338 latch_piston_ = true;
339 } else {
340 // Off the sensor, start loading.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700341 Load(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700342 latch_piston_ = false;
343 }
344 }
345
346 // Hold our current position.
347 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
348 brake_piston_ = false;
349 break;
350 case STATE_LOAD_BACKTRACK:
351 // If we are here, then that means we started past the edge where we want
352 // to zero. Move backwards until we don't see the sensor anymore.
353 // The plunger is contacting the pusher (or will be shortly).
354
355 if (!disabled) {
356 shooter_.SetGoalPosition(
Austin Schuh0e997732015-11-08 15:14:53 -0800357 shooter_.goal_position() + values.shooter.zeroing_speed * kDt,
Brian Silverman17f503e2015-08-02 18:17:18 -0700358 values.shooter.zeroing_speed);
359 }
360 cap_goal = true;
361 shooter_.set_max_voltage(4.0);
362
363 if (position) {
364 if (!position->pusher_distal.current &&
365 !position->pusher_proximal.current) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700366 Load(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700367 }
368 latch_piston_ = position->plunger;
369 }
370
371 brake_piston_ = false;
372 break;
373 case STATE_LOAD:
374 // If we are disabled right now, reset the timer.
375 if (disabled) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700376 Load(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700377 // Latch defaults to true when disabled. Leave it latched until we have
378 // useful sensor data.
379 latch_piston_ = true;
380 }
381 if (output == nullptr) {
382 load_timeout_ += ::aos::controls::kLoopFrequency;
383 }
384 // Go to 0, which should be the latch position, or trigger a hall effect
385 // on the way. If we don't see edges where we are supposed to, the
386 // offset will be updated by code above.
387 shooter_.SetGoalPosition(0.0, 0.0);
388
389 if (position) {
390 CheckCalibrations(position);
391
392 // Latch if the plunger is far enough back to trigger the hall effect.
393 // This happens when the distal sensor is triggered.
394 latch_piston_ = position->pusher_distal.current || position->plunger;
395
396 // Check if we are latched and back. Make sure the plunger is all the
397 // way back as well.
398 if (position->plunger && position->latch &&
399 position->pusher_distal.current) {
400 if (!zeroed_) {
401 state_ = STATE_REQUEST_LOAD;
402 } else {
403 state_ = STATE_PREPARE_SHOT;
404 }
405 } else if (position->plunger &&
406 ::std::abs(shooter_.absolute_position() -
407 shooter_.goal_position()) < 0.001) {
408 // We are at the goal, but not latched.
409 state_ = STATE_LOADING_PROBLEM;
Austin Schuh214e9c12016-11-25 17:26:20 -0800410 loading_problem_end_time_ =
Austin Schuh9fe68f72019-08-10 19:32:03 -0700411 monotonic_now + kLoadProblemEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700412 }
413 }
Austin Schuh9fe68f72019-08-10 19:32:03 -0700414 if (load_timeout_ < monotonic_now) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700415 if (position) {
Austin Schuhadf2cde2015-11-08 20:35:16 -0800416 // If none of the sensors is triggered, estop.
417 // Otherwise, trigger anyways if it has been 0.5 seconds more.
418 if (!(position->pusher_distal.current ||
419 position->pusher_proximal.current) ||
Austin Schuh214e9c12016-11-25 17:26:20 -0800420 (load_timeout_ + chrono::milliseconds(500) <
Austin Schuh9fe68f72019-08-10 19:32:03 -0700421 monotonic_now)) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700422 state_ = STATE_ESTOP;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700423 AOS_LOG(ERROR, "Estopping because took too long to load.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700424 }
425 }
426 }
427 brake_piston_ = false;
428 break;
429 case STATE_LOADING_PROBLEM:
430 if (disabled) {
431 state_ = STATE_REQUEST_LOAD;
432 break;
433 }
434 // We got to the goal, but the latch hasn't registered as down. It might
435 // be stuck, or on it's way but not there yet.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700436 if (monotonic_now > loading_problem_end_time_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700437 // Timeout by unloading.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700438 Unload(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700439 } else if (position && position->plunger && position->latch) {
440 // If both trigger, we are latched.
441 state_ = STATE_PREPARE_SHOT;
442 }
443 // Move a bit further back to help it trigger.
444 // If the latch is slow due to the air flowing through the tubes or
445 // inertia, but is otherwise free, this won't have much time to do
446 // anything and is safe. Otherwise this gives us a bit more room to free
447 // up the latch.
448 shooter_.SetGoalPosition(values.shooter.lower_limit, 0.0);
449 if (position) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700450 AOS_LOG(DEBUG, "Waiting on latch: plunger %d, latch: %d\n",
451 position->plunger, position->latch);
Brian Silverman17f503e2015-08-02 18:17:18 -0700452 }
453
454 latch_piston_ = true;
455 brake_piston_ = false;
456 break;
457 case STATE_PREPARE_SHOT:
458 // Move the shooter to the shot power set point and then lock the brake.
459 // TODO(austin): Timeout. Low priority.
460
461 if (goal) {
462 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
463 }
464
Austin Schuhf257f3c2019-10-27 21:00:43 -0700465 AOS_LOG(DEBUG, "PDIFF: absolute_position: %.2f, pow: %.2f\n",
466 shooter_.absolute_position(),
467 goal ? PowerToPosition(goal->shot_power)
468 : ::std::numeric_limits<double>::quiet_NaN());
Brian Silverman17f503e2015-08-02 18:17:18 -0700469 if (goal &&
470 ::std::abs(shooter_.absolute_position() -
471 PowerToPosition(goal->shot_power)) < 0.001 &&
472 ::std::abs(shooter_.absolute_velocity()) < 0.005) {
473 // We are there, set the brake and move on.
474 latch_piston_ = true;
475 brake_piston_ = true;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700476 shooter_brake_set_time_ = monotonic_now + kShooterBrakeSetTime;
Brian Silverman17f503e2015-08-02 18:17:18 -0700477 state_ = STATE_READY;
478 } else {
479 latch_piston_ = true;
480 brake_piston_ = false;
481 }
482 if (goal && goal->unload_requested) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700483 Unload(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700484 }
485 break;
486 case STATE_READY:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700487 AOS_LOG(DEBUG, "In ready\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700488 // Wait until the brake is set, and a shot is requested or the shot power
489 // is changed.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700490 if (monotonic_now > shooter_brake_set_time_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700491 status->ready = true;
492 // We have waited long enough for the brake to set, turn the shooter
493 // control loop off.
494 shooter_loop_disable = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700495 AOS_LOG(DEBUG, "Brake is now set\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700496 if (goal && goal->shot_requested && !disabled) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700497 AOS_LOG(DEBUG, "Shooting now\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700498 shooter_loop_disable = true;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700499 shot_end_time_ = monotonic_now + kShotEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700500 firing_starting_position_ = shooter_.absolute_position();
501 state_ = STATE_FIRE;
502 }
503 }
504 if (state_ == STATE_READY && goal &&
505 ::std::abs(shooter_.absolute_position() -
506 PowerToPosition(goal->shot_power)) > 0.002) {
507 // TODO(austin): Add a state to release the brake.
508
509 // TODO(austin): Do we want to set the brake here or after shooting?
510 // Depends on air usage.
511 status->ready = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700512 AOS_LOG(DEBUG, "Preparing shot again.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700513 state_ = STATE_PREPARE_SHOT;
514 }
515
516 if (goal) {
517 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
518 }
519
520 latch_piston_ = true;
521 brake_piston_ = true;
522
523 if (goal && goal->unload_requested) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700524 Unload(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700525 }
526 break;
527
528 case STATE_FIRE:
529 if (disabled) {
530 if (position) {
531 if (position->plunger) {
532 // If disabled and the plunger is still back there, reset the
533 // timeout.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700534 shot_end_time_ = monotonic_now + kShotEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700535 }
536 }
537 }
538 shooter_loop_disable = true;
539 // Count the number of contiguous cycles during which we haven't moved.
540 if (::std::abs(last_position_.position - shooter_.absolute_position()) <
541 0.0005) {
542 ++cycles_not_moved_;
543 } else {
544 cycles_not_moved_ = 0;
545 }
546
547 // If we have moved any amount since the start and the shooter has now
548 // been still for a couple cycles, the shot finished.
549 // Also move on if it times out.
Austin Schuh829e6ad2015-11-08 14:10:37 -0800550 if (((::std::abs(firing_starting_position_ -
551 shooter_.absolute_position()) > 0.0005 &&
Austin Schuhadf2cde2015-11-08 20:35:16 -0800552 cycles_not_moved_ > 6) ||
Austin Schuh9fe68f72019-08-10 19:32:03 -0700553 monotonic_now > shot_end_time_) &&
Austin Schuheeec74a2019-01-27 20:58:59 -0800554 robot_state().voltage_battery > 10.5) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700555 state_ = STATE_REQUEST_LOAD;
556 ++shot_count_;
557 }
558 latch_piston_ = false;
559 brake_piston_ = true;
560 break;
561 case STATE_UNLOAD:
562 // Reset the timeouts.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700563 if (disabled) Unload(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700564
565 // If it is latched and the plunger is back, move the pusher back to catch
566 // the plunger.
567 bool all_back;
568 if (position) {
569 all_back = position->plunger && position->latch;
570 } else {
571 all_back = last_position_.plunger && last_position_.latch;
572 }
573
574 if (all_back) {
575 // Pull back to 0, 0.
576 shooter_.SetGoalPosition(0.0, 0.0);
577 if (shooter_.absolute_position() < 0.005) {
578 // When we are close enough, 'fire'.
579 latch_piston_ = false;
580 } else {
581 latch_piston_ = true;
582
583 if (position) {
584 CheckCalibrations(position);
585 }
586 }
587 } else {
588 // The plunger isn't all the way back, or it is and it is unlatched, so
589 // we can now unload.
590 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
591 latch_piston_ = false;
592 state_ = STATE_UNLOAD_MOVE;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700593 unload_timeout_ = monotonic_now + kUnloadTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700594 }
595
Austin Schuh9fe68f72019-08-10 19:32:03 -0700596 if (monotonic_now > unload_timeout_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700597 // We have been stuck trying to unload for way too long, give up and
598 // turn everything off.
599 state_ = STATE_ESTOP;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700600 AOS_LOG(ERROR, "Estopping because took too long to unload.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700601 }
602
603 brake_piston_ = false;
604 break;
605 case STATE_UNLOAD_MOVE: {
606 if (disabled) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700607 unload_timeout_ = monotonic_now + kUnloadTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700608 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
609 }
610 cap_goal = true;
611 shooter_.set_max_voltage(6.0);
612
613 // Slowly move back until we hit the upper limit.
614 // If we were at the limit last cycle, we are done unloading.
615 // This is because if we saturate, we might hit the limit before we are
616 // actually there.
617 if (shooter_.goal_position() >= values.shooter.upper_limit) {
618 shooter_.SetGoalPosition(values.shooter.upper_limit, 0.0);
619 // We don't want the loop fighting the spring when we are unloaded.
620 // Turn it off.
621 shooter_loop_disable = true;
622 state_ = STATE_READY_UNLOAD;
623 } else {
624 shooter_.SetGoalPosition(
625 ::std::min(
626 values.shooter.upper_limit,
Austin Schuh0e997732015-11-08 15:14:53 -0800627 shooter_.goal_position() + values.shooter.unload_speed * kDt),
Brian Silverman17f503e2015-08-02 18:17:18 -0700628 values.shooter.unload_speed);
629 }
630
631 latch_piston_ = false;
632 brake_piston_ = false;
633 } break;
634 case STATE_READY_UNLOAD:
635 if (goal && goal->load_requested) {
636 state_ = STATE_REQUEST_LOAD;
637 }
638 // If we are ready to load again,
639 shooter_loop_disable = true;
640
641 latch_piston_ = false;
642 brake_piston_ = false;
643 break;
644
645 case STATE_ESTOP:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700646 AOS_LOG(WARNING, "estopped\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700647 // Totally lost, go to a safe state.
648 shooter_loop_disable = true;
649 latch_piston_ = true;
650 brake_piston_ = true;
651 break;
652 }
653
654 if (!shooter_loop_disable) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700655 AOS_LOG_STRUCT(DEBUG, "running the loop",
656 ::y2014::control_loops::ShooterStatusToLog(
657 shooter_.goal_position(), shooter_.absolute_position()));
Brian Silverman17f503e2015-08-02 18:17:18 -0700658 if (!cap_goal) {
659 shooter_.set_max_voltage(12.0);
660 }
661 shooter_.Update(output == NULL);
662 if (cap_goal) {
663 shooter_.CapGoal();
664 }
665 // We don't really want to output anything if we went through everything
666 // assuming the motors weren't working.
667 if (output) output->voltage = shooter_.voltage();
668 } else {
669 shooter_.Update(true);
670 shooter_.ZeroPower();
671 if (output) output->voltage = 0.0;
672 }
673
674 status->hard_stop_power = PositionToPower(shooter_.absolute_position());
675
676 if (output) {
677 output->latch_piston = latch_piston_;
678 output->brake_piston = brake_piston_;
679 }
680
681 if (position) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700682 AOS_LOG_STRUCT(
683 DEBUG, "internal state",
684 ::y2014::control_loops::ShooterStateToLog(
685 shooter_.absolute_position(), shooter_.absolute_velocity(), state_,
686 position->latch, position->pusher_proximal.current,
687 position->pusher_distal.current, position->plunger, brake_piston_,
688 latch_piston_));
Brian Silverman17f503e2015-08-02 18:17:18 -0700689
690 last_position_ = *position;
691
692 last_distal_posedge_count_ = position->pusher_distal.posedge_count;
693 last_proximal_posedge_count_ = position->pusher_proximal.posedge_count;
694 last_distal_current_ = position->pusher_distal.current;
695 last_proximal_current_ = position->pusher_proximal.current;
696 }
697
Austin Schuhadf2cde2015-11-08 20:35:16 -0800698 status->absolute_position = shooter_.absolute_position();
699 status->absolute_velocity = shooter_.absolute_velocity();
700 status->state = state_;
701
Brian Silverman17f503e2015-08-02 18:17:18 -0700702 status->shots = shot_count_;
703}
704
Austin Schuhaebfb4f2019-01-27 13:26:38 -0800705void ShooterMotor::Zero(::y2014::control_loops::ShooterQueue::Output *output) {
706 output->voltage = 0.0;
707 output->latch_piston = latch_piston_;
708 output->brake_piston = brake_piston_;
Brian Silverman17f503e2015-08-02 18:17:18 -0700709}
710
711} // namespace control_loops
Austin Schuh24957102015-11-28 16:04:40 -0800712} // namespace y2014