blob: 8ba9c33d6a7f9e6cf842d0b5fc514139f2605fb4 [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 Schuhdff24e22013-03-06 00:41:21 -080023double IndexMotor::Frisbee::ObserveNoTopDiscSensor(
Austin Schuh825bde92013-03-06 00:16:46 -080024 double index_position, double index_velocity) {
Austin Schuhdff24e22013-03-06 00:41:21 -080025 // The absolute disc position in meters.
Austin Schuh1b864a12013-03-07 00:46:50 -080026 double disc_position = absolute_position(index_position);
Austin Schuh825bde92013-03-06 00:16:46 -080027 if (IndexMotor::kTopDiscDetectStart <= disc_position &&
28 disc_position <= IndexMotor::kTopDiscDetectStop) {
29 // Whoops, this shouldn't be happening.
30 // Move the disc off the way that makes most sense.
Austin Schuhdff24e22013-03-06 00:41:21 -080031 double distance_to_above = IndexMotor::ConvertDiscPositionToIndex(
32 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStop));
33 double distance_to_below = IndexMotor::ConvertDiscPositionToIndex(
34 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStart));
Austin Schuha3e8e032013-03-10 18:43:14 -070035 if (::std::abs(index_velocity) < 100000) {
Austin Schuh825bde92013-03-06 00:16:46 -080036 if (distance_to_above < distance_to_below) {
Austin Schuha3e8e032013-03-10 18:43:14 -070037 LOG(INFO, "Moving disc to top slow.\n");
Austin Schuh825bde92013-03-06 00:16:46 -080038 // Move it up.
Austin Schuhdff24e22013-03-06 00:41:21 -080039 index_start_position_ -= distance_to_above;
40 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080041 } else {
Austin Schuha3e8e032013-03-10 18:43:14 -070042 LOG(INFO, "Moving disc to bottom slow.\n");
Austin Schuhdff24e22013-03-06 00:41:21 -080043 index_start_position_ += distance_to_below;
44 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080045 }
46 } else {
47 if (index_velocity > 0) {
48 // Now going up. If we didn't see it before, and we don't see it
49 // now but it should be in view, it must still be below. If it were
50 // above, it would be going further away from us.
Austin Schuha3e8e032013-03-10 18:43:14 -070051 LOG(INFO, "Moving fast up, shifting disc down. Disc was at %f\n",
52 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080053 index_start_position_ += distance_to_below;
Austin Schuha3e8e032013-03-10 18:43:14 -070054 LOG(INFO, "Moving fast up, shifting disc down. Disc now at %f\n",
55 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080056 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080057 } else {
Austin Schuha3e8e032013-03-10 18:43:14 -070058 LOG(INFO, "Moving fast down, shifting disc up. Disc was at %f\n",
59 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080060 index_start_position_ -= distance_to_above;
Austin Schuha3e8e032013-03-10 18:43:14 -070061 LOG(INFO, "Moving fast down, shifting disc up. Disc now at %f\n",
62 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080063 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080064 }
65 }
66 }
Austin Schuhdff24e22013-03-06 00:41:21 -080067 return 0.0;
Austin Schuh825bde92013-03-06 00:16:46 -080068}
69
Austin Schuhd78ab542013-03-01 22:22:19 -080070IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
71 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
Austin Schuh93485832013-03-04 00:01:34 -080072 wrist_loop_(new IndexStateFeedbackLoop(MakeIndexLoop())),
Austin Schuhd78ab542013-03-01 22:22:19 -080073 hopper_disc_count_(0),
74 total_disc_count_(0),
Austin Schuh70be1ba2013-03-10 13:37:17 -070075 shot_disc_count_(0),
Austin Schuhf8c52252013-03-03 02:25:49 -080076 safe_goal_(Goal::HOLD),
77 loader_goal_(LoaderGoal::READY),
78 loader_state_(LoaderState::READY),
Austin Schuhd78ab542013-03-01 22:22:19 -080079 loader_up_(false),
80 disc_clamped_(false),
81 disc_ejected_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080082 last_bottom_disc_detect_(false),
Austin Schuh825bde92013-03-06 00:16:46 -080083 last_top_disc_detect_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080084 no_prior_position_(true),
Austin Schuh723770b2013-03-10 13:26:20 -070085 missing_position_count_(0) {
Austin Schuhd78ab542013-03-01 22:22:19 -080086}
87
Austin Schuhf8c52252013-03-03 02:25:49 -080088/*static*/ const double IndexMotor::kTransferStartPosition = 0.0;
89/*static*/ const double IndexMotor::kIndexStartPosition = 0.2159;
90/*static*/ const double IndexMotor::kIndexFreeLength =
91 IndexMotor::ConvertDiscAngleToDiscPosition((360 * 2 + 14) * M_PI / 180);
92/*static*/ const double IndexMotor::kLoaderFreeStopPosition =
93 kIndexStartPosition + kIndexFreeLength;
Austin Schuh1b864a12013-03-07 00:46:50 -080094/*static*/ const double IndexMotor::kReadyToPreload =
95 kLoaderFreeStopPosition - ConvertDiscAngleToDiscPosition(M_PI / 6.0);
Austin Schuhf8c52252013-03-03 02:25:49 -080096/*static*/ const double IndexMotor::kReadyToLiftPosition =
97 kLoaderFreeStopPosition + 0.2921;
98/*static*/ const double IndexMotor::kGrabberLength = 0.03175;
99/*static*/ const double IndexMotor::kGrabberStartPosition =
100 kReadyToLiftPosition - kGrabberLength;
Austin Schuh6328daf2013-03-05 00:53:15 -0800101/*static*/ const double IndexMotor::kGrabberMovementVelocity = 0.7;
Austin Schuhf8c52252013-03-03 02:25:49 -0800102/*static*/ const double IndexMotor::kLifterStopPosition =
103 kReadyToLiftPosition + 0.161925;
104/*static*/ const double IndexMotor::kLifterMovementVelocity = 1.0;
105/*static*/ const double IndexMotor::kEjectorStopPosition =
106 kLifterStopPosition + 0.01;
107/*static*/ const double IndexMotor::kEjectorMovementVelocity = 1.0;
Austin Schuhcc297022013-03-09 23:26:40 -0800108/*static*/ const double IndexMotor::kBottomDiscDetectStart = 0.00;
109/*static*/ const double IndexMotor::kBottomDiscDetectStop = 0.13;
110/*static*/ const double IndexMotor::kBottomDiscIndexDelay = 0.032;
Austin Schuhf8c52252013-03-03 02:25:49 -0800111
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800112// TODO(aschuh): Verify these with the sensor actually on.
Austin Schuh825bde92013-03-06 00:16:46 -0800113/*static*/ const double IndexMotor::kTopDiscDetectStart =
114 (IndexMotor::kLoaderFreeStopPosition -
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800115 IndexMotor::ConvertDiscAngleToDiscPosition(49 * M_PI / 180));
Austin Schuh825bde92013-03-06 00:16:46 -0800116/*static*/ const double IndexMotor::kTopDiscDetectStop =
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800117 (IndexMotor::kLoaderFreeStopPosition +
118 IndexMotor::ConvertDiscAngleToDiscPosition(19 * M_PI / 180));
119
120// I measured the angle between 2 discs. That then gives me the distance
121// between 2 posedges (or negedges). Then subtract off the width of the
122// positive pulse, and that gives the width of the negative pulse.
123/*static*/ const double IndexMotor::kTopDiscDetectMinSeperation =
124 (IndexMotor::ConvertDiscAngleToDiscPosition(120 * M_PI / 180) -
125 (IndexMotor::kTopDiscDetectStop - IndexMotor::kTopDiscDetectStart));
Austin Schuhf8c52252013-03-03 02:25:49 -0800126
Austin Schuhd78ab542013-03-01 22:22:19 -0800127const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
128const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
Austin Schuhf8c52252013-03-03 02:25:49 -0800129const /*static*/ double IndexMotor::kTransferRollerRadius = 1.25 * 0.0254 / 2;
Austin Schuhd78ab542013-03-01 22:22:19 -0800130
Austin Schuhf8c52252013-03-03 02:25:49 -0800131/*static*/ const int IndexMotor::kGrabbingDelay = 5;
Austin Schuha3e8e032013-03-10 18:43:14 -0700132/*static*/ const int IndexMotor::kLiftingDelay = 30;
Austin Schuhf8c52252013-03-03 02:25:49 -0800133/*static*/ const int IndexMotor::kShootingDelay = 5;
134/*static*/ const int IndexMotor::kLoweringDelay = 20;
Austin Schuhd78ab542013-03-01 22:22:19 -0800135
Austin Schuh93485832013-03-04 00:01:34 -0800136// TODO(aschuh): Tune these.
137/*static*/ const double
Austin Schuha3e8e032013-03-10 18:43:14 -0700138 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 6.0;
Austin Schuh93485832013-03-04 00:01:34 -0800139/*static*/ const double
Austin Schuha3e8e032013-03-10 18:43:14 -0700140 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 20;
Austin Schuh93485832013-03-04 00:01:34 -0800141
Austin Schuhd78ab542013-03-01 22:22:19 -0800142// Distance to move the indexer when grabbing a disc.
143const double kNextPosition = 10.0;
144
145/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
146 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
147}
148
Austin Schuhf8c52252013-03-03 02:25:49 -0800149/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
150 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800151 return angle * (kDiscRadius + kRollerRadius);
152}
153
Austin Schuhf8c52252013-03-03 02:25:49 -0800154/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
155 const double position) {
156 return position / (kDiscRadius + kRollerRadius);
157}
158
Austin Schuhd78ab542013-03-01 22:22:19 -0800159/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
160 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
161}
162
163/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
164 return IndexMotor::ConvertDiscAngleToDiscPosition(
165 ConvertIndexToDiscAngle(angle));
166}
167
Austin Schuhf8c52252013-03-03 02:25:49 -0800168/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
169 const double angle) {
170 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
171 kTransferRollerRadius);
172 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
173}
174
175/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
176 const double position) {
177 return IndexMotor::ConvertDiscAngleToIndex(
178 ConvertDiscPositionToDiscAngle(position));
179}
180
Austin Schuh1b864a12013-03-07 00:46:50 -0800181bool IndexMotor::MinDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800182 bool found_start = false;
183 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800184 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800185 if (!found_start) {
186 if (frisbee.has_position()) {
187 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800188 if (found_disc) {
189 *found_disc = &frisbee;
190 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800191 found_start = true;
192 }
193 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800194 if (frisbee.position() <= *disc_position) {
195 *disc_position = frisbee.position();
196 if (found_disc) {
197 *found_disc = &frisbee;
198 }
199 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800200 }
201 }
202 return found_start;
203}
204
Austin Schuh1b864a12013-03-07 00:46:50 -0800205bool IndexMotor::MaxDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800206 bool found_start = false;
207 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800208 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800209 if (!found_start) {
210 if (frisbee.has_position()) {
211 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800212 if (found_disc) {
213 *found_disc = &frisbee;
214 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800215 found_start = true;
216 }
217 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800218 if (frisbee.position() > *disc_position) {
219 *disc_position = frisbee.position();
220 if (found_disc) {
221 *found_disc = &frisbee;
222 }
223 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800224 }
225 }
226 return found_start;
227}
228
Austin Schuh93485832013-03-04 00:01:34 -0800229void IndexMotor::IndexStateFeedbackLoop::CapU() {
230 // If the voltage has been low for a large number of cycles, cut the motor
231 // power. This is generally very bad controls practice since this isn't LTI,
232 // but we don't really care about tracking anything other than large step
233 // inputs, and the loader doesn't need to be that accurate.
234 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
235 ++low_voltage_count_;
236 if (low_voltage_count_ > kNoMotionCuttoffCount) {
237 printf("Limiting power from %f to 0\n", U(0, 0));
238 U(0, 0) = 0.0;
239 }
240 } else {
241 low_voltage_count_ = 0;
242 }
243
244 for (int i = 0; i < kNumOutputs; ++i) {
245 if (U[i] > plant.U_max[i]) {
246 U[i] = plant.U_max[i];
247 } else if (U[i] < plant.U_min[i]) {
248 U[i] = plant.U_min[i];
249 }
250 }
251}
252
253
Austin Schuhd78ab542013-03-01 22:22:19 -0800254// Positive angle is towards the shooter, and positive power is towards the
255// shooter.
256void IndexMotor::RunIteration(
257 const control_loops::IndexLoop::Goal *goal,
258 const control_loops::IndexLoop::Position *position,
259 control_loops::IndexLoop::Output *output,
260 control_loops::IndexLoop::Status *status) {
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800261 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800262 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800263 if (goal->goal_state < 0 || goal->goal_state > 4) {
Brian Silverman94195052013-03-09 13:45:05 -0800264 LOG(ERROR, "Goal state is %"PRId32" which is out of range. Going to HOLD.\n",
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800265 goal->goal_state);
266 goal_enum = Goal::HOLD;
267 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800268
269 // Disable the motors now so that all early returns will return with the
270 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800271 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800272 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800273 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800274 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800275 output->transfer_voltage = 0.0;
276 output->index_voltage = 0.0;
277 }
278
279 status->ready_to_intake = false;
280
Austin Schuhf8c52252013-03-03 02:25:49 -0800281 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800282 if (position) {
283 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800284 // Set the goal to be the current position if this is the first time through
285 // so we don't always spin the indexer to the 0 position before starting.
286 if (no_prior_position_) {
287 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800288 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800289 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800290 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
291 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
292 last_bottom_disc_negedge_wait_count_ =
293 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800294 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800295 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
296 // The open positions for the upper is right here and isn't a hard edge.
Austin Schuh723770b2013-03-10 13:26:20 -0700297 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
298 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800299 }
300
Austin Schuh1b864a12013-03-07 00:46:50 -0800301 // If the cRIO is gone for over 1/2 of a second, assume that it rebooted.
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800302 if (missing_position_count_ > 50) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800303 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
304 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
305 last_bottom_disc_negedge_wait_count_ =
306 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800307 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800308 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
309 // We can't really trust the open range any more if the crio rebooted.
Austin Schuh723770b2013-03-10 13:26:20 -0700310 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
311 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800312 // Adjust the disc positions so that they don't have to move.
313 const double disc_offset =
314 position->index_position - wrist_loop_->X_hat(0, 0);
315 for (auto frisbee = frisbees_.begin();
316 frisbee != frisbees_.end(); ++frisbee) {
317 frisbee->OffsetDisc(disc_offset);
318 }
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800319 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800320 }
321 missing_position_count_ = 0;
322 } else {
323 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800324 }
325 const double index_position = wrist_loop_->X_hat(0, 0);
326
Austin Schuh825bde92013-03-06 00:16:46 -0800327 if (position) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800328 // Reset the open region if we saw a negedge.
Austin Schuh723770b2013-03-10 13:26:20 -0700329 if (position->bottom_disc_negedge_wait_count !=
330 last_bottom_disc_negedge_wait_count_) {
331 // Saw a negedge, must be a new region.
332 lower_open_region_.Restart(position->bottom_disc_negedge_wait_position);
333 }
334 // Reset the open region if we saw a negedge.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800335 if (position->top_disc_negedge_count != last_top_disc_negedge_count_) {
336 // Saw a negedge, must be a new region.
Austin Schuh723770b2013-03-10 13:26:20 -0700337 upper_open_region_.Restart(position->top_disc_negedge_position);
338 }
339
340 // No disc. Expand the open region.
341 if (!position->bottom_disc_detect) {
342 lower_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800343 }
344
345 // No disc. Expand the open region.
346 if (!position->top_disc_detect) {
Austin Schuh723770b2013-03-10 13:26:20 -0700347 upper_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800348 }
349
Austin Schuh825bde92013-03-06 00:16:46 -0800350 if (!position->top_disc_detect) {
351 // We don't see a disc. Verify that there are no discs that we should be
352 // seeing.
Austin Schuh1b864a12013-03-07 00:46:50 -0800353 // Assume that discs will move slow enough that we won't miss one as it
354 // goes by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800355
356 double cumulative_offset = 0.0;
357 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
358 frisbee != rend; ++frisbee) {
359 frisbee->OffsetDisc(cumulative_offset);
360 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh825bde92013-03-06 00:16:46 -0800361 wrist_loop_->X_hat(0, 0), wrist_loop_->X_hat(1, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800362 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800363 }
364 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800365
Austin Schuh825bde92013-03-06 00:16:46 -0800366 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800367 const double index_position = wrist_loop_->X_hat(0, 0) -
368 position->index_position + position->top_disc_posedge_position;
Austin Schuh825bde92013-03-06 00:16:46 -0800369 // TODO(aschuh): Sanity check this number...
370 // Requires storing when the disc was last seen with the sensor off, and
371 // figuring out what to do if things go south.
372
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800373 // 1 if discs are going up, 0 if we have no clue, and -1 if they are going
374 // down.
375 int disc_direction = 0;
Austin Schuh825bde92013-03-06 00:16:46 -0800376 if (wrist_loop_->X_hat(1, 0) > 50.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800377 disc_direction = 1;
378 } else if (wrist_loop_->X_hat(1, 0) < -50.0) {
379 disc_direction = -1;
380 } else {
381 // Save the upper and lower positions that we last saw a disc at.
382 // If there is a big buffer above, must be a disc from below.
383 // If there is a big buffer below, must be a disc from above.
384 // This should work to replace the velocity threshold above.
385
Austin Schuh723770b2013-03-10 13:26:20 -0700386 const double open_width = upper_open_region_.width();
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800387 const double relative_upper_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700388 (upper_open_region_.upper_bound() - index_position) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800389 const double relative_lower_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700390 (index_position - upper_open_region_.lower_bound()) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800391 printf("Width %f upper %f lower %f\n",
392 open_width, relative_upper_open_precentage,
393 relative_lower_open_precentage);
394
395 if (ConvertIndexToDiscPosition(open_width) <
396 kTopDiscDetectMinSeperation * 0.9) {
397 LOG(ERROR, "Discs are way too close to each other. Doing nothing\n");
398 } else if (relative_upper_open_precentage > 0.75) {
399 // Looks like it is a disc going down from above since we are near
400 // the upper edge.
401 disc_direction = -1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700402 LOG(INFO, "Disc edge going down\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800403 } else if (relative_lower_open_precentage > 0.75) {
404 // Looks like it is a disc going up from below since we are near
405 // the lower edge.
406 disc_direction = 1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700407 LOG(INFO, "Disc edge going up\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800408 } else {
409 LOG(ERROR,
410 "Got an edge in the middle of what should be an open region.\n");
411 LOG(ERROR, "Open width: %f upper precentage %f %%\n",
412 open_width, relative_upper_open_precentage);
413 }
414 }
415
416 if (disc_direction > 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800417 // Moving up at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800418 // Find the highest disc that is below the top disc sensor.
419 // While we are at it, count the number above and log an error if there
420 // are too many.
421 if (frisbees_.size() == 0) {
422 Frisbee new_frisbee;
423 new_frisbee.has_been_indexed_ = true;
424 new_frisbee.index_start_position_ = index_position -
425 ConvertDiscPositionToIndex(kTopDiscDetectStart -
426 kIndexStartPosition);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800427 ++hopper_disc_count_;
428 ++total_disc_count_;
Austin Schuh1b864a12013-03-07 00:46:50 -0800429 frisbees_.push_front(new_frisbee);
430 LOG(WARNING, "Added a disc to the hopper at the top sensor\n");
431 }
432
433 int above_disc_count = 0;
434 double highest_position = 0;
435 Frisbee *highest_frisbee_below_sensor = NULL;
436 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
437 frisbee != rend; ++frisbee) {
438 const double disc_position = frisbee->absolute_position(
439 index_position);
440 // It is save to use the top position for the cuttoff, since the
441 // sensor being low will result in discs being pushed off of it.
442 if (disc_position >= kTopDiscDetectStop) {
443 ++above_disc_count;
444 } else if (!highest_frisbee_below_sensor ||
445 disc_position > highest_position) {
446 highest_frisbee_below_sensor = &*frisbee;
447 highest_position = disc_position;
448 }
449 }
450 if (above_disc_count > 1) {
451 LOG(ERROR, "We have 2 discs above the top sensor.\n");
452 }
453
454 // We now have the disc. Shift all the ones below the sensor up by the
455 // computed delta.
456 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
457 highest_position - kTopDiscDetectStart);
458 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
459 frisbee != rend; ++frisbee) {
460 const double disc_position = frisbee->absolute_position(
461 index_position);
462 if (disc_position < kTopDiscDetectStop) {
463 frisbee->OffsetDisc(disc_delta);
464 }
465 }
Austin Schuha3e8e032013-03-10 18:43:14 -0700466 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800467 "Moving down by %f to %f\n", frisbees_.size(),
Austin Schuh1b864a12013-03-07 00:46:50 -0800468 ConvertIndexToDiscPosition(disc_delta),
469 highest_frisbee_below_sensor->absolute_position(
470 wrist_loop_->X_hat(0, 0)));
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800471 } else if (disc_direction < 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800472 // Moving down at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800473 // There can only be 1 disc up top that would give us a posedge.
474 // Find it and place it at the one spot that it can be.
Brian Silverman94195052013-03-09 13:45:05 -0800475 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800476 Frisbee *min_frisbee = NULL;
477 MinDiscPosition(&min_disc_position, &min_frisbee);
478 if (!min_frisbee) {
479 // Uh, oh, we see a disc but there isn't one...
480 LOG(ERROR, "Saw a disc up top but there isn't one in the hopper\n");
481 } else {
482 const double disc_position = min_frisbee->absolute_position(
483 index_position);
484
485 const double disc_delta_meters = disc_position - kTopDiscDetectStop;
486 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
487 disc_delta_meters);
Austin Schuha3e8e032013-03-10 18:43:14 -0700488 LOG(INFO, "Posedge going down. Moving top disc down by %f\n",
489 disc_delta_meters);
Austin Schuh1b864a12013-03-07 00:46:50 -0800490 for (auto frisbee = frisbees_.begin(), end = frisbees_.end();
491 frisbee != end; ++frisbee) {
492 frisbee->OffsetDisc(disc_delta);
493 }
494 }
Austin Schuh825bde92013-03-06 00:16:46 -0800495 } else {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800496 LOG(ERROR, "Not sure how to handle the upper posedge, doing nothing\n");
Austin Schuh825bde92013-03-06 00:16:46 -0800497 }
498 }
499 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800500
Austin Schuhf8c52252013-03-03 02:25:49 -0800501 // Bool to track if it is safe for the goal to change yet.
Austin Schuhd78ab542013-03-01 22:22:19 -0800502 bool safe_to_change_state_ = true;
503 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800504 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800505 // The goal should already be good, so sit tight with everything the same
506 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800507 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800508 case Goal::READY_LOWER:
509 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800510 {
511 Time now = Time::Now();
Austin Schuhd78ab542013-03-01 22:22:19 -0800512 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800513 // Posedge of the disc entering the beam break.
514 if (position->bottom_disc_posedge_count !=
515 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800516 transfer_frisbee_.Reset();
517 transfer_frisbee_.bottom_posedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700518 LOG(INFO, "Posedge of bottom disc %f\n",
519 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800520 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800521 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800522 }
523
524 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800525 if (position->bottom_disc_negedge_count !=
526 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800527 transfer_frisbee_.bottom_negedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700528 LOG(INFO, "Negedge of bottom disc %f\n",
529 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800530 frisbees_.push_front(transfer_frisbee_);
531 }
532
533 if (position->bottom_disc_detect) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800534 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800535 // Must wait until the disc gets out before we can change state.
536 safe_to_change_state_ = false;
537
Austin Schuhf8c52252013-03-03 02:25:49 -0800538 // TODO(aschuh): A disc on the way through needs to start moving
539 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800540
541 Time elapsed_posedge_time = now -
542 transfer_frisbee_.bottom_posedge_time_;
543 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
544 // It has been too long. The disc must be jammed.
545 LOG(ERROR, "Been way too long. Jammed disc?\n");
546 printf("Been way too long. Jammed disc?\n");
547 }
548 }
549
Austin Schuhf8c52252013-03-03 02:25:49 -0800550 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800551 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800552 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800553 if (!frisbee->has_been_indexed_) {
554 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800555
Austin Schuh6328daf2013-03-05 00:53:15 -0800556 if (last_bottom_disc_negedge_wait_count_ !=
557 position->bottom_disc_negedge_wait_count) {
558 // We have an index difference.
559 // Save the indexer position, and the time.
560 if (last_bottom_disc_negedge_wait_count_ + 1 !=
561 position->bottom_disc_negedge_wait_count) {
562 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
563 }
564
565 // Save the captured position as the position at which the disc
566 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800567 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
568 printf("Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800569 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800570 frisbee->index_start_position_ =
571 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800572 }
573 }
Austin Schuhb6d898b2013-03-03 15:34:35 -0800574 if (!frisbee->has_been_indexed_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800575 // All discs must be indexed before it is safe to stop indexing.
Austin Schuhd78ab542013-03-01 22:22:19 -0800576 safe_to_change_state_ = false;
577 }
578 }
579
Austin Schuhf8c52252013-03-03 02:25:49 -0800580 // Figure out where the indexer should be to move the discs down to
581 // the right position.
Brian Silverman94195052013-03-09 13:45:05 -0800582 double max_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800583 if (MaxDiscPosition(&max_disc_position, NULL)) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700584 LOG(DEBUG, "There is a disc down here!\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800585 // TODO(aschuh): Figure out what to do if grabbing the next one
586 // would cause things to jam into the loader.
587 // Say we aren't ready any more. Undefined behavior will result if
588 // that isn't observed.
589 double bottom_disc_position =
590 max_disc_position + ConvertDiscAngleToIndex(M_PI);
591 wrist_loop_->R << bottom_disc_position, 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800592
Austin Schuhf8c52252013-03-03 02:25:49 -0800593 // Verify that we are close enough to the goal so that we should be
594 // fine accepting the next disc.
595 double disc_error_meters = ConvertIndexToDiscPosition(
596 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
597 // We are ready for the next disc if the first one is in the first
598 // half circle of the indexer. It will take time for the disc to
599 // come into the indexer, so we will be able to move it out of the
600 // way in time.
601 // This choice also makes sure that we don't claim that we aren't
602 // ready between full speed intaking.
603 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
604 disc_error_meters < 0.04) {
605 // We are only ready if we aren't being asked to change state or
606 // are full.
607 status->ready_to_intake =
608 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
609 } else {
610 status->ready_to_intake = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800611 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800612 } else {
613 // No discs! We are always ready for more if we aren't being
614 // asked to change state.
615 status->ready_to_intake = (safe_goal_ == goal_enum);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800616 printf("Ready to intake, zero discs. %d %d %d\n",
617 status->ready_to_intake, hopper_disc_count_, safe_goal_);
Austin Schuhd78ab542013-03-01 22:22:19 -0800618 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800619
620 // Turn on the transfer roller if we are ready.
621 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
622 safe_goal_ == Goal::INTAKE) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800623 intake_voltage = transfer_voltage = 12.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800624 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800625 }
Austin Schuha3e8e032013-03-10 18:43:14 -0700626 LOG(DEBUG, "INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800627 }
628 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800629 case Goal::READY_SHOOTER:
630 case Goal::SHOOT:
631 // Check if we have any discs to shoot or load and handle them.
Brian Silverman94195052013-03-09 13:45:05 -0800632 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800633 if (MinDiscPosition(&min_disc_position, NULL)) {
634 const double ready_disc_position = min_disc_position +
635 ConvertDiscPositionToIndex(kReadyToPreload - kIndexStartPosition);
Austin Schuhf8c52252013-03-03 02:25:49 -0800636
637 const double grabbed_disc_position =
638 min_disc_position +
639 ConvertDiscPositionToIndex(kReadyToLiftPosition -
640 kIndexStartPosition + 0.03);
641
642 // Check the state of the loader FSM.
643 // If it is ready to load discs, position the disc so that it is ready
644 // to be grabbed.
645 // If it isn't ready, there is a disc in there. It needs to finish it's
646 // cycle first.
647 if (loader_state_ != LoaderState::READY) {
648 // We already have a disc in the loader.
649 // Stage the discs back a bit.
650 wrist_loop_->R << ready_disc_position, 0.0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800651 printf("Loader not ready but asked to shoot\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800652
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800653 // Shoot if we are grabbed and being asked to shoot.
654 if (loader_state_ == LoaderState::GRABBED &&
655 safe_goal_ == Goal::SHOOT) {
656 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
657 }
658
Austin Schuhf8c52252013-03-03 02:25:49 -0800659 // Must wait until it has been grabbed to continue.
660 if (loader_state_ == LoaderState::GRABBING) {
661 safe_to_change_state_ = false;
662 }
663 } else {
664 // No disc up top right now.
665 wrist_loop_->R << grabbed_disc_position, 0.0;
666
667 // See if the disc has gotten pretty far up yet.
668 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
669 // Point of no return. We are committing to grabbing it now.
670 safe_to_change_state_ = false;
671 const double robust_grabbed_disc_position =
672 (grabbed_disc_position -
673 ConvertDiscPositionToIndex(kGrabberLength));
674
675 // If close, start grabbing and/or shooting.
676 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
677 // Start the state machine.
678 if (safe_goal_ == Goal::SHOOT) {
679 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
680 } else {
681 loader_goal_ = LoaderGoal::GRAB;
682 }
683 // This frisbee is now gone. Take it out of the queue.
684 frisbees_.pop_back();
Austin Schuhf8c52252013-03-03 02:25:49 -0800685 }
686 }
687 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800688 } else {
689 if (loader_state_ != LoaderState::READY) {
690 // Shoot if we are grabbed and being asked to shoot.
691 if (loader_state_ == LoaderState::GRABBED &&
692 safe_goal_ == Goal::SHOOT) {
693 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
694 }
695 } else {
696 // Ok, no discs in sight. Spin the hopper up by 150% of it's full
697 // range and verify that we don't see anything.
698 printf("Moving the indexer to verify that it is clear\n");
699 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700700 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700701 lower_open_region_.lower_bound()) +
Austin Schuha3e8e032013-03-10 18:43:14 -0700702 ConvertDiscPositionToIndex(kIndexFreeLength) * 2.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800703
704 wrist_loop_->R << hopper_clear_verification_position, 0.0;
705 if (::std::abs(wrist_loop_->X_hat(0, 0) -
706 hopper_clear_verification_position) <
707 ConvertDiscPositionToIndex(0.05)) {
708 printf("Should be empty\n");
709 // We are at the end of the range. There are no more discs here.
710 while (frisbees_.size() > 0) {
711 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
712 frisbees_.pop_back();
713 --hopper_disc_count_;
714 --total_disc_count_;
715 }
716 if (hopper_disc_count_ != 0) {
717 LOG(ERROR,
718 "Emptied the hopper out but there are still discs there\n");
719 }
720 }
721 }
722 }
723
724 {
725 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700726 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700727 lower_open_region_.lower_bound()) +
Austin Schuha3e8e032013-03-10 18:43:14 -0700728 ConvertDiscPositionToIndex(kIndexFreeLength) * 2.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800729
730 if (wrist_loop_->X_hat(0, 0) >
731 hopper_clear_verification_position +
732 ConvertDiscPositionToIndex(0.05)) {
733 // We are at the end of the range. There are no more discs here.
734 while (frisbees_.size() > 0) {
735 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuha3e8e032013-03-10 18:43:14 -0700736 LOG(ERROR, "Upper is [%f %f]\n",
737 upper_open_region_.upper_bound(),
738 upper_open_region_.lower_bound());
739 LOG(ERROR, "Lower is [%f %f]\n",
740 lower_open_region_.upper_bound(),
741 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800742 frisbees_.pop_back();
743 --hopper_disc_count_;
744 --total_disc_count_;
745 }
746 if (hopper_disc_count_ != 0) {
747 LOG(ERROR,
748 "Emptied the hopper out but there are still discs there\n");
749 }
750 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800751 }
752
Austin Schuha3e8e032013-03-10 18:43:14 -0700753 LOG(DEBUG, "READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800754 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800755 }
756
757 // The only way out of the loader is to shoot the disc. The FSM can only go
758 // forwards.
759 switch (loader_state_) {
760 case LoaderState::READY:
Austin Schuha3e8e032013-03-10 18:43:14 -0700761 LOG(DEBUG, "Loader READY\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800762 // Open and down, ready to accept a disc.
763 loader_up_ = false;
764 disc_clamped_ = false;
765 disc_ejected_ = false;
766 if (loader_goal_ == LoaderGoal::GRAB ||
767 loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
768 if (loader_goal_ == LoaderGoal::GRAB) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700769 LOG(INFO, "Told to GRAB, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800770 } else {
Austin Schuha3e8e032013-03-10 18:43:14 -0700771 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800772 }
773 loader_state_ = LoaderState::GRABBING;
774 loader_countdown_ = kGrabbingDelay;
775 } else {
776 break;
777 }
778 case LoaderState::GRABBING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700779 LOG(DEBUG, "Loader GRABBING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800780 // Closing the grabber.
781 loader_up_ = false;
782 disc_clamped_ = true;
783 disc_ejected_ = false;
784 if (loader_countdown_ > 0) {
785 --loader_countdown_;
786 break;
787 } else {
788 loader_state_ = LoaderState::GRABBED;
789 }
790 case LoaderState::GRABBED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700791 LOG(DEBUG, "Loader GRABBED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800792 // Grabber closed.
793 loader_up_ = false;
794 disc_clamped_ = true;
795 disc_ejected_ = false;
796 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700797 shooter.status.FetchLatest();
798 if (shooter.status.get()) {
799 // TODO(aschuh): If we aren't shooting nicely, wait until the shooter
800 // is up to speed rather than just spinning.
801 if (shooter.status->average_velocity > 130) {
802 loader_state_ = LoaderState::LIFTING;
803 loader_countdown_ = kLiftingDelay;
804 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
805 } else {
806 LOG(WARNING, "Told to SHOOT_AND_RESET, shooter too slow at %f\n",
807 shooter.status->average_velocity);
808 break;
809 }
810 } else {
811 LOG(ERROR, "Told to SHOOT_AND_RESET, no shooter data, moving on.\n");
812 loader_state_ = LoaderState::LIFTING;
813 loader_countdown_ = kLiftingDelay;
814 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800815 } else if (loader_goal_ == LoaderGoal::READY) {
816 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
817 printf("Can't go to ready when we have something grabbed.\n");
818 break;
819 } else {
820 break;
821 }
822 case LoaderState::LIFTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700823 LOG(DEBUG, "Loader LIFTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800824 // Lifting the disc.
825 loader_up_ = true;
826 disc_clamped_ = true;
827 disc_ejected_ = false;
828 if (loader_countdown_ > 0) {
829 --loader_countdown_;
830 break;
831 } else {
832 loader_state_ = LoaderState::LIFTED;
833 }
834 case LoaderState::LIFTED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700835 LOG(DEBUG, "Loader LIFTED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800836 // Disc lifted. Time to eject it out.
837 loader_up_ = true;
838 disc_clamped_ = true;
839 disc_ejected_ = false;
840 loader_state_ = LoaderState::SHOOTING;
841 loader_countdown_ = kShootingDelay;
842 case LoaderState::SHOOTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700843 LOG(DEBUG, "Loader SHOOTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800844 // Ejecting the disc into the shooter.
845 loader_up_ = true;
846 disc_clamped_ = false;
847 disc_ejected_ = true;
848 if (loader_countdown_ > 0) {
849 --loader_countdown_;
850 break;
851 } else {
852 loader_state_ = LoaderState::SHOOT;
853 }
854 case LoaderState::SHOOT:
Austin Schuha3e8e032013-03-10 18:43:14 -0700855 LOG(DEBUG, "Loader SHOOT\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800856 // The disc has been shot.
857 loader_up_ = true;
858 disc_clamped_ = false;
859 disc_ejected_ = true;
860 loader_state_ = LoaderState::LOWERING;
861 loader_countdown_ = kLoweringDelay;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800862 --hopper_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700863 ++shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800864 case LoaderState::LOWERING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700865 LOG(DEBUG, "Loader LOWERING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800866 // Lowering the loader back down.
867 loader_up_ = false;
868 disc_clamped_ = false;
869 disc_ejected_ = true;
870 if (loader_countdown_ > 0) {
871 --loader_countdown_;
872 break;
873 } else {
874 loader_state_ = LoaderState::LOWERED;
875 }
876 case LoaderState::LOWERED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700877 LOG(DEBUG, "Loader LOWERED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800878 // The indexer is lowered.
879 loader_up_ = false;
880 disc_clamped_ = false;
881 disc_ejected_ = false;
882 loader_state_ = LoaderState::READY;
883 // Once we have shot, we need to hang out in READY until otherwise
884 // notified.
885 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800886 break;
887 }
888
889 // Update the observer.
890 wrist_loop_->Update(position != NULL, output == NULL);
891
892 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800893 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800894 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -0800895 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -0800896 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
897 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
898 last_bottom_disc_negedge_wait_count_ =
899 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800900 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800901 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -0800902 }
903
904 status->hopper_disc_count = hopper_disc_count_;
905 status->total_disc_count = total_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700906 status->shot_disc_count = shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800907 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800908
909 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800910 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -0800911 output->transfer_voltage = transfer_voltage;
Austin Schuhd78ab542013-03-01 22:22:19 -0800912 output->index_voltage = wrist_loop_->U(0, 0);
Austin Schuhf8c52252013-03-03 02:25:49 -0800913 output->loader_up = loader_up_;
914 output->disc_clamped = disc_clamped_;
915 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800916 }
917
918 if (safe_to_change_state_) {
919 safe_goal_ = goal_enum;
920 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800921 if (hopper_disc_count_ < 0) {
922 LOG(ERROR, "NEGATIVE DISCS. VERY VERY BAD\n");
923 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800924}
925
926} // namespace control_loops
927} // namespace frc971