blob: b5c3a5252f2616462c0c606a49ba55151a4d27ba [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),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080060 no_prior_position_(true),
Austin Schuh723770b2013-03-10 13:26:20 -070061 missing_position_count_(0) {
Austin Schuhd78ab542013-03-01 22:22:19 -080062}
63
Austin Schuhf8c52252013-03-03 02:25:49 -080064/*static*/ const double IndexMotor::kTransferStartPosition = 0.0;
65/*static*/ const double IndexMotor::kIndexStartPosition = 0.2159;
66/*static*/ const double IndexMotor::kIndexFreeLength =
67 IndexMotor::ConvertDiscAngleToDiscPosition((360 * 2 + 14) * M_PI / 180);
68/*static*/ const double IndexMotor::kLoaderFreeStopPosition =
69 kIndexStartPosition + kIndexFreeLength;
Austin Schuh1b864a12013-03-07 00:46:50 -080070/*static*/ const double IndexMotor::kReadyToPreload =
71 kLoaderFreeStopPosition - ConvertDiscAngleToDiscPosition(M_PI / 6.0);
Austin Schuhf8c52252013-03-03 02:25:49 -080072/*static*/ const double IndexMotor::kReadyToLiftPosition =
73 kLoaderFreeStopPosition + 0.2921;
74/*static*/ const double IndexMotor::kGrabberLength = 0.03175;
75/*static*/ const double IndexMotor::kGrabberStartPosition =
76 kReadyToLiftPosition - kGrabberLength;
Austin Schuh6328daf2013-03-05 00:53:15 -080077/*static*/ const double IndexMotor::kGrabberMovementVelocity = 0.7;
Austin Schuhf8c52252013-03-03 02:25:49 -080078/*static*/ const double IndexMotor::kLifterStopPosition =
79 kReadyToLiftPosition + 0.161925;
80/*static*/ const double IndexMotor::kLifterMovementVelocity = 1.0;
81/*static*/ const double IndexMotor::kEjectorStopPosition =
82 kLifterStopPosition + 0.01;
83/*static*/ const double IndexMotor::kEjectorMovementVelocity = 1.0;
Austin Schuhcc297022013-03-09 23:26:40 -080084/*static*/ const double IndexMotor::kBottomDiscDetectStart = 0.00;
85/*static*/ const double IndexMotor::kBottomDiscDetectStop = 0.13;
86/*static*/ const double IndexMotor::kBottomDiscIndexDelay = 0.032;
Austin Schuhd3d0fbf2013-03-14 00:37:00 -070087/*static*/ const ::aos::time::Time IndexMotor::kTransferOffDelay =
Brian Silvermanb7bcef12013-03-16 13:57:11 -070088 ::aos::time::Time::InSeconds(0.3);
Austin Schuhf8c52252013-03-03 02:25:49 -080089
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080090// TODO(aschuh): Verify these with the sensor actually on.
Austin Schuh825bde92013-03-06 00:16:46 -080091/*static*/ const double IndexMotor::kTopDiscDetectStart =
92 (IndexMotor::kLoaderFreeStopPosition -
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080093 IndexMotor::ConvertDiscAngleToDiscPosition(49 * M_PI / 180));
Austin Schuh825bde92013-03-06 00:16:46 -080094/*static*/ const double IndexMotor::kTopDiscDetectStop =
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080095 (IndexMotor::kLoaderFreeStopPosition +
96 IndexMotor::ConvertDiscAngleToDiscPosition(19 * M_PI / 180));
97
98// I measured the angle between 2 discs. That then gives me the distance
99// between 2 posedges (or negedges). Then subtract off the width of the
100// positive pulse, and that gives the width of the negative pulse.
101/*static*/ const double IndexMotor::kTopDiscDetectMinSeperation =
102 (IndexMotor::ConvertDiscAngleToDiscPosition(120 * M_PI / 180) -
103 (IndexMotor::kTopDiscDetectStop - IndexMotor::kTopDiscDetectStart));
Austin Schuhf8c52252013-03-03 02:25:49 -0800104
Austin Schuhd78ab542013-03-01 22:22:19 -0800105const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
106const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
Austin Schuhf8c52252013-03-03 02:25:49 -0800107const /*static*/ double IndexMotor::kTransferRollerRadius = 1.25 * 0.0254 / 2;
Austin Schuhd78ab542013-03-01 22:22:19 -0800108
Austin Schuhf8c52252013-03-03 02:25:49 -0800109/*static*/ const int IndexMotor::kGrabbingDelay = 5;
Brian Silverman25329e62013-09-21 23:52:10 -0700110/*static*/ const int IndexMotor::kLiftingDelay = 2;
Brian Silvermanc540ab82013-09-22 00:00:28 -0700111/*static*/ const int IndexMotor::kLiftingTimeout = 65;
Brian Silverman25329e62013-09-21 23:52:10 -0700112/*static*/ const int IndexMotor::kShootingDelay = 10;
Brian Silverman004ec812013-09-26 15:47:58 -0700113/*static*/ const int IndexMotor::kLoweringDelay = 4;
114/*static*/ const int IndexMotor::kLoweringTimeout = 120;
Austin Schuhd78ab542013-03-01 22:22:19 -0800115
Austin Schuh93485832013-03-04 00:01:34 -0800116// TODO(aschuh): Tune these.
117/*static*/ const double
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700118 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 11.0;
Austin Schuh93485832013-03-04 00:01:34 -0800119/*static*/ const double
Austin Schuha3e8e032013-03-10 18:43:14 -0700120 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 20;
Austin Schuh93485832013-03-04 00:01:34 -0800121
Austin Schuhd78ab542013-03-01 22:22:19 -0800122/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
123 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
124}
125
Austin Schuhf8c52252013-03-03 02:25:49 -0800126/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
127 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800128 return angle * (kDiscRadius + kRollerRadius);
129}
130
Austin Schuhf8c52252013-03-03 02:25:49 -0800131/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
132 const double position) {
133 return position / (kDiscRadius + kRollerRadius);
134}
135
Austin Schuhd78ab542013-03-01 22:22:19 -0800136/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
137 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
138}
139
140/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
141 return IndexMotor::ConvertDiscAngleToDiscPosition(
142 ConvertIndexToDiscAngle(angle));
143}
144
Austin Schuhf8c52252013-03-03 02:25:49 -0800145/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
146 const double angle) {
147 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
148 kTransferRollerRadius);
149 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
150}
151
152/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
153 const double position) {
154 return IndexMotor::ConvertDiscAngleToIndex(
155 ConvertDiscPositionToDiscAngle(position));
156}
157
Austin Schuh1b864a12013-03-07 00:46:50 -0800158bool IndexMotor::MinDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800159 bool found_start = false;
160 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800161 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800162 if (!found_start) {
163 if (frisbee.has_position()) {
164 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800165 if (found_disc) {
166 *found_disc = &frisbee;
167 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800168 found_start = true;
169 }
170 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800171 if (frisbee.position() <= *disc_position) {
172 *disc_position = frisbee.position();
173 if (found_disc) {
174 *found_disc = &frisbee;
175 }
176 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800177 }
178 }
179 return found_start;
180}
181
Austin Schuh1b864a12013-03-07 00:46:50 -0800182bool IndexMotor::MaxDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800183 bool found_start = false;
184 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800185 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800186 if (!found_start) {
187 if (frisbee.has_position()) {
188 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800189 if (found_disc) {
190 *found_disc = &frisbee;
191 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800192 found_start = true;
193 }
194 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800195 if (frisbee.position() > *disc_position) {
196 *disc_position = frisbee.position();
197 if (found_disc) {
198 *found_disc = &frisbee;
199 }
200 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800201 }
202 }
203 return found_start;
204}
205
Austin Schuh93485832013-03-04 00:01:34 -0800206void IndexMotor::IndexStateFeedbackLoop::CapU() {
207 // If the voltage has been low for a large number of cycles, cut the motor
208 // power. This is generally very bad controls practice since this isn't LTI,
209 // but we don't really care about tracking anything other than large step
210 // inputs, and the loader doesn't need to be that accurate.
211 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
212 ++low_voltage_count_;
213 if (low_voltage_count_ > kNoMotionCuttoffCount) {
Austin Schuh93485832013-03-04 00:01:34 -0800214 U(0, 0) = 0.0;
215 }
216 } else {
217 low_voltage_count_ = 0;
218 }
219
220 for (int i = 0; i < kNumOutputs; ++i) {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700221 if (U(i, 0) > U_max(i, 0)) {
222 U(i, 0) = U_max(i, 0);
223 } else if (U(i, 0) < U_min(i, 0)) {
224 U(i, 0) = U_min(i, 0);
Austin Schuh93485832013-03-04 00:01:34 -0800225 }
226 }
227}
228
229
Austin Schuhd78ab542013-03-01 22:22:19 -0800230// Positive angle is towards the shooter, and positive power is towards the
231// shooter.
232void IndexMotor::RunIteration(
233 const control_loops::IndexLoop::Goal *goal,
234 const control_loops::IndexLoop::Position *position,
235 control_loops::IndexLoop::Output *output,
236 control_loops::IndexLoop::Status *status) {
Austin Schuhd3d0fbf2013-03-14 00:37:00 -0700237 Time now = Time::Now();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800238 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800239 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700240 if (goal->goal_state < 0 || goal->goal_state > 5) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700241 LOG(ERROR,
242 "Goal state is %" PRId32 " which is out of range. Going to HOLD.\n",
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800243 goal->goal_state);
244 goal_enum = Goal::HOLD;
245 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800246
247 // Disable the motors now so that all early returns will return with the
248 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800249 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800250 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800251 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800252 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800253 output->transfer_voltage = 0.0;
254 output->index_voltage = 0.0;
255 }
256
257 status->ready_to_intake = false;
258
Austin Schuhe3490622013-03-13 01:24:30 -0700259 // Set the controller to use to be the one designed for the current number of
260 // discs in the hopper. This is safe since the controller prevents the index
261 // from being set out of bounds and picks the closest controller.
262 wrist_loop_->set_controller_index(frisbees_.size());
263
Austin Schuhf8c52252013-03-03 02:25:49 -0800264 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800265 if (position) {
266 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800267 // Set the goal to be the current position if this is the first time through
268 // so we don't always spin the indexer to the 0 position before starting.
269 if (no_prior_position_) {
Brian Silverman43bb73e2013-03-17 13:39:47 -0700270 LOG(INFO, "no prior position; resetting\n");
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800271 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800272 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800273 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800274 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
275 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
276 last_bottom_disc_negedge_wait_count_ =
277 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800278 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800279 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Brian Silvermance86bac2013-03-31 19:07:24 -0700280 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800281 // The open positions for the upper is right here and isn't a hard edge.
Austin Schuh723770b2013-03-10 13:26:20 -0700282 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
283 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800284 }
285
Austin Schuh1b864a12013-03-07 00:46:50 -0800286 // If the cRIO is gone for over 1/2 of a second, assume that it rebooted.
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800287 if (missing_position_count_ > 50) {
Brian Silverman43bb73e2013-03-17 13:39:47 -0700288 LOG(INFO, "assuming cRIO rebooted\n");
Austin Schuh6328daf2013-03-05 00:53:15 -0800289 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
290 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
291 last_bottom_disc_negedge_wait_count_ =
292 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800293 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800294 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Brian Silvermance86bac2013-03-31 19:07:24 -0700295 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800296 // We can't really trust the open range any more if the crio rebooted.
Austin Schuh723770b2013-03-10 13:26:20 -0700297 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
298 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800299 // Adjust the disc positions so that they don't have to move.
300 const double disc_offset =
301 position->index_position - wrist_loop_->X_hat(0, 0);
302 for (auto frisbee = frisbees_.begin();
303 frisbee != frisbees_.end(); ++frisbee) {
304 frisbee->OffsetDisc(disc_offset);
305 }
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800306 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800307 }
308 missing_position_count_ = 0;
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000309 if (last_top_disc_detect_) {
310 if (last_top_disc_posedge_count_ != position->top_disc_posedge_count) {
311 LOG(INFO, "Ignoring a top disc posedge\n");
312 }
Brian Silvermance86bac2013-03-31 19:07:24 -0700313 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
314 }
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000315 if (!last_top_disc_detect_) {
316 if (last_top_disc_negedge_count_ != position->top_disc_negedge_count) {
317 LOG(INFO, "Ignoring a top disc negedge\n");
318 }
Brian Silvermance86bac2013-03-31 19:07:24 -0700319 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
320 }
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800321 } else {
322 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800323 }
324 const double index_position = wrist_loop_->X_hat(0, 0);
325
Austin Schuh825bde92013-03-06 00:16:46 -0800326 if (position) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800327 // Reset the open region if we saw a negedge.
Austin Schuh723770b2013-03-10 13:26:20 -0700328 if (position->bottom_disc_negedge_wait_count !=
329 last_bottom_disc_negedge_wait_count_) {
330 // Saw a negedge, must be a new region.
331 lower_open_region_.Restart(position->bottom_disc_negedge_wait_position);
332 }
333 // Reset the open region if we saw a negedge.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800334 if (position->top_disc_negedge_count != last_top_disc_negedge_count_) {
335 // Saw a negedge, must be a new region.
Austin Schuh723770b2013-03-10 13:26:20 -0700336 upper_open_region_.Restart(position->top_disc_negedge_position);
337 }
338
339 // No disc. Expand the open region.
340 if (!position->bottom_disc_detect) {
341 lower_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800342 }
343
344 // No disc. Expand the open region.
345 if (!position->top_disc_detect) {
Austin Schuh723770b2013-03-10 13:26:20 -0700346 upper_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800347 }
348
Austin Schuh825bde92013-03-06 00:16:46 -0800349 if (!position->top_disc_detect) {
350 // We don't see a disc. Verify that there are no discs that we should be
351 // seeing.
Austin Schuh1b864a12013-03-07 00:46:50 -0800352 // Assume that discs will move slow enough that we won't miss one as it
353 // goes by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800354
355 double cumulative_offset = 0.0;
356 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
357 frisbee != rend; ++frisbee) {
358 frisbee->OffsetDisc(cumulative_offset);
359 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh6b1ad2f2013-03-11 23:23:00 -0700360 wrist_loop_->X_hat(0, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800361 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800362 }
363 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800364
Austin Schuh825bde92013-03-06 00:16:46 -0800365 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
Brian Silverman967c4422013-09-29 16:58:59 -0700366 LOG(INFO, "Saw a top posedge\n");
Austin Schuh1b864a12013-03-07 00:46:50 -0800367 const double index_position = wrist_loop_->X_hat(0, 0) -
368 position->index_position + position->top_disc_posedge_position;
Austin Schuh825bde92013-03-06 00:16:46 -0800369 // TODO(aschuh): Sanity check this number...
370 // Requires storing when the disc was last seen with the sensor off, and
371 // figuring out what to do if things go south.
372
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800373 // 1 if discs are going up, 0 if we have no clue, and -1 if they are going
374 // down.
375 int disc_direction = 0;
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000376 if (wrist_loop_->X_hat(1, 0) > 100.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800377 disc_direction = 1;
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000378 } else if (wrist_loop_->X_hat(1, 0) < -100.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800379 disc_direction = -1;
380 } else {
381 // Save the upper and lower positions that we last saw a disc at.
382 // If there is a big buffer above, must be a disc from below.
383 // If there is a big buffer below, must be a disc from above.
384 // This should work to replace the velocity threshold above.
385
Austin Schuh723770b2013-03-10 13:26:20 -0700386 const double open_width = upper_open_region_.width();
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800387 const double relative_upper_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700388 (upper_open_region_.upper_bound() - index_position) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800389 const double relative_lower_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700390 (index_position - upper_open_region_.lower_bound()) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800391
392 if (ConvertIndexToDiscPosition(open_width) <
393 kTopDiscDetectMinSeperation * 0.9) {
394 LOG(ERROR, "Discs are way too close to each other. Doing nothing\n");
395 } else if (relative_upper_open_precentage > 0.75) {
396 // Looks like it is a disc going down from above since we are near
397 // the upper edge.
398 disc_direction = -1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700399 LOG(INFO, "Disc edge going down\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800400 } else if (relative_lower_open_precentage > 0.75) {
401 // Looks like it is a disc going up from below since we are near
402 // the lower edge.
403 disc_direction = 1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700404 LOG(INFO, "Disc edge going up\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800405 } else {
406 LOG(ERROR,
407 "Got an edge in the middle of what should be an open region.\n");
408 LOG(ERROR, "Open width: %f upper precentage %f %%\n",
409 open_width, relative_upper_open_precentage);
410 }
411 }
412
413 if (disc_direction > 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800414 // Moving up at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800415 // Find the highest disc that is below the top disc sensor.
416 // While we are at it, count the number above and log an error if there
417 // are too many.
418 if (frisbees_.size() == 0) {
419 Frisbee new_frisbee;
420 new_frisbee.has_been_indexed_ = true;
421 new_frisbee.index_start_position_ = index_position -
422 ConvertDiscPositionToIndex(kTopDiscDetectStart -
423 kIndexStartPosition);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800424 ++hopper_disc_count_;
425 ++total_disc_count_;
Austin Schuh1b864a12013-03-07 00:46:50 -0800426 frisbees_.push_front(new_frisbee);
427 LOG(WARNING, "Added a disc to the hopper at the top sensor\n");
428 }
429
430 int above_disc_count = 0;
431 double highest_position = 0;
432 Frisbee *highest_frisbee_below_sensor = NULL;
433 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
434 frisbee != rend; ++frisbee) {
435 const double disc_position = frisbee->absolute_position(
436 index_position);
437 // It is save to use the top position for the cuttoff, since the
438 // sensor being low will result in discs being pushed off of it.
439 if (disc_position >= kTopDiscDetectStop) {
440 ++above_disc_count;
441 } else if (!highest_frisbee_below_sensor ||
442 disc_position > highest_position) {
443 highest_frisbee_below_sensor = &*frisbee;
444 highest_position = disc_position;
445 }
446 }
Austin Schuh09e07082013-03-19 10:04:12 +0000447
448 if (!highest_frisbee_below_sensor) {
449 Frisbee new_frisbee;
450 new_frisbee.has_been_indexed_ = true;
451 new_frisbee.index_start_position_ = index_position -
452 ConvertDiscPositionToIndex(kTopDiscDetectStart -
453 kIndexStartPosition);
454 highest_position = kTopDiscDetectStart;
455 ++hopper_disc_count_;
456 ++total_disc_count_;
457 frisbees_.push_front(new_frisbee);
458 LOG(WARNING, "Added a disc to the hopper at the top sensor because the one we know about is up top\n");
459 }
460
Austin Schuh1b864a12013-03-07 00:46:50 -0800461 if (above_disc_count > 1) {
462 LOG(ERROR, "We have 2 discs above the top sensor.\n");
463 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800464 // We now have the disc. Shift all the ones below the sensor up by the
465 // computed delta.
466 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
467 highest_position - kTopDiscDetectStart);
468 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
469 frisbee != rend; ++frisbee) {
470 const double disc_position = frisbee->absolute_position(
471 index_position);
472 if (disc_position < kTopDiscDetectStop) {
Austin Schuh59004d32013-03-21 04:31:45 +0000473 LOG(INFO, "Moving disc down by %f meters, since it is at %f and top is [%f, %f]\n",
474 ConvertIndexToDiscPosition(disc_delta),
475 disc_position, kTopDiscDetectStart,
476 kTopDiscDetectStop);
Austin Schuh1b864a12013-03-07 00:46:50 -0800477 frisbee->OffsetDisc(disc_delta);
478 }
479 }
Brian Silverman43bb73e2013-03-17 13:39:47 -0700480 if (highest_frisbee_below_sensor) {
481 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
482 "Moving down by %f to %f\n", frisbees_.size(),
483 ConvertIndexToDiscPosition(disc_delta),
484 highest_frisbee_below_sensor->absolute_position(
485 wrist_loop_->X_hat(0, 0)));
486 } else {
487 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
488 "Moving down by %f\n", frisbees_.size(),
489 ConvertIndexToDiscPosition(disc_delta));
490 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800491 } else if (disc_direction < 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800492 // Moving down at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800493 // There can only be 1 disc up top that would give us a posedge.
494 // Find it and place it at the one spot that it can be.
Brian Silverman94195052013-03-09 13:45:05 -0800495 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800496 Frisbee *min_frisbee = NULL;
497 MinDiscPosition(&min_disc_position, &min_frisbee);
498 if (!min_frisbee) {
499 // Uh, oh, we see a disc but there isn't one...
500 LOG(ERROR, "Saw a disc up top but there isn't one in the hopper\n");
501 } else {
502 const double disc_position = min_frisbee->absolute_position(
503 index_position);
504
505 const double disc_delta_meters = disc_position - kTopDiscDetectStop;
506 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
507 disc_delta_meters);
Austin Schuha3e8e032013-03-10 18:43:14 -0700508 LOG(INFO, "Posedge going down. Moving top disc down by %f\n",
509 disc_delta_meters);
Austin Schuh1b864a12013-03-07 00:46:50 -0800510 for (auto frisbee = frisbees_.begin(), end = frisbees_.end();
511 frisbee != end; ++frisbee) {
512 frisbee->OffsetDisc(disc_delta);
513 }
514 }
Austin Schuh825bde92013-03-06 00:16:46 -0800515 } else {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800516 LOG(ERROR, "Not sure how to handle the upper posedge, doing nothing\n");
Austin Schuh825bde92013-03-06 00:16:46 -0800517 }
518 }
519 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800520
Austin Schuhf8c52252013-03-03 02:25:49 -0800521 // Bool to track if it is safe for the goal to change yet.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700522 bool safe_to_change_state = true;
Brian Silvermanf0716e82013-03-17 23:36:11 -0700523 if (!position) {
524 // This fixes a nasty indexer bug.
525 // If we didn't get a position this cycle, we don't run the code below which
526 // checks the state of the disc detect sensor and whether all the discs are
527 // indexed. It is therefore not safe to change state and loose track of
528 // that disc.
529 safe_to_change_state = false;
530 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800531 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800532 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800533 // The goal should already be good, so sit tight with everything the same
534 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800535 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800536 case Goal::READY_LOWER:
537 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800538 {
Austin Schuhd78ab542013-03-01 22:22:19 -0800539 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800540 // Posedge of the disc entering the beam break.
541 if (position->bottom_disc_posedge_count !=
542 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800543 transfer_frisbee_.Reset();
544 transfer_frisbee_.bottom_posedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700545 LOG(INFO, "Posedge of bottom disc %f\n",
546 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800547 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800548 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800549 }
550
551 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800552 if (position->bottom_disc_negedge_count !=
553 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800554 transfer_frisbee_.bottom_negedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700555 LOG(INFO, "Negedge of bottom disc %f\n",
556 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800557 frisbees_.push_front(transfer_frisbee_);
558 }
559
560 if (position->bottom_disc_detect) {
Brian Silvermanc5277542013-03-22 13:33:07 -0700561 intake_voltage = 0.0;
562 transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800563 // Must wait until the disc gets out before we can change state.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700564 safe_to_change_state = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800565
Austin Schuhf8c52252013-03-03 02:25:49 -0800566 // TODO(aschuh): A disc on the way through needs to start moving
567 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800568
569 Time elapsed_posedge_time = now -
570 transfer_frisbee_.bottom_posedge_time_;
571 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
572 // It has been too long. The disc must be jammed.
573 LOG(ERROR, "Been way too long. Jammed disc?\n");
Brian Silvermance86bac2013-03-31 19:07:24 -0700574 intake_voltage = -12.0;
Brian Silvermanf0716e82013-03-17 23:36:11 -0700575 transfer_voltage = -12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800576 }
577 }
578
Austin Schuhf8c52252013-03-03 02:25:49 -0800579 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800580 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800581 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800582 if (!frisbee->has_been_indexed_) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800583 if (last_bottom_disc_negedge_wait_count_ !=
584 position->bottom_disc_negedge_wait_count) {
585 // We have an index difference.
586 // Save the indexer position, and the time.
587 if (last_bottom_disc_negedge_wait_count_ + 1 !=
588 position->bottom_disc_negedge_wait_count) {
589 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
590 }
591
592 // Save the captured position as the position at which the disc
593 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800594 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800595 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800596 frisbee->index_start_position_ =
597 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800598 }
599 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800600 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800601 }
Austin Schuh59004d32013-03-21 04:31:45 +0000602 for (auto frisbee = frisbees_.begin();
603 frisbee != frisbees_.end(); ++frisbee) {
604 if (!frisbee->has_been_indexed_) {
Brian Silvermanc5277542013-03-22 13:33:07 -0700605 intake_voltage = 0.0;
606 transfer_voltage = 12.0;
Austin Schuh59004d32013-03-21 04:31:45 +0000607
608 // All discs must be indexed before it is safe to stop indexing.
609 safe_to_change_state = false;
610 }
611 }
612
613 // Figure out where the indexer should be to move the discs down to
614 // the right position.
615 double max_disc_position = 0;
616 if (MaxDiscPosition(&max_disc_position, NULL)) {
617 LOG(DEBUG, "There is a disc down here!\n");
618 // TODO(aschuh): Figure out what to do if grabbing the next one
619 // would cause things to jam into the loader.
620 // Say we aren't ready any more. Undefined behavior will result if
621 // that isn't observed.
622 double bottom_disc_position =
623 max_disc_position + ConvertDiscAngleToIndex(M_PI);
624 wrist_loop_->R << bottom_disc_position, 0.0;
625
626 // Verify that we are close enough to the goal so that we should be
627 // fine accepting the next disc.
628 double disc_error_meters = ConvertIndexToDiscPosition(
629 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
630 // We are ready for the next disc if the first one is in the first
631 // half circle of the indexer. It will take time for the disc to
632 // come into the indexer, so we will be able to move it out of the
633 // way in time.
634 // This choice also makes sure that we don't claim that we aren't
635 // ready between full speed intaking.
636 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
637 disc_error_meters < 0.04) {
638 // We are only ready if we aren't being asked to change state or
639 // are full.
640 status->ready_to_intake =
641 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
642 } else {
643 status->ready_to_intake = false;
644 }
645 } else {
646 // No discs! We are always ready for more if we aren't being
647 // asked to change state.
648 status->ready_to_intake = (safe_goal_ == goal_enum);
649 }
650
651 // Turn on the transfer roller if we are ready.
652 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
653 safe_goal_ == Goal::INTAKE) {
654 intake_voltage = transfer_voltage = 12.0;
655 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800656 }
Austin Schuh59004d32013-03-21 04:31:45 +0000657 LOG(DEBUG, "INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800658 break;
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700659 case Goal::REINITIALIZE:
660 LOG(WARNING, "Reinitializing the indexer\n");
661 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800662 case Goal::READY_SHOOTER:
663 case Goal::SHOOT:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700664 // Don't let us leave the shoot or preload state if there are 4 discs in
665 // the hopper.
666 if (hopper_disc_count_ >= 4 && goal_enum != Goal::SHOOT) {
667 safe_to_change_state = false;
668 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800669 // Check if we have any discs to shoot or load and handle them.
Brian Silverman94195052013-03-09 13:45:05 -0800670 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800671 if (MinDiscPosition(&min_disc_position, NULL)) {
672 const double ready_disc_position = min_disc_position +
673 ConvertDiscPositionToIndex(kReadyToPreload - kIndexStartPosition);
Austin Schuhf8c52252013-03-03 02:25:49 -0800674
675 const double grabbed_disc_position =
676 min_disc_position +
677 ConvertDiscPositionToIndex(kReadyToLiftPosition -
Austin Schuh59004d32013-03-21 04:31:45 +0000678 kIndexStartPosition + 0.07);
Austin Schuhf8c52252013-03-03 02:25:49 -0800679
680 // Check the state of the loader FSM.
681 // If it is ready to load discs, position the disc so that it is ready
682 // to be grabbed.
683 // If it isn't ready, there is a disc in there. It needs to finish it's
684 // cycle first.
685 if (loader_state_ != LoaderState::READY) {
686 // We already have a disc in the loader.
687 // Stage the discs back a bit.
688 wrist_loop_->R << ready_disc_position, 0.0;
689
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800690 // Shoot if we are grabbed and being asked to shoot.
691 if (loader_state_ == LoaderState::GRABBED &&
692 safe_goal_ == Goal::SHOOT) {
693 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
Brian Silverman7ffb1382013-09-29 16:55:12 -0700694 is_shooting_ = true;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800695 }
696
Austin Schuhf8c52252013-03-03 02:25:49 -0800697 // Must wait until it has been grabbed to continue.
698 if (loader_state_ == LoaderState::GRABBING) {
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700699 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800700 }
701 } else {
702 // No disc up top right now.
703 wrist_loop_->R << grabbed_disc_position, 0.0;
704
705 // See if the disc has gotten pretty far up yet.
706 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
707 // Point of no return. We are committing to grabbing it now.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700708 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800709 const double robust_grabbed_disc_position =
710 (grabbed_disc_position -
711 ConvertDiscPositionToIndex(kGrabberLength));
712
713 // If close, start grabbing and/or shooting.
714 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
715 // Start the state machine.
716 if (safe_goal_ == Goal::SHOOT) {
717 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
718 } else {
719 loader_goal_ = LoaderGoal::GRAB;
720 }
721 // This frisbee is now gone. Take it out of the queue.
722 frisbees_.pop_back();
Austin Schuhf8c52252013-03-03 02:25:49 -0800723 }
724 }
725 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800726 } else {
727 if (loader_state_ != LoaderState::READY) {
728 // Shoot if we are grabbed and being asked to shoot.
729 if (loader_state_ == LoaderState::GRABBED &&
730 safe_goal_ == Goal::SHOOT) {
731 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
732 }
733 } else {
734 // Ok, no discs in sight. Spin the hopper up by 150% of it's full
735 // range and verify that we don't see anything.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800736 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700737 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700738 lower_open_region_.lower_bound()) +
Austin Schuhf60861b2013-03-14 00:14:10 -0700739 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800740
741 wrist_loop_->R << hopper_clear_verification_position, 0.0;
742 if (::std::abs(wrist_loop_->X_hat(0, 0) -
743 hopper_clear_verification_position) <
744 ConvertDiscPositionToIndex(0.05)) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800745 // We are at the end of the range. There are no more discs here.
746 while (frisbees_.size() > 0) {
747 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700748 LOG(ERROR, "Upper is [%f %f]\n",
749 upper_open_region_.upper_bound(),
750 upper_open_region_.lower_bound());
751 LOG(ERROR, "Lower is [%f %f]\n",
752 lower_open_region_.upper_bound(),
753 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800754 frisbees_.pop_back();
755 --hopper_disc_count_;
756 --total_disc_count_;
757 }
758 if (hopper_disc_count_ != 0) {
759 LOG(ERROR,
760 "Emptied the hopper out but there are still discs there\n");
Brian Silvermanf0716e82013-03-17 23:36:11 -0700761 hopper_disc_count_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800762 }
763 }
764 }
765 }
766
767 {
768 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700769 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700770 lower_open_region_.lower_bound()) +
Austin Schuhf60861b2013-03-14 00:14:10 -0700771 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800772
773 if (wrist_loop_->X_hat(0, 0) >
774 hopper_clear_verification_position +
775 ConvertDiscPositionToIndex(0.05)) {
776 // We are at the end of the range. There are no more discs here.
777 while (frisbees_.size() > 0) {
778 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuha3e8e032013-03-10 18:43:14 -0700779 LOG(ERROR, "Upper is [%f %f]\n",
780 upper_open_region_.upper_bound(),
781 upper_open_region_.lower_bound());
782 LOG(ERROR, "Lower is [%f %f]\n",
783 lower_open_region_.upper_bound(),
784 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800785 frisbees_.pop_back();
786 --hopper_disc_count_;
787 --total_disc_count_;
788 }
Brian Silvermanf0716e82013-03-17 23:36:11 -0700789 if (hopper_disc_count_ != 0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800790 LOG(ERROR,
Brian Silverman8efe23e2013-07-07 23:31:37 -0700791 "Emptied the hopper out but there are still %" PRId32 " discs there\n",
Austin Schuhf60861b2013-03-14 00:14:10 -0700792 hopper_disc_count_);
Brian Silvermanf0716e82013-03-17 23:36:11 -0700793 hopper_disc_count_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800794 }
795 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800796 }
797
Austin Schuha3e8e032013-03-10 18:43:14 -0700798 LOG(DEBUG, "READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800799 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800800 }
801
Austin Schuhd3d0fbf2013-03-14 00:37:00 -0700802 // Wait for a period of time to make sure that the disc gets sucked
803 // in properly. We need to do this regardless of what the indexer is doing.
804 for (auto frisbee = frisbees_.begin();
805 frisbee != frisbees_.end(); ++frisbee) {
806 if (now - frisbee->bottom_negedge_time_ < kTransferOffDelay) {
807 transfer_voltage = 12.0;
808 }
809 }
810
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700811 // If we have 4 discs, it is time to preload.
812 if (safe_to_change_state && hopper_disc_count_ >= 4) {
813 switch (safe_goal_) {
814 case Goal::HOLD:
815 case Goal::READY_LOWER:
816 case Goal::INTAKE:
817 safe_goal_ = Goal::READY_SHOOTER;
818 safe_to_change_state = false;
Brian Silverman8efe23e2013-07-07 23:31:37 -0700819 LOG(INFO, "We have %" PRId32 " discs, time to preload automatically\n",
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700820 hopper_disc_count_);
821 break;
822 case Goal::READY_SHOOTER:
823 case Goal::SHOOT:
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700824 case Goal::REINITIALIZE:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700825 break;
826 }
827 }
828
Austin Schuhf8c52252013-03-03 02:25:49 -0800829 // The only way out of the loader is to shoot the disc. The FSM can only go
830 // forwards.
831 switch (loader_state_) {
832 case LoaderState::READY:
Austin Schuha3e8e032013-03-10 18:43:14 -0700833 LOG(DEBUG, "Loader READY\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800834 // Open and down, ready to accept a disc.
835 loader_up_ = false;
836 disc_clamped_ = false;
837 disc_ejected_ = false;
Brian Silverman967c4422013-09-29 16:58:59 -0700838 disk_stuck_in_loader_ = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800839 if (loader_goal_ == LoaderGoal::GRAB ||
Brian Silverman9a1081b2013-04-05 13:50:44 -0700840 loader_goal_ == LoaderGoal::SHOOT_AND_RESET || goal->force_fire) {
841 if (goal->force_fire) {
842 LOG(INFO, "Told to force fire, moving on\n");
843 } else if (loader_goal_ == LoaderGoal::GRAB) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700844 LOG(INFO, "Told to GRAB, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800845 } else {
Austin Schuha3e8e032013-03-10 18:43:14 -0700846 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800847 }
848 loader_state_ = LoaderState::GRABBING;
849 loader_countdown_ = kGrabbingDelay;
850 } else {
851 break;
852 }
853 case LoaderState::GRABBING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700854 LOG(DEBUG, "Loader GRABBING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800855 // Closing the grabber.
856 loader_up_ = false;
857 disc_clamped_ = true;
858 disc_ejected_ = false;
859 if (loader_countdown_ > 0) {
860 --loader_countdown_;
861 break;
862 } else {
863 loader_state_ = LoaderState::GRABBED;
864 }
865 case LoaderState::GRABBED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700866 LOG(DEBUG, "Loader GRABBED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800867 // Grabber closed.
868 loader_up_ = false;
869 disc_clamped_ = true;
870 disc_ejected_ = false;
Brian Silverman9a1081b2013-04-05 13:50:44 -0700871 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET || goal->force_fire) {
Brian Silverman967c4422013-09-29 16:58:59 -0700872 if (shooter.status.FetchLatest() || shooter.status.get()) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700873 // TODO(aschuh): If we aren't shooting nicely, wait until the shooter
874 // is up to speed rather than just spinning.
Austin Schuhf60861b2013-03-14 00:14:10 -0700875 if (shooter.status->average_velocity > 130 && shooter.status->ready) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700876 loader_state_ = LoaderState::LIFTING;
877 loader_countdown_ = kLiftingDelay;
Brian Silvermanc540ab82013-09-22 00:00:28 -0700878 loader_timeout_ = 0;
Austin Schuha3e8e032013-03-10 18:43:14 -0700879 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
880 } else {
881 LOG(WARNING, "Told to SHOOT_AND_RESET, shooter too slow at %f\n",
882 shooter.status->average_velocity);
883 break;
884 }
885 } else {
886 LOG(ERROR, "Told to SHOOT_AND_RESET, no shooter data, moving on.\n");
887 loader_state_ = LoaderState::LIFTING;
888 loader_countdown_ = kLiftingDelay;
Brian Silvermanc540ab82013-09-22 00:00:28 -0700889 loader_timeout_ = 0;
Austin Schuha3e8e032013-03-10 18:43:14 -0700890 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800891 } else if (loader_goal_ == LoaderGoal::READY) {
892 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800893 break;
894 } else {
895 break;
896 }
897 case LoaderState::LIFTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700898 LOG(DEBUG, "Loader LIFTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800899 // Lifting the disc.
900 loader_up_ = true;
901 disc_clamped_ = true;
902 disc_ejected_ = false;
Brian Silverman25329e62013-09-21 23:52:10 -0700903 if (position->loader_top) {
904 if (loader_countdown_ > 0) {
905 --loader_countdown_;
Brian Silverman004ec812013-09-26 15:47:58 -0700906 loader_timeout_ = 0;
907 break;
Brian Silverman25329e62013-09-21 23:52:10 -0700908 } else {
909 loader_state_ = LoaderState::LIFTED;
910 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800911 } else {
Brian Silverman25329e62013-09-21 23:52:10 -0700912 // Restart the countdown if it bounces back down or whatever.
913 loader_countdown_ = kLiftingDelay;
Brian Silvermanc540ab82013-09-22 00:00:28 -0700914 ++loader_timeout_;
915 if (loader_timeout_ > kLiftingTimeout) {
916 LOG(ERROR, "Loader timeout while LIFTING %d\n", loader_timeout_);
917 loader_state_ = LoaderState::LIFTED;
Brian Silverman967c4422013-09-29 16:58:59 -0700918 disk_stuck_in_loader_ = true;
Brian Silverman004ec812013-09-26 15:47:58 -0700919 } else {
920 break;
Brian Silvermanc540ab82013-09-22 00:00:28 -0700921 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800922 }
923 case LoaderState::LIFTED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700924 LOG(DEBUG, "Loader LIFTED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800925 // Disc lifted. Time to eject it out.
926 loader_up_ = true;
927 disc_clamped_ = true;
928 disc_ejected_ = false;
929 loader_state_ = LoaderState::SHOOTING;
930 loader_countdown_ = kShootingDelay;
931 case LoaderState::SHOOTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700932 LOG(DEBUG, "Loader SHOOTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800933 // Ejecting the disc into the shooter.
934 loader_up_ = true;
935 disc_clamped_ = false;
936 disc_ejected_ = true;
937 if (loader_countdown_ > 0) {
938 --loader_countdown_;
939 break;
940 } else {
941 loader_state_ = LoaderState::SHOOT;
942 }
943 case LoaderState::SHOOT:
Austin Schuha3e8e032013-03-10 18:43:14 -0700944 LOG(DEBUG, "Loader SHOOT\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800945 // The disc has been shot.
946 loader_up_ = true;
947 disc_clamped_ = false;
948 disc_ejected_ = true;
949 loader_state_ = LoaderState::LOWERING;
950 loader_countdown_ = kLoweringDelay;
Brian Silverman004ec812013-09-26 15:47:58 -0700951 loader_timeout_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800952 --hopper_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700953 ++shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800954 case LoaderState::LOWERING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700955 LOG(DEBUG, "Loader LOWERING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800956 // Lowering the loader back down.
957 loader_up_ = false;
958 disc_clamped_ = false;
959 disc_ejected_ = true;
Brian Silverman004ec812013-09-26 15:47:58 -0700960 if (position->loader_bottom) {
961 if (loader_countdown_ > 0) {
962 --loader_countdown_;
963 loader_timeout_ = 0;
964 break;
965 } else {
966 loader_state_ = LoaderState::LOWERED;
967 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800968 } else {
Brian Silverman004ec812013-09-26 15:47:58 -0700969 // Restart the countdown if it bounces back up or something.
970 loader_countdown_ = kLoweringDelay;
971 ++loader_timeout_;
972 if (loader_timeout_ > kLoweringTimeout) {
973 LOG(ERROR, "Loader timeout while LOWERING %d\n", loader_timeout_);
974 loader_state_ = LoaderState::LOWERED;
Brian Silverman967c4422013-09-29 16:58:59 -0700975 disk_stuck_in_loader_ = true;
Brian Silverman004ec812013-09-26 15:47:58 -0700976 } else {
977 break;
978 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800979 }
980 case LoaderState::LOWERED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700981 LOG(DEBUG, "Loader LOWERED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800982 loader_up_ = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800983 disc_ejected_ = false;
Brian Silverman7ffb1382013-09-29 16:55:12 -0700984 is_shooting_ = false;
Brian Silverman967c4422013-09-29 16:58:59 -0700985 if (disk_stuck_in_loader_) {
986 disk_stuck_in_loader_ = false;
987 disc_clamped_ = true;
988 loader_state_ = LoaderState::GRABBED;
989 } else {
990 disc_clamped_ = false;
991 loader_state_ = LoaderState::READY;
992 // Once we have shot, we need to hang out in READY until otherwise
993 // notified.
994 loader_goal_ = LoaderGoal::READY;
995 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800996 break;
997 }
998
999 // Update the observer.
1000 wrist_loop_->Update(position != NULL, output == NULL);
1001
1002 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -08001003 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -08001004 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -08001005 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -08001006 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
1007 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
1008 last_bottom_disc_negedge_wait_count_ =
1009 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -08001010 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -08001011 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -08001012 }
1013
Brian Silvermanb8d389f2013-03-19 22:54:06 -07001014 // Clear everything if we are supposed to re-initialize.
1015 if (goal_enum == Goal::REINITIALIZE) {
1016 safe_goal_ = Goal::REINITIALIZE;
1017 no_prior_position_ = true;
1018 hopper_disc_count_ = 0;
1019 total_disc_count_ = 0;
1020 shot_disc_count_ = 0;
1021 loader_state_ = LoaderState::READY;
1022 loader_goal_ = LoaderGoal::READY;
1023 loader_countdown_ = 0;
1024 loader_up_ = false;
1025 disc_clamped_ = false;
1026 disc_ejected_ = false;
1027
1028 intake_voltage = 0.0;
1029 transfer_voltage = 0.0;
1030 wrist_loop_->U(0, 0) = 0.0;
1031 frisbees_.clear();
1032 }
1033
Austin Schuhd78ab542013-03-01 22:22:19 -08001034 status->hopper_disc_count = hopper_disc_count_;
1035 status->total_disc_count = total_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -07001036 status->shot_disc_count = shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -08001037 status->preloaded = (loader_state_ != LoaderState::READY);
Brian Silverman7ffb1382013-09-29 16:55:12 -07001038 status->is_shooting = is_shooting_;
Austin Schuhd78ab542013-03-01 22:22:19 -08001039
1040 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -08001041 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -08001042 output->transfer_voltage = transfer_voltage;
Brian Silverman180e2b82013-04-08 14:29:56 -07001043 if (goal->override_index) {
1044 output->index_voltage = goal->index_voltage;
1045 } else {
1046 output->index_voltage = wrist_loop_->U(0, 0);
1047 }
Austin Schuhf8c52252013-03-03 02:25:49 -08001048 output->loader_up = loader_up_;
1049 output->disc_clamped = disc_clamped_;
1050 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -08001051 }
1052
Austin Schuhae5fc8c2013-03-11 23:23:28 -07001053 if (safe_to_change_state) {
Austin Schuhd78ab542013-03-01 22:22:19 -08001054 safe_goal_ = goal_enum;
1055 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -08001056 if (hopper_disc_count_ < 0) {
1057 LOG(ERROR, "NEGATIVE DISCS. VERY VERY BAD\n");
1058 }
Austin Schuhd78ab542013-03-01 22:22:19 -08001059}
1060
1061} // namespace control_loops
1062} // namespace frc971