blob: 9ecc1da97b1a7077197f19c9a96ebc61c8ccc0ec [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
9#include "aos/common/messages/RobotState.q.h"
10#include "aos/common/control_loop/control_loops.q.h"
11#include "aos/common/logging/logging.h"
Brian Silverman94195052013-03-09 13:45:05 -080012#include "aos/common/inttypes.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080013
14#include "frc971/constants.h"
Austin Schuh00558222013-03-03 14:16:16 -080015#include "frc971/control_loops/index/index_motor_plant.h"
Austin Schuha3e8e032013-03-10 18:43:14 -070016#include "frc971/control_loops/shooter/shooter_motor.q.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080017
18using ::aos::time::Time;
19
20namespace frc971 {
21namespace control_loops {
22
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070023double IndexMotor::Frisbee::ObserveNoTopDiscSensor(double index_position) {
Austin Schuhdff24e22013-03-06 00:41:21 -080024 // The absolute disc position in meters.
Austin Schuh1b864a12013-03-07 00:46:50 -080025 double disc_position = absolute_position(index_position);
Austin Schuh825bde92013-03-06 00:16:46 -080026 if (IndexMotor::kTopDiscDetectStart <= disc_position &&
27 disc_position <= IndexMotor::kTopDiscDetectStop) {
28 // Whoops, this shouldn't be happening.
29 // Move the disc off the way that makes most sense.
Austin Schuhdff24e22013-03-06 00:41:21 -080030 double distance_to_above = IndexMotor::ConvertDiscPositionToIndex(
31 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStop));
32 double distance_to_below = IndexMotor::ConvertDiscPositionToIndex(
33 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStart));
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070034 if (distance_to_above < distance_to_below) {
35 LOG(INFO, "Moving disc to top slow.\n");
36 // Move it up.
37 index_start_position_ -= distance_to_above;
38 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080039 } else {
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070040 LOG(INFO, "Moving disc to bottom slow.\n");
41 index_start_position_ += distance_to_below;
42 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080043 }
44 }
Austin Schuhdff24e22013-03-06 00:41:21 -080045 return 0.0;
Austin Schuh825bde92013-03-06 00:16:46 -080046}
47
Austin Schuhd78ab542013-03-01 22:22:19 -080048IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
49 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
Austin Schuh93485832013-03-04 00:01:34 -080050 wrist_loop_(new IndexStateFeedbackLoop(MakeIndexLoop())),
Austin Schuhd78ab542013-03-01 22:22:19 -080051 hopper_disc_count_(0),
52 total_disc_count_(0),
Austin Schuh70be1ba2013-03-10 13:37:17 -070053 shot_disc_count_(0),
Austin Schuhf8c52252013-03-03 02:25:49 -080054 safe_goal_(Goal::HOLD),
55 loader_goal_(LoaderGoal::READY),
56 loader_state_(LoaderState::READY),
Austin Schuhd78ab542013-03-01 22:22:19 -080057 loader_up_(false),
58 disc_clamped_(false),
59 disc_ejected_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080060 last_bottom_disc_detect_(false),
Austin Schuh825bde92013-03-06 00:16:46 -080061 last_top_disc_detect_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080062 no_prior_position_(true),
Austin Schuh723770b2013-03-10 13:26:20 -070063 missing_position_count_(0) {
Austin Schuhd78ab542013-03-01 22:22:19 -080064}
65
Austin Schuhf8c52252013-03-03 02:25:49 -080066/*static*/ const double IndexMotor::kTransferStartPosition = 0.0;
67/*static*/ const double IndexMotor::kIndexStartPosition = 0.2159;
68/*static*/ const double IndexMotor::kIndexFreeLength =
69 IndexMotor::ConvertDiscAngleToDiscPosition((360 * 2 + 14) * M_PI / 180);
70/*static*/ const double IndexMotor::kLoaderFreeStopPosition =
71 kIndexStartPosition + kIndexFreeLength;
Austin Schuh1b864a12013-03-07 00:46:50 -080072/*static*/ const double IndexMotor::kReadyToPreload =
73 kLoaderFreeStopPosition - ConvertDiscAngleToDiscPosition(M_PI / 6.0);
Austin Schuhf8c52252013-03-03 02:25:49 -080074/*static*/ const double IndexMotor::kReadyToLiftPosition =
75 kLoaderFreeStopPosition + 0.2921;
76/*static*/ const double IndexMotor::kGrabberLength = 0.03175;
77/*static*/ const double IndexMotor::kGrabberStartPosition =
78 kReadyToLiftPosition - kGrabberLength;
Austin Schuh6328daf2013-03-05 00:53:15 -080079/*static*/ const double IndexMotor::kGrabberMovementVelocity = 0.7;
Austin Schuhf8c52252013-03-03 02:25:49 -080080/*static*/ const double IndexMotor::kLifterStopPosition =
81 kReadyToLiftPosition + 0.161925;
82/*static*/ const double IndexMotor::kLifterMovementVelocity = 1.0;
83/*static*/ const double IndexMotor::kEjectorStopPosition =
84 kLifterStopPosition + 0.01;
85/*static*/ const double IndexMotor::kEjectorMovementVelocity = 1.0;
Austin Schuhcc297022013-03-09 23:26:40 -080086/*static*/ const double IndexMotor::kBottomDiscDetectStart = 0.00;
87/*static*/ const double IndexMotor::kBottomDiscDetectStop = 0.13;
88/*static*/ const double IndexMotor::kBottomDiscIndexDelay = 0.032;
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;
Austin Schuha3e8e032013-03-10 18:43:14 -0700110/*static*/ const int IndexMotor::kLiftingDelay = 30;
Austin Schuhf8c52252013-03-03 02:25:49 -0800111/*static*/ const int IndexMotor::kShootingDelay = 5;
112/*static*/ const int IndexMotor::kLoweringDelay = 20;
Austin Schuhd78ab542013-03-01 22:22:19 -0800113
Austin Schuh93485832013-03-04 00:01:34 -0800114// TODO(aschuh): Tune these.
115/*static*/ const double
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700116 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 11.0;
Austin Schuh93485832013-03-04 00:01:34 -0800117/*static*/ const double
Austin Schuha3e8e032013-03-10 18:43:14 -0700118 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 20;
Austin Schuh93485832013-03-04 00:01:34 -0800119
Austin Schuhd78ab542013-03-01 22:22:19 -0800120/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
121 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
122}
123
Austin Schuhf8c52252013-03-03 02:25:49 -0800124/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
125 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800126 return angle * (kDiscRadius + kRollerRadius);
127}
128
Austin Schuhf8c52252013-03-03 02:25:49 -0800129/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
130 const double position) {
131 return position / (kDiscRadius + kRollerRadius);
132}
133
Austin Schuhd78ab542013-03-01 22:22:19 -0800134/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
135 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
136}
137
138/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
139 return IndexMotor::ConvertDiscAngleToDiscPosition(
140 ConvertIndexToDiscAngle(angle));
141}
142
Austin Schuhf8c52252013-03-03 02:25:49 -0800143/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
144 const double angle) {
145 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
146 kTransferRollerRadius);
147 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
148}
149
150/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
151 const double position) {
152 return IndexMotor::ConvertDiscAngleToIndex(
153 ConvertDiscPositionToDiscAngle(position));
154}
155
Austin Schuh1b864a12013-03-07 00:46:50 -0800156bool IndexMotor::MinDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800157 bool found_start = false;
158 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800159 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800160 if (!found_start) {
161 if (frisbee.has_position()) {
162 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800163 if (found_disc) {
164 *found_disc = &frisbee;
165 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800166 found_start = true;
167 }
168 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800169 if (frisbee.position() <= *disc_position) {
170 *disc_position = frisbee.position();
171 if (found_disc) {
172 *found_disc = &frisbee;
173 }
174 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800175 }
176 }
177 return found_start;
178}
179
Austin Schuh1b864a12013-03-07 00:46:50 -0800180bool IndexMotor::MaxDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800181 bool found_start = false;
182 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800183 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800184 if (!found_start) {
185 if (frisbee.has_position()) {
186 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800187 if (found_disc) {
188 *found_disc = &frisbee;
189 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800190 found_start = true;
191 }
192 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800193 if (frisbee.position() > *disc_position) {
194 *disc_position = frisbee.position();
195 if (found_disc) {
196 *found_disc = &frisbee;
197 }
198 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800199 }
200 }
201 return found_start;
202}
203
Austin Schuh93485832013-03-04 00:01:34 -0800204void IndexMotor::IndexStateFeedbackLoop::CapU() {
205 // If the voltage has been low for a large number of cycles, cut the motor
206 // power. This is generally very bad controls practice since this isn't LTI,
207 // but we don't really care about tracking anything other than large step
208 // inputs, and the loader doesn't need to be that accurate.
209 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
210 ++low_voltage_count_;
211 if (low_voltage_count_ > kNoMotionCuttoffCount) {
212 printf("Limiting power from %f to 0\n", U(0, 0));
213 U(0, 0) = 0.0;
214 }
215 } else {
216 low_voltage_count_ = 0;
217 }
218
219 for (int i = 0; i < kNumOutputs; ++i) {
220 if (U[i] > plant.U_max[i]) {
221 U[i] = plant.U_max[i];
222 } else if (U[i] < plant.U_min[i]) {
223 U[i] = plant.U_min[i];
224 }
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 Schuhbcdb90c2013-03-03 23:24:58 -0800236 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800237 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800238 if (goal->goal_state < 0 || goal->goal_state > 4) {
Brian Silverman94195052013-03-09 13:45:05 -0800239 LOG(ERROR, "Goal state is %"PRId32" which is out of range. Going to HOLD.\n",
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800240 goal->goal_state);
241 goal_enum = Goal::HOLD;
242 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800243
244 // Disable the motors now so that all early returns will return with the
245 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800246 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800247 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800248 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800249 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800250 output->transfer_voltage = 0.0;
251 output->index_voltage = 0.0;
252 }
253
254 status->ready_to_intake = false;
255
Austin Schuhf8c52252013-03-03 02:25:49 -0800256 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800257 if (position) {
258 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800259 // Set the goal to be the current position if this is the first time through
260 // so we don't always spin the indexer to the 0 position before starting.
261 if (no_prior_position_) {
262 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800263 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800264 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800265 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
266 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
267 last_bottom_disc_negedge_wait_count_ =
268 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800269 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800270 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
271 // The open positions for the upper is right here and isn't a hard edge.
Austin Schuh723770b2013-03-10 13:26:20 -0700272 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
273 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800274 }
275
Austin Schuh1b864a12013-03-07 00:46:50 -0800276 // If the cRIO is gone for over 1/2 of a second, assume that it rebooted.
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800277 if (missing_position_count_ > 50) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800278 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
279 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
280 last_bottom_disc_negedge_wait_count_ =
281 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800282 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800283 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
284 // We can't really trust the open range any more if the crio rebooted.
Austin Schuh723770b2013-03-10 13:26:20 -0700285 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
286 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800287 // Adjust the disc positions so that they don't have to move.
288 const double disc_offset =
289 position->index_position - wrist_loop_->X_hat(0, 0);
290 for (auto frisbee = frisbees_.begin();
291 frisbee != frisbees_.end(); ++frisbee) {
292 frisbee->OffsetDisc(disc_offset);
293 }
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800294 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800295 }
296 missing_position_count_ = 0;
297 } else {
298 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800299 }
300 const double index_position = wrist_loop_->X_hat(0, 0);
301
Austin Schuh825bde92013-03-06 00:16:46 -0800302 if (position) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800303 // Reset the open region if we saw a negedge.
Austin Schuh723770b2013-03-10 13:26:20 -0700304 if (position->bottom_disc_negedge_wait_count !=
305 last_bottom_disc_negedge_wait_count_) {
306 // Saw a negedge, must be a new region.
307 lower_open_region_.Restart(position->bottom_disc_negedge_wait_position);
308 }
309 // Reset the open region if we saw a negedge.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800310 if (position->top_disc_negedge_count != last_top_disc_negedge_count_) {
311 // Saw a negedge, must be a new region.
Austin Schuh723770b2013-03-10 13:26:20 -0700312 upper_open_region_.Restart(position->top_disc_negedge_position);
313 }
314
315 // No disc. Expand the open region.
316 if (!position->bottom_disc_detect) {
317 lower_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800318 }
319
320 // No disc. Expand the open region.
321 if (!position->top_disc_detect) {
Austin Schuh723770b2013-03-10 13:26:20 -0700322 upper_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800323 }
324
Austin Schuh825bde92013-03-06 00:16:46 -0800325 if (!position->top_disc_detect) {
326 // We don't see a disc. Verify that there are no discs that we should be
327 // seeing.
Austin Schuh1b864a12013-03-07 00:46:50 -0800328 // Assume that discs will move slow enough that we won't miss one as it
329 // goes by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800330
331 double cumulative_offset = 0.0;
332 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
333 frisbee != rend; ++frisbee) {
334 frisbee->OffsetDisc(cumulative_offset);
335 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh6b1ad2f2013-03-11 23:23:00 -0700336 wrist_loop_->X_hat(0, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800337 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800338 }
339 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800340
Austin Schuh825bde92013-03-06 00:16:46 -0800341 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800342 const double index_position = wrist_loop_->X_hat(0, 0) -
343 position->index_position + position->top_disc_posedge_position;
Austin Schuh825bde92013-03-06 00:16:46 -0800344 // TODO(aschuh): Sanity check this number...
345 // Requires storing when the disc was last seen with the sensor off, and
346 // figuring out what to do if things go south.
347
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800348 // 1 if discs are going up, 0 if we have no clue, and -1 if they are going
349 // down.
350 int disc_direction = 0;
Austin Schuh825bde92013-03-06 00:16:46 -0800351 if (wrist_loop_->X_hat(1, 0) > 50.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800352 disc_direction = 1;
353 } else if (wrist_loop_->X_hat(1, 0) < -50.0) {
354 disc_direction = -1;
355 } else {
356 // Save the upper and lower positions that we last saw a disc at.
357 // If there is a big buffer above, must be a disc from below.
358 // If there is a big buffer below, must be a disc from above.
359 // This should work to replace the velocity threshold above.
360
Austin Schuh723770b2013-03-10 13:26:20 -0700361 const double open_width = upper_open_region_.width();
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800362 const double relative_upper_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700363 (upper_open_region_.upper_bound() - index_position) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800364 const double relative_lower_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700365 (index_position - upper_open_region_.lower_bound()) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800366 printf("Width %f upper %f lower %f\n",
367 open_width, relative_upper_open_precentage,
368 relative_lower_open_precentage);
369
370 if (ConvertIndexToDiscPosition(open_width) <
371 kTopDiscDetectMinSeperation * 0.9) {
372 LOG(ERROR, "Discs are way too close to each other. Doing nothing\n");
373 } else if (relative_upper_open_precentage > 0.75) {
374 // Looks like it is a disc going down from above since we are near
375 // the upper edge.
376 disc_direction = -1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700377 LOG(INFO, "Disc edge going down\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800378 } else if (relative_lower_open_precentage > 0.75) {
379 // Looks like it is a disc going up from below since we are near
380 // the lower edge.
381 disc_direction = 1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700382 LOG(INFO, "Disc edge going up\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800383 } else {
384 LOG(ERROR,
385 "Got an edge in the middle of what should be an open region.\n");
386 LOG(ERROR, "Open width: %f upper precentage %f %%\n",
387 open_width, relative_upper_open_precentage);
388 }
389 }
390
391 if (disc_direction > 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800392 // Moving up at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800393 // Find the highest disc that is below the top disc sensor.
394 // While we are at it, count the number above and log an error if there
395 // are too many.
396 if (frisbees_.size() == 0) {
397 Frisbee new_frisbee;
398 new_frisbee.has_been_indexed_ = true;
399 new_frisbee.index_start_position_ = index_position -
400 ConvertDiscPositionToIndex(kTopDiscDetectStart -
401 kIndexStartPosition);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800402 ++hopper_disc_count_;
403 ++total_disc_count_;
Austin Schuh1b864a12013-03-07 00:46:50 -0800404 frisbees_.push_front(new_frisbee);
405 LOG(WARNING, "Added a disc to the hopper at the top sensor\n");
406 }
407
408 int above_disc_count = 0;
409 double highest_position = 0;
410 Frisbee *highest_frisbee_below_sensor = NULL;
411 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
412 frisbee != rend; ++frisbee) {
413 const double disc_position = frisbee->absolute_position(
414 index_position);
415 // It is save to use the top position for the cuttoff, since the
416 // sensor being low will result in discs being pushed off of it.
417 if (disc_position >= kTopDiscDetectStop) {
418 ++above_disc_count;
419 } else if (!highest_frisbee_below_sensor ||
420 disc_position > highest_position) {
421 highest_frisbee_below_sensor = &*frisbee;
422 highest_position = disc_position;
423 }
424 }
425 if (above_disc_count > 1) {
426 LOG(ERROR, "We have 2 discs above the top sensor.\n");
427 }
428
429 // We now have the disc. Shift all the ones below the sensor up by the
430 // computed delta.
431 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
432 highest_position - kTopDiscDetectStart);
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 if (disc_position < kTopDiscDetectStop) {
438 frisbee->OffsetDisc(disc_delta);
439 }
440 }
Austin Schuha3e8e032013-03-10 18:43:14 -0700441 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800442 "Moving down by %f to %f\n", frisbees_.size(),
Austin Schuh1b864a12013-03-07 00:46:50 -0800443 ConvertIndexToDiscPosition(disc_delta),
444 highest_frisbee_below_sensor->absolute_position(
445 wrist_loop_->X_hat(0, 0)));
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800446 } else if (disc_direction < 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800447 // Moving down at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800448 // There can only be 1 disc up top that would give us a posedge.
449 // Find it and place it at the one spot that it can be.
Brian Silverman94195052013-03-09 13:45:05 -0800450 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800451 Frisbee *min_frisbee = NULL;
452 MinDiscPosition(&min_disc_position, &min_frisbee);
453 if (!min_frisbee) {
454 // Uh, oh, we see a disc but there isn't one...
455 LOG(ERROR, "Saw a disc up top but there isn't one in the hopper\n");
456 } else {
457 const double disc_position = min_frisbee->absolute_position(
458 index_position);
459
460 const double disc_delta_meters = disc_position - kTopDiscDetectStop;
461 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
462 disc_delta_meters);
Austin Schuha3e8e032013-03-10 18:43:14 -0700463 LOG(INFO, "Posedge going down. Moving top disc down by %f\n",
464 disc_delta_meters);
Austin Schuh1b864a12013-03-07 00:46:50 -0800465 for (auto frisbee = frisbees_.begin(), end = frisbees_.end();
466 frisbee != end; ++frisbee) {
467 frisbee->OffsetDisc(disc_delta);
468 }
469 }
Austin Schuh825bde92013-03-06 00:16:46 -0800470 } else {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800471 LOG(ERROR, "Not sure how to handle the upper posedge, doing nothing\n");
Austin Schuh825bde92013-03-06 00:16:46 -0800472 }
473 }
474 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800475
Austin Schuhf8c52252013-03-03 02:25:49 -0800476 // Bool to track if it is safe for the goal to change yet.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700477 bool safe_to_change_state = true;
Austin Schuhd78ab542013-03-01 22:22:19 -0800478 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800479 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800480 // The goal should already be good, so sit tight with everything the same
481 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800482 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800483 case Goal::READY_LOWER:
484 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800485 {
486 Time now = Time::Now();
Austin Schuhd78ab542013-03-01 22:22:19 -0800487 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800488 // Posedge of the disc entering the beam break.
489 if (position->bottom_disc_posedge_count !=
490 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800491 transfer_frisbee_.Reset();
492 transfer_frisbee_.bottom_posedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700493 LOG(INFO, "Posedge of bottom disc %f\n",
494 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800495 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800496 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800497 }
498
499 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800500 if (position->bottom_disc_negedge_count !=
501 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800502 transfer_frisbee_.bottom_negedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700503 LOG(INFO, "Negedge of bottom disc %f\n",
504 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800505 frisbees_.push_front(transfer_frisbee_);
506 }
507
508 if (position->bottom_disc_detect) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800509 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800510 // Must wait until the disc gets out before we can change state.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700511 safe_to_change_state = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800512
Austin Schuhf8c52252013-03-03 02:25:49 -0800513 // TODO(aschuh): A disc on the way through needs to start moving
514 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800515
516 Time elapsed_posedge_time = now -
517 transfer_frisbee_.bottom_posedge_time_;
518 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
519 // It has been too long. The disc must be jammed.
520 LOG(ERROR, "Been way too long. Jammed disc?\n");
521 printf("Been way too long. Jammed disc?\n");
522 }
523 }
524
Austin Schuhf8c52252013-03-03 02:25:49 -0800525 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800526 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800527 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800528 if (!frisbee->has_been_indexed_) {
529 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800530
Austin Schuh6328daf2013-03-05 00:53:15 -0800531 if (last_bottom_disc_negedge_wait_count_ !=
532 position->bottom_disc_negedge_wait_count) {
533 // We have an index difference.
534 // Save the indexer position, and the time.
535 if (last_bottom_disc_negedge_wait_count_ + 1 !=
536 position->bottom_disc_negedge_wait_count) {
537 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
538 }
539
540 // Save the captured position as the position at which the disc
541 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800542 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
543 printf("Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800544 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800545 frisbee->index_start_position_ =
546 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800547 }
548 }
Austin Schuhb6d898b2013-03-03 15:34:35 -0800549 if (!frisbee->has_been_indexed_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800550 // All discs must be indexed before it is safe to stop indexing.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700551 safe_to_change_state = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800552 }
553 }
554
Austin Schuhf8c52252013-03-03 02:25:49 -0800555 // Figure out where the indexer should be to move the discs down to
556 // the right position.
Brian Silverman94195052013-03-09 13:45:05 -0800557 double max_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800558 if (MaxDiscPosition(&max_disc_position, NULL)) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700559 LOG(DEBUG, "There is a disc down here!\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800560 // TODO(aschuh): Figure out what to do if grabbing the next one
561 // would cause things to jam into the loader.
562 // Say we aren't ready any more. Undefined behavior will result if
563 // that isn't observed.
564 double bottom_disc_position =
565 max_disc_position + ConvertDiscAngleToIndex(M_PI);
566 wrist_loop_->R << bottom_disc_position, 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800567
Austin Schuhf8c52252013-03-03 02:25:49 -0800568 // Verify that we are close enough to the goal so that we should be
569 // fine accepting the next disc.
570 double disc_error_meters = ConvertIndexToDiscPosition(
571 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
572 // We are ready for the next disc if the first one is in the first
573 // half circle of the indexer. It will take time for the disc to
574 // come into the indexer, so we will be able to move it out of the
575 // way in time.
576 // This choice also makes sure that we don't claim that we aren't
577 // ready between full speed intaking.
578 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
579 disc_error_meters < 0.04) {
580 // We are only ready if we aren't being asked to change state or
581 // are full.
582 status->ready_to_intake =
583 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
584 } else {
585 status->ready_to_intake = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800586 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800587 } else {
588 // No discs! We are always ready for more if we aren't being
589 // asked to change state.
590 status->ready_to_intake = (safe_goal_ == goal_enum);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800591 printf("Ready to intake, zero discs. %d %d %d\n",
592 status->ready_to_intake, hopper_disc_count_, safe_goal_);
Austin Schuhd78ab542013-03-01 22:22:19 -0800593 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800594
595 // Turn on the transfer roller if we are ready.
596 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
597 safe_goal_ == Goal::INTAKE) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800598 intake_voltage = transfer_voltage = 12.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800599 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800600 }
Austin Schuha3e8e032013-03-10 18:43:14 -0700601 LOG(DEBUG, "INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800602 }
603 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800604 case Goal::READY_SHOOTER:
605 case Goal::SHOOT:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700606 // Don't let us leave the shoot or preload state if there are 4 discs in
607 // the hopper.
608 if (hopper_disc_count_ >= 4 && goal_enum != Goal::SHOOT) {
609 safe_to_change_state = false;
610 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800611 // Check if we have any discs to shoot or load and handle them.
Brian Silverman94195052013-03-09 13:45:05 -0800612 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800613 if (MinDiscPosition(&min_disc_position, NULL)) {
614 const double ready_disc_position = min_disc_position +
615 ConvertDiscPositionToIndex(kReadyToPreload - kIndexStartPosition);
Austin Schuhf8c52252013-03-03 02:25:49 -0800616
617 const double grabbed_disc_position =
618 min_disc_position +
619 ConvertDiscPositionToIndex(kReadyToLiftPosition -
620 kIndexStartPosition + 0.03);
621
622 // Check the state of the loader FSM.
623 // If it is ready to load discs, position the disc so that it is ready
624 // to be grabbed.
625 // If it isn't ready, there is a disc in there. It needs to finish it's
626 // cycle first.
627 if (loader_state_ != LoaderState::READY) {
628 // We already have a disc in the loader.
629 // Stage the discs back a bit.
630 wrist_loop_->R << ready_disc_position, 0.0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800631 printf("Loader not ready but asked to shoot\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800632
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800633 // Shoot if we are grabbed and being asked to shoot.
634 if (loader_state_ == LoaderState::GRABBED &&
635 safe_goal_ == Goal::SHOOT) {
636 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
637 }
638
Austin Schuhf8c52252013-03-03 02:25:49 -0800639 // Must wait until it has been grabbed to continue.
640 if (loader_state_ == LoaderState::GRABBING) {
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700641 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800642 }
643 } else {
644 // No disc up top right now.
645 wrist_loop_->R << grabbed_disc_position, 0.0;
646
647 // See if the disc has gotten pretty far up yet.
648 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
649 // Point of no return. We are committing to grabbing it now.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700650 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800651 const double robust_grabbed_disc_position =
652 (grabbed_disc_position -
653 ConvertDiscPositionToIndex(kGrabberLength));
654
655 // If close, start grabbing and/or shooting.
656 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
657 // Start the state machine.
658 if (safe_goal_ == Goal::SHOOT) {
659 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
660 } else {
661 loader_goal_ = LoaderGoal::GRAB;
662 }
663 // This frisbee is now gone. Take it out of the queue.
664 frisbees_.pop_back();
Austin Schuhf8c52252013-03-03 02:25:49 -0800665 }
666 }
667 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800668 } else {
669 if (loader_state_ != LoaderState::READY) {
670 // Shoot if we are grabbed and being asked to shoot.
671 if (loader_state_ == LoaderState::GRABBED &&
672 safe_goal_ == Goal::SHOOT) {
673 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
674 }
675 } else {
676 // Ok, no discs in sight. Spin the hopper up by 150% of it's full
677 // range and verify that we don't see anything.
678 printf("Moving the indexer to verify that it is clear\n");
679 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700680 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700681 lower_open_region_.lower_bound()) +
Austin Schuha3e8e032013-03-10 18:43:14 -0700682 ConvertDiscPositionToIndex(kIndexFreeLength) * 2.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800683
684 wrist_loop_->R << hopper_clear_verification_position, 0.0;
685 if (::std::abs(wrist_loop_->X_hat(0, 0) -
686 hopper_clear_verification_position) <
687 ConvertDiscPositionToIndex(0.05)) {
688 printf("Should be empty\n");
689 // We are at the end of the range. There are no more discs here.
690 while (frisbees_.size() > 0) {
691 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700692 LOG(ERROR, "Upper is [%f %f]\n",
693 upper_open_region_.upper_bound(),
694 upper_open_region_.lower_bound());
695 LOG(ERROR, "Lower is [%f %f]\n",
696 lower_open_region_.upper_bound(),
697 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800698 frisbees_.pop_back();
699 --hopper_disc_count_;
700 --total_disc_count_;
701 }
702 if (hopper_disc_count_ != 0) {
703 LOG(ERROR,
704 "Emptied the hopper out but there are still discs there\n");
705 }
706 }
707 }
708 }
709
710 {
711 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700712 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700713 lower_open_region_.lower_bound()) +
Austin Schuha3e8e032013-03-10 18:43:14 -0700714 ConvertDiscPositionToIndex(kIndexFreeLength) * 2.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800715
716 if (wrist_loop_->X_hat(0, 0) >
717 hopper_clear_verification_position +
718 ConvertDiscPositionToIndex(0.05)) {
719 // We are at the end of the range. There are no more discs here.
720 while (frisbees_.size() > 0) {
721 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuha3e8e032013-03-10 18:43:14 -0700722 LOG(ERROR, "Upper is [%f %f]\n",
723 upper_open_region_.upper_bound(),
724 upper_open_region_.lower_bound());
725 LOG(ERROR, "Lower is [%f %f]\n",
726 lower_open_region_.upper_bound(),
727 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800728 frisbees_.pop_back();
729 --hopper_disc_count_;
730 --total_disc_count_;
731 }
732 if (hopper_disc_count_ != 0) {
733 LOG(ERROR,
734 "Emptied the hopper out but there are still discs there\n");
735 }
736 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800737 }
738
Austin Schuha3e8e032013-03-10 18:43:14 -0700739 LOG(DEBUG, "READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800740 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800741 }
742
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700743 // If we have 4 discs, it is time to preload.
744 if (safe_to_change_state && hopper_disc_count_ >= 4) {
745 switch (safe_goal_) {
746 case Goal::HOLD:
747 case Goal::READY_LOWER:
748 case Goal::INTAKE:
749 safe_goal_ = Goal::READY_SHOOTER;
750 safe_to_change_state = false;
751 LOG(INFO, "We have %d discs, time to preload automatically\n",
752 hopper_disc_count_);
753 break;
754 case Goal::READY_SHOOTER:
755 case Goal::SHOOT:
756 break;
757 }
758 }
759
Austin Schuhf8c52252013-03-03 02:25:49 -0800760 // The only way out of the loader is to shoot the disc. The FSM can only go
761 // forwards.
762 switch (loader_state_) {
763 case LoaderState::READY:
Austin Schuha3e8e032013-03-10 18:43:14 -0700764 LOG(DEBUG, "Loader READY\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800765 // Open and down, ready to accept a disc.
766 loader_up_ = false;
767 disc_clamped_ = false;
768 disc_ejected_ = false;
769 if (loader_goal_ == LoaderGoal::GRAB ||
770 loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
771 if (loader_goal_ == LoaderGoal::GRAB) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700772 LOG(INFO, "Told to GRAB, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800773 } else {
Austin Schuha3e8e032013-03-10 18:43:14 -0700774 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800775 }
776 loader_state_ = LoaderState::GRABBING;
777 loader_countdown_ = kGrabbingDelay;
778 } else {
779 break;
780 }
781 case LoaderState::GRABBING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700782 LOG(DEBUG, "Loader GRABBING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800783 // Closing the grabber.
784 loader_up_ = false;
785 disc_clamped_ = true;
786 disc_ejected_ = false;
787 if (loader_countdown_ > 0) {
788 --loader_countdown_;
789 break;
790 } else {
791 loader_state_ = LoaderState::GRABBED;
792 }
793 case LoaderState::GRABBED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700794 LOG(DEBUG, "Loader GRABBED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800795 // Grabber closed.
796 loader_up_ = false;
797 disc_clamped_ = true;
798 disc_ejected_ = false;
799 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700800 shooter.status.FetchLatest();
801 if (shooter.status.get()) {
802 // TODO(aschuh): If we aren't shooting nicely, wait until the shooter
803 // is up to speed rather than just spinning.
804 if (shooter.status->average_velocity > 130) {
805 loader_state_ = LoaderState::LIFTING;
806 loader_countdown_ = kLiftingDelay;
807 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
808 } else {
809 LOG(WARNING, "Told to SHOOT_AND_RESET, shooter too slow at %f\n",
810 shooter.status->average_velocity);
811 break;
812 }
813 } else {
814 LOG(ERROR, "Told to SHOOT_AND_RESET, no shooter data, moving on.\n");
815 loader_state_ = LoaderState::LIFTING;
816 loader_countdown_ = kLiftingDelay;
817 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800818 } else if (loader_goal_ == LoaderGoal::READY) {
819 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
820 printf("Can't go to ready when we have something grabbed.\n");
821 break;
822 } else {
823 break;
824 }
825 case LoaderState::LIFTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700826 LOG(DEBUG, "Loader LIFTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800827 // Lifting the disc.
828 loader_up_ = true;
829 disc_clamped_ = true;
830 disc_ejected_ = false;
831 if (loader_countdown_ > 0) {
832 --loader_countdown_;
833 break;
834 } else {
835 loader_state_ = LoaderState::LIFTED;
836 }
837 case LoaderState::LIFTED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700838 LOG(DEBUG, "Loader LIFTED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800839 // Disc lifted. Time to eject it out.
840 loader_up_ = true;
841 disc_clamped_ = true;
842 disc_ejected_ = false;
843 loader_state_ = LoaderState::SHOOTING;
844 loader_countdown_ = kShootingDelay;
845 case LoaderState::SHOOTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700846 LOG(DEBUG, "Loader SHOOTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800847 // Ejecting the disc into the shooter.
848 loader_up_ = true;
849 disc_clamped_ = false;
850 disc_ejected_ = true;
851 if (loader_countdown_ > 0) {
852 --loader_countdown_;
853 break;
854 } else {
855 loader_state_ = LoaderState::SHOOT;
856 }
857 case LoaderState::SHOOT:
Austin Schuha3e8e032013-03-10 18:43:14 -0700858 LOG(DEBUG, "Loader SHOOT\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800859 // The disc has been shot.
860 loader_up_ = true;
861 disc_clamped_ = false;
862 disc_ejected_ = true;
863 loader_state_ = LoaderState::LOWERING;
864 loader_countdown_ = kLoweringDelay;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800865 --hopper_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700866 ++shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800867 case LoaderState::LOWERING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700868 LOG(DEBUG, "Loader LOWERING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800869 // Lowering the loader back down.
870 loader_up_ = false;
871 disc_clamped_ = false;
872 disc_ejected_ = true;
873 if (loader_countdown_ > 0) {
874 --loader_countdown_;
875 break;
876 } else {
877 loader_state_ = LoaderState::LOWERED;
878 }
879 case LoaderState::LOWERED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700880 LOG(DEBUG, "Loader LOWERED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800881 // The indexer is lowered.
882 loader_up_ = false;
883 disc_clamped_ = false;
884 disc_ejected_ = false;
885 loader_state_ = LoaderState::READY;
886 // Once we have shot, we need to hang out in READY until otherwise
887 // notified.
888 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800889 break;
890 }
891
892 // Update the observer.
893 wrist_loop_->Update(position != NULL, output == NULL);
894
895 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800896 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800897 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -0800898 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -0800899 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
900 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
901 last_bottom_disc_negedge_wait_count_ =
902 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800903 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800904 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -0800905 }
906
907 status->hopper_disc_count = hopper_disc_count_;
908 status->total_disc_count = total_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700909 status->shot_disc_count = shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800910 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800911
912 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800913 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -0800914 output->transfer_voltage = transfer_voltage;
Austin Schuhd78ab542013-03-01 22:22:19 -0800915 output->index_voltage = wrist_loop_->U(0, 0);
Austin Schuhf8c52252013-03-03 02:25:49 -0800916 output->loader_up = loader_up_;
917 output->disc_clamped = disc_clamped_;
918 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800919 }
920
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700921 if (safe_to_change_state) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800922 safe_goal_ = goal_enum;
923 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800924 if (hopper_disc_count_ < 0) {
925 LOG(ERROR, "NEGATIVE DISCS. VERY VERY BAD\n");
926 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800927}
928
929} // namespace control_loops
930} // namespace frc971