blob: 551441285521c592061d39fe8b1c649d0db89ab7 [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
7#include "aos/aos_core.h"
8
Austin Schuhd78ab542013-03-01 22:22:19 -08009#include "aos/common/control_loop/control_loops.q.h"
10#include "aos/common/logging/logging.h"
Brian Silverman94195052013-03-09 13:45:05 -080011#include "aos/common/inttypes.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080012
13#include "frc971/constants.h"
Austin Schuh00558222013-03-03 14:16:16 -080014#include "frc971/control_loops/index/index_motor_plant.h"
Austin Schuha3e8e032013-03-10 18:43:14 -070015#include "frc971/control_loops/shooter/shooter_motor.q.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080016
17using ::aos::time::Time;
18
19namespace frc971 {
20namespace control_loops {
21
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070022double IndexMotor::Frisbee::ObserveNoTopDiscSensor(double index_position) {
Austin Schuhdff24e22013-03-06 00:41:21 -080023 // The absolute disc position in meters.
Austin Schuh1b864a12013-03-07 00:46:50 -080024 double disc_position = absolute_position(index_position);
Austin Schuh825bde92013-03-06 00:16:46 -080025 if (IndexMotor::kTopDiscDetectStart <= disc_position &&
26 disc_position <= IndexMotor::kTopDiscDetectStop) {
27 // Whoops, this shouldn't be happening.
28 // Move the disc off the way that makes most sense.
Austin Schuhdff24e22013-03-06 00:41:21 -080029 double distance_to_above = IndexMotor::ConvertDiscPositionToIndex(
30 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStop));
31 double distance_to_below = IndexMotor::ConvertDiscPositionToIndex(
32 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStart));
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070033 if (distance_to_above < distance_to_below) {
34 LOG(INFO, "Moving disc to top slow.\n");
35 // Move it up.
36 index_start_position_ -= distance_to_above;
37 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080038 } else {
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070039 LOG(INFO, "Moving disc to bottom slow.\n");
40 index_start_position_ += distance_to_below;
41 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080042 }
43 }
Austin Schuhdff24e22013-03-06 00:41:21 -080044 return 0.0;
Austin Schuh825bde92013-03-06 00:16:46 -080045}
46
Austin Schuhd78ab542013-03-01 22:22:19 -080047IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
48 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
Austin Schuh93485832013-03-04 00:01:34 -080049 wrist_loop_(new IndexStateFeedbackLoop(MakeIndexLoop())),
Austin Schuhd78ab542013-03-01 22:22:19 -080050 hopper_disc_count_(0),
51 total_disc_count_(0),
Austin Schuh70be1ba2013-03-10 13:37:17 -070052 shot_disc_count_(0),
Austin Schuhf8c52252013-03-03 02:25:49 -080053 safe_goal_(Goal::HOLD),
54 loader_goal_(LoaderGoal::READY),
55 loader_state_(LoaderState::READY),
Austin Schuhd78ab542013-03-01 22:22:19 -080056 loader_up_(false),
57 disc_clamped_(false),
58 disc_ejected_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080059 last_bottom_disc_detect_(false),
Austin Schuh825bde92013-03-06 00:16:46 -080060 last_top_disc_detect_(false),
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;
Austin Schuha3e8e032013-03-10 18:43:14 -0700111/*static*/ const int IndexMotor::kLiftingDelay = 30;
Austin Schuhf8c52252013-03-03 02:25:49 -0800112/*static*/ const int IndexMotor::kShootingDelay = 5;
113/*static*/ const int IndexMotor::kLoweringDelay = 20;
Austin Schuhd78ab542013-03-01 22:22:19 -0800114
Austin Schuh93485832013-03-04 00:01:34 -0800115// TODO(aschuh): Tune these.
116/*static*/ const double
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700117 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 11.0;
Austin Schuh93485832013-03-04 00:01:34 -0800118/*static*/ const double
Austin Schuha3e8e032013-03-10 18:43:14 -0700119 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 20;
Austin Schuh93485832013-03-04 00:01:34 -0800120
Austin Schuhd78ab542013-03-01 22:22:19 -0800121/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
122 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
123}
124
Austin Schuhf8c52252013-03-03 02:25:49 -0800125/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
126 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800127 return angle * (kDiscRadius + kRollerRadius);
128}
129
Austin Schuhf8c52252013-03-03 02:25:49 -0800130/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
131 const double position) {
132 return position / (kDiscRadius + kRollerRadius);
133}
134
Austin Schuhd78ab542013-03-01 22:22:19 -0800135/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
136 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
137}
138
139/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
140 return IndexMotor::ConvertDiscAngleToDiscPosition(
141 ConvertIndexToDiscAngle(angle));
142}
143
Austin Schuhf8c52252013-03-03 02:25:49 -0800144/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
145 const double angle) {
146 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
147 kTransferRollerRadius);
148 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
149}
150
151/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
152 const double position) {
153 return IndexMotor::ConvertDiscAngleToIndex(
154 ConvertDiscPositionToDiscAngle(position));
155}
156
Austin Schuh1b864a12013-03-07 00:46:50 -0800157bool IndexMotor::MinDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800158 bool found_start = false;
159 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800160 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800161 if (!found_start) {
162 if (frisbee.has_position()) {
163 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800164 if (found_disc) {
165 *found_disc = &frisbee;
166 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800167 found_start = true;
168 }
169 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800170 if (frisbee.position() <= *disc_position) {
171 *disc_position = frisbee.position();
172 if (found_disc) {
173 *found_disc = &frisbee;
174 }
175 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800176 }
177 }
178 return found_start;
179}
180
Austin Schuh1b864a12013-03-07 00:46:50 -0800181bool IndexMotor::MaxDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800182 bool found_start = false;
183 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800184 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800185 if (!found_start) {
186 if (frisbee.has_position()) {
187 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800188 if (found_disc) {
189 *found_disc = &frisbee;
190 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800191 found_start = true;
192 }
193 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800194 if (frisbee.position() > *disc_position) {
195 *disc_position = frisbee.position();
196 if (found_disc) {
197 *found_disc = &frisbee;
198 }
199 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800200 }
201 }
202 return found_start;
203}
204
Austin Schuh93485832013-03-04 00:01:34 -0800205void IndexMotor::IndexStateFeedbackLoop::CapU() {
206 // If the voltage has been low for a large number of cycles, cut the motor
207 // power. This is generally very bad controls practice since this isn't LTI,
208 // but we don't really care about tracking anything other than large step
209 // inputs, and the loader doesn't need to be that accurate.
210 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
211 ++low_voltage_count_;
212 if (low_voltage_count_ > kNoMotionCuttoffCount) {
Austin Schuh93485832013-03-04 00:01:34 -0800213 U(0, 0) = 0.0;
214 }
215 } else {
216 low_voltage_count_ = 0;
217 }
218
219 for (int i = 0; i < kNumOutputs; ++i) {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700220 if (U(i, 0) > U_max(i, 0)) {
221 U(i, 0) = U_max(i, 0);
222 } else if (U(i, 0) < U_min(i, 0)) {
223 U(i, 0) = U_min(i, 0);
Austin Schuh93485832013-03-04 00:01:34 -0800224 }
225 }
226}
227
228
Austin Schuhd78ab542013-03-01 22:22:19 -0800229// Positive angle is towards the shooter, and positive power is towards the
230// shooter.
231void IndexMotor::RunIteration(
232 const control_loops::IndexLoop::Goal *goal,
233 const control_loops::IndexLoop::Position *position,
234 control_loops::IndexLoop::Output *output,
235 control_loops::IndexLoop::Status *status) {
Austin Schuhd3d0fbf2013-03-14 00:37:00 -0700236 Time now = Time::Now();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800237 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800238 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700239 if (goal->goal_state < 0 || goal->goal_state > 5) {
Brian Silverman94195052013-03-09 13:45:05 -0800240 LOG(ERROR, "Goal state is %"PRId32" which is out of range. Going to HOLD.\n",
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800241 goal->goal_state);
242 goal_enum = Goal::HOLD;
243 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800244
245 // Disable the motors now so that all early returns will return with the
246 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800247 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800248 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800249 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800250 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800251 output->transfer_voltage = 0.0;
252 output->index_voltage = 0.0;
253 }
254
255 status->ready_to_intake = false;
256
Austin Schuhe3490622013-03-13 01:24:30 -0700257 // Set the controller to use to be the one designed for the current number of
258 // discs in the hopper. This is safe since the controller prevents the index
259 // from being set out of bounds and picks the closest controller.
260 wrist_loop_->set_controller_index(frisbees_.size());
261
Austin Schuhf8c52252013-03-03 02:25:49 -0800262 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800263 if (position) {
264 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800265 // Set the goal to be the current position if this is the first time through
266 // so we don't always spin the indexer to the 0 position before starting.
267 if (no_prior_position_) {
Brian Silverman43bb73e2013-03-17 13:39:47 -0700268 LOG(INFO, "no prior position; resetting\n");
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800269 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800270 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800271 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800272 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
273 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
274 last_bottom_disc_negedge_wait_count_ =
275 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800276 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800277 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Brian Silvermance86bac2013-03-31 19:07:24 -0700278 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800279 // The open positions for the upper is right here and isn't a hard edge.
Austin Schuh723770b2013-03-10 13:26:20 -0700280 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
281 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800282 }
283
Austin Schuh1b864a12013-03-07 00:46:50 -0800284 // If the cRIO is gone for over 1/2 of a second, assume that it rebooted.
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800285 if (missing_position_count_ > 50) {
Brian Silverman43bb73e2013-03-17 13:39:47 -0700286 LOG(INFO, "assuming cRIO rebooted\n");
Austin Schuh6328daf2013-03-05 00:53:15 -0800287 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
288 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
289 last_bottom_disc_negedge_wait_count_ =
290 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800291 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800292 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Brian Silvermance86bac2013-03-31 19:07:24 -0700293 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800294 // We can't really trust the open range any more if the crio rebooted.
Austin Schuh723770b2013-03-10 13:26:20 -0700295 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
296 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800297 // Adjust the disc positions so that they don't have to move.
298 const double disc_offset =
299 position->index_position - wrist_loop_->X_hat(0, 0);
300 for (auto frisbee = frisbees_.begin();
301 frisbee != frisbees_.end(); ++frisbee) {
302 frisbee->OffsetDisc(disc_offset);
303 }
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800304 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800305 }
306 missing_position_count_ = 0;
Brian Silvermance86bac2013-03-31 19:07:24 -0700307 if (last_top_disc_detect_ && position->top_disc_detect) {
308 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
309 }
310 if (!last_top_disc_detect_ && !position->top_disc_detect) {
311 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
312 }
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800313 } else {
314 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800315 }
316 const double index_position = wrist_loop_->X_hat(0, 0);
317
Austin Schuh825bde92013-03-06 00:16:46 -0800318 if (position) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800319 // Reset the open region if we saw a negedge.
Austin Schuh723770b2013-03-10 13:26:20 -0700320 if (position->bottom_disc_negedge_wait_count !=
321 last_bottom_disc_negedge_wait_count_) {
322 // Saw a negedge, must be a new region.
323 lower_open_region_.Restart(position->bottom_disc_negedge_wait_position);
324 }
325 // Reset the open region if we saw a negedge.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800326 if (position->top_disc_negedge_count != last_top_disc_negedge_count_) {
327 // Saw a negedge, must be a new region.
Austin Schuh723770b2013-03-10 13:26:20 -0700328 upper_open_region_.Restart(position->top_disc_negedge_position);
329 }
330
331 // No disc. Expand the open region.
332 if (!position->bottom_disc_detect) {
333 lower_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800334 }
335
336 // No disc. Expand the open region.
337 if (!position->top_disc_detect) {
Austin Schuh723770b2013-03-10 13:26:20 -0700338 upper_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800339 }
340
Austin Schuh825bde92013-03-06 00:16:46 -0800341 if (!position->top_disc_detect) {
342 // We don't see a disc. Verify that there are no discs that we should be
343 // seeing.
Austin Schuh1b864a12013-03-07 00:46:50 -0800344 // Assume that discs will move slow enough that we won't miss one as it
345 // goes by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800346
347 double cumulative_offset = 0.0;
348 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
349 frisbee != rend; ++frisbee) {
350 frisbee->OffsetDisc(cumulative_offset);
351 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh6b1ad2f2013-03-11 23:23:00 -0700352 wrist_loop_->X_hat(0, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800353 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800354 }
355 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800356
Austin Schuh825bde92013-03-06 00:16:46 -0800357 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800358 const double index_position = wrist_loop_->X_hat(0, 0) -
359 position->index_position + position->top_disc_posedge_position;
Austin Schuh825bde92013-03-06 00:16:46 -0800360 // TODO(aschuh): Sanity check this number...
361 // Requires storing when the disc was last seen with the sensor off, and
362 // figuring out what to do if things go south.
363
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800364 // 1 if discs are going up, 0 if we have no clue, and -1 if they are going
365 // down.
366 int disc_direction = 0;
Austin Schuh825bde92013-03-06 00:16:46 -0800367 if (wrist_loop_->X_hat(1, 0) > 50.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800368 disc_direction = 1;
369 } else if (wrist_loop_->X_hat(1, 0) < -50.0) {
370 disc_direction = -1;
371 } else {
372 // Save the upper and lower positions that we last saw a disc at.
373 // If there is a big buffer above, must be a disc from below.
374 // If there is a big buffer below, must be a disc from above.
375 // This should work to replace the velocity threshold above.
376
Austin Schuh723770b2013-03-10 13:26:20 -0700377 const double open_width = upper_open_region_.width();
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800378 const double relative_upper_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700379 (upper_open_region_.upper_bound() - index_position) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800380 const double relative_lower_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700381 (index_position - upper_open_region_.lower_bound()) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800382
383 if (ConvertIndexToDiscPosition(open_width) <
384 kTopDiscDetectMinSeperation * 0.9) {
385 LOG(ERROR, "Discs are way too close to each other. Doing nothing\n");
386 } else if (relative_upper_open_precentage > 0.75) {
387 // Looks like it is a disc going down from above since we are near
388 // the upper edge.
389 disc_direction = -1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700390 LOG(INFO, "Disc edge going down\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800391 } else if (relative_lower_open_precentage > 0.75) {
392 // Looks like it is a disc going up from below since we are near
393 // the lower edge.
394 disc_direction = 1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700395 LOG(INFO, "Disc edge going up\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800396 } else {
397 LOG(ERROR,
398 "Got an edge in the middle of what should be an open region.\n");
399 LOG(ERROR, "Open width: %f upper precentage %f %%\n",
400 open_width, relative_upper_open_precentage);
401 }
402 }
403
404 if (disc_direction > 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800405 // Moving up at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800406 // Find the highest disc that is below the top disc sensor.
407 // While we are at it, count the number above and log an error if there
408 // are too many.
409 if (frisbees_.size() == 0) {
410 Frisbee new_frisbee;
411 new_frisbee.has_been_indexed_ = true;
412 new_frisbee.index_start_position_ = index_position -
413 ConvertDiscPositionToIndex(kTopDiscDetectStart -
414 kIndexStartPosition);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800415 ++hopper_disc_count_;
416 ++total_disc_count_;
Austin Schuh1b864a12013-03-07 00:46:50 -0800417 frisbees_.push_front(new_frisbee);
418 LOG(WARNING, "Added a disc to the hopper at the top sensor\n");
419 }
420
421 int above_disc_count = 0;
422 double highest_position = 0;
423 Frisbee *highest_frisbee_below_sensor = NULL;
424 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
425 frisbee != rend; ++frisbee) {
426 const double disc_position = frisbee->absolute_position(
427 index_position);
428 // It is save to use the top position for the cuttoff, since the
429 // sensor being low will result in discs being pushed off of it.
430 if (disc_position >= kTopDiscDetectStop) {
431 ++above_disc_count;
432 } else if (!highest_frisbee_below_sensor ||
433 disc_position > highest_position) {
434 highest_frisbee_below_sensor = &*frisbee;
435 highest_position = disc_position;
436 }
437 }
Austin Schuh09e07082013-03-19 10:04:12 +0000438
439 if (!highest_frisbee_below_sensor) {
440 Frisbee new_frisbee;
441 new_frisbee.has_been_indexed_ = true;
442 new_frisbee.index_start_position_ = index_position -
443 ConvertDiscPositionToIndex(kTopDiscDetectStart -
444 kIndexStartPosition);
445 highest_position = kTopDiscDetectStart;
446 ++hopper_disc_count_;
447 ++total_disc_count_;
448 frisbees_.push_front(new_frisbee);
449 LOG(WARNING, "Added a disc to the hopper at the top sensor because the one we know about is up top\n");
450 }
451
Austin Schuh1b864a12013-03-07 00:46:50 -0800452 if (above_disc_count > 1) {
453 LOG(ERROR, "We have 2 discs above the top sensor.\n");
454 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800455 // We now have the disc. Shift all the ones below the sensor up by the
456 // computed delta.
457 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
458 highest_position - kTopDiscDetectStart);
459 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
460 frisbee != rend; ++frisbee) {
461 const double disc_position = frisbee->absolute_position(
462 index_position);
463 if (disc_position < kTopDiscDetectStop) {
Austin Schuh59004d32013-03-21 04:31:45 +0000464 LOG(INFO, "Moving disc down by %f meters, since it is at %f and top is [%f, %f]\n",
465 ConvertIndexToDiscPosition(disc_delta),
466 disc_position, kTopDiscDetectStart,
467 kTopDiscDetectStop);
Austin Schuh1b864a12013-03-07 00:46:50 -0800468 frisbee->OffsetDisc(disc_delta);
469 }
470 }
Brian Silverman43bb73e2013-03-17 13:39:47 -0700471 if (highest_frisbee_below_sensor) {
472 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
473 "Moving down by %f to %f\n", frisbees_.size(),
474 ConvertIndexToDiscPosition(disc_delta),
475 highest_frisbee_below_sensor->absolute_position(
476 wrist_loop_->X_hat(0, 0)));
477 } else {
478 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
479 "Moving down by %f\n", frisbees_.size(),
480 ConvertIndexToDiscPosition(disc_delta));
481 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800482 } else if (disc_direction < 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800483 // Moving down at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800484 // There can only be 1 disc up top that would give us a posedge.
485 // Find it and place it at the one spot that it can be.
Brian Silverman94195052013-03-09 13:45:05 -0800486 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800487 Frisbee *min_frisbee = NULL;
488 MinDiscPosition(&min_disc_position, &min_frisbee);
489 if (!min_frisbee) {
490 // Uh, oh, we see a disc but there isn't one...
491 LOG(ERROR, "Saw a disc up top but there isn't one in the hopper\n");
492 } else {
493 const double disc_position = min_frisbee->absolute_position(
494 index_position);
495
496 const double disc_delta_meters = disc_position - kTopDiscDetectStop;
497 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
498 disc_delta_meters);
Austin Schuha3e8e032013-03-10 18:43:14 -0700499 LOG(INFO, "Posedge going down. Moving top disc down by %f\n",
500 disc_delta_meters);
Austin Schuh1b864a12013-03-07 00:46:50 -0800501 for (auto frisbee = frisbees_.begin(), end = frisbees_.end();
502 frisbee != end; ++frisbee) {
503 frisbee->OffsetDisc(disc_delta);
504 }
505 }
Austin Schuh825bde92013-03-06 00:16:46 -0800506 } else {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800507 LOG(ERROR, "Not sure how to handle the upper posedge, doing nothing\n");
Austin Schuh825bde92013-03-06 00:16:46 -0800508 }
509 }
510 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800511
Austin Schuhf8c52252013-03-03 02:25:49 -0800512 // Bool to track if it is safe for the goal to change yet.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700513 bool safe_to_change_state = true;
Brian Silvermanf0716e82013-03-17 23:36:11 -0700514 if (!position) {
515 // This fixes a nasty indexer bug.
516 // If we didn't get a position this cycle, we don't run the code below which
517 // checks the state of the disc detect sensor and whether all the discs are
518 // indexed. It is therefore not safe to change state and loose track of
519 // that disc.
520 safe_to_change_state = false;
521 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800522 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800523 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800524 // The goal should already be good, so sit tight with everything the same
525 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800526 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800527 case Goal::READY_LOWER:
528 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800529 {
Austin Schuhd78ab542013-03-01 22:22:19 -0800530 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800531 // Posedge of the disc entering the beam break.
532 if (position->bottom_disc_posedge_count !=
533 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800534 transfer_frisbee_.Reset();
535 transfer_frisbee_.bottom_posedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700536 LOG(INFO, "Posedge of bottom disc %f\n",
537 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800538 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800539 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800540 }
541
542 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800543 if (position->bottom_disc_negedge_count !=
544 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800545 transfer_frisbee_.bottom_negedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700546 LOG(INFO, "Negedge of bottom disc %f\n",
547 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800548 frisbees_.push_front(transfer_frisbee_);
549 }
550
551 if (position->bottom_disc_detect) {
Brian Silvermanc5277542013-03-22 13:33:07 -0700552 intake_voltage = 0.0;
553 transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800554 // Must wait until the disc gets out before we can change state.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700555 safe_to_change_state = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800556
Austin Schuhf8c52252013-03-03 02:25:49 -0800557 // TODO(aschuh): A disc on the way through needs to start moving
558 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800559
560 Time elapsed_posedge_time = now -
561 transfer_frisbee_.bottom_posedge_time_;
562 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
563 // It has been too long. The disc must be jammed.
564 LOG(ERROR, "Been way too long. Jammed disc?\n");
Brian Silvermance86bac2013-03-31 19:07:24 -0700565 intake_voltage = -12.0;
Brian Silvermanf0716e82013-03-17 23:36:11 -0700566 transfer_voltage = -12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800567 }
568 }
569
Austin Schuhf8c52252013-03-03 02:25:49 -0800570 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800571 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800572 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800573 if (!frisbee->has_been_indexed_) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800574 if (last_bottom_disc_negedge_wait_count_ !=
575 position->bottom_disc_negedge_wait_count) {
576 // We have an index difference.
577 // Save the indexer position, and the time.
578 if (last_bottom_disc_negedge_wait_count_ + 1 !=
579 position->bottom_disc_negedge_wait_count) {
580 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
581 }
582
583 // Save the captured position as the position at which the disc
584 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800585 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800586 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800587 frisbee->index_start_position_ =
588 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800589 }
590 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800591 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800592 }
Austin Schuh59004d32013-03-21 04:31:45 +0000593 for (auto frisbee = frisbees_.begin();
594 frisbee != frisbees_.end(); ++frisbee) {
595 if (!frisbee->has_been_indexed_) {
Brian Silvermanc5277542013-03-22 13:33:07 -0700596 intake_voltage = 0.0;
597 transfer_voltage = 12.0;
Austin Schuh59004d32013-03-21 04:31:45 +0000598
599 // All discs must be indexed before it is safe to stop indexing.
600 safe_to_change_state = false;
601 }
602 }
603
604 // Figure out where the indexer should be to move the discs down to
605 // the right position.
606 double max_disc_position = 0;
607 if (MaxDiscPosition(&max_disc_position, NULL)) {
608 LOG(DEBUG, "There is a disc down here!\n");
609 // TODO(aschuh): Figure out what to do if grabbing the next one
610 // would cause things to jam into the loader.
611 // Say we aren't ready any more. Undefined behavior will result if
612 // that isn't observed.
613 double bottom_disc_position =
614 max_disc_position + ConvertDiscAngleToIndex(M_PI);
615 wrist_loop_->R << bottom_disc_position, 0.0;
616
617 // Verify that we are close enough to the goal so that we should be
618 // fine accepting the next disc.
619 double disc_error_meters = ConvertIndexToDiscPosition(
620 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
621 // We are ready for the next disc if the first one is in the first
622 // half circle of the indexer. It will take time for the disc to
623 // come into the indexer, so we will be able to move it out of the
624 // way in time.
625 // This choice also makes sure that we don't claim that we aren't
626 // ready between full speed intaking.
627 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
628 disc_error_meters < 0.04) {
629 // We are only ready if we aren't being asked to change state or
630 // are full.
631 status->ready_to_intake =
632 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
633 } else {
634 status->ready_to_intake = false;
635 }
636 } else {
637 // No discs! We are always ready for more if we aren't being
638 // asked to change state.
639 status->ready_to_intake = (safe_goal_ == goal_enum);
640 }
641
642 // Turn on the transfer roller if we are ready.
643 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
644 safe_goal_ == Goal::INTAKE) {
645 intake_voltage = transfer_voltage = 12.0;
646 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800647 }
Austin Schuh59004d32013-03-21 04:31:45 +0000648 LOG(DEBUG, "INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800649 break;
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700650 case Goal::REINITIALIZE:
651 LOG(WARNING, "Reinitializing the indexer\n");
652 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800653 case Goal::READY_SHOOTER:
654 case Goal::SHOOT:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700655 // Don't let us leave the shoot or preload state if there are 4 discs in
656 // the hopper.
657 if (hopper_disc_count_ >= 4 && goal_enum != Goal::SHOOT) {
658 safe_to_change_state = false;
659 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800660 // Check if we have any discs to shoot or load and handle them.
Brian Silverman94195052013-03-09 13:45:05 -0800661 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800662 if (MinDiscPosition(&min_disc_position, NULL)) {
663 const double ready_disc_position = min_disc_position +
664 ConvertDiscPositionToIndex(kReadyToPreload - kIndexStartPosition);
Austin Schuhf8c52252013-03-03 02:25:49 -0800665
666 const double grabbed_disc_position =
667 min_disc_position +
668 ConvertDiscPositionToIndex(kReadyToLiftPosition -
Austin Schuh59004d32013-03-21 04:31:45 +0000669 kIndexStartPosition + 0.07);
Austin Schuhf8c52252013-03-03 02:25:49 -0800670
671 // Check the state of the loader FSM.
672 // If it is ready to load discs, position the disc so that it is ready
673 // to be grabbed.
674 // If it isn't ready, there is a disc in there. It needs to finish it's
675 // cycle first.
676 if (loader_state_ != LoaderState::READY) {
677 // We already have a disc in the loader.
678 // Stage the discs back a bit.
679 wrist_loop_->R << ready_disc_position, 0.0;
680
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800681 // Shoot if we are grabbed and being asked to shoot.
682 if (loader_state_ == LoaderState::GRABBED &&
683 safe_goal_ == Goal::SHOOT) {
684 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
685 }
686
Austin Schuhf8c52252013-03-03 02:25:49 -0800687 // Must wait until it has been grabbed to continue.
688 if (loader_state_ == LoaderState::GRABBING) {
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700689 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800690 }
691 } else {
692 // No disc up top right now.
693 wrist_loop_->R << grabbed_disc_position, 0.0;
694
695 // See if the disc has gotten pretty far up yet.
696 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
697 // Point of no return. We are committing to grabbing it now.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700698 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800699 const double robust_grabbed_disc_position =
700 (grabbed_disc_position -
701 ConvertDiscPositionToIndex(kGrabberLength));
702
703 // If close, start grabbing and/or shooting.
704 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
705 // Start the state machine.
706 if (safe_goal_ == Goal::SHOOT) {
707 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
708 } else {
709 loader_goal_ = LoaderGoal::GRAB;
710 }
711 // This frisbee is now gone. Take it out of the queue.
712 frisbees_.pop_back();
Austin Schuhf8c52252013-03-03 02:25:49 -0800713 }
714 }
715 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800716 } else {
717 if (loader_state_ != LoaderState::READY) {
718 // Shoot if we are grabbed and being asked to shoot.
719 if (loader_state_ == LoaderState::GRABBED &&
720 safe_goal_ == Goal::SHOOT) {
721 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
722 }
723 } else {
724 // Ok, no discs in sight. Spin the hopper up by 150% of it's full
725 // range and verify that we don't see anything.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800726 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700727 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700728 lower_open_region_.lower_bound()) +
Austin Schuhf60861b2013-03-14 00:14:10 -0700729 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800730
731 wrist_loop_->R << hopper_clear_verification_position, 0.0;
732 if (::std::abs(wrist_loop_->X_hat(0, 0) -
733 hopper_clear_verification_position) <
734 ConvertDiscPositionToIndex(0.05)) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800735 // We are at the end of the range. There are no more discs here.
736 while (frisbees_.size() > 0) {
737 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700738 LOG(ERROR, "Upper is [%f %f]\n",
739 upper_open_region_.upper_bound(),
740 upper_open_region_.lower_bound());
741 LOG(ERROR, "Lower is [%f %f]\n",
742 lower_open_region_.upper_bound(),
743 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800744 frisbees_.pop_back();
745 --hopper_disc_count_;
746 --total_disc_count_;
747 }
748 if (hopper_disc_count_ != 0) {
749 LOG(ERROR,
750 "Emptied the hopper out but there are still discs there\n");
Brian Silvermanf0716e82013-03-17 23:36:11 -0700751 hopper_disc_count_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800752 }
753 }
754 }
755 }
756
757 {
758 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700759 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700760 lower_open_region_.lower_bound()) +
Austin Schuhf60861b2013-03-14 00:14:10 -0700761 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800762
763 if (wrist_loop_->X_hat(0, 0) >
764 hopper_clear_verification_position +
765 ConvertDiscPositionToIndex(0.05)) {
766 // We are at the end of the range. There are no more discs here.
767 while (frisbees_.size() > 0) {
768 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuha3e8e032013-03-10 18:43:14 -0700769 LOG(ERROR, "Upper is [%f %f]\n",
770 upper_open_region_.upper_bound(),
771 upper_open_region_.lower_bound());
772 LOG(ERROR, "Lower is [%f %f]\n",
773 lower_open_region_.upper_bound(),
774 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800775 frisbees_.pop_back();
776 --hopper_disc_count_;
777 --total_disc_count_;
778 }
Brian Silvermanf0716e82013-03-17 23:36:11 -0700779 if (hopper_disc_count_ != 0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800780 LOG(ERROR,
Brian Silvermanb7bcef12013-03-16 13:57:11 -0700781 "Emptied the hopper out but there are still %"PRId32" discs there\n",
Austin Schuhf60861b2013-03-14 00:14:10 -0700782 hopper_disc_count_);
Brian Silvermanf0716e82013-03-17 23:36:11 -0700783 hopper_disc_count_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800784 }
785 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800786 }
787
Austin Schuha3e8e032013-03-10 18:43:14 -0700788 LOG(DEBUG, "READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800789 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800790 }
791
Austin Schuhd3d0fbf2013-03-14 00:37:00 -0700792 // Wait for a period of time to make sure that the disc gets sucked
793 // in properly. We need to do this regardless of what the indexer is doing.
794 for (auto frisbee = frisbees_.begin();
795 frisbee != frisbees_.end(); ++frisbee) {
796 if (now - frisbee->bottom_negedge_time_ < kTransferOffDelay) {
797 transfer_voltage = 12.0;
798 }
799 }
800
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700801 // If we have 4 discs, it is time to preload.
802 if (safe_to_change_state && hopper_disc_count_ >= 4) {
803 switch (safe_goal_) {
804 case Goal::HOLD:
805 case Goal::READY_LOWER:
806 case Goal::INTAKE:
807 safe_goal_ = Goal::READY_SHOOTER;
808 safe_to_change_state = false;
Brian Silvermana0238ce2013-03-13 17:58:45 -0700809 LOG(INFO, "We have %"PRId32" discs, time to preload automatically\n",
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700810 hopper_disc_count_);
811 break;
812 case Goal::READY_SHOOTER:
813 case Goal::SHOOT:
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700814 case Goal::REINITIALIZE:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700815 break;
816 }
817 }
818
Austin Schuhf8c52252013-03-03 02:25:49 -0800819 // The only way out of the loader is to shoot the disc. The FSM can only go
820 // forwards.
821 switch (loader_state_) {
822 case LoaderState::READY:
Austin Schuha3e8e032013-03-10 18:43:14 -0700823 LOG(DEBUG, "Loader READY\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800824 // Open and down, ready to accept a disc.
825 loader_up_ = false;
826 disc_clamped_ = false;
827 disc_ejected_ = false;
828 if (loader_goal_ == LoaderGoal::GRAB ||
829 loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
830 if (loader_goal_ == LoaderGoal::GRAB) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700831 LOG(INFO, "Told to GRAB, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800832 } else {
Austin Schuha3e8e032013-03-10 18:43:14 -0700833 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800834 }
835 loader_state_ = LoaderState::GRABBING;
836 loader_countdown_ = kGrabbingDelay;
837 } else {
838 break;
839 }
840 case LoaderState::GRABBING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700841 LOG(DEBUG, "Loader GRABBING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800842 // Closing the grabber.
843 loader_up_ = false;
844 disc_clamped_ = true;
845 disc_ejected_ = false;
846 if (loader_countdown_ > 0) {
847 --loader_countdown_;
848 break;
849 } else {
850 loader_state_ = LoaderState::GRABBED;
851 }
852 case LoaderState::GRABBED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700853 LOG(DEBUG, "Loader GRABBED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800854 // Grabber closed.
855 loader_up_ = false;
856 disc_clamped_ = true;
857 disc_ejected_ = false;
858 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700859 shooter.status.FetchLatest();
860 if (shooter.status.get()) {
861 // TODO(aschuh): If we aren't shooting nicely, wait until the shooter
862 // is up to speed rather than just spinning.
Austin Schuhf60861b2013-03-14 00:14:10 -0700863 if (shooter.status->average_velocity > 130 && shooter.status->ready) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700864 loader_state_ = LoaderState::LIFTING;
865 loader_countdown_ = kLiftingDelay;
866 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
867 } else {
868 LOG(WARNING, "Told to SHOOT_AND_RESET, shooter too slow at %f\n",
869 shooter.status->average_velocity);
870 break;
871 }
872 } else {
873 LOG(ERROR, "Told to SHOOT_AND_RESET, no shooter data, moving on.\n");
874 loader_state_ = LoaderState::LIFTING;
875 loader_countdown_ = kLiftingDelay;
876 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800877 } else if (loader_goal_ == LoaderGoal::READY) {
878 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800879 break;
880 } else {
881 break;
882 }
883 case LoaderState::LIFTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700884 LOG(DEBUG, "Loader LIFTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800885 // Lifting the disc.
886 loader_up_ = true;
887 disc_clamped_ = true;
888 disc_ejected_ = false;
889 if (loader_countdown_ > 0) {
890 --loader_countdown_;
891 break;
892 } else {
893 loader_state_ = LoaderState::LIFTED;
894 }
895 case LoaderState::LIFTED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700896 LOG(DEBUG, "Loader LIFTED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800897 // Disc lifted. Time to eject it out.
898 loader_up_ = true;
899 disc_clamped_ = true;
900 disc_ejected_ = false;
901 loader_state_ = LoaderState::SHOOTING;
902 loader_countdown_ = kShootingDelay;
903 case LoaderState::SHOOTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700904 LOG(DEBUG, "Loader SHOOTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800905 // Ejecting the disc into the shooter.
906 loader_up_ = true;
907 disc_clamped_ = false;
908 disc_ejected_ = true;
909 if (loader_countdown_ > 0) {
910 --loader_countdown_;
911 break;
912 } else {
913 loader_state_ = LoaderState::SHOOT;
914 }
915 case LoaderState::SHOOT:
Austin Schuha3e8e032013-03-10 18:43:14 -0700916 LOG(DEBUG, "Loader SHOOT\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800917 // The disc has been shot.
918 loader_up_ = true;
919 disc_clamped_ = false;
920 disc_ejected_ = true;
921 loader_state_ = LoaderState::LOWERING;
922 loader_countdown_ = kLoweringDelay;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800923 --hopper_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700924 ++shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800925 case LoaderState::LOWERING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700926 LOG(DEBUG, "Loader LOWERING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800927 // Lowering the loader back down.
928 loader_up_ = false;
929 disc_clamped_ = false;
930 disc_ejected_ = true;
931 if (loader_countdown_ > 0) {
932 --loader_countdown_;
933 break;
934 } else {
935 loader_state_ = LoaderState::LOWERED;
936 }
937 case LoaderState::LOWERED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700938 LOG(DEBUG, "Loader LOWERED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800939 // The indexer is lowered.
940 loader_up_ = false;
941 disc_clamped_ = false;
942 disc_ejected_ = false;
943 loader_state_ = LoaderState::READY;
944 // Once we have shot, we need to hang out in READY until otherwise
945 // notified.
946 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800947 break;
948 }
949
950 // Update the observer.
951 wrist_loop_->Update(position != NULL, output == NULL);
952
953 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800954 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800955 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -0800956 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -0800957 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
958 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
959 last_bottom_disc_negedge_wait_count_ =
960 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800961 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800962 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -0800963 }
964
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700965 // Clear everything if we are supposed to re-initialize.
966 if (goal_enum == Goal::REINITIALIZE) {
967 safe_goal_ = Goal::REINITIALIZE;
968 no_prior_position_ = true;
969 hopper_disc_count_ = 0;
970 total_disc_count_ = 0;
971 shot_disc_count_ = 0;
972 loader_state_ = LoaderState::READY;
973 loader_goal_ = LoaderGoal::READY;
974 loader_countdown_ = 0;
975 loader_up_ = false;
976 disc_clamped_ = false;
977 disc_ejected_ = false;
978
979 intake_voltage = 0.0;
980 transfer_voltage = 0.0;
981 wrist_loop_->U(0, 0) = 0.0;
982 frisbees_.clear();
983 }
984
Austin Schuhd78ab542013-03-01 22:22:19 -0800985 status->hopper_disc_count = hopper_disc_count_;
986 status->total_disc_count = total_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700987 status->shot_disc_count = shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800988 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800989
990 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800991 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -0800992 output->transfer_voltage = transfer_voltage;
Austin Schuhd78ab542013-03-01 22:22:19 -0800993 output->index_voltage = wrist_loop_->U(0, 0);
Austin Schuhf8c52252013-03-03 02:25:49 -0800994 output->loader_up = loader_up_;
995 output->disc_clamped = disc_clamped_;
996 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800997 }
998
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700999 if (safe_to_change_state) {
Austin Schuhd78ab542013-03-01 22:22:19 -08001000 safe_goal_ = goal_enum;
1001 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -08001002 if (hopper_disc_count_ < 0) {
1003 LOG(ERROR, "NEGATIVE DISCS. VERY VERY BAD\n");
1004 }
Austin Schuhd78ab542013-03-01 22:22:19 -08001005}
1006
1007} // namespace control_loops
1008} // namespace frc971