blob: 72597c6f0a5ca66d3b91b739ca194713434a0c35 [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) {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700220 if (U(i, 0) > U_max(i, 0)) {
221 U(i, 0) = U_max(i, 0);
222 } else if (U(i, 0) < U_min(i, 0)) {
223 U(i, 0) = U_min(i, 0);
Austin Schuh93485832013-03-04 00:01:34 -0800224 }
225 }
226}
227
228
Austin Schuhd78ab542013-03-01 22:22:19 -0800229// Positive angle is towards the shooter, and positive power is towards the
230// shooter.
231void IndexMotor::RunIteration(
232 const control_loops::IndexLoop::Goal *goal,
233 const control_loops::IndexLoop::Position *position,
234 control_loops::IndexLoop::Output *output,
235 control_loops::IndexLoop::Status *status) {
Austin 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 Schuhe3490622013-03-13 01:24:30 -0700256 // Set the controller to use to be the one designed for the current number of
257 // discs in the hopper. This is safe since the controller prevents the index
258 // from being set out of bounds and picks the closest controller.
259 wrist_loop_->set_controller_index(frisbees_.size());
260
Austin Schuhf8c52252013-03-03 02:25:49 -0800261 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800262 if (position) {
263 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800264 // Set the goal to be the current position if this is the first time through
265 // so we don't always spin the indexer to the 0 position before starting.
266 if (no_prior_position_) {
267 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800268 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800269 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800270 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
271 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
272 last_bottom_disc_negedge_wait_count_ =
273 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800274 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800275 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
276 // The open positions for the upper is right here and isn't a hard edge.
Austin Schuh723770b2013-03-10 13:26:20 -0700277 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
278 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800279 }
280
Austin Schuh1b864a12013-03-07 00:46:50 -0800281 // If the cRIO is gone for over 1/2 of a second, assume that it rebooted.
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800282 if (missing_position_count_ > 50) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800283 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
284 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
285 last_bottom_disc_negedge_wait_count_ =
286 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800287 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800288 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
289 // We can't really trust the open range any more if the crio rebooted.
Austin Schuh723770b2013-03-10 13:26:20 -0700290 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
291 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800292 // Adjust the disc positions so that they don't have to move.
293 const double disc_offset =
294 position->index_position - wrist_loop_->X_hat(0, 0);
295 for (auto frisbee = frisbees_.begin();
296 frisbee != frisbees_.end(); ++frisbee) {
297 frisbee->OffsetDisc(disc_offset);
298 }
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800299 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800300 }
301 missing_position_count_ = 0;
302 } else {
303 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800304 }
305 const double index_position = wrist_loop_->X_hat(0, 0);
306
Austin Schuh825bde92013-03-06 00:16:46 -0800307 if (position) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800308 // Reset the open region if we saw a negedge.
Austin Schuh723770b2013-03-10 13:26:20 -0700309 if (position->bottom_disc_negedge_wait_count !=
310 last_bottom_disc_negedge_wait_count_) {
311 // Saw a negedge, must be a new region.
312 lower_open_region_.Restart(position->bottom_disc_negedge_wait_position);
313 }
314 // Reset the open region if we saw a negedge.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800315 if (position->top_disc_negedge_count != last_top_disc_negedge_count_) {
316 // Saw a negedge, must be a new region.
Austin Schuh723770b2013-03-10 13:26:20 -0700317 upper_open_region_.Restart(position->top_disc_negedge_position);
318 }
319
320 // No disc. Expand the open region.
321 if (!position->bottom_disc_detect) {
322 lower_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800323 }
324
325 // No disc. Expand the open region.
326 if (!position->top_disc_detect) {
Austin Schuh723770b2013-03-10 13:26:20 -0700327 upper_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800328 }
329
Austin Schuh825bde92013-03-06 00:16:46 -0800330 if (!position->top_disc_detect) {
331 // We don't see a disc. Verify that there are no discs that we should be
332 // seeing.
Austin Schuh1b864a12013-03-07 00:46:50 -0800333 // Assume that discs will move slow enough that we won't miss one as it
334 // goes by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800335
336 double cumulative_offset = 0.0;
337 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
338 frisbee != rend; ++frisbee) {
339 frisbee->OffsetDisc(cumulative_offset);
340 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh6b1ad2f2013-03-11 23:23:00 -0700341 wrist_loop_->X_hat(0, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800342 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800343 }
344 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800345
Austin Schuh825bde92013-03-06 00:16:46 -0800346 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800347 const double index_position = wrist_loop_->X_hat(0, 0) -
348 position->index_position + position->top_disc_posedge_position;
Austin Schuh825bde92013-03-06 00:16:46 -0800349 // TODO(aschuh): Sanity check this number...
350 // Requires storing when the disc was last seen with the sensor off, and
351 // figuring out what to do if things go south.
352
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800353 // 1 if discs are going up, 0 if we have no clue, and -1 if they are going
354 // down.
355 int disc_direction = 0;
Austin Schuh825bde92013-03-06 00:16:46 -0800356 if (wrist_loop_->X_hat(1, 0) > 50.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800357 disc_direction = 1;
358 } else if (wrist_loop_->X_hat(1, 0) < -50.0) {
359 disc_direction = -1;
360 } else {
361 // Save the upper and lower positions that we last saw a disc at.
362 // If there is a big buffer above, must be a disc from below.
363 // If there is a big buffer below, must be a disc from above.
364 // This should work to replace the velocity threshold above.
365
Austin Schuh723770b2013-03-10 13:26:20 -0700366 const double open_width = upper_open_region_.width();
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800367 const double relative_upper_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700368 (upper_open_region_.upper_bound() - index_position) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800369 const double relative_lower_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700370 (index_position - upper_open_region_.lower_bound()) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800371 printf("Width %f upper %f lower %f\n",
372 open_width, relative_upper_open_precentage,
373 relative_lower_open_precentage);
374
375 if (ConvertIndexToDiscPosition(open_width) <
376 kTopDiscDetectMinSeperation * 0.9) {
377 LOG(ERROR, "Discs are way too close to each other. Doing nothing\n");
378 } else if (relative_upper_open_precentage > 0.75) {
379 // Looks like it is a disc going down from above since we are near
380 // the upper edge.
381 disc_direction = -1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700382 LOG(INFO, "Disc edge going down\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800383 } else if (relative_lower_open_precentage > 0.75) {
384 // Looks like it is a disc going up from below since we are near
385 // the lower edge.
386 disc_direction = 1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700387 LOG(INFO, "Disc edge going up\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800388 } else {
389 LOG(ERROR,
390 "Got an edge in the middle of what should be an open region.\n");
391 LOG(ERROR, "Open width: %f upper precentage %f %%\n",
392 open_width, relative_upper_open_precentage);
393 }
394 }
395
396 if (disc_direction > 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800397 // Moving up at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800398 // Find the highest disc that is below the top disc sensor.
399 // While we are at it, count the number above and log an error if there
400 // are too many.
401 if (frisbees_.size() == 0) {
402 Frisbee new_frisbee;
403 new_frisbee.has_been_indexed_ = true;
404 new_frisbee.index_start_position_ = index_position -
405 ConvertDiscPositionToIndex(kTopDiscDetectStart -
406 kIndexStartPosition);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800407 ++hopper_disc_count_;
408 ++total_disc_count_;
Austin Schuh1b864a12013-03-07 00:46:50 -0800409 frisbees_.push_front(new_frisbee);
410 LOG(WARNING, "Added a disc to the hopper at the top sensor\n");
411 }
412
413 int above_disc_count = 0;
414 double highest_position = 0;
415 Frisbee *highest_frisbee_below_sensor = NULL;
416 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
417 frisbee != rend; ++frisbee) {
418 const double disc_position = frisbee->absolute_position(
419 index_position);
420 // It is save to use the top position for the cuttoff, since the
421 // sensor being low will result in discs being pushed off of it.
422 if (disc_position >= kTopDiscDetectStop) {
423 ++above_disc_count;
424 } else if (!highest_frisbee_below_sensor ||
425 disc_position > highest_position) {
426 highest_frisbee_below_sensor = &*frisbee;
427 highest_position = disc_position;
428 }
429 }
430 if (above_disc_count > 1) {
431 LOG(ERROR, "We have 2 discs above the top sensor.\n");
432 }
433
434 // We now have the disc. Shift all the ones below the sensor up by the
435 // computed delta.
436 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
437 highest_position - kTopDiscDetectStart);
438 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
439 frisbee != rend; ++frisbee) {
440 const double disc_position = frisbee->absolute_position(
441 index_position);
442 if (disc_position < kTopDiscDetectStop) {
443 frisbee->OffsetDisc(disc_delta);
444 }
445 }
Austin Schuha3e8e032013-03-10 18:43:14 -0700446 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800447 "Moving down by %f to %f\n", frisbees_.size(),
Austin Schuh1b864a12013-03-07 00:46:50 -0800448 ConvertIndexToDiscPosition(disc_delta),
449 highest_frisbee_below_sensor->absolute_position(
450 wrist_loop_->X_hat(0, 0)));
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800451 } else if (disc_direction < 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800452 // Moving down at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800453 // There can only be 1 disc up top that would give us a posedge.
454 // Find it and place it at the one spot that it can be.
Brian Silverman94195052013-03-09 13:45:05 -0800455 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800456 Frisbee *min_frisbee = NULL;
457 MinDiscPosition(&min_disc_position, &min_frisbee);
458 if (!min_frisbee) {
459 // Uh, oh, we see a disc but there isn't one...
460 LOG(ERROR, "Saw a disc up top but there isn't one in the hopper\n");
461 } else {
462 const double disc_position = min_frisbee->absolute_position(
463 index_position);
464
465 const double disc_delta_meters = disc_position - kTopDiscDetectStop;
466 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
467 disc_delta_meters);
Austin Schuha3e8e032013-03-10 18:43:14 -0700468 LOG(INFO, "Posedge going down. Moving top disc down by %f\n",
469 disc_delta_meters);
Austin Schuh1b864a12013-03-07 00:46:50 -0800470 for (auto frisbee = frisbees_.begin(), end = frisbees_.end();
471 frisbee != end; ++frisbee) {
472 frisbee->OffsetDisc(disc_delta);
473 }
474 }
Austin Schuh825bde92013-03-06 00:16:46 -0800475 } else {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800476 LOG(ERROR, "Not sure how to handle the upper posedge, doing nothing\n");
Austin Schuh825bde92013-03-06 00:16:46 -0800477 }
478 }
479 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800480
Austin Schuhf8c52252013-03-03 02:25:49 -0800481 // Bool to track if it is safe for the goal to change yet.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700482 bool safe_to_change_state = true;
Austin Schuhd78ab542013-03-01 22:22:19 -0800483 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800484 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800485 // The goal should already be good, so sit tight with everything the same
486 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800487 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800488 case Goal::READY_LOWER:
489 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800490 {
491 Time now = Time::Now();
Austin Schuhd78ab542013-03-01 22:22:19 -0800492 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800493 // Posedge of the disc entering the beam break.
494 if (position->bottom_disc_posedge_count !=
495 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800496 transfer_frisbee_.Reset();
497 transfer_frisbee_.bottom_posedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700498 LOG(INFO, "Posedge of bottom disc %f\n",
499 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800500 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800501 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800502 }
503
504 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800505 if (position->bottom_disc_negedge_count !=
506 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800507 transfer_frisbee_.bottom_negedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700508 LOG(INFO, "Negedge of bottom disc %f\n",
509 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800510 frisbees_.push_front(transfer_frisbee_);
511 }
512
513 if (position->bottom_disc_detect) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800514 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800515 // Must wait until the disc gets out before we can change state.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700516 safe_to_change_state = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800517
Austin Schuhf8c52252013-03-03 02:25:49 -0800518 // TODO(aschuh): A disc on the way through needs to start moving
519 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800520
521 Time elapsed_posedge_time = now -
522 transfer_frisbee_.bottom_posedge_time_;
523 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
524 // It has been too long. The disc must be jammed.
525 LOG(ERROR, "Been way too long. Jammed disc?\n");
526 printf("Been way too long. Jammed disc?\n");
527 }
528 }
529
Austin Schuhf8c52252013-03-03 02:25:49 -0800530 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800531 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800532 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800533 if (!frisbee->has_been_indexed_) {
534 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800535
Austin Schuh6328daf2013-03-05 00:53:15 -0800536 if (last_bottom_disc_negedge_wait_count_ !=
537 position->bottom_disc_negedge_wait_count) {
538 // We have an index difference.
539 // Save the indexer position, and the time.
540 if (last_bottom_disc_negedge_wait_count_ + 1 !=
541 position->bottom_disc_negedge_wait_count) {
542 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
543 }
544
545 // Save the captured position as the position at which the disc
546 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800547 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
548 printf("Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800549 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800550 frisbee->index_start_position_ =
551 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800552 }
553 }
Austin Schuhb6d898b2013-03-03 15:34:35 -0800554 if (!frisbee->has_been_indexed_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800555 // All discs must be indexed before it is safe to stop indexing.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700556 safe_to_change_state = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800557 }
558 }
559
Austin Schuhf8c52252013-03-03 02:25:49 -0800560 // Figure out where the indexer should be to move the discs down to
561 // the right position.
Brian Silverman94195052013-03-09 13:45:05 -0800562 double max_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800563 if (MaxDiscPosition(&max_disc_position, NULL)) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700564 LOG(DEBUG, "There is a disc down here!\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800565 // TODO(aschuh): Figure out what to do if grabbing the next one
566 // would cause things to jam into the loader.
567 // Say we aren't ready any more. Undefined behavior will result if
568 // that isn't observed.
569 double bottom_disc_position =
570 max_disc_position + ConvertDiscAngleToIndex(M_PI);
571 wrist_loop_->R << bottom_disc_position, 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800572
Austin Schuhf8c52252013-03-03 02:25:49 -0800573 // Verify that we are close enough to the goal so that we should be
574 // fine accepting the next disc.
575 double disc_error_meters = ConvertIndexToDiscPosition(
576 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
577 // We are ready for the next disc if the first one is in the first
578 // half circle of the indexer. It will take time for the disc to
579 // come into the indexer, so we will be able to move it out of the
580 // way in time.
581 // This choice also makes sure that we don't claim that we aren't
582 // ready between full speed intaking.
583 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
584 disc_error_meters < 0.04) {
585 // We are only ready if we aren't being asked to change state or
586 // are full.
587 status->ready_to_intake =
588 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
589 } else {
590 status->ready_to_intake = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800591 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800592 } else {
593 // No discs! We are always ready for more if we aren't being
594 // asked to change state.
595 status->ready_to_intake = (safe_goal_ == goal_enum);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800596 printf("Ready to intake, zero discs. %d %d %d\n",
597 status->ready_to_intake, hopper_disc_count_, safe_goal_);
Austin Schuhd78ab542013-03-01 22:22:19 -0800598 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800599
600 // Turn on the transfer roller if we are ready.
601 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
602 safe_goal_ == Goal::INTAKE) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800603 intake_voltage = transfer_voltage = 12.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800604 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800605 }
Austin Schuha3e8e032013-03-10 18:43:14 -0700606 LOG(DEBUG, "INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800607 }
608 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800609 case Goal::READY_SHOOTER:
610 case Goal::SHOOT:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700611 // Don't let us leave the shoot or preload state if there are 4 discs in
612 // the hopper.
613 if (hopper_disc_count_ >= 4 && goal_enum != Goal::SHOOT) {
614 safe_to_change_state = false;
615 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800616 // Check if we have any discs to shoot or load and handle them.
Brian Silverman94195052013-03-09 13:45:05 -0800617 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800618 if (MinDiscPosition(&min_disc_position, NULL)) {
619 const double ready_disc_position = min_disc_position +
620 ConvertDiscPositionToIndex(kReadyToPreload - kIndexStartPosition);
Austin Schuhf8c52252013-03-03 02:25:49 -0800621
622 const double grabbed_disc_position =
623 min_disc_position +
624 ConvertDiscPositionToIndex(kReadyToLiftPosition -
625 kIndexStartPosition + 0.03);
626
627 // Check the state of the loader FSM.
628 // If it is ready to load discs, position the disc so that it is ready
629 // to be grabbed.
630 // If it isn't ready, there is a disc in there. It needs to finish it's
631 // cycle first.
632 if (loader_state_ != LoaderState::READY) {
633 // We already have a disc in the loader.
634 // Stage the discs back a bit.
635 wrist_loop_->R << ready_disc_position, 0.0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800636 printf("Loader not ready but asked to shoot\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800637
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800638 // Shoot if we are grabbed and being asked to shoot.
639 if (loader_state_ == LoaderState::GRABBED &&
640 safe_goal_ == Goal::SHOOT) {
641 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
642 }
643
Austin Schuhf8c52252013-03-03 02:25:49 -0800644 // Must wait until it has been grabbed to continue.
645 if (loader_state_ == LoaderState::GRABBING) {
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700646 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800647 }
648 } else {
649 // No disc up top right now.
650 wrist_loop_->R << grabbed_disc_position, 0.0;
651
652 // See if the disc has gotten pretty far up yet.
653 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
654 // Point of no return. We are committing to grabbing it now.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700655 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800656 const double robust_grabbed_disc_position =
657 (grabbed_disc_position -
658 ConvertDiscPositionToIndex(kGrabberLength));
659
660 // If close, start grabbing and/or shooting.
661 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
662 // Start the state machine.
663 if (safe_goal_ == Goal::SHOOT) {
664 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
665 } else {
666 loader_goal_ = LoaderGoal::GRAB;
667 }
668 // This frisbee is now gone. Take it out of the queue.
669 frisbees_.pop_back();
Austin Schuhf8c52252013-03-03 02:25:49 -0800670 }
671 }
672 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800673 } else {
674 if (loader_state_ != LoaderState::READY) {
675 // Shoot if we are grabbed and being asked to shoot.
676 if (loader_state_ == LoaderState::GRABBED &&
677 safe_goal_ == Goal::SHOOT) {
678 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
679 }
680 } else {
681 // Ok, no discs in sight. Spin the hopper up by 150% of it's full
682 // range and verify that we don't see anything.
683 printf("Moving the indexer to verify that it is clear\n");
684 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700685 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700686 lower_open_region_.lower_bound()) +
Austin Schuha3e8e032013-03-10 18:43:14 -0700687 ConvertDiscPositionToIndex(kIndexFreeLength) * 2.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800688
689 wrist_loop_->R << hopper_clear_verification_position, 0.0;
690 if (::std::abs(wrist_loop_->X_hat(0, 0) -
691 hopper_clear_verification_position) <
692 ConvertDiscPositionToIndex(0.05)) {
693 printf("Should be empty\n");
694 // We are at the end of the range. There are no more discs here.
695 while (frisbees_.size() > 0) {
696 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700697 LOG(ERROR, "Upper is [%f %f]\n",
698 upper_open_region_.upper_bound(),
699 upper_open_region_.lower_bound());
700 LOG(ERROR, "Lower is [%f %f]\n",
701 lower_open_region_.upper_bound(),
702 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800703 frisbees_.pop_back();
704 --hopper_disc_count_;
705 --total_disc_count_;
706 }
707 if (hopper_disc_count_ != 0) {
708 LOG(ERROR,
709 "Emptied the hopper out but there are still discs there\n");
710 }
711 }
712 }
713 }
714
715 {
716 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700717 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700718 lower_open_region_.lower_bound()) +
Austin Schuha3e8e032013-03-10 18:43:14 -0700719 ConvertDiscPositionToIndex(kIndexFreeLength) * 2.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800720
721 if (wrist_loop_->X_hat(0, 0) >
722 hopper_clear_verification_position +
723 ConvertDiscPositionToIndex(0.05)) {
724 // We are at the end of the range. There are no more discs here.
725 while (frisbees_.size() > 0) {
726 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuha3e8e032013-03-10 18:43:14 -0700727 LOG(ERROR, "Upper is [%f %f]\n",
728 upper_open_region_.upper_bound(),
729 upper_open_region_.lower_bound());
730 LOG(ERROR, "Lower is [%f %f]\n",
731 lower_open_region_.upper_bound(),
732 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800733 frisbees_.pop_back();
734 --hopper_disc_count_;
735 --total_disc_count_;
736 }
737 if (hopper_disc_count_ != 0) {
738 LOG(ERROR,
739 "Emptied the hopper out but there are still discs there\n");
740 }
741 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800742 }
743
Austin Schuha3e8e032013-03-10 18:43:14 -0700744 LOG(DEBUG, "READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800745 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800746 }
747
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700748 // If we have 4 discs, it is time to preload.
749 if (safe_to_change_state && hopper_disc_count_ >= 4) {
750 switch (safe_goal_) {
751 case Goal::HOLD:
752 case Goal::READY_LOWER:
753 case Goal::INTAKE:
754 safe_goal_ = Goal::READY_SHOOTER;
755 safe_to_change_state = false;
756 LOG(INFO, "We have %d discs, time to preload automatically\n",
757 hopper_disc_count_);
758 break;
759 case Goal::READY_SHOOTER:
760 case Goal::SHOOT:
761 break;
762 }
763 }
764
Austin Schuhf8c52252013-03-03 02:25:49 -0800765 // The only way out of the loader is to shoot the disc. The FSM can only go
766 // forwards.
767 switch (loader_state_) {
768 case LoaderState::READY:
Austin Schuha3e8e032013-03-10 18:43:14 -0700769 LOG(DEBUG, "Loader READY\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800770 // Open and down, ready to accept a disc.
771 loader_up_ = false;
772 disc_clamped_ = false;
773 disc_ejected_ = false;
774 if (loader_goal_ == LoaderGoal::GRAB ||
775 loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
776 if (loader_goal_ == LoaderGoal::GRAB) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700777 LOG(INFO, "Told to GRAB, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800778 } else {
Austin Schuha3e8e032013-03-10 18:43:14 -0700779 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800780 }
781 loader_state_ = LoaderState::GRABBING;
782 loader_countdown_ = kGrabbingDelay;
783 } else {
784 break;
785 }
786 case LoaderState::GRABBING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700787 LOG(DEBUG, "Loader GRABBING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800788 // Closing the grabber.
789 loader_up_ = false;
790 disc_clamped_ = true;
791 disc_ejected_ = false;
792 if (loader_countdown_ > 0) {
793 --loader_countdown_;
794 break;
795 } else {
796 loader_state_ = LoaderState::GRABBED;
797 }
798 case LoaderState::GRABBED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700799 LOG(DEBUG, "Loader GRABBED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800800 // Grabber closed.
801 loader_up_ = false;
802 disc_clamped_ = true;
803 disc_ejected_ = false;
804 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700805 shooter.status.FetchLatest();
806 if (shooter.status.get()) {
807 // TODO(aschuh): If we aren't shooting nicely, wait until the shooter
808 // is up to speed rather than just spinning.
809 if (shooter.status->average_velocity > 130) {
810 loader_state_ = LoaderState::LIFTING;
811 loader_countdown_ = kLiftingDelay;
812 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
813 } else {
814 LOG(WARNING, "Told to SHOOT_AND_RESET, shooter too slow at %f\n",
815 shooter.status->average_velocity);
816 break;
817 }
818 } else {
819 LOG(ERROR, "Told to SHOOT_AND_RESET, no shooter data, moving on.\n");
820 loader_state_ = LoaderState::LIFTING;
821 loader_countdown_ = kLiftingDelay;
822 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800823 } else if (loader_goal_ == LoaderGoal::READY) {
824 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
825 printf("Can't go to ready when we have something grabbed.\n");
826 break;
827 } else {
828 break;
829 }
830 case LoaderState::LIFTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700831 LOG(DEBUG, "Loader LIFTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800832 // Lifting the disc.
833 loader_up_ = true;
834 disc_clamped_ = true;
835 disc_ejected_ = false;
836 if (loader_countdown_ > 0) {
837 --loader_countdown_;
838 break;
839 } else {
840 loader_state_ = LoaderState::LIFTED;
841 }
842 case LoaderState::LIFTED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700843 LOG(DEBUG, "Loader LIFTED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800844 // Disc lifted. Time to eject it out.
845 loader_up_ = true;
846 disc_clamped_ = true;
847 disc_ejected_ = false;
848 loader_state_ = LoaderState::SHOOTING;
849 loader_countdown_ = kShootingDelay;
850 case LoaderState::SHOOTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700851 LOG(DEBUG, "Loader SHOOTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800852 // Ejecting the disc into the shooter.
853 loader_up_ = true;
854 disc_clamped_ = false;
855 disc_ejected_ = true;
856 if (loader_countdown_ > 0) {
857 --loader_countdown_;
858 break;
859 } else {
860 loader_state_ = LoaderState::SHOOT;
861 }
862 case LoaderState::SHOOT:
Austin Schuha3e8e032013-03-10 18:43:14 -0700863 LOG(DEBUG, "Loader SHOOT\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800864 // The disc has been shot.
865 loader_up_ = true;
866 disc_clamped_ = false;
867 disc_ejected_ = true;
868 loader_state_ = LoaderState::LOWERING;
869 loader_countdown_ = kLoweringDelay;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800870 --hopper_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700871 ++shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800872 case LoaderState::LOWERING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700873 LOG(DEBUG, "Loader LOWERING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800874 // Lowering the loader back down.
875 loader_up_ = false;
876 disc_clamped_ = false;
877 disc_ejected_ = true;
878 if (loader_countdown_ > 0) {
879 --loader_countdown_;
880 break;
881 } else {
882 loader_state_ = LoaderState::LOWERED;
883 }
884 case LoaderState::LOWERED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700885 LOG(DEBUG, "Loader LOWERED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800886 // The indexer is lowered.
887 loader_up_ = false;
888 disc_clamped_ = false;
889 disc_ejected_ = false;
890 loader_state_ = LoaderState::READY;
891 // Once we have shot, we need to hang out in READY until otherwise
892 // notified.
893 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800894 break;
895 }
896
897 // Update the observer.
898 wrist_loop_->Update(position != NULL, output == NULL);
899
900 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800901 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800902 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -0800903 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -0800904 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
905 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
906 last_bottom_disc_negedge_wait_count_ =
907 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800908 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800909 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -0800910 }
911
912 status->hopper_disc_count = hopper_disc_count_;
913 status->total_disc_count = total_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700914 status->shot_disc_count = shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800915 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800916
917 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800918 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -0800919 output->transfer_voltage = transfer_voltage;
Austin Schuhd78ab542013-03-01 22:22:19 -0800920 output->index_voltage = wrist_loop_->U(0, 0);
Austin Schuhf8c52252013-03-03 02:25:49 -0800921 output->loader_up = loader_up_;
922 output->disc_clamped = disc_clamped_;
923 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800924 }
925
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700926 if (safe_to_change_state) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800927 safe_goal_ = goal_enum;
928 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800929 if (hopper_disc_count_ < 0) {
930 LOG(ERROR, "NEGATIVE DISCS. VERY VERY BAD\n");
931 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800932}
933
934} // namespace control_loops
935} // namespace frc971