blob: da2aa420f25c47bdb1c5bf7b62f17a944f94c243 [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"
Brian Silvermanf48fab32014-03-09 14:32:24 -07009#include "aos/common/logging/queue_logging.h"
joe2d92e852014-01-25 14:31:24 -080010
11#include "frc971/constants.h"
joe93778a62014-02-15 13:22:14 -080012#include "frc971/control_loops/shooter/shooter_motor_plant.h"
joe2d92e852014-01-25 14:31:24 -080013
14namespace frc971 {
15namespace control_loops {
16
Ben Fredricksonedf0e092014-02-16 10:46:50 +000017using ::aos::time::Time;
Ben Fredrickson22c93322014-02-17 05:56:33 +000018
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000019void ZeroedStateFeedbackLoop::CapU() {
20 const double old_voltage = voltage_;
21 voltage_ += U(0, 0);
22
23 uncapped_voltage_ = voltage_;
24
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000025 // Make sure that reality and the observer can't get too far off. There is a
26 // delay by one cycle between the applied voltage and X_hat(2, 0), so compare
27 // against last cycle's voltage.
Austin Schuhbe1401f2014-02-18 03:18:41 -080028 if (X_hat(2, 0) > last_voltage_ + 4.0) {
29 voltage_ -= X_hat(2, 0) - (last_voltage_ + 4.0);
Brian Silverman101b9642014-03-08 12:45:16 -080030 LOG(DEBUG, "Capping due to runaway\n");
Austin Schuhbe1401f2014-02-18 03:18:41 -080031 } else if (X_hat(2, 0) < last_voltage_ - 4.0) {
32 voltage_ += X_hat(2, 0) - (last_voltage_ - 4.0);
Brian Silverman101b9642014-03-08 12:45:16 -080033 LOG(DEBUG, "Capping due to runaway\n");
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000034 }
35
Austin Schuhd34569d2014-02-18 20:26:38 -080036 voltage_ = std::min(max_voltage_, voltage_);
37 voltage_ = std::max(-max_voltage_, voltage_);
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000038 U(0, 0) = voltage_ - old_voltage;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000039
Brian Silvermanf48fab32014-03-09 14:32:24 -070040 LOG_STRUCT(DEBUG, "output", ShooterVoltageToLog(X_hat(2, 0), voltage_));
Austin Schuhbe1401f2014-02-18 03:18:41 -080041
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000042 last_voltage_ = voltage_;
Austin Schuhd34569d2014-02-18 20:26:38 -080043 capped_goal_ = false;
44}
45
46void ZeroedStateFeedbackLoop::CapGoal() {
47 if (uncapped_voltage() > max_voltage_) {
48 double dx;
49 if (controller_index() == 0) {
50 dx = (uncapped_voltage() - max_voltage_) /
51 (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
52 R(0, 0) -= dx;
53 R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
54 } else {
55 dx = (uncapped_voltage() - max_voltage_) / K(0, 0);
56 R(0, 0) -= dx;
57 }
58 capped_goal_ = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -070059 LOG_STRUCT(DEBUG, "to prevent windup", ShooterMovingGoal(dx));
Austin Schuhd34569d2014-02-18 20:26:38 -080060 } else if (uncapped_voltage() < -max_voltage_) {
61 double dx;
62 if (controller_index() == 0) {
63 dx = (uncapped_voltage() + max_voltage_) /
64 (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
65 R(0, 0) -= dx;
66 R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
67 } else {
68 dx = (uncapped_voltage() + max_voltage_) / K(0, 0);
69 R(0, 0) -= dx;
70 }
71 capped_goal_ = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -070072 LOG_STRUCT(DEBUG, "to prevent windup", ShooterMovingGoal(dx));
Austin Schuhd34569d2014-02-18 20:26:38 -080073 } else {
74 capped_goal_ = false;
75 }
76}
77
78void ZeroedStateFeedbackLoop::RecalculatePowerGoal() {
79 if (controller_index() == 0) {
80 R(2, 0) = (-A(1, 0) / A(1, 2) * R(0, 0) - A(1, 1) / A(1, 2) * R(1, 0));
81 } else {
82 R(2, 0) = -A(1, 1) / A(1, 2) * R(1, 0);
83 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000084}
85
Austin Schuh30537882014-02-18 01:07:23 -080086void ZeroedStateFeedbackLoop::SetCalibration(double encoder_val,
87 double known_position) {
Brian Silvermanf48fab32014-03-09 14:32:24 -070088 double old_position = absolute_position();
Austin Schuh30537882014-02-18 01:07:23 -080089 double previous_offset = offset_;
90 offset_ = known_position - encoder_val;
91 double doffset = offset_ - previous_offset;
Austin Schuh30537882014-02-18 01:07:23 -080092 X_hat(0, 0) += doffset;
93 // Offset our measurements because the offset is baked into them.
94 Y_(0, 0) += doffset;
95 // Offset the goal so we don't move.
96 R(0, 0) += doffset;
Austin Schuhd34569d2014-02-18 20:26:38 -080097 if (controller_index() == 0) {
98 R(2, 0) += -A(1, 0) / A(1, 2) * (doffset);
99 }
Brian Silvermanf48fab32014-03-09 14:32:24 -0700100 LOG_STRUCT(
Brian Silvermand6337412014-03-14 18:51:26 -0700101 DEBUG, "sensor edge (fake?)",
Brian Silvermanf48fab32014-03-09 14:32:24 -0700102 ShooterChangeCalibration(encoder_val, known_position, old_position,
103 absolute_position(), previous_offset, offset_));
Austin Schuh30537882014-02-18 01:07:23 -0800104}
105
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000106ShooterMotor::ShooterMotor(control_loops::ShooterGroup *my_shooter)
107 : aos::control_loops::ControlLoop<control_loops::ShooterGroup>(my_shooter),
108 shooter_(MakeShooterLoop()),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000109 state_(STATE_INITIALIZE),
110 loading_problem_end_time_(0, 0),
Austin Schuh30537882014-02-18 01:07:23 -0800111 load_timeout_(0, 0),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000112 shooter_brake_set_time_(0, 0),
Austin Schuh30537882014-02-18 01:07:23 -0800113 unload_timeout_(0, 0),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000114 shot_end_time_(0, 0),
Austin Schuh4edad872014-03-02 11:56:12 -0800115 cycles_not_moved_(0),
Austin Schuhd30d7382014-03-02 21:15:38 -0800116 shot_count_(0),
117 zeroed_(false),
118 distal_posedge_validation_cycles_left_(0),
Austin Schuh2e976812014-03-05 19:56:58 -0800119 proximal_posedge_validation_cycles_left_(0),
120 last_distal_current_(true),
121 last_proximal_current_(true) {}
Austin Schuh30537882014-02-18 01:07:23 -0800122
123double ShooterMotor::PowerToPosition(double power) {
Austin Schuh30537882014-02-18 01:07:23 -0800124 const frc971::constants::Values &values = constants::GetValues();
Ben Fredricksonda334d12014-02-23 09:09:23 +0000125 double maxpower = 0.5 * kSpringConstant *
126 (kMaxExtension * kMaxExtension -
127 (kMaxExtension - values.shooter.upper_limit) *
128 (kMaxExtension - values.shooter.upper_limit));
129 if (power < 0) {
Brian Silverman0ebe9442014-03-22 13:57:35 -0700130 LOG_STRUCT(WARNING, "negative power", PowerAdjustment(power, 0));
Ben Fredricksonda334d12014-02-23 09:09:23 +0000131 power = 0;
132 } else if (power > maxpower) {
Brian Silverman0ebe9442014-03-22 13:57:35 -0700133 LOG_STRUCT(WARNING, "power too high", PowerAdjustment(power, maxpower));
Ben Fredricksonda334d12014-02-23 09:09:23 +0000134 power = maxpower;
135 }
136
137 double mp = kMaxExtension * kMaxExtension - (power + power) / kSpringConstant;
138 double new_pos = 0.10;
139 if (mp < 0) {
140 LOG(ERROR,
141 "Power calculation has negative number before square root (%f).\n", mp);
142 } else {
143 new_pos = kMaxExtension - ::std::sqrt(mp);
144 }
145
146 new_pos = ::std::min(::std::max(new_pos, values.shooter.lower_limit),
Austin Schuh30537882014-02-18 01:07:23 -0800147 values.shooter.upper_limit);
148 return new_pos;
149}
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000150
James Kuszmauld29689f2014-03-02 13:06:54 -0800151double ShooterMotor::PositionToPower(double position) {
152 double power = kSpringConstant * position * (kMaxExtension - position / 2.0);
153 return power;
154}
155
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000156// Positive is out, and positive power is out.
joe2d92e852014-01-25 14:31:24 -0800157void ShooterMotor::RunIteration(
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000158 const control_loops::ShooterGroup::Goal *goal,
159 const control_loops::ShooterGroup::Position *position,
160 control_loops::ShooterGroup::Output *output,
Ben Fredrickson22c93322014-02-17 05:56:33 +0000161 control_loops::ShooterGroup::Status *status) {
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000162 constexpr double dt = 0.01;
163
Ben Fredrickson61893d52014-03-02 09:43:23 +0000164 if (::std::isnan(goal->shot_power)) {
165 state_ = STATE_ESTOP;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700166 LOG(ERROR, "Estopping because got a shot power of NAN.\n");
Ben Fredrickson61893d52014-03-02 09:43:23 +0000167 }
168
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000169 // we must always have these or we have issues.
170 if (goal == NULL || status == NULL) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000171 if (output) output->voltage = 0;
172 LOG(ERROR, "Thought I would just check for null and die.\n");
173 return;
174 }
James Kuszmaul4abaf482014-02-26 21:16:35 -0800175 status->ready = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000176
Austin Schuh30537882014-02-18 01:07:23 -0800177 if (reset()) {
178 state_ = STATE_INITIALIZE;
Austin Schuh2e976812014-03-05 19:56:58 -0800179 last_distal_current_ = position->pusher_distal.current;
180 last_proximal_current_ = position->pusher_proximal.current;
Austin Schuh30537882014-02-18 01:07:23 -0800181 }
182 if (position) {
183 shooter_.CorrectPosition(position->position);
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000184 }
joe2d92e852014-01-25 14:31:24 -0800185
186 // Disable the motors now so that all early returns will return with the
187 // motors disabled.
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000188 if (output) output->voltage = 0;
joe2d92e852014-01-25 14:31:24 -0800189
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000190 const frc971::constants::Values &values = constants::GetValues();
191
Brian Silvermanaae236a2014-02-17 01:49:39 -0800192 // Don't even let the control loops run.
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000193 bool shooter_loop_disable = false;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000194
Brian Silvermand8a7cf02014-03-22 17:15:23 -0700195 const bool disabled =
196 !::aos::robot_state.get() || !::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;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700247 LOG(DEBUG,
248 "Not moving on until the latch has moved to avoid a crash\n");
Austin Schuh1a08bd82014-03-09 00:47:11 -0800249 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000250 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800251 // If we can't start yet because we don't know where we are, set the
252 // latch and brake to their defaults.
253 latch_piston_ = true;
254 brake_piston_ = true;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000255 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000256 break;
257 case STATE_REQUEST_LOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800258 if (position) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800259 zeroed_ = false;
Ben Fredrickson26a0dee2014-02-23 04:38:23 +0000260 if (position->pusher_distal.current ||
261 position->pusher_proximal.current) {
Austin Schuh30537882014-02-18 01:07:23 -0800262 // We started on the sensor, back up until we are found.
263 // If the plunger is all the way back and not latched, it won't be
264 // there for long.
265 state_ = STATE_LOAD_BACKTRACK;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800266
267 // The plunger is already back and latched. Don't release it.
268 if (position->plunger && position->latch) {
269 latch_piston_ = true;
270 } else {
271 latch_piston_ = false;
272 }
273 } else if (position->plunger && position->latch) {
274 // The plunger is back and we are latched. We most likely got here
275 // from Initialize, in which case we want to 'load' again anyways to
276 // zero.
277 Load();
278 latch_piston_ = true;
Austin Schuh30537882014-02-18 01:07:23 -0800279 } else {
280 // Off the sensor, start loading.
281 Load();
282 latch_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000283 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000284 }
285
Austin Schuh30537882014-02-18 01:07:23 -0800286 // Hold our current position.
287 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
288 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000289 break;
290 case STATE_LOAD_BACKTRACK:
Austin Schuh30537882014-02-18 01:07:23 -0800291 // If we are here, then that means we started past the edge where we want
292 // to zero. Move backwards until we don't see the sensor anymore.
293 // The plunger is contacting the pusher (or will be shortly).
294
Austin Schuh30537882014-02-18 01:07:23 -0800295 if (!disabled) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000296 shooter_.SetGoalPosition(
Austin Schuhfaeee632014-02-18 01:24:05 -0800297 shooter_.goal_position() + values.shooter.zeroing_speed * dt,
298 values.shooter.zeroing_speed);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000299 }
Austin Schuhd34569d2014-02-18 20:26:38 -0800300 cap_goal = true;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800301 shooter_.set_max_voltage(4.0);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000302
Austin Schuh30537882014-02-18 01:07:23 -0800303 if (position) {
Ben Fredrickson26a0dee2014-02-23 04:38:23 +0000304 if (!position->pusher_distal.current &&
305 !position->pusher_proximal.current) {
Austin Schuh30537882014-02-18 01:07:23 -0800306 Load();
307 }
Austin Schuh6b428602014-02-22 21:02:00 -0800308 latch_piston_ = position->plunger;
Austin Schuh30537882014-02-18 01:07:23 -0800309 }
310
Austin Schuh30537882014-02-18 01:07:23 -0800311 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000312 break;
313 case STATE_LOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800314 // If we are disabled right now, reset the timer.
315 if (disabled) {
316 Load();
317 // Latch defaults to true when disabled. Leave it latched until we have
318 // useful sensor data.
319 latch_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000320 }
Austin Schuh30537882014-02-18 01:07:23 -0800321 // Go to 0, which should be the latch position, or trigger a hall effect
322 // on the way. If we don't see edges where we are supposed to, the
323 // offset will be updated by code above.
324 shooter_.SetGoalPosition(0.0, 0.0);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000325
Austin Schuh30537882014-02-18 01:07:23 -0800326 if (position) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800327 // TODO(austin): Validate that this is the right edge.
Austin Schuh30537882014-02-18 01:07:23 -0800328 // If we see a posedge on any of the hall effects,
329 if (position->pusher_proximal.posedge_count !=
Austin Schuh2e976812014-03-05 19:56:58 -0800330 last_proximal_posedge_count_ &&
331 !last_proximal_current_) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800332 proximal_posedge_validation_cycles_left_ = 2;
Austin Schuh30537882014-02-18 01:07:23 -0800333 }
Austin Schuhd30d7382014-03-02 21:15:38 -0800334 if (proximal_posedge_validation_cycles_left_ > 0) {
335 if (position->pusher_proximal.current) {
336 --proximal_posedge_validation_cycles_left_;
337 if (proximal_posedge_validation_cycles_left_ == 0) {
338 shooter_.SetCalibration(
339 position->pusher_proximal.posedge_value,
340 values.shooter.pusher_proximal.upper_angle);
341
342 LOG(DEBUG, "Setting calibration using proximal sensor\n");
343 zeroed_ = true;
344 }
345 } else {
346 proximal_posedge_validation_cycles_left_ = 0;
347 }
348 }
349
Austin Schuh30537882014-02-18 01:07:23 -0800350 if (position->pusher_distal.posedge_count !=
Austin Schuh2e976812014-03-05 19:56:58 -0800351 last_distal_posedge_count_ &&
352 !last_distal_current_) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800353 distal_posedge_validation_cycles_left_ = 2;
354 }
355 if (distal_posedge_validation_cycles_left_ > 0) {
356 if (position->pusher_distal.current) {
357 --distal_posedge_validation_cycles_left_;
358 if (distal_posedge_validation_cycles_left_ == 0) {
359 shooter_.SetCalibration(
360 position->pusher_distal.posedge_value,
361 values.shooter.pusher_distal.upper_angle);
362
363 LOG(DEBUG, "Setting calibration using distal sensor\n");
364 zeroed_ = true;
365 }
366 } else {
367 distal_posedge_validation_cycles_left_ = 0;
368 }
Austin Schuh30537882014-02-18 01:07:23 -0800369 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000370
Austin Schuh30537882014-02-18 01:07:23 -0800371 // Latch if the plunger is far enough back to trigger the hall effect.
372 // This happens when the distal sensor is triggered.
Austin Schuh06cbbf12014-02-22 02:07:31 -0800373 latch_piston_ = position->pusher_distal.current || position->plunger;
Austin Schuh30537882014-02-18 01:07:23 -0800374
Austin Schuh06cbbf12014-02-22 02:07:31 -0800375 // Check if we are latched and back. Make sure the plunger is all the
376 // way back as well.
377 if (position->plunger && position->latch &&
378 position->pusher_distal.current) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800379 if (!zeroed_) {
380 state_ = STATE_REQUEST_LOAD;
381 } else {
382 state_ = STATE_PREPARE_SHOT;
383 }
Austin Schuh30537882014-02-18 01:07:23 -0800384 } else if (position->plunger &&
385 ::std::abs(shooter_.absolute_position() -
386 shooter_.goal_position()) < 0.001) {
387 // We are at the goal, but not latched.
388 state_ = STATE_LOADING_PROBLEM;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800389 loading_problem_end_time_ = Time::Now() + kLoadProblemEndTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800390 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000391 }
Austin Schuh30537882014-02-18 01:07:23 -0800392 if (load_timeout_ < Time::Now()) {
393 if (position) {
394 if (!position->pusher_distal.current ||
395 !position->pusher_proximal.current) {
396 state_ = STATE_ESTOP;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700397 LOG(ERROR, "Estopping because took too long to load.\n");
Austin Schuh30537882014-02-18 01:07:23 -0800398 }
399 }
Austin Schuh30537882014-02-18 01:07:23 -0800400 }
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);
Brian Silverman3693f9a2014-03-30 17:54:15 -0700423 if (position) {
424 LOG(DEBUG, "Waiting on latch: plunger %d, latch: %d\n",
425 position->plunger, position->latch);
426 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000427
Austin Schuh30537882014-02-18 01:07:23 -0800428 latch_piston_ = true;
429 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000430 break;
431 case STATE_PREPARE_SHOT:
Austin Schuh30537882014-02-18 01:07:23 -0800432 // Move the shooter to the shot power set point and then lock the brake.
433 // TODO(austin): Timeout. Low priority.
434
Ben Fredrickson22c93322014-02-17 05:56:33 +0000435 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
Austin Schuh30537882014-02-18 01:07:23 -0800436
437 LOG(DEBUG, "PDIFF: absolute_position: %.2f, pow: %.2f\n",
Austin Schuhbe1401f2014-02-18 03:18:41 -0800438 shooter_.absolute_position(), PowerToPosition(goal->shot_power));
Austin Schuh30537882014-02-18 01:07:23 -0800439 if (::std::abs(shooter_.absolute_position() -
Austin Schuhbe1401f2014-02-18 03:18:41 -0800440 PowerToPosition(goal->shot_power)) +
441 ::std::abs(shooter_.absolute_velocity()) <
442 0.001) {
Austin Schuh30537882014-02-18 01:07:23 -0800443 // We are there, set the brake and move on.
444 latch_piston_ = true;
445 brake_piston_ = true;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800446 shooter_brake_set_time_ = Time::Now() + kShooterBrakeSetTime;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000447 state_ = STATE_READY;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000448 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800449 latch_piston_ = true;
450 brake_piston_ = false;
451 }
452 if (goal->unload_requested) {
453 Unload();
Ben Fredrickson22c93322014-02-17 05:56:33 +0000454 }
455 break;
456 case STATE_READY:
Austin Schuh30537882014-02-18 01:07:23 -0800457 LOG(DEBUG, "In ready\n");
Austin Schuhbe1401f2014-02-18 03:18:41 -0800458 // Wait until the brake is set, and a shot is requested or the shot power
459 // is changed.
Austin Schuh6b428602014-02-22 21:02:00 -0800460 if (Time::Now() > shooter_brake_set_time_) {
James Kuszmaul4abaf482014-02-26 21:16:35 -0800461 status->ready = true;
Austin Schuhbe1401f2014-02-18 03:18:41 -0800462 // We have waited long enough for the brake to set, turn the shooter
463 // control loop off.
Ben Fredrickson22c93322014-02-17 05:56:33 +0000464 shooter_loop_disable = true;
Austin Schuh30537882014-02-18 01:07:23 -0800465 LOG(DEBUG, "Brake is now set\n");
466 if (goal->shot_requested && !disabled) {
467 LOG(DEBUG, "Shooting now\n");
468 shooter_loop_disable = true;
James Kuszmaul7290a942014-02-26 20:04:13 -0800469 shot_end_time_ = Time::Now() + kShotEndTimeout;
470 firing_starting_position_ = shooter_.absolute_position();
471 state_ = STATE_FIRE;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000472 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000473 }
Austin Schuh6b428602014-02-22 21:02:00 -0800474 if (state_ == STATE_READY &&
475 ::std::abs(shooter_.absolute_position() -
476 PowerToPosition(goal->shot_power)) > 0.002) {
477 // TODO(austin): Add a state to release the brake.
478
479 // TODO(austin): Do we want to set the brake here or after shooting?
480 // Depends on air usage.
James Kuszmaul4abaf482014-02-26 21:16:35 -0800481 status->ready = false;
Austin Schuh6b428602014-02-22 21:02:00 -0800482 LOG(DEBUG, "Preparing shot again.\n");
483 state_ = STATE_PREPARE_SHOT;
484 }
485
Ben Fredricksona6d77542014-02-17 07:54:43 +0000486 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
487
Austin Schuh30537882014-02-18 01:07:23 -0800488 latch_piston_ = true;
489 brake_piston_ = true;
490
491 if (goal->unload_requested) {
492 Unload();
493 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000494 break;
Austin Schuh30537882014-02-18 01:07:23 -0800495
Ben Fredrickson22c93322014-02-17 05:56:33 +0000496 case STATE_FIRE:
Austin Schuh30537882014-02-18 01:07:23 -0800497 if (disabled) {
498 if (position) {
499 if (position->plunger) {
500 // If disabled and the plunger is still back there, reset the
501 // timeout.
Austin Schuh06cbbf12014-02-22 02:07:31 -0800502 shot_end_time_ = Time::Now() + kShotEndTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800503 }
504 }
505 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000506 shooter_loop_disable = true;
Austin Schuh30537882014-02-18 01:07:23 -0800507 // Count the number of contiguous cycles during which we haven't moved.
508 if (::std::abs(last_position_.position - shooter_.absolute_position()) <
509 0.0005) {
510 ++cycles_not_moved_;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000511 } else {
512 cycles_not_moved_ = 0;
513 }
Austin Schuh30537882014-02-18 01:07:23 -0800514
515 // If we have moved any amount since the start and the shooter has now
516 // been still for a couple cycles, the shot finished.
517 // Also move on if it times out.
518 if ((::std::abs(firing_starting_position_ -
519 shooter_.absolute_position()) > 0.0005 &&
520 cycles_not_moved_ > 3) ||
521 Time::Now() > shot_end_time_) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000522 state_ = STATE_REQUEST_LOAD;
Austin Schuh4edad872014-03-02 11:56:12 -0800523 ++shot_count_;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000524 }
Austin Schuh30537882014-02-18 01:07:23 -0800525 latch_piston_ = false;
526 brake_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000527 break;
528 case STATE_UNLOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800529 // Reset the timeouts.
530 if (disabled) Unload();
531
532 // If it is latched and the plunger is back, move the pusher back to catch
533 // the plunger.
Austin Schuhf84a1302014-02-19 00:23:30 -0800534 bool all_back;
535 if (position) {
536 all_back = position->plunger && position->latch;
537 } else {
538 all_back = last_position_.plunger && last_position_.latch;
539 }
540
541 if (all_back) {
Austin Schuh30537882014-02-18 01:07:23 -0800542 // Pull back to 0, 0.
543 shooter_.SetGoalPosition(0.0, 0.0);
544 if (shooter_.absolute_position() < 0.005) {
545 // When we are close enough, 'fire'.
546 latch_piston_ = false;
547 } else {
548 latch_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000549 }
550 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800551 // The plunger isn't all the way back, or it is and it is unlatched, so
552 // we can now unload.
553 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
554 latch_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000555 state_ = STATE_UNLOAD_MOVE;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800556 unload_timeout_ = Time::Now() + kUnloadTimeout;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000557 }
558
Austin Schuh30537882014-02-18 01:07:23 -0800559 if (Time::Now() > unload_timeout_) {
560 // We have been stuck trying to unload for way too long, give up and
561 // turn everything off.
562 state_ = STATE_ESTOP;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700563 LOG(ERROR, "Estopping because took too long to unload.\n");
Austin Schuh30537882014-02-18 01:07:23 -0800564 }
565
566 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000567 break;
Austin Schuh30537882014-02-18 01:07:23 -0800568 case STATE_UNLOAD_MOVE: {
569 if (disabled) {
Austin Schuh06cbbf12014-02-22 02:07:31 -0800570 unload_timeout_ = Time::Now() + kUnloadTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800571 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
572 }
Austin Schuhd34569d2014-02-18 20:26:38 -0800573 cap_goal = true;
Austin Schuh6b428602014-02-22 21:02:00 -0800574 shooter_.set_max_voltage(6.0);
Austin Schuh30537882014-02-18 01:07:23 -0800575
576 // Slowly move back until we hit the upper limit.
Austin Schuhd34569d2014-02-18 20:26:38 -0800577 // If we were at the limit last cycle, we are done unloading.
578 // This is because if we saturate, we might hit the limit before we are
579 // actually there.
580 if (shooter_.goal_position() >= values.shooter.upper_limit) {
Austin Schuh30537882014-02-18 01:07:23 -0800581 shooter_.SetGoalPosition(values.shooter.upper_limit, 0.0);
582 // We don't want the loop fighting the spring when we are unloaded.
583 // Turn it off.
584 shooter_loop_disable = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000585 state_ = STATE_READY_UNLOAD;
586 } else {
Austin Schuhd34569d2014-02-18 20:26:38 -0800587 shooter_.SetGoalPosition(
588 ::std::min(
589 values.shooter.upper_limit,
Austin Schuh6b428602014-02-22 21:02:00 -0800590 shooter_.goal_position() + values.shooter.unload_speed * dt),
591 values.shooter.unload_speed);
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000592 }
593
Austin Schuh30537882014-02-18 01:07:23 -0800594 latch_piston_ = false;
595 brake_piston_ = false;
596 } break;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000597 case STATE_READY_UNLOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800598 if (goal->load_requested) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000599 state_ = STATE_REQUEST_LOAD;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000600 }
James Kuszmaul7290a942014-02-26 20:04:13 -0800601 // If we are ready to load again,
Austin Schuh30537882014-02-18 01:07:23 -0800602 shooter_loop_disable = true;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000603
Austin Schuh30537882014-02-18 01:07:23 -0800604 latch_piston_ = false;
605 brake_piston_ = false;
606 break;
607
608 case STATE_ESTOP:
Brian Silverman0ebe9442014-03-22 13:57:35 -0700609 LOG(WARNING, "estopped\n");
Austin Schuh30537882014-02-18 01:07:23 -0800610 // Totally lost, go to a safe state.
611 shooter_loop_disable = true;
612 latch_piston_ = true;
613 brake_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000614 break;
joe2d92e852014-01-25 14:31:24 -0800615 }
616
James Kuszmaul7290a942014-02-26 20:04:13 -0800617 if (!shooter_loop_disable) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700618 LOG_STRUCT(DEBUG, "running the loop",
619 ShooterStatusToLog(shooter_.goal_position(),
620 shooter_.absolute_position()));
Austin Schuhd34569d2014-02-18 20:26:38 -0800621 if (!cap_goal) {
622 shooter_.set_max_voltage(12.0);
623 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000624 shooter_.Update(output == NULL);
Austin Schuhd34569d2014-02-18 20:26:38 -0800625 if (cap_goal) {
626 shooter_.CapGoal();
627 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000628 if (output) output->voltage = shooter_.voltage();
Ben Fredrickson7d980c22014-02-16 21:39:02 +0000629 } else {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000630 shooter_.Update(true);
Austin Schuhd34569d2014-02-18 20:26:38 -0800631 shooter_.ZeroPower();
Ben Fredrickson22c93322014-02-17 05:56:33 +0000632 if (output) output->voltage = 0.0;
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000633 }
Ben Fredrickson7d980c22014-02-16 21:39:02 +0000634
Austin Schuhf4392d42014-03-02 14:00:37 -0800635 status->hard_stop_power = PositionToPower(shooter_.absolute_position());
James Kuszmauld29689f2014-03-02 13:06:54 -0800636
Austin Schuh30537882014-02-18 01:07:23 -0800637 if (output) {
638 output->latch_piston = latch_piston_;
639 output->brake_piston = brake_piston_;
640 }
641
Austin Schuh30537882014-02-18 01:07:23 -0800642 if (position) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700643 LOG_STRUCT(DEBUG, "internal state",
644 ShooterStateToLog(
645 shooter_.absolute_position(), shooter_.absolute_velocity(),
646 state_, position->latch, position->pusher_proximal.current,
647 position->pusher_distal.current, position->plunger,
648 brake_piston_, latch_piston_));
649
Austin Schuh30537882014-02-18 01:07:23 -0800650 last_position_ = *position;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700651
Austin Schuh30537882014-02-18 01:07:23 -0800652 last_distal_posedge_count_ = position->pusher_distal.posedge_count;
653 last_proximal_posedge_count_ = position->pusher_proximal.posedge_count;
Austin Schuh2e976812014-03-05 19:56:58 -0800654 last_distal_current_ = position->pusher_distal.current;
655 last_proximal_current_ = position->pusher_proximal.current;
Austin Schuh30537882014-02-18 01:07:23 -0800656 }
Austin Schuh4edad872014-03-02 11:56:12 -0800657
658 status->shots = shot_count_;
joe2d92e852014-01-25 14:31:24 -0800659}
660
Brian Silvermand1e65b92014-03-08 17:07:14 -0800661void ShooterMotor::ZeroOutputs() {
662 queue_group()->output.MakeWithBuilder()
663 .voltage(0)
664 .latch_piston(latch_piston_)
665 .brake_piston(brake_piston_)
666 .Send();
667}
668
joe2d92e852014-01-25 14:31:24 -0800669} // namespace control_loops
670} // namespace frc971