blob: 0ba599bb114aee6f488a1b4503fbe51a3ad5e92c [file] [log] [blame]
joe93778a62014-02-15 13:22:14 -08001#include "frc971/control_loops/shooter/shooter.h"
joe2d92e852014-01-25 14:31:24 -08002
3#include <stdio.h>
4
5#include <algorithm>
6
7#include "aos/common/control_loop/control_loops.q.h"
8#include "aos/common/logging/logging.h"
9
10#include "frc971/constants.h"
joe93778a62014-02-15 13:22:14 -080011#include "frc971/control_loops/shooter/shooter_motor_plant.h"
joe2d92e852014-01-25 14:31:24 -080012
13namespace frc971 {
14namespace control_loops {
15
Ben Fredricksonedf0e092014-02-16 10:46:50 +000016using ::aos::time::Time;
Ben Fredrickson22c93322014-02-17 05:56:33 +000017
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000018void ZeroedStateFeedbackLoop::CapU() {
19 const double old_voltage = voltage_;
20 voltage_ += U(0, 0);
21
22 uncapped_voltage_ = voltage_;
23
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000024 // Make sure that reality and the observer can't get too far off. There is a
25 // delay by one cycle between the applied voltage and X_hat(2, 0), so compare
26 // against last cycle's voltage.
Austin Schuhbe1401f2014-02-18 03:18:41 -080027 if (X_hat(2, 0) > last_voltage_ + 4.0) {
28 voltage_ -= X_hat(2, 0) - (last_voltage_ + 4.0);
Brian Silverman101b9642014-03-08 12:45:16 -080029 LOG(DEBUG, "Capping due to runaway\n");
Austin Schuhbe1401f2014-02-18 03:18:41 -080030 } else if (X_hat(2, 0) < last_voltage_ - 4.0) {
31 voltage_ += X_hat(2, 0) - (last_voltage_ - 4.0);
Brian Silverman101b9642014-03-08 12:45:16 -080032 LOG(DEBUG, "Capping due to runaway\n");
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000033 }
34
Austin Schuhd34569d2014-02-18 20:26:38 -080035 voltage_ = std::min(max_voltage_, voltage_);
36 voltage_ = std::max(-max_voltage_, voltage_);
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000037 U(0, 0) = voltage_ - old_voltage;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000038
Brian Silverman101b9642014-03-08 12:45:16 -080039 LOG(DEBUG, "X_hat is %f, applied is %f\n", X_hat(2, 0), voltage_);
Austin Schuhbe1401f2014-02-18 03:18:41 -080040
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000041 last_voltage_ = voltage_;
Austin Schuhd34569d2014-02-18 20:26:38 -080042 capped_goal_ = false;
43}
44
45void ZeroedStateFeedbackLoop::CapGoal() {
46 if (uncapped_voltage() > max_voltage_) {
47 double dx;
48 if (controller_index() == 0) {
49 dx = (uncapped_voltage() - max_voltage_) /
50 (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
51 R(0, 0) -= dx;
52 R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
53 } else {
54 dx = (uncapped_voltage() - max_voltage_) / K(0, 0);
55 R(0, 0) -= dx;
56 }
57 capped_goal_ = true;
58 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
59 } else if (uncapped_voltage() < -max_voltage_) {
60 double dx;
61 if (controller_index() == 0) {
62 dx = (uncapped_voltage() + max_voltage_) /
63 (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
64 R(0, 0) -= dx;
65 R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
66 } else {
67 dx = (uncapped_voltage() + max_voltage_) / K(0, 0);
68 R(0, 0) -= dx;
69 }
70 capped_goal_ = true;
71 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
72 } else {
73 capped_goal_ = false;
74 }
75}
76
77void ZeroedStateFeedbackLoop::RecalculatePowerGoal() {
78 if (controller_index() == 0) {
79 R(2, 0) = (-A(1, 0) / A(1, 2) * R(0, 0) - A(1, 1) / A(1, 2) * R(1, 0));
80 } else {
81 R(2, 0) = -A(1, 1) / A(1, 2) * R(1, 0);
82 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000083}
84
Austin Schuh30537882014-02-18 01:07:23 -080085void ZeroedStateFeedbackLoop::SetCalibration(double encoder_val,
86 double known_position) {
87 LOG(INFO, "Setting calibration such that %f -> %f\n", encoder_val,
88 known_position);
89 LOG(INFO, "Position was %f\n", absolute_position());
90 double previous_offset = offset_;
91 offset_ = known_position - encoder_val;
92 double doffset = offset_ - previous_offset;
93 LOG(INFO, "Changing offset from %f to %f\n", previous_offset, offset_);
94 X_hat(0, 0) += doffset;
95 // Offset our measurements because the offset is baked into them.
96 Y_(0, 0) += doffset;
97 // Offset the goal so we don't move.
98 R(0, 0) += doffset;
Austin Schuhd34569d2014-02-18 20:26:38 -080099 if (controller_index() == 0) {
100 R(2, 0) += -A(1, 0) / A(1, 2) * (doffset);
101 }
Austin Schuh30537882014-02-18 01:07:23 -0800102 LOG(INFO, "Validation: position is %f\n", absolute_position());
103}
104
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000105ShooterMotor::ShooterMotor(control_loops::ShooterGroup *my_shooter)
106 : aos::control_loops::ControlLoop<control_loops::ShooterGroup>(my_shooter),
107 shooter_(MakeShooterLoop()),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000108 state_(STATE_INITIALIZE),
109 loading_problem_end_time_(0, 0),
Austin Schuh30537882014-02-18 01:07:23 -0800110 load_timeout_(0, 0),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000111 shooter_brake_set_time_(0, 0),
Austin Schuh30537882014-02-18 01:07:23 -0800112 unload_timeout_(0, 0),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000113 shot_end_time_(0, 0),
Austin Schuh4edad872014-03-02 11:56:12 -0800114 cycles_not_moved_(0),
Austin Schuhd30d7382014-03-02 21:15:38 -0800115 shot_count_(0),
116 zeroed_(false),
117 distal_posedge_validation_cycles_left_(0),
Austin Schuh2e976812014-03-05 19:56:58 -0800118 proximal_posedge_validation_cycles_left_(0),
119 last_distal_current_(true),
120 last_proximal_current_(true) {}
Austin Schuh30537882014-02-18 01:07:23 -0800121
122double ShooterMotor::PowerToPosition(double power) {
Austin Schuh30537882014-02-18 01:07:23 -0800123 const frc971::constants::Values &values = constants::GetValues();
Ben Fredricksonda334d12014-02-23 09:09:23 +0000124 double maxpower = 0.5 * kSpringConstant *
125 (kMaxExtension * kMaxExtension -
126 (kMaxExtension - values.shooter.upper_limit) *
127 (kMaxExtension - values.shooter.upper_limit));
128 if (power < 0) {
Ben Fredrickson7c9314f2014-02-23 09:26:17 +0000129 //LOG(ERROR, "Power too low giving minimum (%f) (%f).\n", power,
130 // 0.0);
Ben Fredricksonda334d12014-02-23 09:09:23 +0000131 power = 0;
132 } else if (power > maxpower) {
Ben Fredrickson7c9314f2014-02-23 09:26:17 +0000133 //LOG(ERROR, "Power too high giving maximum (%f) (%f).\n", power,
134 // maxpower);
Ben Fredricksonda334d12014-02-23 09:09:23 +0000135 power = maxpower;
136 }
137
138 double mp = kMaxExtension * kMaxExtension - (power + power) / kSpringConstant;
139 double new_pos = 0.10;
140 if (mp < 0) {
141 LOG(ERROR,
142 "Power calculation has negative number before square root (%f).\n", mp);
143 } else {
144 new_pos = kMaxExtension - ::std::sqrt(mp);
145 }
146
147 new_pos = ::std::min(::std::max(new_pos, values.shooter.lower_limit),
Austin Schuh30537882014-02-18 01:07:23 -0800148 values.shooter.upper_limit);
149 return new_pos;
150}
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000151
James Kuszmauld29689f2014-03-02 13:06:54 -0800152double ShooterMotor::PositionToPower(double position) {
153 double power = kSpringConstant * position * (kMaxExtension - position / 2.0);
154 return power;
155}
156
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000157// Positive is out, and positive power is out.
joe2d92e852014-01-25 14:31:24 -0800158void ShooterMotor::RunIteration(
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000159 const control_loops::ShooterGroup::Goal *goal,
160 const control_loops::ShooterGroup::Position *position,
161 control_loops::ShooterGroup::Output *output,
Ben Fredrickson22c93322014-02-17 05:56:33 +0000162 control_loops::ShooterGroup::Status *status) {
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000163 constexpr double dt = 0.01;
164
Ben Fredrickson61893d52014-03-02 09:43:23 +0000165 if (::std::isnan(goal->shot_power)) {
166 state_ = STATE_ESTOP;
167 LOG(ERROR, "Got a shot power of NAN.\n");
168 }
169
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000170 // we must always have these or we have issues.
171 if (goal == NULL || status == NULL) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000172 if (output) output->voltage = 0;
173 LOG(ERROR, "Thought I would just check for null and die.\n");
174 return;
175 }
James Kuszmaul4abaf482014-02-26 21:16:35 -0800176 status->ready = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000177
Austin Schuh30537882014-02-18 01:07:23 -0800178 if (reset()) {
179 state_ = STATE_INITIALIZE;
Austin Schuh2e976812014-03-05 19:56:58 -0800180 last_distal_current_ = position->pusher_distal.current;
181 last_proximal_current_ = position->pusher_proximal.current;
Austin Schuh30537882014-02-18 01:07:23 -0800182 }
183 if (position) {
184 shooter_.CorrectPosition(position->position);
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000185 }
joe2d92e852014-01-25 14:31:24 -0800186
187 // Disable the motors now so that all early returns will return with the
188 // motors disabled.
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000189 if (output) output->voltage = 0;
joe2d92e852014-01-25 14:31:24 -0800190
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000191 const frc971::constants::Values &values = constants::GetValues();
192
Brian Silvermanaae236a2014-02-17 01:49:39 -0800193 // Don't even let the control loops run.
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000194 bool shooter_loop_disable = false;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000195
Austin Schuh30537882014-02-18 01:07:23 -0800196 const bool disabled = !::aos::robot_state->enabled;
Austin Schuhd34569d2014-02-18 20:26:38 -0800197 // If true, move the goal if we saturate.
198 bool cap_goal = false;
199
200 // TODO(austin): Move the offset if we see or don't see a hall effect when we
201 // expect to see one.
202 // Probably not needed yet.
203
204 if (position) {
205 int last_controller_index = shooter_.controller_index();
206 if (position->plunger && position->latch) {
207 // Use the controller without the spring if the latch is set and the
208 // plunger is back
209 shooter_.set_controller_index(1);
210 } else {
211 // Otherwise use the controller with the spring.
212 shooter_.set_controller_index(0);
213 }
214 if (shooter_.controller_index() != last_controller_index) {
215 shooter_.RecalculatePowerGoal();
216 }
217 }
Austin Schuh30537882014-02-18 01:07:23 -0800218
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000219 switch (state_) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000220 case STATE_INITIALIZE:
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000221 if (position) {
Austin Schuh30537882014-02-18 01:07:23 -0800222 // Reinitialize the internal filter state.
223 shooter_.InitializeState(position->position);
Austin Schuh30537882014-02-18 01:07:23 -0800224
225 // Start off with the assumption that we are at the value
226 // futhest back given our sensors.
227 if (position->pusher_distal.current) {
228 shooter_.SetCalibration(position->position,
229 values.shooter.pusher_distal.lower_angle);
230 } else if (position->pusher_proximal.current) {
231 shooter_.SetCalibration(position->position,
Austin Schuhd34569d2014-02-18 20:26:38 -0800232 values.shooter.pusher_proximal.upper_angle);
Austin Schuh30537882014-02-18 01:07:23 -0800233 } else {
234 shooter_.SetCalibration(position->position,
235 values.shooter.upper_limit);
236 }
237
Austin Schuh30537882014-02-18 01:07:23 -0800238 // Go to the current position.
239 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
240 // If the plunger is all the way back, we want to be latched.
241 latch_piston_ = position->plunger;
242 brake_piston_ = false;
Austin Schuh1a08bd82014-03-09 00:47:11 -0800243 if (position->latch == latch_piston_) {
244 state_ = STATE_REQUEST_LOAD;
245 } else {
246 shooter_loop_disable = true;
247 LOG(DEBUG, "Not moving on until the latch has moved to avoid a crash\n");
248 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000249 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800250 // If we can't start yet because we don't know where we are, set the
251 // latch and brake to their defaults.
252 latch_piston_ = true;
253 brake_piston_ = true;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000254 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000255 break;
256 case STATE_REQUEST_LOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800257 if (position) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800258 zeroed_ = false;
Ben Fredrickson26a0dee2014-02-23 04:38:23 +0000259 if (position->pusher_distal.current ||
260 position->pusher_proximal.current) {
Austin Schuh30537882014-02-18 01:07:23 -0800261 // We started on the sensor, back up until we are found.
262 // If the plunger is all the way back and not latched, it won't be
263 // there for long.
264 state_ = STATE_LOAD_BACKTRACK;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800265
266 // The plunger is already back and latched. Don't release it.
267 if (position->plunger && position->latch) {
268 latch_piston_ = true;
269 } else {
270 latch_piston_ = false;
271 }
272 } else if (position->plunger && position->latch) {
273 // The plunger is back and we are latched. We most likely got here
274 // from Initialize, in which case we want to 'load' again anyways to
275 // zero.
276 Load();
277 latch_piston_ = true;
Austin Schuh30537882014-02-18 01:07:23 -0800278 } else {
279 // Off the sensor, start loading.
280 Load();
281 latch_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000282 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000283 }
284
Austin Schuh30537882014-02-18 01:07:23 -0800285 // Hold our current position.
286 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
287 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000288 break;
289 case STATE_LOAD_BACKTRACK:
Austin Schuh30537882014-02-18 01:07:23 -0800290 // If we are here, then that means we started past the edge where we want
291 // to zero. Move backwards until we don't see the sensor anymore.
292 // The plunger is contacting the pusher (or will be shortly).
293
Austin Schuh30537882014-02-18 01:07:23 -0800294 if (!disabled) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000295 shooter_.SetGoalPosition(
Austin Schuhfaeee632014-02-18 01:24:05 -0800296 shooter_.goal_position() + values.shooter.zeroing_speed * dt,
297 values.shooter.zeroing_speed);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000298 }
Austin Schuhd34569d2014-02-18 20:26:38 -0800299 cap_goal = true;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800300 shooter_.set_max_voltage(4.0);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000301
Austin Schuh30537882014-02-18 01:07:23 -0800302 if (position) {
Ben Fredrickson26a0dee2014-02-23 04:38:23 +0000303 if (!position->pusher_distal.current &&
304 !position->pusher_proximal.current) {
Austin Schuh30537882014-02-18 01:07:23 -0800305 Load();
306 }
Austin Schuh6b428602014-02-22 21:02:00 -0800307 latch_piston_ = position->plunger;
Austin Schuh30537882014-02-18 01:07:23 -0800308 }
309
Austin Schuh30537882014-02-18 01:07:23 -0800310 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000311 break;
312 case STATE_LOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800313 // If we are disabled right now, reset the timer.
314 if (disabled) {
315 Load();
316 // Latch defaults to true when disabled. Leave it latched until we have
317 // useful sensor data.
318 latch_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000319 }
Austin Schuh30537882014-02-18 01:07:23 -0800320 // Go to 0, which should be the latch position, or trigger a hall effect
321 // on the way. If we don't see edges where we are supposed to, the
322 // offset will be updated by code above.
323 shooter_.SetGoalPosition(0.0, 0.0);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000324
Austin Schuh30537882014-02-18 01:07:23 -0800325 if (position) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800326 // TODO(austin): Validate that this is the right edge.
Austin Schuh30537882014-02-18 01:07:23 -0800327 // If we see a posedge on any of the hall effects,
328 if (position->pusher_proximal.posedge_count !=
Austin Schuh2e976812014-03-05 19:56:58 -0800329 last_proximal_posedge_count_ &&
330 !last_proximal_current_) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800331 proximal_posedge_validation_cycles_left_ = 2;
Austin Schuh30537882014-02-18 01:07:23 -0800332 }
Austin Schuhd30d7382014-03-02 21:15:38 -0800333 if (proximal_posedge_validation_cycles_left_ > 0) {
334 if (position->pusher_proximal.current) {
335 --proximal_posedge_validation_cycles_left_;
336 if (proximal_posedge_validation_cycles_left_ == 0) {
337 shooter_.SetCalibration(
338 position->pusher_proximal.posedge_value,
339 values.shooter.pusher_proximal.upper_angle);
340
341 LOG(DEBUG, "Setting calibration using proximal sensor\n");
342 zeroed_ = true;
343 }
344 } else {
345 proximal_posedge_validation_cycles_left_ = 0;
346 }
347 }
348
Austin Schuh30537882014-02-18 01:07:23 -0800349 if (position->pusher_distal.posedge_count !=
Austin Schuh2e976812014-03-05 19:56:58 -0800350 last_distal_posedge_count_ &&
351 !last_distal_current_) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800352 distal_posedge_validation_cycles_left_ = 2;
353 }
354 if (distal_posedge_validation_cycles_left_ > 0) {
355 if (position->pusher_distal.current) {
356 --distal_posedge_validation_cycles_left_;
357 if (distal_posedge_validation_cycles_left_ == 0) {
358 shooter_.SetCalibration(
359 position->pusher_distal.posedge_value,
360 values.shooter.pusher_distal.upper_angle);
361
362 LOG(DEBUG, "Setting calibration using distal sensor\n");
363 zeroed_ = true;
364 }
365 } else {
366 distal_posedge_validation_cycles_left_ = 0;
367 }
Austin Schuh30537882014-02-18 01:07:23 -0800368 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000369
Austin Schuh30537882014-02-18 01:07:23 -0800370 // Latch if the plunger is far enough back to trigger the hall effect.
371 // This happens when the distal sensor is triggered.
Austin Schuh06cbbf12014-02-22 02:07:31 -0800372 latch_piston_ = position->pusher_distal.current || position->plunger;
Austin Schuh30537882014-02-18 01:07:23 -0800373
Austin Schuh06cbbf12014-02-22 02:07:31 -0800374 // Check if we are latched and back. Make sure the plunger is all the
375 // way back as well.
376 if (position->plunger && position->latch &&
377 position->pusher_distal.current) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800378 if (!zeroed_) {
379 state_ = STATE_REQUEST_LOAD;
380 } else {
381 state_ = STATE_PREPARE_SHOT;
382 }
Austin Schuh30537882014-02-18 01:07:23 -0800383 } else if (position->plunger &&
384 ::std::abs(shooter_.absolute_position() -
385 shooter_.goal_position()) < 0.001) {
386 // We are at the goal, but not latched.
387 state_ = STATE_LOADING_PROBLEM;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800388 loading_problem_end_time_ = Time::Now() + kLoadProblemEndTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800389 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000390 }
Austin Schuh30537882014-02-18 01:07:23 -0800391 if (load_timeout_ < Time::Now()) {
392 if (position) {
393 if (!position->pusher_distal.current ||
394 !position->pusher_proximal.current) {
395 state_ = STATE_ESTOP;
396 }
397 }
398 } else if (goal->unload_requested) {
399 Unload();
400 }
401 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000402 break;
403 case STATE_LOADING_PROBLEM:
Austin Schuh30537882014-02-18 01:07:23 -0800404 if (disabled) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800405 state_ = STATE_REQUEST_LOAD;
406 break;
Austin Schuh30537882014-02-18 01:07:23 -0800407 }
408 // We got to the goal, but the latch hasn't registered as down. It might
409 // be stuck, or on it's way but not there yet.
Ben Fredrickson22c93322014-02-17 05:56:33 +0000410 if (Time::Now() > loading_problem_end_time_) {
Austin Schuh30537882014-02-18 01:07:23 -0800411 // Timeout by unloading.
412 Unload();
413 } else if (position && position->plunger && position->latch) {
414 // If both trigger, we are latched.
Ben Fredrickson22c93322014-02-17 05:56:33 +0000415 state_ = STATE_PREPARE_SHOT;
416 }
Austin Schuh30537882014-02-18 01:07:23 -0800417 // Move a bit further back to help it trigger.
418 // If the latch is slow due to the air flowing through the tubes or
419 // inertia, but is otherwise free, this won't have much time to do
420 // anything and is safe. Otherwise this gives us a bit more room to free
421 // up the latch.
422 shooter_.SetGoalPosition(values.shooter.lower_limit, 0.0);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000423 LOG(DEBUG, "Waiting on latch: plunger %d, latch: %d\n",
Austin Schuh60c56662014-02-17 14:37:19 -0800424 position->plunger, position->latch);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000425
Austin Schuh30537882014-02-18 01:07:23 -0800426 latch_piston_ = true;
427 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000428 break;
429 case STATE_PREPARE_SHOT:
Austin Schuh30537882014-02-18 01:07:23 -0800430 // Move the shooter to the shot power set point and then lock the brake.
431 // TODO(austin): Timeout. Low priority.
432
Ben Fredrickson22c93322014-02-17 05:56:33 +0000433 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
Austin Schuh30537882014-02-18 01:07:23 -0800434
435 LOG(DEBUG, "PDIFF: absolute_position: %.2f, pow: %.2f\n",
Austin Schuhbe1401f2014-02-18 03:18:41 -0800436 shooter_.absolute_position(), PowerToPosition(goal->shot_power));
Austin Schuh30537882014-02-18 01:07:23 -0800437 if (::std::abs(shooter_.absolute_position() -
Austin Schuhbe1401f2014-02-18 03:18:41 -0800438 PowerToPosition(goal->shot_power)) +
439 ::std::abs(shooter_.absolute_velocity()) <
440 0.001) {
Austin Schuh30537882014-02-18 01:07:23 -0800441 // We are there, set the brake and move on.
442 latch_piston_ = true;
443 brake_piston_ = true;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800444 shooter_brake_set_time_ = Time::Now() + kShooterBrakeSetTime;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000445 state_ = STATE_READY;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000446 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800447 latch_piston_ = true;
448 brake_piston_ = false;
449 }
450 if (goal->unload_requested) {
451 Unload();
Ben Fredrickson22c93322014-02-17 05:56:33 +0000452 }
453 break;
454 case STATE_READY:
Austin Schuh30537882014-02-18 01:07:23 -0800455 LOG(DEBUG, "In ready\n");
Austin Schuhbe1401f2014-02-18 03:18:41 -0800456 // Wait until the brake is set, and a shot is requested or the shot power
457 // is changed.
Austin Schuh6b428602014-02-22 21:02:00 -0800458 if (Time::Now() > shooter_brake_set_time_) {
James Kuszmaul4abaf482014-02-26 21:16:35 -0800459 status->ready = true;
Austin Schuhbe1401f2014-02-18 03:18:41 -0800460 // We have waited long enough for the brake to set, turn the shooter
461 // control loop off.
Ben Fredrickson22c93322014-02-17 05:56:33 +0000462 shooter_loop_disable = true;
Austin Schuh30537882014-02-18 01:07:23 -0800463 LOG(DEBUG, "Brake is now set\n");
464 if (goal->shot_requested && !disabled) {
465 LOG(DEBUG, "Shooting now\n");
466 shooter_loop_disable = true;
James Kuszmaul7290a942014-02-26 20:04:13 -0800467 shot_end_time_ = Time::Now() + kShotEndTimeout;
468 firing_starting_position_ = shooter_.absolute_position();
469 state_ = STATE_FIRE;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000470 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000471 }
Austin Schuh6b428602014-02-22 21:02:00 -0800472 if (state_ == STATE_READY &&
473 ::std::abs(shooter_.absolute_position() -
474 PowerToPosition(goal->shot_power)) > 0.002) {
475 // TODO(austin): Add a state to release the brake.
476
477 // TODO(austin): Do we want to set the brake here or after shooting?
478 // Depends on air usage.
James Kuszmaul4abaf482014-02-26 21:16:35 -0800479 status->ready = false;
Austin Schuh6b428602014-02-22 21:02:00 -0800480 LOG(DEBUG, "Preparing shot again.\n");
481 state_ = STATE_PREPARE_SHOT;
482 }
483
Ben Fredricksona6d77542014-02-17 07:54:43 +0000484 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
485
Austin Schuh30537882014-02-18 01:07:23 -0800486 latch_piston_ = true;
487 brake_piston_ = true;
488
489 if (goal->unload_requested) {
490 Unload();
491 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000492 break;
Austin Schuh30537882014-02-18 01:07:23 -0800493
Ben Fredrickson22c93322014-02-17 05:56:33 +0000494 case STATE_FIRE:
Austin Schuh30537882014-02-18 01:07:23 -0800495 if (disabled) {
496 if (position) {
497 if (position->plunger) {
498 // If disabled and the plunger is still back there, reset the
499 // timeout.
Austin Schuh06cbbf12014-02-22 02:07:31 -0800500 shot_end_time_ = Time::Now() + kShotEndTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800501 }
502 }
503 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000504 shooter_loop_disable = true;
Austin Schuh30537882014-02-18 01:07:23 -0800505 // Count the number of contiguous cycles during which we haven't moved.
506 if (::std::abs(last_position_.position - shooter_.absolute_position()) <
507 0.0005) {
508 ++cycles_not_moved_;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000509 } else {
510 cycles_not_moved_ = 0;
511 }
Austin Schuh30537882014-02-18 01:07:23 -0800512
513 // If we have moved any amount since the start and the shooter has now
514 // been still for a couple cycles, the shot finished.
515 // Also move on if it times out.
516 if ((::std::abs(firing_starting_position_ -
517 shooter_.absolute_position()) > 0.0005 &&
518 cycles_not_moved_ > 3) ||
519 Time::Now() > shot_end_time_) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000520 state_ = STATE_REQUEST_LOAD;
Austin Schuh4edad872014-03-02 11:56:12 -0800521 ++shot_count_;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000522 }
Austin Schuh30537882014-02-18 01:07:23 -0800523 latch_piston_ = false;
524 brake_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000525 break;
526 case STATE_UNLOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800527 // Reset the timeouts.
528 if (disabled) Unload();
529
530 // If it is latched and the plunger is back, move the pusher back to catch
531 // the plunger.
Austin Schuhf84a1302014-02-19 00:23:30 -0800532 bool all_back;
533 if (position) {
534 all_back = position->plunger && position->latch;
535 } else {
536 all_back = last_position_.plunger && last_position_.latch;
537 }
538
539 if (all_back) {
Austin Schuh30537882014-02-18 01:07:23 -0800540 // Pull back to 0, 0.
541 shooter_.SetGoalPosition(0.0, 0.0);
542 if (shooter_.absolute_position() < 0.005) {
543 // When we are close enough, 'fire'.
544 latch_piston_ = false;
545 } else {
546 latch_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000547 }
548 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800549 // The plunger isn't all the way back, or it is and it is unlatched, so
550 // we can now unload.
551 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
552 latch_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000553 state_ = STATE_UNLOAD_MOVE;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800554 unload_timeout_ = Time::Now() + kUnloadTimeout;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000555 }
556
Austin Schuh30537882014-02-18 01:07:23 -0800557 if (Time::Now() > unload_timeout_) {
558 // We have been stuck trying to unload for way too long, give up and
559 // turn everything off.
560 state_ = STATE_ESTOP;
561 }
562
563 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000564 break;
Austin Schuh30537882014-02-18 01:07:23 -0800565 case STATE_UNLOAD_MOVE: {
566 if (disabled) {
Austin Schuh06cbbf12014-02-22 02:07:31 -0800567 unload_timeout_ = Time::Now() + kUnloadTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800568 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
569 }
Austin Schuhd34569d2014-02-18 20:26:38 -0800570 cap_goal = true;
Austin Schuh6b428602014-02-22 21:02:00 -0800571 shooter_.set_max_voltage(6.0);
Austin Schuh30537882014-02-18 01:07:23 -0800572
573 // Slowly move back until we hit the upper limit.
Austin Schuhd34569d2014-02-18 20:26:38 -0800574 // If we were at the limit last cycle, we are done unloading.
575 // This is because if we saturate, we might hit the limit before we are
576 // actually there.
577 if (shooter_.goal_position() >= values.shooter.upper_limit) {
Austin Schuh30537882014-02-18 01:07:23 -0800578 shooter_.SetGoalPosition(values.shooter.upper_limit, 0.0);
579 // We don't want the loop fighting the spring when we are unloaded.
580 // Turn it off.
581 shooter_loop_disable = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000582 state_ = STATE_READY_UNLOAD;
583 } else {
Austin Schuhd34569d2014-02-18 20:26:38 -0800584 shooter_.SetGoalPosition(
585 ::std::min(
586 values.shooter.upper_limit,
Austin Schuh6b428602014-02-22 21:02:00 -0800587 shooter_.goal_position() + values.shooter.unload_speed * dt),
588 values.shooter.unload_speed);
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000589 }
590
Austin Schuh30537882014-02-18 01:07:23 -0800591 latch_piston_ = false;
592 brake_piston_ = false;
593 } break;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000594 case STATE_READY_UNLOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800595 if (goal->load_requested) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000596 state_ = STATE_REQUEST_LOAD;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000597 }
James Kuszmaul7290a942014-02-26 20:04:13 -0800598 // If we are ready to load again,
Austin Schuh30537882014-02-18 01:07:23 -0800599 shooter_loop_disable = true;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000600
Austin Schuh30537882014-02-18 01:07:23 -0800601 latch_piston_ = false;
602 brake_piston_ = false;
603 break;
604
605 case STATE_ESTOP:
606 // Totally lost, go to a safe state.
607 shooter_loop_disable = true;
608 latch_piston_ = true;
609 brake_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000610 break;
joe2d92e852014-01-25 14:31:24 -0800611 }
612
James Kuszmaul7290a942014-02-26 20:04:13 -0800613 if (!shooter_loop_disable) {
Austin Schuh30537882014-02-18 01:07:23 -0800614 LOG(DEBUG, "Running the loop, goal is %f, position is %f\n",
615 shooter_.goal_position(), shooter_.absolute_position());
Austin Schuhd34569d2014-02-18 20:26:38 -0800616 if (!cap_goal) {
617 shooter_.set_max_voltage(12.0);
618 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000619 shooter_.Update(output == NULL);
Austin Schuhd34569d2014-02-18 20:26:38 -0800620 if (cap_goal) {
621 shooter_.CapGoal();
622 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000623 if (output) output->voltage = shooter_.voltage();
Ben Fredrickson7d980c22014-02-16 21:39:02 +0000624 } else {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000625 shooter_.Update(true);
Austin Schuhd34569d2014-02-18 20:26:38 -0800626 shooter_.ZeroPower();
Ben Fredrickson22c93322014-02-17 05:56:33 +0000627 if (output) output->voltage = 0.0;
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000628 }
Ben Fredrickson7d980c22014-02-16 21:39:02 +0000629
Austin Schuhf4392d42014-03-02 14:00:37 -0800630 status->hard_stop_power = PositionToPower(shooter_.absolute_position());
James Kuszmauld29689f2014-03-02 13:06:54 -0800631
Austin Schuh30537882014-02-18 01:07:23 -0800632 if (output) {
633 output->latch_piston = latch_piston_;
634 output->brake_piston = brake_piston_;
635 }
636
Austin Schuh30537882014-02-18 01:07:23 -0800637 if (position) {
638 last_position_ = *position;
Ben Fredricksona9dcfa42014-02-23 02:05:59 +0000639 LOG(DEBUG, "pos > absolute: %f velocity: %f state= %d l= %d pp= %d, pd= %d "
640 "p= %d b=%d\n",
Austin Schuhbe1401f2014-02-18 03:18:41 -0800641 shooter_.absolute_position(), shooter_.absolute_velocity(),
James Kuszmaul7290a942014-02-26 20:04:13 -0800642 state_, position->latch, position->pusher_proximal.current,
643 position->pusher_distal.current,
644 position->plunger, brake_piston_);
Austin Schuh30537882014-02-18 01:07:23 -0800645 }
646 if (position) {
647 last_distal_posedge_count_ = position->pusher_distal.posedge_count;
648 last_proximal_posedge_count_ = position->pusher_proximal.posedge_count;
Austin Schuh2e976812014-03-05 19:56:58 -0800649 last_distal_current_ = position->pusher_distal.current;
650 last_proximal_current_ = position->pusher_proximal.current;
Austin Schuh30537882014-02-18 01:07:23 -0800651 }
Austin Schuh4edad872014-03-02 11:56:12 -0800652
653 status->shots = shot_count_;
joe2d92e852014-01-25 14:31:24 -0800654}
655
Brian Silvermand1e65b92014-03-08 17:07:14 -0800656void ShooterMotor::ZeroOutputs() {
657 queue_group()->output.MakeWithBuilder()
658 .voltage(0)
659 .latch_piston(latch_piston_)
660 .brake_piston(brake_piston_)
661 .Send();
662}
663
joe2d92e852014-01-25 14:31:24 -0800664} // namespace control_loops
665} // namespace frc971