blob: 8ab202d167d3c54f3f3cf9c08fe6ec83b2f603eb [file] [log] [blame]
Austin Schuh00558222013-03-03 14:16:16 -08001#include "frc971/control_loops/index/index.h"
Austin Schuhd78ab542013-03-01 22:22:19 -08002
3#include <stdio.h>
4
5#include <algorithm>
6
Austin Schuhd78ab542013-03-01 22:22:19 -08007#include "aos/common/control_loop/control_loops.q.h"
8#include "aos/common/logging/logging.h"
Brian Silverman94195052013-03-09 13:45:05 -08009#include "aos/common/inttypes.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080010
11#include "frc971/constants.h"
Austin Schuh00558222013-03-03 14:16:16 -080012#include "frc971/control_loops/index/index_motor_plant.h"
Austin Schuha3e8e032013-03-10 18:43:14 -070013#include "frc971/control_loops/shooter/shooter_motor.q.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080014
15using ::aos::time::Time;
16
17namespace frc971 {
18namespace control_loops {
19
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070020double IndexMotor::Frisbee::ObserveNoTopDiscSensor(double index_position) {
Austin Schuhdff24e22013-03-06 00:41:21 -080021 // The absolute disc position in meters.
Austin Schuh1b864a12013-03-07 00:46:50 -080022 double disc_position = absolute_position(index_position);
Austin Schuh825bde92013-03-06 00:16:46 -080023 if (IndexMotor::kTopDiscDetectStart <= disc_position &&
24 disc_position <= IndexMotor::kTopDiscDetectStop) {
25 // Whoops, this shouldn't be happening.
26 // Move the disc off the way that makes most sense.
Austin Schuhdff24e22013-03-06 00:41:21 -080027 double distance_to_above = IndexMotor::ConvertDiscPositionToIndex(
28 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStop));
29 double distance_to_below = IndexMotor::ConvertDiscPositionToIndex(
30 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStart));
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070031 if (distance_to_above < distance_to_below) {
32 LOG(INFO, "Moving disc to top slow.\n");
33 // Move it up.
34 index_start_position_ -= distance_to_above;
35 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080036 } else {
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070037 LOG(INFO, "Moving disc to bottom slow.\n");
38 index_start_position_ += distance_to_below;
39 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080040 }
41 }
Austin Schuhdff24e22013-03-06 00:41:21 -080042 return 0.0;
Austin Schuh825bde92013-03-06 00:16:46 -080043}
44
Austin Schuhd78ab542013-03-01 22:22:19 -080045IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
46 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
Austin Schuh93485832013-03-04 00:01:34 -080047 wrist_loop_(new IndexStateFeedbackLoop(MakeIndexLoop())),
Austin Schuhd78ab542013-03-01 22:22:19 -080048 hopper_disc_count_(0),
49 total_disc_count_(0),
Austin Schuh70be1ba2013-03-10 13:37:17 -070050 shot_disc_count_(0),
Austin Schuhf8c52252013-03-03 02:25:49 -080051 safe_goal_(Goal::HOLD),
52 loader_goal_(LoaderGoal::READY),
53 loader_state_(LoaderState::READY),
Austin Schuhd78ab542013-03-01 22:22:19 -080054 loader_up_(false),
55 disc_clamped_(false),
56 disc_ejected_(false),
Brian Silverman7ffb1382013-09-29 16:55:12 -070057 is_shooting_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080058 last_bottom_disc_detect_(false),
Austin Schuh825bde92013-03-06 00:16:46 -080059 last_top_disc_detect_(false),
Brian Silverman7d39d862013-09-29 17:00:30 -070060 hopper_clear_(true),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080061 no_prior_position_(true),
Austin Schuh723770b2013-03-10 13:26:20 -070062 missing_position_count_(0) {
Austin Schuhd78ab542013-03-01 22:22:19 -080063}
64
Austin Schuhf8c52252013-03-03 02:25:49 -080065/*static*/ const double IndexMotor::kTransferStartPosition = 0.0;
66/*static*/ const double IndexMotor::kIndexStartPosition = 0.2159;
67/*static*/ const double IndexMotor::kIndexFreeLength =
68 IndexMotor::ConvertDiscAngleToDiscPosition((360 * 2 + 14) * M_PI / 180);
69/*static*/ const double IndexMotor::kLoaderFreeStopPosition =
70 kIndexStartPosition + kIndexFreeLength;
Austin Schuh1b864a12013-03-07 00:46:50 -080071/*static*/ const double IndexMotor::kReadyToPreload =
72 kLoaderFreeStopPosition - ConvertDiscAngleToDiscPosition(M_PI / 6.0);
Austin Schuhf8c52252013-03-03 02:25:49 -080073/*static*/ const double IndexMotor::kReadyToLiftPosition =
74 kLoaderFreeStopPosition + 0.2921;
75/*static*/ const double IndexMotor::kGrabberLength = 0.03175;
76/*static*/ const double IndexMotor::kGrabberStartPosition =
77 kReadyToLiftPosition - kGrabberLength;
Austin Schuh6328daf2013-03-05 00:53:15 -080078/*static*/ const double IndexMotor::kGrabberMovementVelocity = 0.7;
Austin Schuhf8c52252013-03-03 02:25:49 -080079/*static*/ const double IndexMotor::kLifterStopPosition =
80 kReadyToLiftPosition + 0.161925;
81/*static*/ const double IndexMotor::kLifterMovementVelocity = 1.0;
82/*static*/ const double IndexMotor::kEjectorStopPosition =
83 kLifterStopPosition + 0.01;
84/*static*/ const double IndexMotor::kEjectorMovementVelocity = 1.0;
Austin Schuhcc297022013-03-09 23:26:40 -080085/*static*/ const double IndexMotor::kBottomDiscDetectStart = 0.00;
86/*static*/ const double IndexMotor::kBottomDiscDetectStop = 0.13;
87/*static*/ const double IndexMotor::kBottomDiscIndexDelay = 0.032;
Austin Schuhd3d0fbf2013-03-14 00:37:00 -070088/*static*/ const ::aos::time::Time IndexMotor::kTransferOffDelay =
Brian Silvermanb7bcef12013-03-16 13:57:11 -070089 ::aos::time::Time::InSeconds(0.3);
Austin Schuhf8c52252013-03-03 02:25:49 -080090
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080091// TODO(aschuh): Verify these with the sensor actually on.
Austin Schuh825bde92013-03-06 00:16:46 -080092/*static*/ const double IndexMotor::kTopDiscDetectStart =
93 (IndexMotor::kLoaderFreeStopPosition -
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080094 IndexMotor::ConvertDiscAngleToDiscPosition(49 * M_PI / 180));
Austin Schuh825bde92013-03-06 00:16:46 -080095/*static*/ const double IndexMotor::kTopDiscDetectStop =
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080096 (IndexMotor::kLoaderFreeStopPosition +
97 IndexMotor::ConvertDiscAngleToDiscPosition(19 * M_PI / 180));
98
99// I measured the angle between 2 discs. That then gives me the distance
100// between 2 posedges (or negedges). Then subtract off the width of the
101// positive pulse, and that gives the width of the negative pulse.
102/*static*/ const double IndexMotor::kTopDiscDetectMinSeperation =
103 (IndexMotor::ConvertDiscAngleToDiscPosition(120 * M_PI / 180) -
104 (IndexMotor::kTopDiscDetectStop - IndexMotor::kTopDiscDetectStart));
Austin Schuhf8c52252013-03-03 02:25:49 -0800105
Austin Schuhd78ab542013-03-01 22:22:19 -0800106const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
107const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
Austin Schuhf8c52252013-03-03 02:25:49 -0800108const /*static*/ double IndexMotor::kTransferRollerRadius = 1.25 * 0.0254 / 2;
Austin Schuhd78ab542013-03-01 22:22:19 -0800109
Austin Schuhf8c52252013-03-03 02:25:49 -0800110/*static*/ const int IndexMotor::kGrabbingDelay = 5;
Brian Silverman25329e62013-09-21 23:52:10 -0700111/*static*/ const int IndexMotor::kLiftingDelay = 2;
Brian Silverman44d8a9b2013-09-29 17:03:05 -0700112/*static*/ const int IndexMotor::kLiftingTimeout = 100;
Brian Silverman25329e62013-09-21 23:52:10 -0700113/*static*/ const int IndexMotor::kShootingDelay = 10;
Brian Silverman004ec812013-09-26 15:47:58 -0700114/*static*/ const int IndexMotor::kLoweringDelay = 4;
115/*static*/ const int IndexMotor::kLoweringTimeout = 120;
Austin Schuhd78ab542013-03-01 22:22:19 -0800116
Austin Schuh93485832013-03-04 00:01:34 -0800117// TODO(aschuh): Tune these.
118/*static*/ const double
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700119 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 11.0;
Austin Schuh93485832013-03-04 00:01:34 -0800120/*static*/ const double
Austin Schuha3e8e032013-03-10 18:43:14 -0700121 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 20;
Austin Schuh93485832013-03-04 00:01:34 -0800122
Austin Schuhd78ab542013-03-01 22:22:19 -0800123/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
124 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
125}
126
Austin Schuhf8c52252013-03-03 02:25:49 -0800127/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
128 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800129 return angle * (kDiscRadius + kRollerRadius);
130}
131
Austin Schuhf8c52252013-03-03 02:25:49 -0800132/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
133 const double position) {
134 return position / (kDiscRadius + kRollerRadius);
135}
136
Austin Schuhd78ab542013-03-01 22:22:19 -0800137/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
138 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
139}
140
141/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
142 return IndexMotor::ConvertDiscAngleToDiscPosition(
143 ConvertIndexToDiscAngle(angle));
144}
145
Austin Schuhf8c52252013-03-03 02:25:49 -0800146/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
147 const double angle) {
148 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
149 kTransferRollerRadius);
150 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
151}
152
153/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
154 const double position) {
155 return IndexMotor::ConvertDiscAngleToIndex(
156 ConvertDiscPositionToDiscAngle(position));
157}
158
Austin Schuh1b864a12013-03-07 00:46:50 -0800159bool IndexMotor::MinDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800160 bool found_start = false;
161 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800162 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800163 if (!found_start) {
164 if (frisbee.has_position()) {
165 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800166 if (found_disc) {
167 *found_disc = &frisbee;
168 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800169 found_start = true;
170 }
171 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800172 if (frisbee.position() <= *disc_position) {
173 *disc_position = frisbee.position();
174 if (found_disc) {
175 *found_disc = &frisbee;
176 }
177 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800178 }
179 }
180 return found_start;
181}
182
Austin Schuh1b864a12013-03-07 00:46:50 -0800183bool IndexMotor::MaxDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800184 bool found_start = false;
185 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800186 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800187 if (!found_start) {
188 if (frisbee.has_position()) {
189 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800190 if (found_disc) {
191 *found_disc = &frisbee;
192 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800193 found_start = true;
194 }
195 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800196 if (frisbee.position() > *disc_position) {
197 *disc_position = frisbee.position();
198 if (found_disc) {
199 *found_disc = &frisbee;
200 }
201 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800202 }
203 }
204 return found_start;
205}
206
Austin Schuh93485832013-03-04 00:01:34 -0800207void IndexMotor::IndexStateFeedbackLoop::CapU() {
208 // If the voltage has been low for a large number of cycles, cut the motor
209 // power. This is generally very bad controls practice since this isn't LTI,
210 // but we don't really care about tracking anything other than large step
211 // inputs, and the loader doesn't need to be that accurate.
212 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
213 ++low_voltage_count_;
214 if (low_voltage_count_ > kNoMotionCuttoffCount) {
Austin Schuh93485832013-03-04 00:01:34 -0800215 U(0, 0) = 0.0;
216 }
217 } else {
218 low_voltage_count_ = 0;
219 }
220
221 for (int i = 0; i < kNumOutputs; ++i) {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700222 if (U(i, 0) > U_max(i, 0)) {
223 U(i, 0) = U_max(i, 0);
224 } else if (U(i, 0) < U_min(i, 0)) {
225 U(i, 0) = U_min(i, 0);
Austin Schuh93485832013-03-04 00:01:34 -0800226 }
227 }
228}
229
230
Austin Schuhd78ab542013-03-01 22:22:19 -0800231// Positive angle is towards the shooter, and positive power is towards the
232// shooter.
233void IndexMotor::RunIteration(
234 const control_loops::IndexLoop::Goal *goal,
235 const control_loops::IndexLoop::Position *position,
236 control_loops::IndexLoop::Output *output,
237 control_loops::IndexLoop::Status *status) {
Austin Schuhd3d0fbf2013-03-14 00:37:00 -0700238 Time now = Time::Now();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800239 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800240 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700241 if (goal->goal_state < 0 || goal->goal_state > 5) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700242 LOG(ERROR,
243 "Goal state is %" PRId32 " which is out of range. Going to HOLD.\n",
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800244 goal->goal_state);
245 goal_enum = Goal::HOLD;
246 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800247
248 // Disable the motors now so that all early returns will return with the
249 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800250 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800251 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800252 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800253 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800254 output->transfer_voltage = 0.0;
255 output->index_voltage = 0.0;
256 }
257
258 status->ready_to_intake = false;
259
Austin Schuhe3490622013-03-13 01:24:30 -0700260 // Set the controller to use to be the one designed for the current number of
261 // discs in the hopper. This is safe since the controller prevents the index
262 // from being set out of bounds and picks the closest controller.
263 wrist_loop_->set_controller_index(frisbees_.size());
264
Austin Schuhf8c52252013-03-03 02:25:49 -0800265 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800266 if (position) {
267 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800268 // Set the goal to be the current position if this is the first time through
269 // so we don't always spin the indexer to the 0 position before starting.
270 if (no_prior_position_) {
Brian Silverman43bb73e2013-03-17 13:39:47 -0700271 LOG(INFO, "no prior position; resetting\n");
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800272 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800273 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800274 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800275 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
276 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
277 last_bottom_disc_negedge_wait_count_ =
278 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800279 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800280 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Brian Silvermance86bac2013-03-31 19:07:24 -0700281 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800282 // The open positions for the upper is right here and isn't a hard edge.
Austin Schuh723770b2013-03-10 13:26:20 -0700283 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
284 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800285 }
286
Austin Schuh1b864a12013-03-07 00:46:50 -0800287 // If the cRIO is gone for over 1/2 of a second, assume that it rebooted.
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800288 if (missing_position_count_ > 50) {
Brian Silverman43bb73e2013-03-17 13:39:47 -0700289 LOG(INFO, "assuming cRIO rebooted\n");
Austin Schuh6328daf2013-03-05 00:53:15 -0800290 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
291 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
292 last_bottom_disc_negedge_wait_count_ =
293 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800294 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800295 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Brian Silvermance86bac2013-03-31 19:07:24 -0700296 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800297 // We can't really trust the open range any more if the crio rebooted.
Austin Schuh723770b2013-03-10 13:26:20 -0700298 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
299 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800300 // Adjust the disc positions so that they don't have to move.
301 const double disc_offset =
302 position->index_position - wrist_loop_->X_hat(0, 0);
303 for (auto frisbee = frisbees_.begin();
304 frisbee != frisbees_.end(); ++frisbee) {
305 frisbee->OffsetDisc(disc_offset);
306 }
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800307 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800308 }
309 missing_position_count_ = 0;
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000310 if (last_top_disc_detect_) {
311 if (last_top_disc_posedge_count_ != position->top_disc_posedge_count) {
312 LOG(INFO, "Ignoring a top disc posedge\n");
313 }
Brian Silvermance86bac2013-03-31 19:07:24 -0700314 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
315 }
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000316 if (!last_top_disc_detect_) {
317 if (last_top_disc_negedge_count_ != position->top_disc_negedge_count) {
318 LOG(INFO, "Ignoring a top disc negedge\n");
319 }
Brian Silvermance86bac2013-03-31 19:07:24 -0700320 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
321 }
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800322 } else {
323 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800324 }
325 const double index_position = wrist_loop_->X_hat(0, 0);
326
Austin Schuh825bde92013-03-06 00:16:46 -0800327 if (position) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800328 // Reset the open region if we saw a negedge.
Austin Schuh723770b2013-03-10 13:26:20 -0700329 if (position->bottom_disc_negedge_wait_count !=
330 last_bottom_disc_negedge_wait_count_) {
331 // Saw a negedge, must be a new region.
332 lower_open_region_.Restart(position->bottom_disc_negedge_wait_position);
333 }
334 // Reset the open region if we saw a negedge.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800335 if (position->top_disc_negedge_count != last_top_disc_negedge_count_) {
336 // Saw a negedge, must be a new region.
Austin Schuh723770b2013-03-10 13:26:20 -0700337 upper_open_region_.Restart(position->top_disc_negedge_position);
338 }
339
340 // No disc. Expand the open region.
341 if (!position->bottom_disc_detect) {
342 lower_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800343 }
344
345 // No disc. Expand the open region.
346 if (!position->top_disc_detect) {
Austin Schuh723770b2013-03-10 13:26:20 -0700347 upper_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800348 }
349
Austin Schuh825bde92013-03-06 00:16:46 -0800350 if (!position->top_disc_detect) {
351 // We don't see a disc. Verify that there are no discs that we should be
352 // seeing.
Austin Schuh1b864a12013-03-07 00:46:50 -0800353 // Assume that discs will move slow enough that we won't miss one as it
354 // goes by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800355
356 double cumulative_offset = 0.0;
357 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
358 frisbee != rend; ++frisbee) {
359 frisbee->OffsetDisc(cumulative_offset);
360 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh6b1ad2f2013-03-11 23:23:00 -0700361 wrist_loop_->X_hat(0, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800362 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800363 }
364 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800365
Austin Schuh825bde92013-03-06 00:16:46 -0800366 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
Brian Silverman967c4422013-09-29 16:58:59 -0700367 LOG(INFO, "Saw a top posedge\n");
Austin Schuh1b864a12013-03-07 00:46:50 -0800368 const double index_position = wrist_loop_->X_hat(0, 0) -
369 position->index_position + position->top_disc_posedge_position;
Austin Schuh825bde92013-03-06 00:16:46 -0800370 // TODO(aschuh): Sanity check this number...
371 // Requires storing when the disc was last seen with the sensor off, and
372 // figuring out what to do if things go south.
373
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800374 // 1 if discs are going up, 0 if we have no clue, and -1 if they are going
375 // down.
376 int disc_direction = 0;
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000377 if (wrist_loop_->X_hat(1, 0) > 100.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800378 disc_direction = 1;
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000379 } else if (wrist_loop_->X_hat(1, 0) < -100.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800380 disc_direction = -1;
381 } else {
382 // Save the upper and lower positions that we last saw a disc at.
383 // If there is a big buffer above, must be a disc from below.
384 // If there is a big buffer below, must be a disc from above.
385 // This should work to replace the velocity threshold above.
386
Austin Schuh723770b2013-03-10 13:26:20 -0700387 const double open_width = upper_open_region_.width();
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800388 const double relative_upper_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700389 (upper_open_region_.upper_bound() - index_position) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800390 const double relative_lower_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700391 (index_position - upper_open_region_.lower_bound()) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800392
393 if (ConvertIndexToDiscPosition(open_width) <
394 kTopDiscDetectMinSeperation * 0.9) {
395 LOG(ERROR, "Discs are way too close to each other. Doing nothing\n");
396 } else if (relative_upper_open_precentage > 0.75) {
397 // Looks like it is a disc going down from above since we are near
398 // the upper edge.
399 disc_direction = -1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700400 LOG(INFO, "Disc edge going down\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800401 } else if (relative_lower_open_precentage > 0.75) {
402 // Looks like it is a disc going up from below since we are near
403 // the lower edge.
404 disc_direction = 1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700405 LOG(INFO, "Disc edge going up\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800406 } else {
407 LOG(ERROR,
408 "Got an edge in the middle of what should be an open region.\n");
409 LOG(ERROR, "Open width: %f upper precentage %f %%\n",
410 open_width, relative_upper_open_precentage);
411 }
412 }
413
414 if (disc_direction > 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800415 // Moving up at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800416 // Find the highest disc that is below the top disc sensor.
417 // While we are at it, count the number above and log an error if there
418 // are too many.
419 if (frisbees_.size() == 0) {
420 Frisbee new_frisbee;
421 new_frisbee.has_been_indexed_ = true;
422 new_frisbee.index_start_position_ = index_position -
423 ConvertDiscPositionToIndex(kTopDiscDetectStart -
424 kIndexStartPosition);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800425 ++hopper_disc_count_;
426 ++total_disc_count_;
Austin Schuh1b864a12013-03-07 00:46:50 -0800427 frisbees_.push_front(new_frisbee);
428 LOG(WARNING, "Added a disc to the hopper at the top sensor\n");
429 }
430
431 int above_disc_count = 0;
432 double highest_position = 0;
433 Frisbee *highest_frisbee_below_sensor = NULL;
434 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
435 frisbee != rend; ++frisbee) {
436 const double disc_position = frisbee->absolute_position(
437 index_position);
438 // It is save to use the top position for the cuttoff, since the
439 // sensor being low will result in discs being pushed off of it.
440 if (disc_position >= kTopDiscDetectStop) {
441 ++above_disc_count;
442 } else if (!highest_frisbee_below_sensor ||
443 disc_position > highest_position) {
444 highest_frisbee_below_sensor = &*frisbee;
445 highest_position = disc_position;
446 }
447 }
Austin Schuh09e07082013-03-19 10:04:12 +0000448
449 if (!highest_frisbee_below_sensor) {
450 Frisbee new_frisbee;
451 new_frisbee.has_been_indexed_ = true;
452 new_frisbee.index_start_position_ = index_position -
453 ConvertDiscPositionToIndex(kTopDiscDetectStart -
454 kIndexStartPosition);
455 highest_position = kTopDiscDetectStart;
456 ++hopper_disc_count_;
457 ++total_disc_count_;
458 frisbees_.push_front(new_frisbee);
459 LOG(WARNING, "Added a disc to the hopper at the top sensor because the one we know about is up top\n");
460 }
461
Austin Schuh1b864a12013-03-07 00:46:50 -0800462 if (above_disc_count > 1) {
463 LOG(ERROR, "We have 2 discs above the top sensor.\n");
464 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800465 // We now have the disc. Shift all the ones below the sensor up by the
466 // computed delta.
467 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
468 highest_position - kTopDiscDetectStart);
469 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
470 frisbee != rend; ++frisbee) {
471 const double disc_position = frisbee->absolute_position(
472 index_position);
473 if (disc_position < kTopDiscDetectStop) {
Austin Schuh59004d32013-03-21 04:31:45 +0000474 LOG(INFO, "Moving disc down by %f meters, since it is at %f and top is [%f, %f]\n",
475 ConvertIndexToDiscPosition(disc_delta),
476 disc_position, kTopDiscDetectStart,
477 kTopDiscDetectStop);
Austin Schuh1b864a12013-03-07 00:46:50 -0800478 frisbee->OffsetDisc(disc_delta);
479 }
480 }
Brian Silverman43bb73e2013-03-17 13:39:47 -0700481 if (highest_frisbee_below_sensor) {
482 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
483 "Moving down by %f to %f\n", frisbees_.size(),
484 ConvertIndexToDiscPosition(disc_delta),
485 highest_frisbee_below_sensor->absolute_position(
486 wrist_loop_->X_hat(0, 0)));
487 } else {
488 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
489 "Moving down by %f\n", frisbees_.size(),
490 ConvertIndexToDiscPosition(disc_delta));
491 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800492 } else if (disc_direction < 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800493 // Moving down at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800494 // There can only be 1 disc up top that would give us a posedge.
495 // Find it and place it at the one spot that it can be.
Brian Silverman94195052013-03-09 13:45:05 -0800496 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800497 Frisbee *min_frisbee = NULL;
498 MinDiscPosition(&min_disc_position, &min_frisbee);
499 if (!min_frisbee) {
500 // Uh, oh, we see a disc but there isn't one...
501 LOG(ERROR, "Saw a disc up top but there isn't one in the hopper\n");
502 } else {
503 const double disc_position = min_frisbee->absolute_position(
504 index_position);
505
506 const double disc_delta_meters = disc_position - kTopDiscDetectStop;
507 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
508 disc_delta_meters);
Austin Schuha3e8e032013-03-10 18:43:14 -0700509 LOG(INFO, "Posedge going down. Moving top disc down by %f\n",
510 disc_delta_meters);
Austin Schuh1b864a12013-03-07 00:46:50 -0800511 for (auto frisbee = frisbees_.begin(), end = frisbees_.end();
512 frisbee != end; ++frisbee) {
513 frisbee->OffsetDisc(disc_delta);
514 }
515 }
Austin Schuh825bde92013-03-06 00:16:46 -0800516 } else {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800517 LOG(ERROR, "Not sure how to handle the upper posedge, doing nothing\n");
Austin Schuh825bde92013-03-06 00:16:46 -0800518 }
519 }
520 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800521
Austin Schuhf8c52252013-03-03 02:25:49 -0800522 // Bool to track if it is safe for the goal to change yet.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700523 bool safe_to_change_state = true;
Brian Silvermanf0716e82013-03-17 23:36:11 -0700524 if (!position) {
525 // This fixes a nasty indexer bug.
526 // If we didn't get a position this cycle, we don't run the code below which
527 // checks the state of the disc detect sensor and whether all the discs are
528 // indexed. It is therefore not safe to change state and loose track of
529 // that disc.
530 safe_to_change_state = false;
531 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800532 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800533 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800534 // The goal should already be good, so sit tight with everything the same
535 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800536 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800537 case Goal::READY_LOWER:
538 case Goal::INTAKE:
Brian Silverman7d39d862013-09-29 17:00:30 -0700539 hopper_clear_ = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800540 {
Austin Schuhd78ab542013-03-01 22:22:19 -0800541 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800542 // Posedge of the disc entering the beam break.
543 if (position->bottom_disc_posedge_count !=
544 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800545 transfer_frisbee_.Reset();
546 transfer_frisbee_.bottom_posedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700547 LOG(INFO, "Posedge of bottom disc %f\n",
548 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800549 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800550 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800551 }
552
553 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800554 if (position->bottom_disc_negedge_count !=
555 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800556 transfer_frisbee_.bottom_negedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700557 LOG(INFO, "Negedge of bottom disc %f\n",
558 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800559 frisbees_.push_front(transfer_frisbee_);
560 }
561
562 if (position->bottom_disc_detect) {
Brian Silvermanc5277542013-03-22 13:33:07 -0700563 intake_voltage = 0.0;
564 transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800565 // Must wait until the disc gets out before we can change state.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700566 safe_to_change_state = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800567
Austin Schuhf8c52252013-03-03 02:25:49 -0800568 // TODO(aschuh): A disc on the way through needs to start moving
569 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800570
571 Time elapsed_posedge_time = now -
572 transfer_frisbee_.bottom_posedge_time_;
573 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
574 // It has been too long. The disc must be jammed.
575 LOG(ERROR, "Been way too long. Jammed disc?\n");
Brian Silvermance86bac2013-03-31 19:07:24 -0700576 intake_voltage = -12.0;
Brian Silvermanf0716e82013-03-17 23:36:11 -0700577 transfer_voltage = -12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800578 }
579 }
580
Austin Schuhf8c52252013-03-03 02:25:49 -0800581 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800582 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800583 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800584 if (!frisbee->has_been_indexed_) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800585 if (last_bottom_disc_negedge_wait_count_ !=
586 position->bottom_disc_negedge_wait_count) {
587 // We have an index difference.
588 // Save the indexer position, and the time.
589 if (last_bottom_disc_negedge_wait_count_ + 1 !=
590 position->bottom_disc_negedge_wait_count) {
591 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
592 }
593
594 // Save the captured position as the position at which the disc
595 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800596 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800597 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800598 frisbee->index_start_position_ =
599 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800600 }
601 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800602 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800603 }
Austin Schuh59004d32013-03-21 04:31:45 +0000604 for (auto frisbee = frisbees_.begin();
605 frisbee != frisbees_.end(); ++frisbee) {
606 if (!frisbee->has_been_indexed_) {
Brian Silvermanc5277542013-03-22 13:33:07 -0700607 intake_voltage = 0.0;
608 transfer_voltage = 12.0;
Austin Schuh59004d32013-03-21 04:31:45 +0000609
610 // All discs must be indexed before it is safe to stop indexing.
611 safe_to_change_state = false;
612 }
613 }
614
615 // Figure out where the indexer should be to move the discs down to
616 // the right position.
617 double max_disc_position = 0;
618 if (MaxDiscPosition(&max_disc_position, NULL)) {
619 LOG(DEBUG, "There is a disc down here!\n");
620 // TODO(aschuh): Figure out what to do if grabbing the next one
621 // would cause things to jam into the loader.
622 // Say we aren't ready any more. Undefined behavior will result if
623 // that isn't observed.
624 double bottom_disc_position =
625 max_disc_position + ConvertDiscAngleToIndex(M_PI);
626 wrist_loop_->R << bottom_disc_position, 0.0;
627
628 // Verify that we are close enough to the goal so that we should be
629 // fine accepting the next disc.
630 double disc_error_meters = ConvertIndexToDiscPosition(
631 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
632 // We are ready for the next disc if the first one is in the first
633 // half circle of the indexer. It will take time for the disc to
634 // come into the indexer, so we will be able to move it out of the
635 // way in time.
636 // This choice also makes sure that we don't claim that we aren't
637 // ready between full speed intaking.
638 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
639 disc_error_meters < 0.04) {
640 // We are only ready if we aren't being asked to change state or
641 // are full.
642 status->ready_to_intake =
643 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
644 } else {
645 status->ready_to_intake = false;
646 }
647 } else {
648 // No discs! We are always ready for more if we aren't being
649 // asked to change state.
650 status->ready_to_intake = (safe_goal_ == goal_enum);
651 }
652
653 // Turn on the transfer roller if we are ready.
654 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
655 safe_goal_ == Goal::INTAKE) {
656 intake_voltage = transfer_voltage = 12.0;
657 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800658 }
Austin Schuh59004d32013-03-21 04:31:45 +0000659 LOG(DEBUG, "INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800660 break;
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700661 case Goal::REINITIALIZE:
662 LOG(WARNING, "Reinitializing the indexer\n");
663 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800664 case Goal::READY_SHOOTER:
665 case Goal::SHOOT:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700666 // Don't let us leave the shoot or preload state if there are 4 discs in
667 // the hopper.
668 if (hopper_disc_count_ >= 4 && goal_enum != Goal::SHOOT) {
669 safe_to_change_state = false;
670 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800671 // Check if we have any discs to shoot or load and handle them.
Brian Silverman94195052013-03-09 13:45:05 -0800672 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800673 if (MinDiscPosition(&min_disc_position, NULL)) {
674 const double ready_disc_position = min_disc_position +
675 ConvertDiscPositionToIndex(kReadyToPreload - kIndexStartPosition);
Austin Schuhf8c52252013-03-03 02:25:49 -0800676
677 const double grabbed_disc_position =
678 min_disc_position +
679 ConvertDiscPositionToIndex(kReadyToLiftPosition -
Austin Schuh59004d32013-03-21 04:31:45 +0000680 kIndexStartPosition + 0.07);
Austin Schuhf8c52252013-03-03 02:25:49 -0800681
682 // Check the state of the loader FSM.
683 // If it is ready to load discs, position the disc so that it is ready
684 // to be grabbed.
685 // If it isn't ready, there is a disc in there. It needs to finish it's
686 // cycle first.
687 if (loader_state_ != LoaderState::READY) {
688 // We already have a disc in the loader.
689 // Stage the discs back a bit.
690 wrist_loop_->R << ready_disc_position, 0.0;
691
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800692 // Shoot if we are grabbed and being asked to shoot.
693 if (loader_state_ == LoaderState::GRABBED &&
694 safe_goal_ == Goal::SHOOT) {
695 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
Brian Silverman7ffb1382013-09-29 16:55:12 -0700696 is_shooting_ = true;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800697 }
698
Austin Schuhf8c52252013-03-03 02:25:49 -0800699 // Must wait until it has been grabbed to continue.
700 if (loader_state_ == LoaderState::GRABBING) {
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700701 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800702 }
703 } else {
704 // No disc up top right now.
705 wrist_loop_->R << grabbed_disc_position, 0.0;
706
707 // See if the disc has gotten pretty far up yet.
708 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
709 // Point of no return. We are committing to grabbing it now.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700710 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800711 const double robust_grabbed_disc_position =
712 (grabbed_disc_position -
713 ConvertDiscPositionToIndex(kGrabberLength));
714
715 // If close, start grabbing and/or shooting.
716 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
717 // Start the state machine.
718 if (safe_goal_ == Goal::SHOOT) {
719 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
720 } else {
721 loader_goal_ = LoaderGoal::GRAB;
722 }
723 // This frisbee is now gone. Take it out of the queue.
724 frisbees_.pop_back();
Austin Schuhf8c52252013-03-03 02:25:49 -0800725 }
726 }
727 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800728 } else {
729 if (loader_state_ != LoaderState::READY) {
730 // Shoot if we are grabbed and being asked to shoot.
731 if (loader_state_ == LoaderState::GRABBED &&
732 safe_goal_ == Goal::SHOOT) {
733 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
734 }
735 } else {
736 // Ok, no discs in sight. Spin the hopper up by 150% of it's full
737 // range and verify that we don't see anything.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800738 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700739 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700740 lower_open_region_.lower_bound()) +
Austin Schuhf60861b2013-03-14 00:14:10 -0700741 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800742
743 wrist_loop_->R << hopper_clear_verification_position, 0.0;
744 if (::std::abs(wrist_loop_->X_hat(0, 0) -
745 hopper_clear_verification_position) <
746 ConvertDiscPositionToIndex(0.05)) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800747 // We are at the end of the range. There are no more discs here.
748 while (frisbees_.size() > 0) {
749 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700750 LOG(ERROR, "Upper is [%f %f]\n",
751 upper_open_region_.upper_bound(),
752 upper_open_region_.lower_bound());
753 LOG(ERROR, "Lower is [%f %f]\n",
754 lower_open_region_.upper_bound(),
755 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800756 frisbees_.pop_back();
757 --hopper_disc_count_;
758 --total_disc_count_;
759 }
760 if (hopper_disc_count_ != 0) {
761 LOG(ERROR,
762 "Emptied the hopper out but there are still discs there\n");
Brian Silvermanf0716e82013-03-17 23:36:11 -0700763 hopper_disc_count_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800764 }
Brian Silverman7d39d862013-09-29 17:00:30 -0700765 hopper_clear_ = true;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800766 }
767 }
768 }
769
770 {
771 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700772 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700773 lower_open_region_.lower_bound()) +
Austin Schuhf60861b2013-03-14 00:14:10 -0700774 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800775
776 if (wrist_loop_->X_hat(0, 0) >
777 hopper_clear_verification_position +
778 ConvertDiscPositionToIndex(0.05)) {
779 // We are at the end of the range. There are no more discs here.
780 while (frisbees_.size() > 0) {
781 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuha3e8e032013-03-10 18:43:14 -0700782 LOG(ERROR, "Upper is [%f %f]\n",
783 upper_open_region_.upper_bound(),
784 upper_open_region_.lower_bound());
785 LOG(ERROR, "Lower is [%f %f]\n",
786 lower_open_region_.upper_bound(),
787 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800788 frisbees_.pop_back();
789 --hopper_disc_count_;
790 --total_disc_count_;
791 }
Brian Silvermanf0716e82013-03-17 23:36:11 -0700792 if (hopper_disc_count_ != 0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800793 LOG(ERROR,
Brian Silverman8efe23e2013-07-07 23:31:37 -0700794 "Emptied the hopper out but there are still %" PRId32 " discs there\n",
Austin Schuhf60861b2013-03-14 00:14:10 -0700795 hopper_disc_count_);
Brian Silvermanf0716e82013-03-17 23:36:11 -0700796 hopper_disc_count_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800797 }
Brian Silverman7d39d862013-09-29 17:00:30 -0700798 hopper_clear_ = true;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800799 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800800 }
801
Austin Schuha3e8e032013-03-10 18:43:14 -0700802 LOG(DEBUG, "READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800803 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800804 }
805
Austin Schuhd3d0fbf2013-03-14 00:37:00 -0700806 // Wait for a period of time to make sure that the disc gets sucked
807 // in properly. We need to do this regardless of what the indexer is doing.
808 for (auto frisbee = frisbees_.begin();
809 frisbee != frisbees_.end(); ++frisbee) {
810 if (now - frisbee->bottom_negedge_time_ < kTransferOffDelay) {
811 transfer_voltage = 12.0;
812 }
813 }
814
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700815 // If we have 4 discs, it is time to preload.
816 if (safe_to_change_state && hopper_disc_count_ >= 4) {
817 switch (safe_goal_) {
818 case Goal::HOLD:
819 case Goal::READY_LOWER:
820 case Goal::INTAKE:
821 safe_goal_ = Goal::READY_SHOOTER;
822 safe_to_change_state = false;
Brian Silverman8efe23e2013-07-07 23:31:37 -0700823 LOG(INFO, "We have %" PRId32 " discs, time to preload automatically\n",
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700824 hopper_disc_count_);
825 break;
826 case Goal::READY_SHOOTER:
827 case Goal::SHOOT:
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700828 case Goal::REINITIALIZE:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700829 break;
830 }
831 }
832
Austin Schuhf8c52252013-03-03 02:25:49 -0800833 // The only way out of the loader is to shoot the disc. The FSM can only go
834 // forwards.
835 switch (loader_state_) {
836 case LoaderState::READY:
Austin Schuha3e8e032013-03-10 18:43:14 -0700837 LOG(DEBUG, "Loader READY\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800838 // Open and down, ready to accept a disc.
839 loader_up_ = false;
840 disc_clamped_ = false;
841 disc_ejected_ = false;
Brian Silverman967c4422013-09-29 16:58:59 -0700842 disk_stuck_in_loader_ = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800843 if (loader_goal_ == LoaderGoal::GRAB ||
Brian Silverman9a1081b2013-04-05 13:50:44 -0700844 loader_goal_ == LoaderGoal::SHOOT_AND_RESET || goal->force_fire) {
845 if (goal->force_fire) {
846 LOG(INFO, "Told to force fire, moving on\n");
847 } else if (loader_goal_ == LoaderGoal::GRAB) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700848 LOG(INFO, "Told to GRAB, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800849 } else {
Austin Schuha3e8e032013-03-10 18:43:14 -0700850 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800851 }
852 loader_state_ = LoaderState::GRABBING;
853 loader_countdown_ = kGrabbingDelay;
854 } else {
855 break;
856 }
857 case LoaderState::GRABBING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700858 LOG(DEBUG, "Loader GRABBING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800859 // Closing the grabber.
860 loader_up_ = false;
861 disc_clamped_ = true;
862 disc_ejected_ = false;
863 if (loader_countdown_ > 0) {
864 --loader_countdown_;
865 break;
866 } else {
867 loader_state_ = LoaderState::GRABBED;
868 }
869 case LoaderState::GRABBED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700870 LOG(DEBUG, "Loader GRABBED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800871 // Grabber closed.
872 loader_up_ = false;
873 disc_clamped_ = true;
874 disc_ejected_ = false;
Brian Silverman9a1081b2013-04-05 13:50:44 -0700875 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET || goal->force_fire) {
Brian Silverman967c4422013-09-29 16:58:59 -0700876 if (shooter.status.FetchLatest() || shooter.status.get()) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700877 // TODO(aschuh): If we aren't shooting nicely, wait until the shooter
878 // is up to speed rather than just spinning.
Austin Schuhf60861b2013-03-14 00:14:10 -0700879 if (shooter.status->average_velocity > 130 && shooter.status->ready) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700880 loader_state_ = LoaderState::LIFTING;
881 loader_countdown_ = kLiftingDelay;
Brian Silvermanc540ab82013-09-22 00:00:28 -0700882 loader_timeout_ = 0;
Austin Schuha3e8e032013-03-10 18:43:14 -0700883 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
884 } else {
885 LOG(WARNING, "Told to SHOOT_AND_RESET, shooter too slow at %f\n",
886 shooter.status->average_velocity);
887 break;
888 }
889 } else {
890 LOG(ERROR, "Told to SHOOT_AND_RESET, no shooter data, moving on.\n");
891 loader_state_ = LoaderState::LIFTING;
892 loader_countdown_ = kLiftingDelay;
Brian Silvermanc540ab82013-09-22 00:00:28 -0700893 loader_timeout_ = 0;
Austin Schuha3e8e032013-03-10 18:43:14 -0700894 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800895 } else if (loader_goal_ == LoaderGoal::READY) {
896 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800897 break;
898 } else {
899 break;
900 }
901 case LoaderState::LIFTING:
Brian Silverman674d9702013-10-11 18:00:15 -0700902 LOG(DEBUG, "Loader LIFTING %d %d\n", loader_countdown_, loader_timeout_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800903 // Lifting the disc.
904 loader_up_ = true;
905 disc_clamped_ = true;
906 disc_ejected_ = false;
Brian Silverman25329e62013-09-21 23:52:10 -0700907 if (position->loader_top) {
908 if (loader_countdown_ > 0) {
909 --loader_countdown_;
Brian Silverman004ec812013-09-26 15:47:58 -0700910 loader_timeout_ = 0;
911 break;
Brian Silverman25329e62013-09-21 23:52:10 -0700912 } else {
913 loader_state_ = LoaderState::LIFTED;
914 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800915 } else {
Brian Silverman25329e62013-09-21 23:52:10 -0700916 // Restart the countdown if it bounces back down or whatever.
917 loader_countdown_ = kLiftingDelay;
Brian Silvermanc540ab82013-09-22 00:00:28 -0700918 ++loader_timeout_;
919 if (loader_timeout_ > kLiftingTimeout) {
920 LOG(ERROR, "Loader timeout while LIFTING %d\n", loader_timeout_);
Brian Silverman562af3e2013-10-19 16:44:23 -0700921 loader_state_ = LoaderState::LOWERING;
922 loader_countdown_ = kLoweringDelay;
923 loader_timeout_ = 0;
Brian Silverman967c4422013-09-29 16:58:59 -0700924 disk_stuck_in_loader_ = true;
Brian Silverman004ec812013-09-26 15:47:58 -0700925 } else {
926 break;
Brian Silvermanc540ab82013-09-22 00:00:28 -0700927 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800928 }
929 case LoaderState::LIFTED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700930 LOG(DEBUG, "Loader LIFTED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800931 // Disc lifted. Time to eject it out.
932 loader_up_ = true;
933 disc_clamped_ = true;
934 disc_ejected_ = false;
935 loader_state_ = LoaderState::SHOOTING;
936 loader_countdown_ = kShootingDelay;
937 case LoaderState::SHOOTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700938 LOG(DEBUG, "Loader SHOOTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800939 // Ejecting the disc into the shooter.
940 loader_up_ = true;
941 disc_clamped_ = false;
942 disc_ejected_ = true;
943 if (loader_countdown_ > 0) {
944 --loader_countdown_;
945 break;
946 } else {
947 loader_state_ = LoaderState::SHOOT;
948 }
949 case LoaderState::SHOOT:
Austin Schuha3e8e032013-03-10 18:43:14 -0700950 LOG(DEBUG, "Loader SHOOT\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800951 // The disc has been shot.
952 loader_up_ = true;
953 disc_clamped_ = false;
954 disc_ejected_ = true;
955 loader_state_ = LoaderState::LOWERING;
956 loader_countdown_ = kLoweringDelay;
Brian Silverman004ec812013-09-26 15:47:58 -0700957 loader_timeout_ = 0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800958 case LoaderState::LOWERING:
Brian Silverman674d9702013-10-11 18:00:15 -0700959 LOG(DEBUG, "Loader LOWERING %d %d\n", loader_countdown_, loader_timeout_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800960 // Lowering the loader back down.
961 loader_up_ = false;
962 disc_clamped_ = false;
Brian Silverman562af3e2013-10-19 16:44:23 -0700963 // We don't want to eject if we're stuck because it will force the disc
964 // into the green loader wheel.
965 disc_ejected_ = disk_stuck_in_loader_ ? false : true;
Brian Silverman004ec812013-09-26 15:47:58 -0700966 if (position->loader_bottom) {
967 if (loader_countdown_ > 0) {
968 --loader_countdown_;
969 loader_timeout_ = 0;
970 break;
971 } else {
972 loader_state_ = LoaderState::LOWERED;
Brian Silverman674d9702013-10-11 18:00:15 -0700973 --hopper_disc_count_;
974 ++shot_disc_count_;
Brian Silverman004ec812013-09-26 15:47:58 -0700975 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800976 } else {
Brian Silverman004ec812013-09-26 15:47:58 -0700977 // Restart the countdown if it bounces back up or something.
978 loader_countdown_ = kLoweringDelay;
979 ++loader_timeout_;
980 if (loader_timeout_ > kLoweringTimeout) {
981 LOG(ERROR, "Loader timeout while LOWERING %d\n", loader_timeout_);
982 loader_state_ = LoaderState::LOWERED;
Brian Silverman967c4422013-09-29 16:58:59 -0700983 disk_stuck_in_loader_ = true;
Brian Silverman004ec812013-09-26 15:47:58 -0700984 } else {
985 break;
986 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800987 }
988 case LoaderState::LOWERED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700989 LOG(DEBUG, "Loader LOWERED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800990 loader_up_ = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800991 disc_ejected_ = false;
Brian Silverman7ffb1382013-09-29 16:55:12 -0700992 is_shooting_ = false;
Brian Silverman967c4422013-09-29 16:58:59 -0700993 if (disk_stuck_in_loader_) {
994 disk_stuck_in_loader_ = false;
995 disc_clamped_ = true;
996 loader_state_ = LoaderState::GRABBED;
997 } else {
998 disc_clamped_ = false;
999 loader_state_ = LoaderState::READY;
1000 // Once we have shot, we need to hang out in READY until otherwise
1001 // notified.
1002 loader_goal_ = LoaderGoal::READY;
1003 }
Austin Schuhd78ab542013-03-01 22:22:19 -08001004 break;
1005 }
1006
1007 // Update the observer.
1008 wrist_loop_->Update(position != NULL, output == NULL);
1009
1010 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -08001011 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -08001012 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -08001013 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -08001014 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
1015 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
1016 last_bottom_disc_negedge_wait_count_ =
1017 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -08001018 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -08001019 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -08001020 }
1021
Brian Silvermanb8d389f2013-03-19 22:54:06 -07001022 // Clear everything if we are supposed to re-initialize.
1023 if (goal_enum == Goal::REINITIALIZE) {
1024 safe_goal_ = Goal::REINITIALIZE;
1025 no_prior_position_ = true;
1026 hopper_disc_count_ = 0;
1027 total_disc_count_ = 0;
1028 shot_disc_count_ = 0;
1029 loader_state_ = LoaderState::READY;
1030 loader_goal_ = LoaderGoal::READY;
1031 loader_countdown_ = 0;
1032 loader_up_ = false;
1033 disc_clamped_ = false;
1034 disc_ejected_ = false;
1035
1036 intake_voltage = 0.0;
1037 transfer_voltage = 0.0;
1038 wrist_loop_->U(0, 0) = 0.0;
1039 frisbees_.clear();
1040 }
1041
Austin Schuhd78ab542013-03-01 22:22:19 -08001042 status->hopper_disc_count = hopper_disc_count_;
1043 status->total_disc_count = total_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -07001044 status->shot_disc_count = shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -08001045 status->preloaded = (loader_state_ != LoaderState::READY);
Brian Silverman7ffb1382013-09-29 16:55:12 -07001046 status->is_shooting = is_shooting_;
Brian Silverman7d39d862013-09-29 17:00:30 -07001047 status->hopper_clear = hopper_clear_;
Austin Schuhd78ab542013-03-01 22:22:19 -08001048
1049 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -08001050 output->intake_voltage = intake_voltage;
Brian Silverman759d2632013-10-11 18:01:21 -07001051 if (goal->override_transfer) {
1052 output->transfer_voltage = goal->transfer_voltage;
1053 } else {
1054 output->transfer_voltage = transfer_voltage;
1055 }
Brian Silverman180e2b82013-04-08 14:29:56 -07001056 if (goal->override_index) {
1057 output->index_voltage = goal->index_voltage;
1058 } else {
1059 output->index_voltage = wrist_loop_->U(0, 0);
1060 }
Austin Schuhf8c52252013-03-03 02:25:49 -08001061 output->loader_up = loader_up_;
1062 output->disc_clamped = disc_clamped_;
1063 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -08001064 }
1065
Austin Schuhae5fc8c2013-03-11 23:23:28 -07001066 if (safe_to_change_state) {
Austin Schuhd78ab542013-03-01 22:22:19 -08001067 safe_goal_ = goal_enum;
1068 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -08001069 if (hopper_disc_count_ < 0) {
1070 LOG(ERROR, "NEGATIVE DISCS. VERY VERY BAD\n");
1071 }
Austin Schuhd78ab542013-03-01 22:22:19 -08001072}
1073
1074} // namespace control_loops
1075} // namespace frc971