blob: 52f900ab919649459d43de76360c52a2dada1d79 [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#include "y2014/control_loops/shooter/shooter.h"
2
3#include <stdio.h>
4
5#include <algorithm>
Austin Schuh214e9c12016-11-25 17:26:20 -08006#include <chrono>
James Kuszmaul61750662021-06-21 21:32:33 -07007#include <limits>
Brian Silverman17f503e2015-08-02 18:17:18 -07008
John Park33858a32018-09-28 23:05:48 -07009#include "aos/logging/logging.h"
Brian Silverman17f503e2015-08-02 18:17:18 -070010#include "y2014/constants.h"
11#include "y2014/control_loops/shooter/shooter_motor_plant.h"
12
Austin Schuh24957102015-11-28 16:04:40 -080013namespace y2014 {
Brian Silverman17f503e2015-08-02 18:17:18 -070014namespace control_loops {
Alex Perrycb7da4b2019-08-28 19:35:56 -070015namespace shooter {
Brian Silverman17f503e2015-08-02 18:17:18 -070016
Austin Schuh214e9c12016-11-25 17:26:20 -080017namespace chrono = ::std::chrono;
18using ::aos::monotonic_clock;
19
Austin Schuh0e997732015-11-08 15:14:53 -080020using ::y2014::control_loops::shooter::kDt;
James Kuszmaul61750662021-06-21 21:32:33 -070021using ::y2014::control_loops::shooter::kMaxExtension;
22using ::y2014::control_loops::shooter::kSpringConstant;
Austin Schuh9d4aca82015-11-08 14:41:31 -080023using ::y2014::control_loops::shooter::MakeShooterLoop;
Brian Silverman17f503e2015-08-02 18:17:18 -070024
25void ZeroedStateFeedbackLoop::CapU() {
26 const double old_voltage = voltage_;
27 voltage_ += U(0, 0);
28
29 uncapped_voltage_ = voltage_;
30
31 // Make sure that reality and the observer can't get too far off. There is a
32 // delay by one cycle between the applied voltage and X_hat(2, 0), so compare
33 // against last cycle's voltage.
34 if (X_hat(2, 0) > last_voltage_ + 4.0) {
35 voltage_ -= X_hat(2, 0) - (last_voltage_ + 4.0);
Austin Schuhf257f3c2019-10-27 21:00:43 -070036 AOS_LOG(DEBUG, "Capping due to runaway\n");
Brian Silverman17f503e2015-08-02 18:17:18 -070037 } else if (X_hat(2, 0) < last_voltage_ - 4.0) {
38 voltage_ += X_hat(2, 0) - (last_voltage_ - 4.0);
Austin Schuhf257f3c2019-10-27 21:00:43 -070039 AOS_LOG(DEBUG, "Capping due to runaway\n");
Brian Silverman17f503e2015-08-02 18:17:18 -070040 }
41
42 voltage_ = std::min(max_voltage_, voltage_);
43 voltage_ = std::max(-max_voltage_, voltage_);
44 mutable_U(0, 0) = voltage_ - old_voltage;
45
Brian Silverman17f503e2015-08-02 18:17:18 -070046 last_voltage_ = voltage_;
47 capped_goal_ = false;
48}
49
50void ZeroedStateFeedbackLoop::CapGoal() {
51 if (uncapped_voltage() > max_voltage_) {
52 double dx;
Austin Schuhc5fceb82017-02-25 16:24:12 -080053 if (index() == 0) {
Brian Silverman17f503e2015-08-02 18:17:18 -070054 dx = (uncapped_voltage() - max_voltage_) /
Austin Schuh32501832017-02-25 18:32:56 -080055 (controller().K(0, 0) -
56 plant().A(1, 0) * controller().K(0, 2) / plant().A(1, 2));
Brian Silverman17f503e2015-08-02 18:17:18 -070057 mutable_R(0, 0) -= dx;
Austin Schuhc5fceb82017-02-25 16:24:12 -080058 mutable_R(2, 0) -= -plant().A(1, 0) / plant().A(1, 2) * dx;
Brian Silverman17f503e2015-08-02 18:17:18 -070059 } else {
Austin Schuh32501832017-02-25 18:32:56 -080060 dx = (uncapped_voltage() - max_voltage_) / controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -070061 mutable_R(0, 0) -= dx;
62 }
63 capped_goal_ = true;
Brian Silverman17f503e2015-08-02 18:17:18 -070064 } else if (uncapped_voltage() < -max_voltage_) {
65 double dx;
Austin Schuhc5fceb82017-02-25 16:24:12 -080066 if (index() == 0) {
Brian Silverman17f503e2015-08-02 18:17:18 -070067 dx = (uncapped_voltage() + max_voltage_) /
Austin Schuh32501832017-02-25 18:32:56 -080068 (controller().K(0, 0) -
69 plant().A(1, 0) * controller().K(0, 2) / plant().A(1, 2));
Brian Silverman17f503e2015-08-02 18:17:18 -070070 mutable_R(0, 0) -= dx;
Austin Schuhc5fceb82017-02-25 16:24:12 -080071 mutable_R(2, 0) -= -plant().A(1, 0) / plant().A(1, 2) * dx;
Brian Silverman17f503e2015-08-02 18:17:18 -070072 } else {
Austin Schuh32501832017-02-25 18:32:56 -080073 dx = (uncapped_voltage() + max_voltage_) / controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -070074 mutable_R(0, 0) -= dx;
75 }
76 capped_goal_ = true;
Brian Silverman17f503e2015-08-02 18:17:18 -070077 } else {
78 capped_goal_ = false;
79 }
80}
81
82void ZeroedStateFeedbackLoop::RecalculatePowerGoal() {
Austin Schuhc5fceb82017-02-25 16:24:12 -080083 if (index() == 0) {
84 mutable_R(2, 0) = (-plant().A(1, 0) / plant().A(1, 2) * R(0, 0) -
85 plant().A(1, 1) / plant().A(1, 2) * R(1, 0));
Brian Silverman17f503e2015-08-02 18:17:18 -070086 } else {
Austin Schuhc5fceb82017-02-25 16:24:12 -080087 mutable_R(2, 0) = -plant().A(1, 1) / plant().A(1, 2) * R(1, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -070088 }
89}
90
91void ZeroedStateFeedbackLoop::SetCalibration(double encoder_val,
92 double known_position) {
Brian Silverman17f503e2015-08-02 18:17:18 -070093 double previous_offset = offset_;
94 offset_ = known_position - encoder_val;
95 double doffset = offset_ - previous_offset;
96 mutable_X_hat(0, 0) += doffset;
Brian Silverman17f503e2015-08-02 18:17:18 -070097 // Offset the goal so we don't move.
98 mutable_R(0, 0) += doffset;
Austin Schuhc5fceb82017-02-25 16:24:12 -080099 if (index() == 0) {
100 mutable_R(2, 0) += -plant().A(1, 0) / plant().A(1, 2) * (doffset);
Brian Silverman17f503e2015-08-02 18:17:18 -0700101 }
Brian Silverman17f503e2015-08-02 18:17:18 -0700102}
103
Austin Schuh55a13dc2019-01-27 22:39:03 -0800104ShooterMotor::ShooterMotor(::aos::EventLoop *event_loop,
105 const ::std::string &name)
James Kuszmaul61750662021-06-21 21:32:33 -0700106 : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
107 name),
Brian Silverman17f503e2015-08-02 18:17:18 -0700108 shooter_(MakeShooterLoop()),
109 state_(STATE_INITIALIZE),
Brian Silverman17f503e2015-08-02 18:17:18 -0700110 cycles_not_moved_(0),
111 shot_count_(0),
112 zeroed_(false),
113 distal_posedge_validation_cycles_left_(0),
114 proximal_posedge_validation_cycles_left_(0),
115 last_distal_current_(true),
116 last_proximal_current_(true) {}
117
118double ShooterMotor::PowerToPosition(double power) {
Austin Schuh24957102015-11-28 16:04:40 -0800119 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700120 double maxpower = 0.5 * kSpringConstant *
121 (kMaxExtension * kMaxExtension -
122 (kMaxExtension - values.shooter.upper_limit) *
123 (kMaxExtension - values.shooter.upper_limit));
124 if (power < 0) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700125 power = 0;
126 } else if (power > maxpower) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700127 power = maxpower;
128 }
129
130 double mp = kMaxExtension * kMaxExtension - (power + power) / kSpringConstant;
131 double new_pos = 0.10;
132 if (mp < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700133 AOS_LOG(ERROR,
134 "Power calculation has negative number before square root (%f).\n",
135 mp);
Brian Silverman17f503e2015-08-02 18:17:18 -0700136 } else {
137 new_pos = kMaxExtension - ::std::sqrt(mp);
138 }
139
140 new_pos = ::std::min(::std::max(new_pos, values.shooter.lower_limit),
James Kuszmaul61750662021-06-21 21:32:33 -0700141 values.shooter.upper_limit);
Brian Silverman17f503e2015-08-02 18:17:18 -0700142 return new_pos;
143}
144
145double ShooterMotor::PositionToPower(double position) {
146 double power = kSpringConstant * position * (kMaxExtension - position / 2.0);
147 return power;
148}
149
Alex Perrycb7da4b2019-08-28 19:35:56 -0700150void ShooterMotor::CheckCalibrations(const Position *position) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700151 AOS_CHECK_NOTNULL(position);
Austin Schuh24957102015-11-28 16:04:40 -0800152 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700153
154 // TODO(austin): Validate that this is the right edge.
155 // If we see a posedge on any of the hall effects,
Alex Perrycb7da4b2019-08-28 19:35:56 -0700156 if (position->pusher_proximal()->posedge_count() !=
157 last_proximal_posedge_count_ &&
Brian Silverman17f503e2015-08-02 18:17:18 -0700158 !last_proximal_current_) {
159 proximal_posedge_validation_cycles_left_ = 2;
160 }
161 if (proximal_posedge_validation_cycles_left_ > 0) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700162 if (position->pusher_proximal()->current()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700163 --proximal_posedge_validation_cycles_left_;
164 if (proximal_posedge_validation_cycles_left_ == 0) {
James Kuszmaul61750662021-06-21 21:32:33 -0700165 shooter_.SetCalibration(position->pusher_proximal()->posedge_value(),
166 values.shooter.pusher_proximal.upper_angle);
Brian Silverman17f503e2015-08-02 18:17:18 -0700167
Austin Schuhf257f3c2019-10-27 21:00:43 -0700168 AOS_LOG(DEBUG, "Setting calibration using proximal sensor\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700169 zeroed_ = true;
170 }
171 } else {
172 proximal_posedge_validation_cycles_left_ = 0;
173 }
174 }
175
Alex Perrycb7da4b2019-08-28 19:35:56 -0700176 if (position->pusher_distal()->posedge_count() !=
177 last_distal_posedge_count_ &&
Brian Silverman17f503e2015-08-02 18:17:18 -0700178 !last_distal_current_) {
179 distal_posedge_validation_cycles_left_ = 2;
180 }
181 if (distal_posedge_validation_cycles_left_ > 0) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700182 if (position->pusher_distal()->current()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700183 --distal_posedge_validation_cycles_left_;
184 if (distal_posedge_validation_cycles_left_ == 0) {
James Kuszmaul61750662021-06-21 21:32:33 -0700185 shooter_.SetCalibration(position->pusher_distal()->posedge_value(),
186 values.shooter.pusher_distal.upper_angle);
Brian Silverman17f503e2015-08-02 18:17:18 -0700187
Austin Schuhf257f3c2019-10-27 21:00:43 -0700188 AOS_LOG(DEBUG, "Setting calibration using distal sensor\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700189 zeroed_ = true;
190 }
191 } else {
192 distal_posedge_validation_cycles_left_ = 0;
193 }
194 }
195}
196
197// Positive is out, and positive power is out.
James Kuszmaul61750662021-06-21 21:32:33 -0700198void ShooterMotor::RunIteration(const Goal *goal, const Position *position,
199 aos::Sender<Output>::Builder *output,
200 aos::Sender<Status>::Builder *status) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700201 OutputT output_struct;
James Kuszmaul61750662021-06-21 21:32:33 -0700202 const monotonic_clock::time_point monotonic_now =
203 event_loop()->monotonic_now();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700204 if (goal && ::std::isnan(goal->shot_power())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700205 state_ = STATE_ESTOP;
206 AOS_LOG(ERROR, "Estopping because got a shot power of NAN.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700207 }
208
209 // we must always have these or we have issues.
210 if (status == NULL) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700211 if (output) output_struct.voltage = 0;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700212 AOS_LOG(ERROR, "Thought I would just check for null and die.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700213 return;
214 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700215 bool status_ready = false;
Brian Silverman17f503e2015-08-02 18:17:18 -0700216
217 if (WasReset()) {
218 state_ = STATE_INITIALIZE;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700219 last_distal_current_ = position->pusher_distal()->current();
220 last_proximal_current_ = position->pusher_proximal()->current();
Brian Silverman17f503e2015-08-02 18:17:18 -0700221 }
222 if (position) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700223 shooter_.CorrectPosition(position->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700224 }
225
226 // Disable the motors now so that all early returns will return with the
227 // motors disabled.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700228 if (output) output_struct.voltage = 0;
Brian Silverman17f503e2015-08-02 18:17:18 -0700229
Austin Schuh24957102015-11-28 16:04:40 -0800230 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700231
232 // Don't even let the control loops run.
233 bool shooter_loop_disable = false;
234
Alex Perrycb7da4b2019-08-28 19:35:56 -0700235 const bool disabled = !has_joystick_state() || !joystick_state().enabled();
Brian Silverman17f503e2015-08-02 18:17:18 -0700236
237 // If true, move the goal if we saturate.
238 bool cap_goal = false;
239
240 // TODO(austin): Move the offset if we see or don't see a hall effect when we
241 // expect to see one.
242 // Probably not needed yet.
243
244 if (position) {
Austin Schuhc5fceb82017-02-25 16:24:12 -0800245 int last_index = shooter_.index();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700246 if (position->plunger() && position->latch()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700247 // Use the controller without the spring if the latch is set and the
248 // plunger is back
Austin Schuhc5fceb82017-02-25 16:24:12 -0800249 shooter_.set_index(1);
Brian Silverman17f503e2015-08-02 18:17:18 -0700250 } else {
251 // Otherwise use the controller with the spring.
Austin Schuhc5fceb82017-02-25 16:24:12 -0800252 shooter_.set_index(0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700253 }
Austin Schuhc5fceb82017-02-25 16:24:12 -0800254 if (shooter_.index() != last_index) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700255 shooter_.RecalculatePowerGoal();
256 }
257 }
258
259 switch (state_) {
260 case STATE_INITIALIZE:
261 if (position) {
262 // Reinitialize the internal filter state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700263 shooter_.InitializeState(position->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700264
265 // Start off with the assumption that we are at the value
266 // futhest back given our sensors.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700267 if (position->pusher_distal()->current()) {
268 shooter_.SetCalibration(position->position(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700269 values.shooter.pusher_distal.lower_angle);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700270 } else if (position->pusher_proximal()->current()) {
271 shooter_.SetCalibration(position->position(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700272 values.shooter.pusher_proximal.upper_angle);
273 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700274 shooter_.SetCalibration(position->position(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700275 values.shooter.upper_limit);
276 }
277
278 // Go to the current position.
279 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
280 // If the plunger is all the way back, we want to be latched.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700281 latch_piston_ = position->plunger();
Brian Silverman17f503e2015-08-02 18:17:18 -0700282 brake_piston_ = false;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700283 if (position->latch() == latch_piston_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700284 state_ = STATE_REQUEST_LOAD;
285 } else {
286 shooter_loop_disable = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700287 AOS_LOG(DEBUG,
288 "Not moving on until the latch has moved to avoid a crash\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700289 }
290 } else {
291 // If we can't start yet because we don't know where we are, set the
292 // latch and brake to their defaults.
293 latch_piston_ = true;
294 brake_piston_ = true;
295 }
296 break;
297 case STATE_REQUEST_LOAD:
298 if (position) {
299 zeroed_ = false;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700300 if (position->pusher_distal()->current() ||
301 position->pusher_proximal()->current()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700302 // We started on the sensor, back up until we are found.
303 // If the plunger is all the way back and not latched, it won't be
304 // there for long.
305 state_ = STATE_LOAD_BACKTRACK;
306
307 // The plunger is already back and latched. Don't release it.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700308 if (position->plunger() && position->latch()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700309 latch_piston_ = true;
310 } else {
311 latch_piston_ = false;
312 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700313 } else if (position->plunger() && position->latch()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700314 // The plunger is back and we are latched. We most likely got here
315 // from Initialize, in which case we want to 'load' again anyways to
316 // zero.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700317 Load(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700318 latch_piston_ = true;
319 } else {
320 // Off the sensor, start loading.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700321 Load(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700322 latch_piston_ = false;
323 }
324 }
325
326 // Hold our current position.
327 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
328 brake_piston_ = false;
329 break;
330 case STATE_LOAD_BACKTRACK:
331 // If we are here, then that means we started past the edge where we want
332 // to zero. Move backwards until we don't see the sensor anymore.
333 // The plunger is contacting the pusher (or will be shortly).
334
335 if (!disabled) {
336 shooter_.SetGoalPosition(
Austin Schuh0e997732015-11-08 15:14:53 -0800337 shooter_.goal_position() + values.shooter.zeroing_speed * kDt,
Brian Silverman17f503e2015-08-02 18:17:18 -0700338 values.shooter.zeroing_speed);
339 }
340 cap_goal = true;
341 shooter_.set_max_voltage(4.0);
342
343 if (position) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700344 if (!position->pusher_distal()->current() &&
345 !position->pusher_proximal()->current()) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700346 Load(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700347 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700348 latch_piston_ = position->plunger();
Brian Silverman17f503e2015-08-02 18:17:18 -0700349 }
350
351 brake_piston_ = false;
352 break;
353 case STATE_LOAD:
354 // If we are disabled right now, reset the timer.
355 if (disabled) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700356 Load(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700357 // Latch defaults to true when disabled. Leave it latched until we have
358 // useful sensor data.
359 latch_piston_ = true;
360 }
361 if (output == nullptr) {
James Kuszmaul61750662021-06-21 21:32:33 -0700362 load_timeout_ += ::frc971::controls::kLoopFrequency;
Brian Silverman17f503e2015-08-02 18:17:18 -0700363 }
364 // Go to 0, which should be the latch position, or trigger a hall effect
365 // on the way. If we don't see edges where we are supposed to, the
366 // offset will be updated by code above.
367 shooter_.SetGoalPosition(0.0, 0.0);
368
369 if (position) {
370 CheckCalibrations(position);
371
372 // Latch if the plunger is far enough back to trigger the hall effect.
373 // This happens when the distal sensor is triggered.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700374 latch_piston_ =
375 position->pusher_distal()->current() || position->plunger();
Brian Silverman17f503e2015-08-02 18:17:18 -0700376
377 // Check if we are latched and back. Make sure the plunger is all the
378 // way back as well.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700379 if (position->plunger() && position->latch() &&
380 position->pusher_distal()->current()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700381 if (!zeroed_) {
382 state_ = STATE_REQUEST_LOAD;
383 } else {
384 state_ = STATE_PREPARE_SHOT;
385 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700386 } else if (position->plunger() &&
Brian Silverman17f503e2015-08-02 18:17:18 -0700387 ::std::abs(shooter_.absolute_position() -
388 shooter_.goal_position()) < 0.001) {
389 // We are at the goal, but not latched.
390 state_ = STATE_LOADING_PROBLEM;
James Kuszmaul61750662021-06-21 21:32:33 -0700391 loading_problem_end_time_ = monotonic_now + kLoadProblemEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700392 }
393 }
Austin Schuh9fe68f72019-08-10 19:32:03 -0700394 if (load_timeout_ < monotonic_now) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700395 if (position) {
Austin Schuhadf2cde2015-11-08 20:35:16 -0800396 // If none of the sensors is triggered, estop.
397 // Otherwise, trigger anyways if it has been 0.5 seconds more.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700398 if (!(position->pusher_distal()->current() ||
399 position->pusher_proximal()->current()) ||
400 (load_timeout_ + chrono::milliseconds(500) < monotonic_now)) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700401 state_ = STATE_ESTOP;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700402 AOS_LOG(ERROR, "Estopping because took too long to load.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700403 }
404 }
405 }
406 brake_piston_ = false;
407 break;
408 case STATE_LOADING_PROBLEM:
409 if (disabled) {
410 state_ = STATE_REQUEST_LOAD;
411 break;
412 }
413 // We got to the goal, but the latch hasn't registered as down. It might
414 // be stuck, or on it's way but not there yet.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700415 if (monotonic_now > loading_problem_end_time_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700416 // Timeout by unloading.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700417 Unload(monotonic_now);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700418 } else if (position && position->plunger() && position->latch()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700419 // If both trigger, we are latched.
420 state_ = STATE_PREPARE_SHOT;
421 }
422 // Move a bit further back to help it trigger.
423 // If the latch is slow due to the air flowing through the tubes or
424 // inertia, but is otherwise free, this won't have much time to do
425 // anything and is safe. Otherwise this gives us a bit more room to free
426 // up the latch.
427 shooter_.SetGoalPosition(values.shooter.lower_limit, 0.0);
428 if (position) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700429 AOS_LOG(DEBUG, "Waiting on latch: plunger %d, latch: %d\n",
Alex Perrycb7da4b2019-08-28 19:35:56 -0700430 position->plunger(), position->latch());
Brian Silverman17f503e2015-08-02 18:17:18 -0700431 }
432
433 latch_piston_ = true;
434 brake_piston_ = false;
435 break;
436 case STATE_PREPARE_SHOT:
437 // Move the shooter to the shot power set point and then lock the brake.
438 // TODO(austin): Timeout. Low priority.
439
440 if (goal) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700441 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power()), 0.0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700442 }
443
Austin Schuhf257f3c2019-10-27 21:00:43 -0700444 AOS_LOG(DEBUG, "PDIFF: absolute_position: %.2f, pow: %.2f\n",
445 shooter_.absolute_position(),
Alex Perrycb7da4b2019-08-28 19:35:56 -0700446 goal ? PowerToPosition(goal->shot_power())
Austin Schuhf257f3c2019-10-27 21:00:43 -0700447 : ::std::numeric_limits<double>::quiet_NaN());
Brian Silverman17f503e2015-08-02 18:17:18 -0700448 if (goal &&
449 ::std::abs(shooter_.absolute_position() -
Alex Perrycb7da4b2019-08-28 19:35:56 -0700450 PowerToPosition(goal->shot_power())) < 0.001 &&
Brian Silverman17f503e2015-08-02 18:17:18 -0700451 ::std::abs(shooter_.absolute_velocity()) < 0.005) {
452 // We are there, set the brake and move on.
453 latch_piston_ = true;
454 brake_piston_ = true;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700455 shooter_brake_set_time_ = monotonic_now + kShooterBrakeSetTime;
Brian Silverman17f503e2015-08-02 18:17:18 -0700456 state_ = STATE_READY;
457 } else {
458 latch_piston_ = true;
459 brake_piston_ = false;
460 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700461 if (goal && goal->unload_requested()) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700462 Unload(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700463 }
464 break;
465 case STATE_READY:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700466 AOS_LOG(DEBUG, "In ready\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700467 // Wait until the brake is set, and a shot is requested or the shot power
468 // is changed.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700469 if (monotonic_now > shooter_brake_set_time_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700470 status_ready = true;
Brian Silverman17f503e2015-08-02 18:17:18 -0700471 // We have waited long enough for the brake to set, turn the shooter
472 // control loop off.
473 shooter_loop_disable = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700474 AOS_LOG(DEBUG, "Brake is now set\n");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700475 if (goal && goal->shot_requested() && !disabled) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700476 AOS_LOG(DEBUG, "Shooting now\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700477 shooter_loop_disable = true;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700478 shot_end_time_ = monotonic_now + kShotEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700479 firing_starting_position_ = shooter_.absolute_position();
480 state_ = STATE_FIRE;
481 }
482 }
483 if (state_ == STATE_READY && goal &&
484 ::std::abs(shooter_.absolute_position() -
Alex Perrycb7da4b2019-08-28 19:35:56 -0700485 PowerToPosition(goal->shot_power())) > 0.002) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700486 // TODO(austin): Add a state to release the brake.
487
488 // TODO(austin): Do we want to set the brake here or after shooting?
489 // Depends on air usage.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700490 status_ready = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700491 AOS_LOG(DEBUG, "Preparing shot again.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700492 state_ = STATE_PREPARE_SHOT;
493 }
494
495 if (goal) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700496 shooter_.SetGoalPosition(PowerToPosition(goal->shot_power()), 0.0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700497 }
498
499 latch_piston_ = true;
500 brake_piston_ = true;
501
Alex Perrycb7da4b2019-08-28 19:35:56 -0700502 if (goal && goal->unload_requested()) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700503 Unload(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700504 }
505 break;
506
507 case STATE_FIRE:
508 if (disabled) {
509 if (position) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700510 if (position->plunger()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700511 // If disabled and the plunger is still back there, reset the
512 // timeout.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700513 shot_end_time_ = monotonic_now + kShotEndTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700514 }
515 }
516 }
517 shooter_loop_disable = true;
518 // Count the number of contiguous cycles during which we haven't moved.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700519 if (::std::abs(last_position_position_ - shooter_.absolute_position()) <
Brian Silverman17f503e2015-08-02 18:17:18 -0700520 0.0005) {
521 ++cycles_not_moved_;
522 } else {
523 cycles_not_moved_ = 0;
524 }
525
526 // If we have moved any amount since the start and the shooter has now
527 // been still for a couple cycles, the shot finished.
528 // Also move on if it times out.
Austin Schuh829e6ad2015-11-08 14:10:37 -0800529 if (((::std::abs(firing_starting_position_ -
530 shooter_.absolute_position()) > 0.0005 &&
Austin Schuhadf2cde2015-11-08 20:35:16 -0800531 cycles_not_moved_ > 6) ||
Austin Schuh9fe68f72019-08-10 19:32:03 -0700532 monotonic_now > shot_end_time_) &&
Alex Perrycb7da4b2019-08-28 19:35:56 -0700533 robot_state().voltage_battery() > 10.5) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700534 state_ = STATE_REQUEST_LOAD;
535 ++shot_count_;
536 }
537 latch_piston_ = false;
538 brake_piston_ = true;
539 break;
540 case STATE_UNLOAD:
541 // Reset the timeouts.
Austin Schuh9fe68f72019-08-10 19:32:03 -0700542 if (disabled) Unload(monotonic_now);
Brian Silverman17f503e2015-08-02 18:17:18 -0700543
544 // If it is latched and the plunger is back, move the pusher back to catch
545 // the plunger.
546 bool all_back;
547 if (position) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700548 all_back = position->plunger() && position->latch();
Brian Silverman17f503e2015-08-02 18:17:18 -0700549 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700550 all_back = last_position_plunger_ && last_position_latch_;
Brian Silverman17f503e2015-08-02 18:17:18 -0700551 }
552
553 if (all_back) {
554 // Pull back to 0, 0.
555 shooter_.SetGoalPosition(0.0, 0.0);
556 if (shooter_.absolute_position() < 0.005) {
557 // When we are close enough, 'fire'.
558 latch_piston_ = false;
559 } else {
560 latch_piston_ = true;
561
562 if (position) {
563 CheckCalibrations(position);
564 }
565 }
566 } else {
567 // 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;
571 state_ = STATE_UNLOAD_MOVE;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700572 unload_timeout_ = monotonic_now + kUnloadTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700573 }
574
Austin Schuh9fe68f72019-08-10 19:32:03 -0700575 if (monotonic_now > unload_timeout_) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700576 // We have been stuck trying to unload for way too long, give up and
577 // turn everything off.
578 state_ = STATE_ESTOP;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700579 AOS_LOG(ERROR, "Estopping because took too long to unload.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700580 }
581
582 brake_piston_ = false;
583 break;
584 case STATE_UNLOAD_MOVE: {
585 if (disabled) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700586 unload_timeout_ = monotonic_now + kUnloadTimeout;
Brian Silverman17f503e2015-08-02 18:17:18 -0700587 shooter_.SetGoalPosition(shooter_.absolute_position(), 0.0);
588 }
589 cap_goal = true;
590 shooter_.set_max_voltage(6.0);
591
592 // Slowly move back until we hit the upper limit.
593 // 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) {
597 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;
601 state_ = STATE_READY_UNLOAD;
602 } else {
603 shooter_.SetGoalPosition(
604 ::std::min(
605 values.shooter.upper_limit,
Austin Schuh0e997732015-11-08 15:14:53 -0800606 shooter_.goal_position() + values.shooter.unload_speed * kDt),
Brian Silverman17f503e2015-08-02 18:17:18 -0700607 values.shooter.unload_speed);
608 }
609
610 latch_piston_ = false;
611 brake_piston_ = false;
612 } break;
613 case STATE_READY_UNLOAD:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700614 if (goal && goal->load_requested()) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700615 state_ = STATE_REQUEST_LOAD;
616 }
617 // If we are ready to load again,
618 shooter_loop_disable = true;
619
620 latch_piston_ = false;
621 brake_piston_ = false;
622 break;
623
624 case STATE_ESTOP:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700625 AOS_LOG(WARNING, "estopped\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700626 // Totally lost, go to a safe state.
627 shooter_loop_disable = true;
628 latch_piston_ = true;
629 brake_piston_ = true;
630 break;
631 }
632
633 if (!shooter_loop_disable) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700634 if (!cap_goal) {
635 shooter_.set_max_voltage(12.0);
636 }
637 shooter_.Update(output == NULL);
638 if (cap_goal) {
639 shooter_.CapGoal();
640 }
641 // We don't really want to output anything if we went through everything
642 // assuming the motors weren't working.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700643 if (output) output_struct.voltage = shooter_.voltage();
Brian Silverman17f503e2015-08-02 18:17:18 -0700644 } else {
645 shooter_.Update(true);
646 shooter_.ZeroPower();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700647 if (output) output_struct.voltage = 0.0;
Brian Silverman17f503e2015-08-02 18:17:18 -0700648 }
649
Alex Perrycb7da4b2019-08-28 19:35:56 -0700650 Status::Builder status_builder = status->MakeBuilder<Status>();
651
652 status_builder.add_ready(status_ready);
653 status_builder.add_hard_stop_power(
654 PositionToPower(shooter_.absolute_position()));
Brian Silverman17f503e2015-08-02 18:17:18 -0700655
656 if (output) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700657 output_struct.latch_piston = latch_piston_;
658 output_struct.brake_piston = brake_piston_;
659
660 output->Send(Output::Pack(*output->fbb(), &output_struct));
Brian Silverman17f503e2015-08-02 18:17:18 -0700661 }
662
663 if (position) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700664 last_position_latch_ = position->latch();
665 last_position_plunger_ = position->plunger();
666 last_position_position_ = position->position();
Brian Silverman17f503e2015-08-02 18:17:18 -0700667
Alex Perrycb7da4b2019-08-28 19:35:56 -0700668 last_distal_posedge_count_ = position->pusher_distal()->posedge_count();
669 last_proximal_posedge_count_ = position->pusher_proximal()->posedge_count();
670 last_distal_current_ = position->pusher_distal()->current();
671 last_proximal_current_ = position->pusher_proximal()->current();
Brian Silverman17f503e2015-08-02 18:17:18 -0700672 }
673
Alex Perrycb7da4b2019-08-28 19:35:56 -0700674 status_builder.add_absolute_position(shooter_.absolute_position());
675 status_builder.add_absolute_velocity(shooter_.absolute_velocity());
676 status_builder.add_state(state_);
Austin Schuhadf2cde2015-11-08 20:35:16 -0800677
Alex Perrycb7da4b2019-08-28 19:35:56 -0700678 status_builder.add_shots(shot_count_);
679
680 status->Send(status_builder.Finish());
Brian Silverman17f503e2015-08-02 18:17:18 -0700681}
682
Alex Perrycb7da4b2019-08-28 19:35:56 -0700683flatbuffers::Offset<Output> ShooterMotor::Zero(
684 aos::Sender<Output>::Builder *output) {
685 Output::Builder output_builder = output->MakeBuilder<Output>();
686 output_builder.add_voltage(0.0);
687 output_builder.add_latch_piston(latch_piston_);
688 output_builder.add_brake_piston(brake_piston_);
689 return output_builder.Finish();
Brian Silverman17f503e2015-08-02 18:17:18 -0700690}
691
Alex Perrycb7da4b2019-08-28 19:35:56 -0700692} // namespace shooter
Brian Silverman17f503e2015-08-02 18:17:18 -0700693} // namespace control_loops
Austin Schuh24957102015-11-28 16:04:40 -0800694} // namespace y2014