blob: e55c43b2f9a695b745b9a4d606f5ef7dfeac58a2 [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"
Brian Silverman36407c92014-04-01 15:54:02 -070013#include "frc971/queues/output_check.q.h"
joe2d92e852014-01-25 14:31:24 -080014
15namespace frc971 {
16namespace control_loops {
17
Ben Fredricksonedf0e092014-02-16 10:46:50 +000018using ::aos::time::Time;
Ben Fredrickson22c93322014-02-17 05:56:33 +000019
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000020void ZeroedStateFeedbackLoop::CapU() {
21 const double old_voltage = voltage_;
22 voltage_ += U(0, 0);
23
24 uncapped_voltage_ = voltage_;
25
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000026 // Make sure that reality and the observer can't get too far off. There is a
27 // delay by one cycle between the applied voltage and X_hat(2, 0), so compare
28 // against last cycle's voltage.
Austin Schuhbe1401f2014-02-18 03:18:41 -080029 if (X_hat(2, 0) > last_voltage_ + 4.0) {
30 voltage_ -= X_hat(2, 0) - (last_voltage_ + 4.0);
Brian Silverman101b9642014-03-08 12:45:16 -080031 LOG(DEBUG, "Capping due to runaway\n");
Austin Schuhbe1401f2014-02-18 03:18:41 -080032 } else if (X_hat(2, 0) < last_voltage_ - 4.0) {
33 voltage_ += X_hat(2, 0) - (last_voltage_ - 4.0);
Brian Silverman101b9642014-03-08 12:45:16 -080034 LOG(DEBUG, "Capping due to runaway\n");
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000035 }
36
Austin Schuhd34569d2014-02-18 20:26:38 -080037 voltage_ = std::min(max_voltage_, voltage_);
38 voltage_ = std::max(-max_voltage_, voltage_);
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000039 U(0, 0) = voltage_ - old_voltage;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000040
Brian Silvermanf48fab32014-03-09 14:32:24 -070041 LOG_STRUCT(DEBUG, "output", ShooterVoltageToLog(X_hat(2, 0), voltage_));
Austin Schuhbe1401f2014-02-18 03:18:41 -080042
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000043 last_voltage_ = voltage_;
Austin Schuhd34569d2014-02-18 20:26:38 -080044 capped_goal_ = false;
45}
46
47void ZeroedStateFeedbackLoop::CapGoal() {
48 if (uncapped_voltage() > max_voltage_) {
49 double dx;
50 if (controller_index() == 0) {
51 dx = (uncapped_voltage() - max_voltage_) /
52 (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
53 R(0, 0) -= dx;
54 R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
55 } else {
56 dx = (uncapped_voltage() - max_voltage_) / K(0, 0);
57 R(0, 0) -= dx;
58 }
59 capped_goal_ = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -070060 LOG_STRUCT(DEBUG, "to prevent windup", ShooterMovingGoal(dx));
Austin Schuhd34569d2014-02-18 20:26:38 -080061 } else if (uncapped_voltage() < -max_voltage_) {
62 double dx;
63 if (controller_index() == 0) {
64 dx = (uncapped_voltage() + max_voltage_) /
65 (K(0, 0) - A(1, 0) * K(0, 2) / A(1, 2));
66 R(0, 0) -= dx;
67 R(2, 0) -= -A(1, 0) / A(1, 2) * dx;
68 } else {
69 dx = (uncapped_voltage() + max_voltage_) / K(0, 0);
70 R(0, 0) -= dx;
71 }
72 capped_goal_ = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -070073 LOG_STRUCT(DEBUG, "to prevent windup", ShooterMovingGoal(dx));
Austin Schuhd34569d2014-02-18 20:26:38 -080074 } else {
75 capped_goal_ = false;
76 }
77}
78
79void ZeroedStateFeedbackLoop::RecalculatePowerGoal() {
80 if (controller_index() == 0) {
81 R(2, 0) = (-A(1, 0) / A(1, 2) * R(0, 0) - A(1, 1) / A(1, 2) * R(1, 0));
82 } else {
83 R(2, 0) = -A(1, 1) / A(1, 2) * R(1, 0);
84 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +000085}
86
Austin Schuh30537882014-02-18 01:07:23 -080087void ZeroedStateFeedbackLoop::SetCalibration(double encoder_val,
88 double known_position) {
Brian Silvermanf48fab32014-03-09 14:32:24 -070089 double old_position = absolute_position();
Austin Schuh30537882014-02-18 01:07:23 -080090 double previous_offset = offset_;
91 offset_ = known_position - encoder_val;
92 double doffset = offset_ - previous_offset;
Austin Schuh30537882014-02-18 01:07:23 -080093 X_hat(0, 0) += doffset;
94 // Offset our measurements because the offset is baked into them.
95 Y_(0, 0) += doffset;
96 // Offset the goal so we don't move.
97 R(0, 0) += doffset;
Austin Schuhd34569d2014-02-18 20:26:38 -080098 if (controller_index() == 0) {
99 R(2, 0) += -A(1, 0) / A(1, 2) * (doffset);
100 }
Brian Silvermanf48fab32014-03-09 14:32:24 -0700101 LOG_STRUCT(
Brian Silvermand6337412014-03-14 18:51:26 -0700102 DEBUG, "sensor edge (fake?)",
Brian Silvermanf48fab32014-03-09 14:32:24 -0700103 ShooterChangeCalibration(encoder_val, known_position, old_position,
104 absolute_position(), previous_offset, offset_));
Austin Schuh30537882014-02-18 01:07:23 -0800105}
106
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000107ShooterMotor::ShooterMotor(control_loops::ShooterGroup *my_shooter)
108 : aos::control_loops::ControlLoop<control_loops::ShooterGroup>(my_shooter),
109 shooter_(MakeShooterLoop()),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000110 state_(STATE_INITIALIZE),
111 loading_problem_end_time_(0, 0),
Austin Schuh30537882014-02-18 01:07:23 -0800112 load_timeout_(0, 0),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000113 shooter_brake_set_time_(0, 0),
Austin Schuh30537882014-02-18 01:07:23 -0800114 unload_timeout_(0, 0),
Ben Fredrickson22c93322014-02-17 05:56:33 +0000115 shot_end_time_(0, 0),
Austin Schuh4edad872014-03-02 11:56:12 -0800116 cycles_not_moved_(0),
Austin Schuhd30d7382014-03-02 21:15:38 -0800117 shot_count_(0),
118 zeroed_(false),
119 distal_posedge_validation_cycles_left_(0),
Austin Schuh2e976812014-03-05 19:56:58 -0800120 proximal_posedge_validation_cycles_left_(0),
121 last_distal_current_(true),
122 last_proximal_current_(true) {}
Austin Schuh30537882014-02-18 01:07:23 -0800123
124double ShooterMotor::PowerToPosition(double power) {
Austin Schuh30537882014-02-18 01:07:23 -0800125 const frc971::constants::Values &values = constants::GetValues();
Ben Fredricksonda334d12014-02-23 09:09:23 +0000126 double maxpower = 0.5 * kSpringConstant *
127 (kMaxExtension * kMaxExtension -
128 (kMaxExtension - values.shooter.upper_limit) *
129 (kMaxExtension - values.shooter.upper_limit));
130 if (power < 0) {
Brian Silverman0ebe9442014-03-22 13:57:35 -0700131 LOG_STRUCT(WARNING, "negative power", PowerAdjustment(power, 0));
Ben Fredricksonda334d12014-02-23 09:09:23 +0000132 power = 0;
133 } else if (power > maxpower) {
Brian Silverman0ebe9442014-03-22 13:57:35 -0700134 LOG_STRUCT(WARNING, "power too high", PowerAdjustment(power, 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;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700167 LOG(ERROR, "Estopping because got a shot power of NAN.\n");
Ben Fredrickson61893d52014-03-02 09:43:23 +0000168 }
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
Brian Silvermand8a7cf02014-03-22 17:15:23 -0700196 const bool disabled =
197 !::aos::robot_state.get() || !::aos::robot_state->enabled;
Brian Silverman36407c92014-04-01 15:54:02 -0700198 output_check_received.FetchLatest();
199 // True if we're enabled but the motors aren't working.
200 // TODO(brians): Make this more general.
201 // The 100ms is the result of disabling the robot while it's putting out a lot
202 // of power and looking at the time delay between the last PWM pulse and the
203 // battery voltage coming back up.
204 const bool motors_off =
205 !disabled && (!output_check_received.get() ||
206 !output_check_received.IsNewerThanMS(100));
207 motors_off_log_.Print();
208 if (motors_off) LOG_INTERVAL(motors_off_log_);
209
Austin Schuhd34569d2014-02-18 20:26:38 -0800210 // If true, move the goal if we saturate.
211 bool cap_goal = false;
212
213 // TODO(austin): Move the offset if we see or don't see a hall effect when we
214 // expect to see one.
215 // Probably not needed yet.
216
217 if (position) {
218 int last_controller_index = shooter_.controller_index();
219 if (position->plunger && position->latch) {
220 // Use the controller without the spring if the latch is set and the
221 // plunger is back
222 shooter_.set_controller_index(1);
223 } else {
224 // Otherwise use the controller with the spring.
225 shooter_.set_controller_index(0);
226 }
227 if (shooter_.controller_index() != last_controller_index) {
228 shooter_.RecalculatePowerGoal();
229 }
230 }
Austin Schuh30537882014-02-18 01:07:23 -0800231
Ben Fredrickson1e512ff2014-02-15 21:36:52 +0000232 switch (state_) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000233 case STATE_INITIALIZE:
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000234 if (position) {
Austin Schuh30537882014-02-18 01:07:23 -0800235 // Reinitialize the internal filter state.
236 shooter_.InitializeState(position->position);
Austin Schuh30537882014-02-18 01:07:23 -0800237
238 // Start off with the assumption that we are at the value
239 // futhest back given our sensors.
240 if (position->pusher_distal.current) {
241 shooter_.SetCalibration(position->position,
242 values.shooter.pusher_distal.lower_angle);
243 } else if (position->pusher_proximal.current) {
244 shooter_.SetCalibration(position->position,
Austin Schuhd34569d2014-02-18 20:26:38 -0800245 values.shooter.pusher_proximal.upper_angle);
Austin Schuh30537882014-02-18 01:07:23 -0800246 } else {
247 shooter_.SetCalibration(position->position,
248 values.shooter.upper_limit);
249 }
250
Austin Schuh30537882014-02-18 01:07:23 -0800251 // Go to the current position.
252 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
253 // If the plunger is all the way back, we want to be latched.
254 latch_piston_ = position->plunger;
255 brake_piston_ = false;
Austin Schuh1a08bd82014-03-09 00:47:11 -0800256 if (position->latch == latch_piston_) {
257 state_ = STATE_REQUEST_LOAD;
258 } else {
259 shooter_loop_disable = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700260 LOG(DEBUG,
261 "Not moving on until the latch has moved to avoid a crash\n");
Austin Schuh1a08bd82014-03-09 00:47:11 -0800262 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000263 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800264 // If we can't start yet because we don't know where we are, set the
265 // latch and brake to their defaults.
266 latch_piston_ = true;
267 brake_piston_ = true;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000268 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000269 break;
270 case STATE_REQUEST_LOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800271 if (position) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800272 zeroed_ = false;
Ben Fredrickson26a0dee2014-02-23 04:38:23 +0000273 if (position->pusher_distal.current ||
274 position->pusher_proximal.current) {
Austin Schuh30537882014-02-18 01:07:23 -0800275 // We started on the sensor, back up until we are found.
276 // If the plunger is all the way back and not latched, it won't be
277 // there for long.
278 state_ = STATE_LOAD_BACKTRACK;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800279
280 // The plunger is already back and latched. Don't release it.
281 if (position->plunger && position->latch) {
282 latch_piston_ = true;
283 } else {
284 latch_piston_ = false;
285 }
286 } else if (position->plunger && position->latch) {
287 // The plunger is back and we are latched. We most likely got here
288 // from Initialize, in which case we want to 'load' again anyways to
289 // zero.
290 Load();
291 latch_piston_ = true;
Austin Schuh30537882014-02-18 01:07:23 -0800292 } else {
293 // Off the sensor, start loading.
294 Load();
295 latch_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000296 }
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000297 }
298
Austin Schuh30537882014-02-18 01:07:23 -0800299 // Hold our current position.
300 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
301 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000302 break;
303 case STATE_LOAD_BACKTRACK:
Austin Schuh30537882014-02-18 01:07:23 -0800304 // If we are here, then that means we started past the edge where we want
305 // to zero. Move backwards until we don't see the sensor anymore.
306 // The plunger is contacting the pusher (or will be shortly).
307
Austin Schuh30537882014-02-18 01:07:23 -0800308 if (!disabled) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000309 shooter_.SetGoalPosition(
Austin Schuhfaeee632014-02-18 01:24:05 -0800310 shooter_.goal_position() + values.shooter.zeroing_speed * dt,
311 values.shooter.zeroing_speed);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000312 }
Austin Schuhd34569d2014-02-18 20:26:38 -0800313 cap_goal = true;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800314 shooter_.set_max_voltage(4.0);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000315
Austin Schuh30537882014-02-18 01:07:23 -0800316 if (position) {
Ben Fredrickson26a0dee2014-02-23 04:38:23 +0000317 if (!position->pusher_distal.current &&
318 !position->pusher_proximal.current) {
Austin Schuh30537882014-02-18 01:07:23 -0800319 Load();
320 }
Austin Schuh6b428602014-02-22 21:02:00 -0800321 latch_piston_ = position->plunger;
Austin Schuh30537882014-02-18 01:07:23 -0800322 }
323
Austin Schuh30537882014-02-18 01:07:23 -0800324 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000325 break;
326 case STATE_LOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800327 // If we are disabled right now, reset the timer.
328 if (disabled) {
329 Load();
330 // Latch defaults to true when disabled. Leave it latched until we have
331 // useful sensor data.
332 latch_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000333 }
Brian Silverman36407c92014-04-01 15:54:02 -0700334 if (motors_off) {
335 load_timeout_ += ::aos::control_loops::kLoopFrequency;
336 }
Austin Schuh30537882014-02-18 01:07:23 -0800337 // Go to 0, which should be the latch position, or trigger a hall effect
338 // on the way. If we don't see edges where we are supposed to, the
339 // offset will be updated by code above.
340 shooter_.SetGoalPosition(0.0, 0.0);
Ben Fredrickson22c93322014-02-17 05:56:33 +0000341
Austin Schuh30537882014-02-18 01:07:23 -0800342 if (position) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800343 // TODO(austin): Validate that this is the right edge.
Austin Schuh30537882014-02-18 01:07:23 -0800344 // If we see a posedge on any of the hall effects,
345 if (position->pusher_proximal.posedge_count !=
Austin Schuh2e976812014-03-05 19:56:58 -0800346 last_proximal_posedge_count_ &&
347 !last_proximal_current_) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800348 proximal_posedge_validation_cycles_left_ = 2;
Austin Schuh30537882014-02-18 01:07:23 -0800349 }
Austin Schuhd30d7382014-03-02 21:15:38 -0800350 if (proximal_posedge_validation_cycles_left_ > 0) {
351 if (position->pusher_proximal.current) {
352 --proximal_posedge_validation_cycles_left_;
353 if (proximal_posedge_validation_cycles_left_ == 0) {
354 shooter_.SetCalibration(
355 position->pusher_proximal.posedge_value,
356 values.shooter.pusher_proximal.upper_angle);
357
358 LOG(DEBUG, "Setting calibration using proximal sensor\n");
359 zeroed_ = true;
360 }
361 } else {
362 proximal_posedge_validation_cycles_left_ = 0;
363 }
364 }
365
Austin Schuh30537882014-02-18 01:07:23 -0800366 if (position->pusher_distal.posedge_count !=
Austin Schuh2e976812014-03-05 19:56:58 -0800367 last_distal_posedge_count_ &&
368 !last_distal_current_) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800369 distal_posedge_validation_cycles_left_ = 2;
370 }
371 if (distal_posedge_validation_cycles_left_ > 0) {
372 if (position->pusher_distal.current) {
373 --distal_posedge_validation_cycles_left_;
374 if (distal_posedge_validation_cycles_left_ == 0) {
375 shooter_.SetCalibration(
376 position->pusher_distal.posedge_value,
377 values.shooter.pusher_distal.upper_angle);
378
379 LOG(DEBUG, "Setting calibration using distal sensor\n");
380 zeroed_ = true;
381 }
382 } else {
383 distal_posedge_validation_cycles_left_ = 0;
384 }
Austin Schuh30537882014-02-18 01:07:23 -0800385 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000386
Austin Schuh30537882014-02-18 01:07:23 -0800387 // Latch if the plunger is far enough back to trigger the hall effect.
388 // This happens when the distal sensor is triggered.
Austin Schuh06cbbf12014-02-22 02:07:31 -0800389 latch_piston_ = position->pusher_distal.current || position->plunger;
Austin Schuh30537882014-02-18 01:07:23 -0800390
Austin Schuh06cbbf12014-02-22 02:07:31 -0800391 // Check if we are latched and back. Make sure the plunger is all the
392 // way back as well.
393 if (position->plunger && position->latch &&
394 position->pusher_distal.current) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800395 if (!zeroed_) {
396 state_ = STATE_REQUEST_LOAD;
397 } else {
398 state_ = STATE_PREPARE_SHOT;
399 }
Austin Schuh30537882014-02-18 01:07:23 -0800400 } else if (position->plunger &&
401 ::std::abs(shooter_.absolute_position() -
402 shooter_.goal_position()) < 0.001) {
403 // We are at the goal, but not latched.
404 state_ = STATE_LOADING_PROBLEM;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800405 loading_problem_end_time_ = Time::Now() + kLoadProblemEndTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800406 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000407 }
Austin Schuh30537882014-02-18 01:07:23 -0800408 if (load_timeout_ < Time::Now()) {
409 if (position) {
410 if (!position->pusher_distal.current ||
411 !position->pusher_proximal.current) {
412 state_ = STATE_ESTOP;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700413 LOG(ERROR, "Estopping because took too long to load.\n");
Austin Schuh30537882014-02-18 01:07:23 -0800414 }
415 }
Austin Schuh30537882014-02-18 01:07:23 -0800416 }
417 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000418 break;
419 case STATE_LOADING_PROBLEM:
Austin Schuh30537882014-02-18 01:07:23 -0800420 if (disabled) {
Austin Schuhd30d7382014-03-02 21:15:38 -0800421 state_ = STATE_REQUEST_LOAD;
422 break;
Austin Schuh30537882014-02-18 01:07:23 -0800423 }
424 // We got to the goal, but the latch hasn't registered as down. It might
425 // be stuck, or on it's way but not there yet.
Ben Fredrickson22c93322014-02-17 05:56:33 +0000426 if (Time::Now() > loading_problem_end_time_) {
Austin Schuh30537882014-02-18 01:07:23 -0800427 // Timeout by unloading.
428 Unload();
429 } else if (position && position->plunger && position->latch) {
430 // If both trigger, we are latched.
Ben Fredrickson22c93322014-02-17 05:56:33 +0000431 state_ = STATE_PREPARE_SHOT;
432 }
Austin Schuh30537882014-02-18 01:07:23 -0800433 // Move a bit further back to help it trigger.
434 // If the latch is slow due to the air flowing through the tubes or
435 // inertia, but is otherwise free, this won't have much time to do
436 // anything and is safe. Otherwise this gives us a bit more room to free
437 // up the latch.
438 shooter_.SetGoalPosition(values.shooter.lower_limit, 0.0);
Brian Silverman3693f9a2014-03-30 17:54:15 -0700439 if (position) {
440 LOG(DEBUG, "Waiting on latch: plunger %d, latch: %d\n",
441 position->plunger, position->latch);
442 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000443
Austin Schuh30537882014-02-18 01:07:23 -0800444 latch_piston_ = true;
445 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000446 break;
447 case STATE_PREPARE_SHOT:
Austin Schuh30537882014-02-18 01:07:23 -0800448 // Move the shooter to the shot power set point and then lock the brake.
449 // TODO(austin): Timeout. Low priority.
450
Ben Fredrickson22c93322014-02-17 05:56:33 +0000451 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
Austin Schuh30537882014-02-18 01:07:23 -0800452
453 LOG(DEBUG, "PDIFF: absolute_position: %.2f, pow: %.2f\n",
Austin Schuhbe1401f2014-02-18 03:18:41 -0800454 shooter_.absolute_position(), PowerToPosition(goal->shot_power));
Austin Schuh30537882014-02-18 01:07:23 -0800455 if (::std::abs(shooter_.absolute_position() -
Austin Schuhbe1401f2014-02-18 03:18:41 -0800456 PowerToPosition(goal->shot_power)) +
457 ::std::abs(shooter_.absolute_velocity()) <
458 0.001) {
Austin Schuh30537882014-02-18 01:07:23 -0800459 // We are there, set the brake and move on.
460 latch_piston_ = true;
461 brake_piston_ = true;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800462 shooter_brake_set_time_ = Time::Now() + kShooterBrakeSetTime;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000463 state_ = STATE_READY;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000464 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800465 latch_piston_ = true;
466 brake_piston_ = false;
467 }
468 if (goal->unload_requested) {
469 Unload();
Ben Fredrickson22c93322014-02-17 05:56:33 +0000470 }
471 break;
472 case STATE_READY:
Austin Schuh30537882014-02-18 01:07:23 -0800473 LOG(DEBUG, "In ready\n");
Austin Schuhbe1401f2014-02-18 03:18:41 -0800474 // Wait until the brake is set, and a shot is requested or the shot power
475 // is changed.
Austin Schuh6b428602014-02-22 21:02:00 -0800476 if (Time::Now() > shooter_brake_set_time_) {
James Kuszmaul4abaf482014-02-26 21:16:35 -0800477 status->ready = true;
Austin Schuhbe1401f2014-02-18 03:18:41 -0800478 // We have waited long enough for the brake to set, turn the shooter
479 // control loop off.
Ben Fredrickson22c93322014-02-17 05:56:33 +0000480 shooter_loop_disable = true;
Austin Schuh30537882014-02-18 01:07:23 -0800481 LOG(DEBUG, "Brake is now set\n");
482 if (goal->shot_requested && !disabled) {
483 LOG(DEBUG, "Shooting now\n");
484 shooter_loop_disable = true;
James Kuszmaul7290a942014-02-26 20:04:13 -0800485 shot_end_time_ = Time::Now() + kShotEndTimeout;
486 firing_starting_position_ = shooter_.absolute_position();
487 state_ = STATE_FIRE;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000488 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000489 }
Austin Schuh6b428602014-02-22 21:02:00 -0800490 if (state_ == STATE_READY &&
491 ::std::abs(shooter_.absolute_position() -
492 PowerToPosition(goal->shot_power)) > 0.002) {
493 // TODO(austin): Add a state to release the brake.
494
495 // TODO(austin): Do we want to set the brake here or after shooting?
496 // Depends on air usage.
James Kuszmaul4abaf482014-02-26 21:16:35 -0800497 status->ready = false;
Austin Schuh6b428602014-02-22 21:02:00 -0800498 LOG(DEBUG, "Preparing shot again.\n");
499 state_ = STATE_PREPARE_SHOT;
500 }
501
Ben Fredricksona6d77542014-02-17 07:54:43 +0000502 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power), 0.0);
503
Austin Schuh30537882014-02-18 01:07:23 -0800504 latch_piston_ = true;
505 brake_piston_ = true;
506
507 if (goal->unload_requested) {
508 Unload();
509 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000510 break;
Austin Schuh30537882014-02-18 01:07:23 -0800511
Ben Fredrickson22c93322014-02-17 05:56:33 +0000512 case STATE_FIRE:
Austin Schuh30537882014-02-18 01:07:23 -0800513 if (disabled) {
514 if (position) {
515 if (position->plunger) {
516 // If disabled and the plunger is still back there, reset the
517 // timeout.
Austin Schuh06cbbf12014-02-22 02:07:31 -0800518 shot_end_time_ = Time::Now() + kShotEndTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800519 }
520 }
521 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000522 shooter_loop_disable = true;
Austin Schuh30537882014-02-18 01:07:23 -0800523 // Count the number of contiguous cycles during which we haven't moved.
524 if (::std::abs(last_position_.position - shooter_.absolute_position()) <
525 0.0005) {
526 ++cycles_not_moved_;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000527 } else {
528 cycles_not_moved_ = 0;
529 }
Austin Schuh30537882014-02-18 01:07:23 -0800530
531 // If we have moved any amount since the start and the shooter has now
532 // been still for a couple cycles, the shot finished.
533 // Also move on if it times out.
534 if ((::std::abs(firing_starting_position_ -
535 shooter_.absolute_position()) > 0.0005 &&
536 cycles_not_moved_ > 3) ||
537 Time::Now() > shot_end_time_) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000538 state_ = STATE_REQUEST_LOAD;
Austin Schuh4edad872014-03-02 11:56:12 -0800539 ++shot_count_;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000540 }
Austin Schuh30537882014-02-18 01:07:23 -0800541 latch_piston_ = false;
542 brake_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000543 break;
544 case STATE_UNLOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800545 // Reset the timeouts.
546 if (disabled) Unload();
547
548 // If it is latched and the plunger is back, move the pusher back to catch
549 // the plunger.
Austin Schuhf84a1302014-02-19 00:23:30 -0800550 bool all_back;
551 if (position) {
552 all_back = position->plunger && position->latch;
553 } else {
554 all_back = last_position_.plunger && last_position_.latch;
555 }
556
557 if (all_back) {
Austin Schuh30537882014-02-18 01:07:23 -0800558 // Pull back to 0, 0.
559 shooter_.SetGoalPosition(0.0, 0.0);
560 if (shooter_.absolute_position() < 0.005) {
561 // When we are close enough, 'fire'.
562 latch_piston_ = false;
563 } else {
564 latch_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000565 }
566 } else {
Austin Schuh30537882014-02-18 01:07:23 -0800567 // The plunger isn't all the way back, or it is and it is unlatched, so
568 // we can now unload.
569 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
570 latch_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000571 state_ = STATE_UNLOAD_MOVE;
Austin Schuh06cbbf12014-02-22 02:07:31 -0800572 unload_timeout_ = Time::Now() + kUnloadTimeout;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000573 }
574
Austin Schuh30537882014-02-18 01:07:23 -0800575 if (Time::Now() > unload_timeout_) {
576 // We have been stuck trying to unload for way too long, give up and
577 // turn everything off.
578 state_ = STATE_ESTOP;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700579 LOG(ERROR, "Estopping because took too long to unload.\n");
Austin Schuh30537882014-02-18 01:07:23 -0800580 }
581
582 brake_piston_ = false;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000583 break;
Austin Schuh30537882014-02-18 01:07:23 -0800584 case STATE_UNLOAD_MOVE: {
585 if (disabled) {
Austin Schuh06cbbf12014-02-22 02:07:31 -0800586 unload_timeout_ = Time::Now() + kUnloadTimeout;
Austin Schuh30537882014-02-18 01:07:23 -0800587 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
588 }
Austin Schuhd34569d2014-02-18 20:26:38 -0800589 cap_goal = true;
Austin Schuh6b428602014-02-22 21:02:00 -0800590 shooter_.set_max_voltage(6.0);
Austin Schuh30537882014-02-18 01:07:23 -0800591
592 // Slowly move back until we hit the upper limit.
Austin Schuhd34569d2014-02-18 20:26:38 -0800593 // If we were at the limit last cycle, we are done unloading.
594 // This is because if we saturate, we might hit the limit before we are
595 // actually there.
596 if (shooter_.goal_position() >= values.shooter.upper_limit) {
Austin Schuh30537882014-02-18 01:07:23 -0800597 shooter_.SetGoalPosition(values.shooter.upper_limit, 0.0);
598 // We don't want the loop fighting the spring when we are unloaded.
599 // Turn it off.
600 shooter_loop_disable = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000601 state_ = STATE_READY_UNLOAD;
602 } else {
Austin Schuhd34569d2014-02-18 20:26:38 -0800603 shooter_.SetGoalPosition(
604 ::std::min(
605 values.shooter.upper_limit,
Austin Schuh6b428602014-02-22 21:02:00 -0800606 shooter_.goal_position() + values.shooter.unload_speed * dt),
607 values.shooter.unload_speed);
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000608 }
609
Austin Schuh30537882014-02-18 01:07:23 -0800610 latch_piston_ = false;
611 brake_piston_ = false;
612 } break;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000613 case STATE_READY_UNLOAD:
Austin Schuh30537882014-02-18 01:07:23 -0800614 if (goal->load_requested) {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000615 state_ = STATE_REQUEST_LOAD;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000616 }
James Kuszmaul7290a942014-02-26 20:04:13 -0800617 // If we are ready to load again,
Austin Schuh30537882014-02-18 01:07:23 -0800618 shooter_loop_disable = true;
Ben Fredrickson1f633ef2014-02-16 05:35:45 +0000619
Austin Schuh30537882014-02-18 01:07:23 -0800620 latch_piston_ = false;
621 brake_piston_ = false;
622 break;
623
624 case STATE_ESTOP:
Brian Silverman0ebe9442014-03-22 13:57:35 -0700625 LOG(WARNING, "estopped\n");
Austin Schuh30537882014-02-18 01:07:23 -0800626 // Totally lost, go to a safe state.
627 shooter_loop_disable = true;
628 latch_piston_ = true;
629 brake_piston_ = true;
Ben Fredrickson22c93322014-02-17 05:56:33 +0000630 break;
joe2d92e852014-01-25 14:31:24 -0800631 }
632
James Kuszmaul7290a942014-02-26 20:04:13 -0800633 if (!shooter_loop_disable) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700634 LOG_STRUCT(DEBUG, "running the loop",
635 ShooterStatusToLog(shooter_.goal_position(),
636 shooter_.absolute_position()));
Austin Schuhd34569d2014-02-18 20:26:38 -0800637 if (!cap_goal) {
638 shooter_.set_max_voltage(12.0);
639 }
Ben Fredrickson22c93322014-02-17 05:56:33 +0000640 shooter_.Update(output == NULL);
Austin Schuhd34569d2014-02-18 20:26:38 -0800641 if (cap_goal) {
642 shooter_.CapGoal();
643 }
Brian Silverman36407c92014-04-01 15:54:02 -0700644 // We don't really want to output anything if we went through everything
645 // assuming the motors weren't working.
646 if (output && !motors_off) output->voltage = shooter_.voltage();
Ben Fredrickson7d980c22014-02-16 21:39:02 +0000647 } else {
Ben Fredrickson22c93322014-02-17 05:56:33 +0000648 shooter_.Update(true);
Austin Schuhd34569d2014-02-18 20:26:38 -0800649 shooter_.ZeroPower();
Ben Fredrickson22c93322014-02-17 05:56:33 +0000650 if (output) output->voltage = 0.0;
Ben Fredricksonedf0e092014-02-16 10:46:50 +0000651 }
Ben Fredrickson7d980c22014-02-16 21:39:02 +0000652
Austin Schuhf4392d42014-03-02 14:00:37 -0800653 status->hard_stop_power = PositionToPower(shooter_.absolute_position());
James Kuszmauld29689f2014-03-02 13:06:54 -0800654
Austin Schuh30537882014-02-18 01:07:23 -0800655 if (output) {
656 output->latch_piston = latch_piston_;
657 output->brake_piston = brake_piston_;
658 }
659
Austin Schuh30537882014-02-18 01:07:23 -0800660 if (position) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700661 LOG_STRUCT(DEBUG, "internal state",
662 ShooterStateToLog(
663 shooter_.absolute_position(), shooter_.absolute_velocity(),
664 state_, position->latch, position->pusher_proximal.current,
665 position->pusher_distal.current, position->plunger,
666 brake_piston_, latch_piston_));
667
Austin Schuh30537882014-02-18 01:07:23 -0800668 last_position_ = *position;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700669
Austin Schuh30537882014-02-18 01:07:23 -0800670 last_distal_posedge_count_ = position->pusher_distal.posedge_count;
671 last_proximal_posedge_count_ = position->pusher_proximal.posedge_count;
Austin Schuh2e976812014-03-05 19:56:58 -0800672 last_distal_current_ = position->pusher_distal.current;
673 last_proximal_current_ = position->pusher_proximal.current;
Austin Schuh30537882014-02-18 01:07:23 -0800674 }
Austin Schuh4edad872014-03-02 11:56:12 -0800675
676 status->shots = shot_count_;
joe2d92e852014-01-25 14:31:24 -0800677}
678
Brian Silvermand1e65b92014-03-08 17:07:14 -0800679void ShooterMotor::ZeroOutputs() {
680 queue_group()->output.MakeWithBuilder()
681 .voltage(0)
682 .latch_piston(latch_piston_)
683 .brake_piston(brake_piston_)
684 .Send();
685}
686
joe2d92e852014-01-25 14:31:24 -0800687} // namespace control_loops
688} // namespace frc971