blob: d3ef6522f9dbb21728747b8eb1d4d7cd0fa1ca2c [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 Schuhd3d0fbf2013-03-14 00:37:00 -070089/*static*/ const ::aos::time::Time IndexMotor::kTransferOffDelay =
Brian Silvermanb7bcef12013-03-16 13:57:11 -070090 ::aos::time::Time::InSeconds(0.3);
Austin Schuhf8c52252013-03-03 02:25:49 -080091
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080092// TODO(aschuh): Verify these with the sensor actually on.
Austin Schuh825bde92013-03-06 00:16:46 -080093/*static*/ const double IndexMotor::kTopDiscDetectStart =
94 (IndexMotor::kLoaderFreeStopPosition -
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080095 IndexMotor::ConvertDiscAngleToDiscPosition(49 * M_PI / 180));
Austin Schuh825bde92013-03-06 00:16:46 -080096/*static*/ const double IndexMotor::kTopDiscDetectStop =
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080097 (IndexMotor::kLoaderFreeStopPosition +
98 IndexMotor::ConvertDiscAngleToDiscPosition(19 * M_PI / 180));
99
100// I measured the angle between 2 discs. That then gives me the distance
101// between 2 posedges (or negedges). Then subtract off the width of the
102// positive pulse, and that gives the width of the negative pulse.
103/*static*/ const double IndexMotor::kTopDiscDetectMinSeperation =
104 (IndexMotor::ConvertDiscAngleToDiscPosition(120 * M_PI / 180) -
105 (IndexMotor::kTopDiscDetectStop - IndexMotor::kTopDiscDetectStart));
Austin Schuhf8c52252013-03-03 02:25:49 -0800106
Austin Schuhd78ab542013-03-01 22:22:19 -0800107const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
108const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
Austin Schuhf8c52252013-03-03 02:25:49 -0800109const /*static*/ double IndexMotor::kTransferRollerRadius = 1.25 * 0.0254 / 2;
Austin Schuhd78ab542013-03-01 22:22:19 -0800110
Austin Schuhf8c52252013-03-03 02:25:49 -0800111/*static*/ const int IndexMotor::kGrabbingDelay = 5;
Austin Schuha3e8e032013-03-10 18:43:14 -0700112/*static*/ const int IndexMotor::kLiftingDelay = 30;
Austin Schuhf8c52252013-03-03 02:25:49 -0800113/*static*/ const int IndexMotor::kShootingDelay = 5;
114/*static*/ const int IndexMotor::kLoweringDelay = 20;
Austin Schuhd78ab542013-03-01 22:22:19 -0800115
Austin Schuh93485832013-03-04 00:01:34 -0800116// TODO(aschuh): Tune these.
117/*static*/ const double
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700118 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 11.0;
Austin Schuh93485832013-03-04 00:01:34 -0800119/*static*/ const double
Austin Schuha3e8e032013-03-10 18:43:14 -0700120 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 20;
Austin Schuh93485832013-03-04 00:01:34 -0800121
Austin Schuhd78ab542013-03-01 22:22:19 -0800122/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
123 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
124}
125
Austin Schuhf8c52252013-03-03 02:25:49 -0800126/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
127 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800128 return angle * (kDiscRadius + kRollerRadius);
129}
130
Austin Schuhf8c52252013-03-03 02:25:49 -0800131/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
132 const double position) {
133 return position / (kDiscRadius + kRollerRadius);
134}
135
Austin Schuhd78ab542013-03-01 22:22:19 -0800136/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
137 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
138}
139
140/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
141 return IndexMotor::ConvertDiscAngleToDiscPosition(
142 ConvertIndexToDiscAngle(angle));
143}
144
Austin Schuhf8c52252013-03-03 02:25:49 -0800145/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
146 const double angle) {
147 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
148 kTransferRollerRadius);
149 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
150}
151
152/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
153 const double position) {
154 return IndexMotor::ConvertDiscAngleToIndex(
155 ConvertDiscPositionToDiscAngle(position));
156}
157
Austin Schuh1b864a12013-03-07 00:46:50 -0800158bool IndexMotor::MinDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800159 bool found_start = false;
160 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800161 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800162 if (!found_start) {
163 if (frisbee.has_position()) {
164 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800165 if (found_disc) {
166 *found_disc = &frisbee;
167 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800168 found_start = true;
169 }
170 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800171 if (frisbee.position() <= *disc_position) {
172 *disc_position = frisbee.position();
173 if (found_disc) {
174 *found_disc = &frisbee;
175 }
176 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800177 }
178 }
179 return found_start;
180}
181
Austin Schuh1b864a12013-03-07 00:46:50 -0800182bool IndexMotor::MaxDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800183 bool found_start = false;
184 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800185 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800186 if (!found_start) {
187 if (frisbee.has_position()) {
188 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800189 if (found_disc) {
190 *found_disc = &frisbee;
191 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800192 found_start = true;
193 }
194 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800195 if (frisbee.position() > *disc_position) {
196 *disc_position = frisbee.position();
197 if (found_disc) {
198 *found_disc = &frisbee;
199 }
200 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800201 }
202 }
203 return found_start;
204}
205
Austin Schuh93485832013-03-04 00:01:34 -0800206void IndexMotor::IndexStateFeedbackLoop::CapU() {
207 // If the voltage has been low for a large number of cycles, cut the motor
208 // power. This is generally very bad controls practice since this isn't LTI,
209 // but we don't really care about tracking anything other than large step
210 // inputs, and the loader doesn't need to be that accurate.
211 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
212 ++low_voltage_count_;
213 if (low_voltage_count_ > kNoMotionCuttoffCount) {
Austin Schuh93485832013-03-04 00:01:34 -0800214 U(0, 0) = 0.0;
215 }
216 } else {
217 low_voltage_count_ = 0;
218 }
219
220 for (int i = 0; i < kNumOutputs; ++i) {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700221 if (U(i, 0) > U_max(i, 0)) {
222 U(i, 0) = U_max(i, 0);
223 } else if (U(i, 0) < U_min(i, 0)) {
224 U(i, 0) = U_min(i, 0);
Austin Schuh93485832013-03-04 00:01:34 -0800225 }
226 }
227}
228
229
Austin Schuhd78ab542013-03-01 22:22:19 -0800230// Positive angle is towards the shooter, and positive power is towards the
231// shooter.
232void IndexMotor::RunIteration(
233 const control_loops::IndexLoop::Goal *goal,
234 const control_loops::IndexLoop::Position *position,
235 control_loops::IndexLoop::Output *output,
236 control_loops::IndexLoop::Status *status) {
Austin Schuhd3d0fbf2013-03-14 00:37:00 -0700237 Time now = Time::Now();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800238 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800239 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800240 if (goal->goal_state < 0 || goal->goal_state > 4) {
Brian Silverman94195052013-03-09 13:45:05 -0800241 LOG(ERROR, "Goal state is %"PRId32" which is out of range. Going to HOLD.\n",
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800242 goal->goal_state);
243 goal_enum = Goal::HOLD;
244 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800245
246 // Disable the motors now so that all early returns will return with the
247 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800248 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800249 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800250 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800251 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800252 output->transfer_voltage = 0.0;
253 output->index_voltage = 0.0;
254 }
255
256 status->ready_to_intake = false;
257
Austin Schuhe3490622013-03-13 01:24:30 -0700258 // Set the controller to use to be the one designed for the current number of
259 // discs in the hopper. This is safe since the controller prevents the index
260 // from being set out of bounds and picks the closest controller.
261 wrist_loop_->set_controller_index(frisbees_.size());
262
Austin Schuhf8c52252013-03-03 02:25:49 -0800263 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800264 if (position) {
265 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800266 // Set the goal to be the current position if this is the first time through
267 // so we don't always spin the indexer to the 0 position before starting.
268 if (no_prior_position_) {
Brian Silverman43bb73e2013-03-17 13:39:47 -0700269 LOG(INFO, "no prior position; resetting\n");
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800270 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800271 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800272 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800273 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
274 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
275 last_bottom_disc_negedge_wait_count_ =
276 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800277 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800278 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
279 // The open positions for the upper is right here and isn't a hard edge.
Austin Schuh723770b2013-03-10 13:26:20 -0700280 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
281 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800282 }
283
Austin Schuh1b864a12013-03-07 00:46:50 -0800284 // If the cRIO is gone for over 1/2 of a second, assume that it rebooted.
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800285 if (missing_position_count_ > 50) {
Brian Silverman43bb73e2013-03-17 13:39:47 -0700286 LOG(INFO, "assuming cRIO rebooted\n");
Austin Schuh6328daf2013-03-05 00:53:15 -0800287 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
288 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
289 last_bottom_disc_negedge_wait_count_ =
290 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800291 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800292 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
293 // We can't really trust the open range any more if the crio rebooted.
Austin Schuh723770b2013-03-10 13:26:20 -0700294 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
295 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800296 // Adjust the disc positions so that they don't have to move.
297 const double disc_offset =
298 position->index_position - wrist_loop_->X_hat(0, 0);
299 for (auto frisbee = frisbees_.begin();
300 frisbee != frisbees_.end(); ++frisbee) {
301 frisbee->OffsetDisc(disc_offset);
302 }
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800303 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800304 }
305 missing_position_count_ = 0;
306 } else {
307 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800308 }
309 const double index_position = wrist_loop_->X_hat(0, 0);
310
Austin Schuh825bde92013-03-06 00:16:46 -0800311 if (position) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800312 // Reset the open region if we saw a negedge.
Austin Schuh723770b2013-03-10 13:26:20 -0700313 if (position->bottom_disc_negedge_wait_count !=
314 last_bottom_disc_negedge_wait_count_) {
315 // Saw a negedge, must be a new region.
316 lower_open_region_.Restart(position->bottom_disc_negedge_wait_position);
317 }
318 // Reset the open region if we saw a negedge.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800319 if (position->top_disc_negedge_count != last_top_disc_negedge_count_) {
320 // Saw a negedge, must be a new region.
Austin Schuh723770b2013-03-10 13:26:20 -0700321 upper_open_region_.Restart(position->top_disc_negedge_position);
322 }
323
324 // No disc. Expand the open region.
325 if (!position->bottom_disc_detect) {
326 lower_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800327 }
328
329 // No disc. Expand the open region.
330 if (!position->top_disc_detect) {
Austin Schuh723770b2013-03-10 13:26:20 -0700331 upper_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800332 }
333
Austin Schuh825bde92013-03-06 00:16:46 -0800334 if (!position->top_disc_detect) {
335 // We don't see a disc. Verify that there are no discs that we should be
336 // seeing.
Austin Schuh1b864a12013-03-07 00:46:50 -0800337 // Assume that discs will move slow enough that we won't miss one as it
338 // goes by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800339
340 double cumulative_offset = 0.0;
341 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
342 frisbee != rend; ++frisbee) {
343 frisbee->OffsetDisc(cumulative_offset);
344 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh6b1ad2f2013-03-11 23:23:00 -0700345 wrist_loop_->X_hat(0, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800346 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800347 }
348 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800349
Austin Schuh825bde92013-03-06 00:16:46 -0800350 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800351 const double index_position = wrist_loop_->X_hat(0, 0) -
352 position->index_position + position->top_disc_posedge_position;
Austin Schuh825bde92013-03-06 00:16:46 -0800353 // TODO(aschuh): Sanity check this number...
354 // Requires storing when the disc was last seen with the sensor off, and
355 // figuring out what to do if things go south.
356
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800357 // 1 if discs are going up, 0 if we have no clue, and -1 if they are going
358 // down.
359 int disc_direction = 0;
Austin Schuh825bde92013-03-06 00:16:46 -0800360 if (wrist_loop_->X_hat(1, 0) > 50.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800361 disc_direction = 1;
362 } else if (wrist_loop_->X_hat(1, 0) < -50.0) {
363 disc_direction = -1;
364 } else {
365 // Save the upper and lower positions that we last saw a disc at.
366 // If there is a big buffer above, must be a disc from below.
367 // If there is a big buffer below, must be a disc from above.
368 // This should work to replace the velocity threshold above.
369
Austin Schuh723770b2013-03-10 13:26:20 -0700370 const double open_width = upper_open_region_.width();
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800371 const double relative_upper_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700372 (upper_open_region_.upper_bound() - index_position) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800373 const double relative_lower_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700374 (index_position - upper_open_region_.lower_bound()) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800375
376 if (ConvertIndexToDiscPosition(open_width) <
377 kTopDiscDetectMinSeperation * 0.9) {
378 LOG(ERROR, "Discs are way too close to each other. Doing nothing\n");
379 } else if (relative_upper_open_precentage > 0.75) {
380 // Looks like it is a disc going down from above since we are near
381 // the upper edge.
382 disc_direction = -1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700383 LOG(INFO, "Disc edge going down\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800384 } else if (relative_lower_open_precentage > 0.75) {
385 // Looks like it is a disc going up from below since we are near
386 // the lower edge.
387 disc_direction = 1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700388 LOG(INFO, "Disc edge going up\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800389 } else {
390 LOG(ERROR,
391 "Got an edge in the middle of what should be an open region.\n");
392 LOG(ERROR, "Open width: %f upper precentage %f %%\n",
393 open_width, relative_upper_open_precentage);
394 }
395 }
396
397 if (disc_direction > 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800398 // Moving up at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800399 // Find the highest disc that is below the top disc sensor.
400 // While we are at it, count the number above and log an error if there
401 // are too many.
402 if (frisbees_.size() == 0) {
403 Frisbee new_frisbee;
404 new_frisbee.has_been_indexed_ = true;
405 new_frisbee.index_start_position_ = index_position -
406 ConvertDiscPositionToIndex(kTopDiscDetectStart -
407 kIndexStartPosition);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800408 ++hopper_disc_count_;
409 ++total_disc_count_;
Austin Schuh1b864a12013-03-07 00:46:50 -0800410 frisbees_.push_front(new_frisbee);
411 LOG(WARNING, "Added a disc to the hopper at the top sensor\n");
412 }
413
414 int above_disc_count = 0;
415 double highest_position = 0;
416 Frisbee *highest_frisbee_below_sensor = NULL;
417 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
418 frisbee != rend; ++frisbee) {
419 const double disc_position = frisbee->absolute_position(
420 index_position);
421 // It is save to use the top position for the cuttoff, since the
422 // sensor being low will result in discs being pushed off of it.
423 if (disc_position >= kTopDiscDetectStop) {
424 ++above_disc_count;
425 } else if (!highest_frisbee_below_sensor ||
426 disc_position > highest_position) {
427 highest_frisbee_below_sensor = &*frisbee;
428 highest_position = disc_position;
429 }
430 }
Austin Schuh09e07082013-03-19 10:04:12 +0000431
432 if (!highest_frisbee_below_sensor) {
433 Frisbee new_frisbee;
434 new_frisbee.has_been_indexed_ = true;
435 new_frisbee.index_start_position_ = index_position -
436 ConvertDiscPositionToIndex(kTopDiscDetectStart -
437 kIndexStartPosition);
438 highest_position = kTopDiscDetectStart;
439 ++hopper_disc_count_;
440 ++total_disc_count_;
441 frisbees_.push_front(new_frisbee);
442 LOG(WARNING, "Added a disc to the hopper at the top sensor because the one we know about is up top\n");
443 }
444
Austin Schuh1b864a12013-03-07 00:46:50 -0800445 if (above_disc_count > 1) {
446 LOG(ERROR, "We have 2 discs above the top sensor.\n");
447 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800448 // We now have the disc. Shift all the ones below the sensor up by the
449 // computed delta.
450 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
451 highest_position - kTopDiscDetectStart);
452 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
453 frisbee != rend; ++frisbee) {
454 const double disc_position = frisbee->absolute_position(
455 index_position);
456 if (disc_position < kTopDiscDetectStop) {
Austin Schuh59004d32013-03-21 04:31:45 +0000457 LOG(INFO, "Moving disc down by %f meters, since it is at %f and top is [%f, %f]\n",
458 ConvertIndexToDiscPosition(disc_delta),
459 disc_position, kTopDiscDetectStart,
460 kTopDiscDetectStop);
Austin Schuh1b864a12013-03-07 00:46:50 -0800461 frisbee->OffsetDisc(disc_delta);
462 }
463 }
Brian Silverman43bb73e2013-03-17 13:39:47 -0700464 if (highest_frisbee_below_sensor) {
465 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
466 "Moving down by %f to %f\n", frisbees_.size(),
467 ConvertIndexToDiscPosition(disc_delta),
468 highest_frisbee_below_sensor->absolute_position(
469 wrist_loop_->X_hat(0, 0)));
470 } else {
471 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
472 "Moving down by %f\n", frisbees_.size(),
473 ConvertIndexToDiscPosition(disc_delta));
474 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800475 } else if (disc_direction < 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800476 // Moving down at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800477 // There can only be 1 disc up top that would give us a posedge.
478 // Find it and place it at the one spot that it can be.
Brian Silverman94195052013-03-09 13:45:05 -0800479 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800480 Frisbee *min_frisbee = NULL;
481 MinDiscPosition(&min_disc_position, &min_frisbee);
482 if (!min_frisbee) {
483 // Uh, oh, we see a disc but there isn't one...
484 LOG(ERROR, "Saw a disc up top but there isn't one in the hopper\n");
485 } else {
486 const double disc_position = min_frisbee->absolute_position(
487 index_position);
488
489 const double disc_delta_meters = disc_position - kTopDiscDetectStop;
490 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
491 disc_delta_meters);
Austin Schuha3e8e032013-03-10 18:43:14 -0700492 LOG(INFO, "Posedge going down. Moving top disc down by %f\n",
493 disc_delta_meters);
Austin Schuh1b864a12013-03-07 00:46:50 -0800494 for (auto frisbee = frisbees_.begin(), end = frisbees_.end();
495 frisbee != end; ++frisbee) {
496 frisbee->OffsetDisc(disc_delta);
497 }
498 }
Austin Schuh825bde92013-03-06 00:16:46 -0800499 } else {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800500 LOG(ERROR, "Not sure how to handle the upper posedge, doing nothing\n");
Austin Schuh825bde92013-03-06 00:16:46 -0800501 }
502 }
503 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800504
Austin Schuhf8c52252013-03-03 02:25:49 -0800505 // Bool to track if it is safe for the goal to change yet.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700506 bool safe_to_change_state = true;
Brian Silvermanf0716e82013-03-17 23:36:11 -0700507 if (!position) {
508 // This fixes a nasty indexer bug.
509 // If we didn't get a position this cycle, we don't run the code below which
510 // checks the state of the disc detect sensor and whether all the discs are
511 // indexed. It is therefore not safe to change state and loose track of
512 // that disc.
513 safe_to_change_state = false;
514 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800515 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800516 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800517 // The goal should already be good, so sit tight with everything the same
518 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800519 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800520 case Goal::READY_LOWER:
521 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800522 {
Austin Schuhd78ab542013-03-01 22:22:19 -0800523 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800524 // Posedge of the disc entering the beam break.
525 if (position->bottom_disc_posedge_count !=
526 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800527 transfer_frisbee_.Reset();
528 transfer_frisbee_.bottom_posedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700529 LOG(INFO, "Posedge of bottom disc %f\n",
530 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800531 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800532 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800533 }
534
535 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800536 if (position->bottom_disc_negedge_count !=
537 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800538 transfer_frisbee_.bottom_negedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700539 LOG(INFO, "Negedge of bottom disc %f\n",
540 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800541 frisbees_.push_front(transfer_frisbee_);
542 }
543
544 if (position->bottom_disc_detect) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800545 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800546 // Must wait until the disc gets out before we can change state.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700547 safe_to_change_state = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800548
Austin Schuhf8c52252013-03-03 02:25:49 -0800549 // TODO(aschuh): A disc on the way through needs to start moving
550 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800551
552 Time elapsed_posedge_time = now -
553 transfer_frisbee_.bottom_posedge_time_;
554 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
555 // It has been too long. The disc must be jammed.
556 LOG(ERROR, "Been way too long. Jammed disc?\n");
Brian Silvermanf0716e82013-03-17 23:36:11 -0700557 intake_voltage = 0.0;
558 transfer_voltage = -12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800559 }
560 }
561
Austin Schuhf8c52252013-03-03 02:25:49 -0800562 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800563 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800564 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800565 if (!frisbee->has_been_indexed_) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800566 if (last_bottom_disc_negedge_wait_count_ !=
567 position->bottom_disc_negedge_wait_count) {
568 // We have an index difference.
569 // Save the indexer position, and the time.
570 if (last_bottom_disc_negedge_wait_count_ + 1 !=
571 position->bottom_disc_negedge_wait_count) {
572 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
573 }
574
575 // Save the captured position as the position at which the disc
576 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800577 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800578 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800579 frisbee->index_start_position_ =
580 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800581 }
582 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800583 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800584 }
Austin Schuh59004d32013-03-21 04:31:45 +0000585 for (auto frisbee = frisbees_.begin();
586 frisbee != frisbees_.end(); ++frisbee) {
587 if (!frisbee->has_been_indexed_) {
588 intake_voltage = transfer_voltage = 12.0;
589
590 // All discs must be indexed before it is safe to stop indexing.
591 safe_to_change_state = false;
592 }
593 }
594
595 // Figure out where the indexer should be to move the discs down to
596 // the right position.
597 double max_disc_position = 0;
598 if (MaxDiscPosition(&max_disc_position, NULL)) {
599 LOG(DEBUG, "There is a disc down here!\n");
600 // TODO(aschuh): Figure out what to do if grabbing the next one
601 // would cause things to jam into the loader.
602 // Say we aren't ready any more. Undefined behavior will result if
603 // that isn't observed.
604 double bottom_disc_position =
605 max_disc_position + ConvertDiscAngleToIndex(M_PI);
606 wrist_loop_->R << bottom_disc_position, 0.0;
607
608 // Verify that we are close enough to the goal so that we should be
609 // fine accepting the next disc.
610 double disc_error_meters = ConvertIndexToDiscPosition(
611 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
612 // We are ready for the next disc if the first one is in the first
613 // half circle of the indexer. It will take time for the disc to
614 // come into the indexer, so we will be able to move it out of the
615 // way in time.
616 // This choice also makes sure that we don't claim that we aren't
617 // ready between full speed intaking.
618 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
619 disc_error_meters < 0.04) {
620 // We are only ready if we aren't being asked to change state or
621 // are full.
622 status->ready_to_intake =
623 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
624 } else {
625 status->ready_to_intake = false;
626 }
627 } else {
628 // No discs! We are always ready for more if we aren't being
629 // asked to change state.
630 status->ready_to_intake = (safe_goal_ == goal_enum);
631 }
632
633 // Turn on the transfer roller if we are ready.
634 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
635 safe_goal_ == Goal::INTAKE) {
636 intake_voltage = transfer_voltage = 12.0;
637 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800638 }
Austin Schuh59004d32013-03-21 04:31:45 +0000639 LOG(DEBUG, "INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800640 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800641 case Goal::READY_SHOOTER:
642 case Goal::SHOOT:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700643 // Don't let us leave the shoot or preload state if there are 4 discs in
644 // the hopper.
645 if (hopper_disc_count_ >= 4 && goal_enum != Goal::SHOOT) {
646 safe_to_change_state = false;
647 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800648 // Check if we have any discs to shoot or load and handle them.
Brian Silverman94195052013-03-09 13:45:05 -0800649 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800650 if (MinDiscPosition(&min_disc_position, NULL)) {
651 const double ready_disc_position = min_disc_position +
652 ConvertDiscPositionToIndex(kReadyToPreload - kIndexStartPosition);
Austin Schuhf8c52252013-03-03 02:25:49 -0800653
654 const double grabbed_disc_position =
655 min_disc_position +
656 ConvertDiscPositionToIndex(kReadyToLiftPosition -
Austin Schuh59004d32013-03-21 04:31:45 +0000657 kIndexStartPosition + 0.07);
Austin Schuhf8c52252013-03-03 02:25:49 -0800658
659 // Check the state of the loader FSM.
660 // If it is ready to load discs, position the disc so that it is ready
661 // to be grabbed.
662 // If it isn't ready, there is a disc in there. It needs to finish it's
663 // cycle first.
664 if (loader_state_ != LoaderState::READY) {
665 // We already have a disc in the loader.
666 // Stage the discs back a bit.
667 wrist_loop_->R << ready_disc_position, 0.0;
668
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800669 // Shoot if we are grabbed and being asked to shoot.
670 if (loader_state_ == LoaderState::GRABBED &&
671 safe_goal_ == Goal::SHOOT) {
672 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
673 }
674
Austin Schuhf8c52252013-03-03 02:25:49 -0800675 // Must wait until it has been grabbed to continue.
676 if (loader_state_ == LoaderState::GRABBING) {
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700677 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800678 }
679 } else {
680 // No disc up top right now.
681 wrist_loop_->R << grabbed_disc_position, 0.0;
682
683 // See if the disc has gotten pretty far up yet.
684 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
685 // Point of no return. We are committing to grabbing it now.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700686 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800687 const double robust_grabbed_disc_position =
688 (grabbed_disc_position -
689 ConvertDiscPositionToIndex(kGrabberLength));
690
691 // If close, start grabbing and/or shooting.
692 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
693 // Start the state machine.
694 if (safe_goal_ == Goal::SHOOT) {
695 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
696 } else {
697 loader_goal_ = LoaderGoal::GRAB;
698 }
699 // This frisbee is now gone. Take it out of the queue.
700 frisbees_.pop_back();
Austin Schuhf8c52252013-03-03 02:25:49 -0800701 }
702 }
703 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800704 } else {
705 if (loader_state_ != LoaderState::READY) {
706 // Shoot if we are grabbed and being asked to shoot.
707 if (loader_state_ == LoaderState::GRABBED &&
708 safe_goal_ == Goal::SHOOT) {
709 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
710 }
711 } else {
712 // Ok, no discs in sight. Spin the hopper up by 150% of it's full
713 // range and verify that we don't see anything.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800714 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700715 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700716 lower_open_region_.lower_bound()) +
Austin Schuhf60861b2013-03-14 00:14:10 -0700717 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800718
719 wrist_loop_->R << hopper_clear_verification_position, 0.0;
720 if (::std::abs(wrist_loop_->X_hat(0, 0) -
721 hopper_clear_verification_position) <
722 ConvertDiscPositionToIndex(0.05)) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800723 // We are at the end of the range. There are no more discs here.
724 while (frisbees_.size() > 0) {
725 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700726 LOG(ERROR, "Upper is [%f %f]\n",
727 upper_open_region_.upper_bound(),
728 upper_open_region_.lower_bound());
729 LOG(ERROR, "Lower is [%f %f]\n",
730 lower_open_region_.upper_bound(),
731 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800732 frisbees_.pop_back();
733 --hopper_disc_count_;
734 --total_disc_count_;
735 }
736 if (hopper_disc_count_ != 0) {
737 LOG(ERROR,
738 "Emptied the hopper out but there are still discs there\n");
Brian Silvermanf0716e82013-03-17 23:36:11 -0700739 hopper_disc_count_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800740 }
741 }
742 }
743 }
744
745 {
746 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700747 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700748 lower_open_region_.lower_bound()) +
Austin Schuhf60861b2013-03-14 00:14:10 -0700749 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800750
751 if (wrist_loop_->X_hat(0, 0) >
752 hopper_clear_verification_position +
753 ConvertDiscPositionToIndex(0.05)) {
754 // We are at the end of the range. There are no more discs here.
755 while (frisbees_.size() > 0) {
756 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuha3e8e032013-03-10 18:43:14 -0700757 LOG(ERROR, "Upper is [%f %f]\n",
758 upper_open_region_.upper_bound(),
759 upper_open_region_.lower_bound());
760 LOG(ERROR, "Lower is [%f %f]\n",
761 lower_open_region_.upper_bound(),
762 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800763 frisbees_.pop_back();
764 --hopper_disc_count_;
765 --total_disc_count_;
766 }
Brian Silvermanf0716e82013-03-17 23:36:11 -0700767 if (hopper_disc_count_ != 0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800768 LOG(ERROR,
Brian Silvermanb7bcef12013-03-16 13:57:11 -0700769 "Emptied the hopper out but there are still %"PRId32" discs there\n",
Austin Schuhf60861b2013-03-14 00:14:10 -0700770 hopper_disc_count_);
Brian Silvermanf0716e82013-03-17 23:36:11 -0700771 hopper_disc_count_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800772 }
773 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800774 }
775
Austin Schuha3e8e032013-03-10 18:43:14 -0700776 LOG(DEBUG, "READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800777 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800778 }
779
Austin Schuhd3d0fbf2013-03-14 00:37:00 -0700780 // Wait for a period of time to make sure that the disc gets sucked
781 // in properly. We need to do this regardless of what the indexer is doing.
782 for (auto frisbee = frisbees_.begin();
783 frisbee != frisbees_.end(); ++frisbee) {
784 if (now - frisbee->bottom_negedge_time_ < kTransferOffDelay) {
785 transfer_voltage = 12.0;
786 }
787 }
788
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700789 // If we have 4 discs, it is time to preload.
790 if (safe_to_change_state && hopper_disc_count_ >= 4) {
791 switch (safe_goal_) {
792 case Goal::HOLD:
793 case Goal::READY_LOWER:
794 case Goal::INTAKE:
795 safe_goal_ = Goal::READY_SHOOTER;
796 safe_to_change_state = false;
Brian Silvermana0238ce2013-03-13 17:58:45 -0700797 LOG(INFO, "We have %"PRId32" discs, time to preload automatically\n",
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700798 hopper_disc_count_);
799 break;
800 case Goal::READY_SHOOTER:
801 case Goal::SHOOT:
802 break;
803 }
804 }
805
Austin Schuhf8c52252013-03-03 02:25:49 -0800806 // The only way out of the loader is to shoot the disc. The FSM can only go
807 // forwards.
808 switch (loader_state_) {
809 case LoaderState::READY:
Austin Schuha3e8e032013-03-10 18:43:14 -0700810 LOG(DEBUG, "Loader READY\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800811 // Open and down, ready to accept a disc.
812 loader_up_ = false;
813 disc_clamped_ = false;
814 disc_ejected_ = false;
815 if (loader_goal_ == LoaderGoal::GRAB ||
816 loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
817 if (loader_goal_ == LoaderGoal::GRAB) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700818 LOG(INFO, "Told to GRAB, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800819 } else {
Austin Schuha3e8e032013-03-10 18:43:14 -0700820 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800821 }
822 loader_state_ = LoaderState::GRABBING;
823 loader_countdown_ = kGrabbingDelay;
824 } else {
825 break;
826 }
827 case LoaderState::GRABBING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700828 LOG(DEBUG, "Loader GRABBING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800829 // Closing the grabber.
830 loader_up_ = false;
831 disc_clamped_ = true;
832 disc_ejected_ = false;
833 if (loader_countdown_ > 0) {
834 --loader_countdown_;
835 break;
836 } else {
837 loader_state_ = LoaderState::GRABBED;
838 }
839 case LoaderState::GRABBED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700840 LOG(DEBUG, "Loader GRABBED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800841 // Grabber closed.
842 loader_up_ = false;
843 disc_clamped_ = true;
844 disc_ejected_ = false;
845 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700846 shooter.status.FetchLatest();
847 if (shooter.status.get()) {
848 // TODO(aschuh): If we aren't shooting nicely, wait until the shooter
849 // is up to speed rather than just spinning.
Austin Schuhf60861b2013-03-14 00:14:10 -0700850 if (shooter.status->average_velocity > 130 && shooter.status->ready) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700851 loader_state_ = LoaderState::LIFTING;
852 loader_countdown_ = kLiftingDelay;
853 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
854 } else {
855 LOG(WARNING, "Told to SHOOT_AND_RESET, shooter too slow at %f\n",
856 shooter.status->average_velocity);
857 break;
858 }
859 } else {
860 LOG(ERROR, "Told to SHOOT_AND_RESET, no shooter data, moving on.\n");
861 loader_state_ = LoaderState::LIFTING;
862 loader_countdown_ = kLiftingDelay;
863 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800864 } else if (loader_goal_ == LoaderGoal::READY) {
865 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800866 break;
867 } else {
868 break;
869 }
870 case LoaderState::LIFTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700871 LOG(DEBUG, "Loader LIFTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800872 // Lifting the disc.
873 loader_up_ = true;
874 disc_clamped_ = true;
875 disc_ejected_ = false;
876 if (loader_countdown_ > 0) {
877 --loader_countdown_;
878 break;
879 } else {
880 loader_state_ = LoaderState::LIFTED;
881 }
882 case LoaderState::LIFTED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700883 LOG(DEBUG, "Loader LIFTED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800884 // Disc lifted. Time to eject it out.
885 loader_up_ = true;
886 disc_clamped_ = true;
887 disc_ejected_ = false;
888 loader_state_ = LoaderState::SHOOTING;
889 loader_countdown_ = kShootingDelay;
890 case LoaderState::SHOOTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700891 LOG(DEBUG, "Loader SHOOTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800892 // Ejecting the disc into the shooter.
893 loader_up_ = true;
894 disc_clamped_ = false;
895 disc_ejected_ = true;
896 if (loader_countdown_ > 0) {
897 --loader_countdown_;
898 break;
899 } else {
900 loader_state_ = LoaderState::SHOOT;
901 }
902 case LoaderState::SHOOT:
Austin Schuha3e8e032013-03-10 18:43:14 -0700903 LOG(DEBUG, "Loader SHOOT\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800904 // The disc has been shot.
905 loader_up_ = true;
906 disc_clamped_ = false;
907 disc_ejected_ = true;
908 loader_state_ = LoaderState::LOWERING;
909 loader_countdown_ = kLoweringDelay;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800910 --hopper_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700911 ++shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800912 case LoaderState::LOWERING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700913 LOG(DEBUG, "Loader LOWERING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800914 // Lowering the loader back down.
915 loader_up_ = false;
916 disc_clamped_ = false;
917 disc_ejected_ = true;
918 if (loader_countdown_ > 0) {
919 --loader_countdown_;
920 break;
921 } else {
922 loader_state_ = LoaderState::LOWERED;
923 }
924 case LoaderState::LOWERED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700925 LOG(DEBUG, "Loader LOWERED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800926 // The indexer is lowered.
927 loader_up_ = false;
928 disc_clamped_ = false;
929 disc_ejected_ = false;
930 loader_state_ = LoaderState::READY;
931 // Once we have shot, we need to hang out in READY until otherwise
932 // notified.
933 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800934 break;
935 }
936
937 // Update the observer.
938 wrist_loop_->Update(position != NULL, output == NULL);
939
940 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800941 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800942 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -0800943 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -0800944 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
945 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
946 last_bottom_disc_negedge_wait_count_ =
947 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800948 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800949 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -0800950 }
951
952 status->hopper_disc_count = hopper_disc_count_;
953 status->total_disc_count = total_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700954 status->shot_disc_count = shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800955 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800956
957 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800958 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -0800959 output->transfer_voltage = transfer_voltage;
Austin Schuhd78ab542013-03-01 22:22:19 -0800960 output->index_voltage = wrist_loop_->U(0, 0);
Austin Schuhf8c52252013-03-03 02:25:49 -0800961 output->loader_up = loader_up_;
962 output->disc_clamped = disc_clamped_;
963 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800964 }
965
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700966 if (safe_to_change_state) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800967 safe_goal_ = goal_enum;
968 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800969 if (hopper_disc_count_ < 0) {
970 LOG(ERROR, "NEGATIVE DISCS. VERY VERY BAD\n");
971 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800972}
973
974} // namespace control_loops
975} // namespace frc971