blob: f8dbada898b1f73333e235cc8bfa45961be9896a [file] [log] [blame]
Austin Schuh00558222013-03-03 14:16:16 -08001#include "frc971/control_loops/index/index.h"
Austin Schuhd78ab542013-03-01 22:22:19 -08002
3#include <stdio.h>
4
5#include <algorithm>
6
Austin Schuhd78ab542013-03-01 22:22:19 -08007#include "aos/common/control_loop/control_loops.q.h"
8#include "aos/common/logging/logging.h"
Brian Silverman94195052013-03-09 13:45:05 -08009#include "aos/common/inttypes.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080010
11#include "frc971/constants.h"
Austin Schuh00558222013-03-03 14:16:16 -080012#include "frc971/control_loops/index/index_motor_plant.h"
Austin Schuha3e8e032013-03-10 18:43:14 -070013#include "frc971/control_loops/shooter/shooter_motor.q.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080014
15using ::aos::time::Time;
16
17namespace frc971 {
18namespace control_loops {
19
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070020double IndexMotor::Frisbee::ObserveNoTopDiscSensor(double index_position) {
Austin Schuhdff24e22013-03-06 00:41:21 -080021 // The absolute disc position in meters.
Austin Schuh1b864a12013-03-07 00:46:50 -080022 double disc_position = absolute_position(index_position);
Austin Schuh825bde92013-03-06 00:16:46 -080023 if (IndexMotor::kTopDiscDetectStart <= disc_position &&
24 disc_position <= IndexMotor::kTopDiscDetectStop) {
25 // Whoops, this shouldn't be happening.
26 // Move the disc off the way that makes most sense.
Austin Schuhdff24e22013-03-06 00:41:21 -080027 double distance_to_above = IndexMotor::ConvertDiscPositionToIndex(
28 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStop));
29 double distance_to_below = IndexMotor::ConvertDiscPositionToIndex(
30 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStart));
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070031 if (distance_to_above < distance_to_below) {
32 LOG(INFO, "Moving disc to top slow.\n");
33 // Move it up.
34 index_start_position_ -= distance_to_above;
35 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080036 } else {
Austin Schuh6b1ad2f2013-03-11 23:23:00 -070037 LOG(INFO, "Moving disc to bottom slow.\n");
38 index_start_position_ += distance_to_below;
39 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080040 }
41 }
Austin Schuhdff24e22013-03-06 00:41:21 -080042 return 0.0;
Austin Schuh825bde92013-03-06 00:16:46 -080043}
44
Austin Schuhd78ab542013-03-01 22:22:19 -080045IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
46 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
Austin Schuh93485832013-03-04 00:01:34 -080047 wrist_loop_(new IndexStateFeedbackLoop(MakeIndexLoop())),
Austin Schuhd78ab542013-03-01 22:22:19 -080048 hopper_disc_count_(0),
49 total_disc_count_(0),
Austin Schuh70be1ba2013-03-10 13:37:17 -070050 shot_disc_count_(0),
Austin Schuhf8c52252013-03-03 02:25:49 -080051 safe_goal_(Goal::HOLD),
52 loader_goal_(LoaderGoal::READY),
53 loader_state_(LoaderState::READY),
Austin Schuhd78ab542013-03-01 22:22:19 -080054 loader_up_(false),
55 disc_clamped_(false),
56 disc_ejected_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080057 last_bottom_disc_detect_(false),
Austin Schuh825bde92013-03-06 00:16:46 -080058 last_top_disc_detect_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080059 no_prior_position_(true),
Austin Schuh723770b2013-03-10 13:26:20 -070060 missing_position_count_(0) {
Austin Schuhd78ab542013-03-01 22:22:19 -080061}
62
Austin Schuhf8c52252013-03-03 02:25:49 -080063/*static*/ const double IndexMotor::kTransferStartPosition = 0.0;
64/*static*/ const double IndexMotor::kIndexStartPosition = 0.2159;
65/*static*/ const double IndexMotor::kIndexFreeLength =
66 IndexMotor::ConvertDiscAngleToDiscPosition((360 * 2 + 14) * M_PI / 180);
67/*static*/ const double IndexMotor::kLoaderFreeStopPosition =
68 kIndexStartPosition + kIndexFreeLength;
Austin Schuh1b864a12013-03-07 00:46:50 -080069/*static*/ const double IndexMotor::kReadyToPreload =
70 kLoaderFreeStopPosition - ConvertDiscAngleToDiscPosition(M_PI / 6.0);
Austin Schuhf8c52252013-03-03 02:25:49 -080071/*static*/ const double IndexMotor::kReadyToLiftPosition =
72 kLoaderFreeStopPosition + 0.2921;
73/*static*/ const double IndexMotor::kGrabberLength = 0.03175;
74/*static*/ const double IndexMotor::kGrabberStartPosition =
75 kReadyToLiftPosition - kGrabberLength;
Austin Schuh6328daf2013-03-05 00:53:15 -080076/*static*/ const double IndexMotor::kGrabberMovementVelocity = 0.7;
Austin Schuhf8c52252013-03-03 02:25:49 -080077/*static*/ const double IndexMotor::kLifterStopPosition =
78 kReadyToLiftPosition + 0.161925;
79/*static*/ const double IndexMotor::kLifterMovementVelocity = 1.0;
80/*static*/ const double IndexMotor::kEjectorStopPosition =
81 kLifterStopPosition + 0.01;
82/*static*/ const double IndexMotor::kEjectorMovementVelocity = 1.0;
Austin Schuhcc297022013-03-09 23:26:40 -080083/*static*/ const double IndexMotor::kBottomDiscDetectStart = 0.00;
84/*static*/ const double IndexMotor::kBottomDiscDetectStop = 0.13;
85/*static*/ const double IndexMotor::kBottomDiscIndexDelay = 0.032;
Austin Schuhd3d0fbf2013-03-14 00:37:00 -070086/*static*/ const ::aos::time::Time IndexMotor::kTransferOffDelay =
Brian Silvermanb7bcef12013-03-16 13:57:11 -070087 ::aos::time::Time::InSeconds(0.3);
Austin Schuhf8c52252013-03-03 02:25:49 -080088
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080089// TODO(aschuh): Verify these with the sensor actually on.
Austin Schuh825bde92013-03-06 00:16:46 -080090/*static*/ const double IndexMotor::kTopDiscDetectStart =
91 (IndexMotor::kLoaderFreeStopPosition -
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080092 IndexMotor::ConvertDiscAngleToDiscPosition(49 * M_PI / 180));
Austin Schuh825bde92013-03-06 00:16:46 -080093/*static*/ const double IndexMotor::kTopDiscDetectStop =
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080094 (IndexMotor::kLoaderFreeStopPosition +
95 IndexMotor::ConvertDiscAngleToDiscPosition(19 * M_PI / 180));
96
97// I measured the angle between 2 discs. That then gives me the distance
98// between 2 posedges (or negedges). Then subtract off the width of the
99// positive pulse, and that gives the width of the negative pulse.
100/*static*/ const double IndexMotor::kTopDiscDetectMinSeperation =
101 (IndexMotor::ConvertDiscAngleToDiscPosition(120 * M_PI / 180) -
102 (IndexMotor::kTopDiscDetectStop - IndexMotor::kTopDiscDetectStart));
Austin Schuhf8c52252013-03-03 02:25:49 -0800103
Austin Schuhd78ab542013-03-01 22:22:19 -0800104const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
105const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
Austin Schuhf8c52252013-03-03 02:25:49 -0800106const /*static*/ double IndexMotor::kTransferRollerRadius = 1.25 * 0.0254 / 2;
Austin Schuhd78ab542013-03-01 22:22:19 -0800107
Austin Schuhf8c52252013-03-03 02:25:49 -0800108/*static*/ const int IndexMotor::kGrabbingDelay = 5;
Austin Schuha3e8e032013-03-10 18:43:14 -0700109/*static*/ const int IndexMotor::kLiftingDelay = 30;
Austin Schuhf8c52252013-03-03 02:25:49 -0800110/*static*/ const int IndexMotor::kShootingDelay = 5;
111/*static*/ const int IndexMotor::kLoweringDelay = 20;
Austin Schuhd78ab542013-03-01 22:22:19 -0800112
Austin Schuh93485832013-03-04 00:01:34 -0800113// TODO(aschuh): Tune these.
114/*static*/ const double
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700115 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 11.0;
Austin Schuh93485832013-03-04 00:01:34 -0800116/*static*/ const double
Austin Schuha3e8e032013-03-10 18:43:14 -0700117 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 20;
Austin Schuh93485832013-03-04 00:01:34 -0800118
Austin Schuhd78ab542013-03-01 22:22:19 -0800119/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
120 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
121}
122
Austin Schuhf8c52252013-03-03 02:25:49 -0800123/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
124 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800125 return angle * (kDiscRadius + kRollerRadius);
126}
127
Austin Schuhf8c52252013-03-03 02:25:49 -0800128/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
129 const double position) {
130 return position / (kDiscRadius + kRollerRadius);
131}
132
Austin Schuhd78ab542013-03-01 22:22:19 -0800133/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
134 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
135}
136
137/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
138 return IndexMotor::ConvertDiscAngleToDiscPosition(
139 ConvertIndexToDiscAngle(angle));
140}
141
Austin Schuhf8c52252013-03-03 02:25:49 -0800142/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
143 const double angle) {
144 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
145 kTransferRollerRadius);
146 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
147}
148
149/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
150 const double position) {
151 return IndexMotor::ConvertDiscAngleToIndex(
152 ConvertDiscPositionToDiscAngle(position));
153}
154
Austin Schuh1b864a12013-03-07 00:46:50 -0800155bool IndexMotor::MinDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800156 bool found_start = false;
157 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800158 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800159 if (!found_start) {
160 if (frisbee.has_position()) {
161 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800162 if (found_disc) {
163 *found_disc = &frisbee;
164 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800165 found_start = true;
166 }
167 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800168 if (frisbee.position() <= *disc_position) {
169 *disc_position = frisbee.position();
170 if (found_disc) {
171 *found_disc = &frisbee;
172 }
173 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800174 }
175 }
176 return found_start;
177}
178
Austin Schuh1b864a12013-03-07 00:46:50 -0800179bool IndexMotor::MaxDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800180 bool found_start = false;
181 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800182 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800183 if (!found_start) {
184 if (frisbee.has_position()) {
185 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800186 if (found_disc) {
187 *found_disc = &frisbee;
188 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800189 found_start = true;
190 }
191 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800192 if (frisbee.position() > *disc_position) {
193 *disc_position = frisbee.position();
194 if (found_disc) {
195 *found_disc = &frisbee;
196 }
197 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800198 }
199 }
200 return found_start;
201}
202
Austin Schuh93485832013-03-04 00:01:34 -0800203void IndexMotor::IndexStateFeedbackLoop::CapU() {
204 // If the voltage has been low for a large number of cycles, cut the motor
205 // power. This is generally very bad controls practice since this isn't LTI,
206 // but we don't really care about tracking anything other than large step
207 // inputs, and the loader doesn't need to be that accurate.
208 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
209 ++low_voltage_count_;
210 if (low_voltage_count_ > kNoMotionCuttoffCount) {
Austin Schuh93485832013-03-04 00:01:34 -0800211 U(0, 0) = 0.0;
212 }
213 } else {
214 low_voltage_count_ = 0;
215 }
216
217 for (int i = 0; i < kNumOutputs; ++i) {
Austin Schuh9644e1c2013-03-12 00:40:36 -0700218 if (U(i, 0) > U_max(i, 0)) {
219 U(i, 0) = U_max(i, 0);
220 } else if (U(i, 0) < U_min(i, 0)) {
221 U(i, 0) = U_min(i, 0);
Austin Schuh93485832013-03-04 00:01:34 -0800222 }
223 }
224}
225
226
Austin Schuhd78ab542013-03-01 22:22:19 -0800227// Positive angle is towards the shooter, and positive power is towards the
228// shooter.
229void IndexMotor::RunIteration(
230 const control_loops::IndexLoop::Goal *goal,
231 const control_loops::IndexLoop::Position *position,
232 control_loops::IndexLoop::Output *output,
233 control_loops::IndexLoop::Status *status) {
Austin Schuhd3d0fbf2013-03-14 00:37:00 -0700234 Time now = Time::Now();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800235 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800236 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700237 if (goal->goal_state < 0 || goal->goal_state > 5) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700238 LOG(ERROR,
239 "Goal state is %" PRId32 " which is out of range. Going to HOLD.\n",
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800240 goal->goal_state);
241 goal_enum = Goal::HOLD;
242 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800243
244 // Disable the motors now so that all early returns will return with the
245 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800246 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800247 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800248 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800249 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800250 output->transfer_voltage = 0.0;
251 output->index_voltage = 0.0;
252 }
253
254 status->ready_to_intake = false;
255
Austin Schuhe3490622013-03-13 01:24:30 -0700256 // Set the controller to use to be the one designed for the current number of
257 // discs in the hopper. This is safe since the controller prevents the index
258 // from being set out of bounds and picks the closest controller.
259 wrist_loop_->set_controller_index(frisbees_.size());
260
Austin Schuhf8c52252013-03-03 02:25:49 -0800261 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800262 if (position) {
263 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800264 // Set the goal to be the current position if this is the first time through
265 // so we don't always spin the indexer to the 0 position before starting.
266 if (no_prior_position_) {
Brian Silverman43bb73e2013-03-17 13:39:47 -0700267 LOG(INFO, "no prior position; resetting\n");
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800268 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
Austin Schuhc5ef1bb2013-03-10 00:42:05 -0800269 wrist_loop_->X_hat(0, 0) = wrist_loop_->Y(0, 0);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800270 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800271 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
272 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
273 last_bottom_disc_negedge_wait_count_ =
274 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800275 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800276 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Brian Silvermance86bac2013-03-31 19:07:24 -0700277 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800278 // The open positions for the upper is right here and isn't a hard edge.
Austin Schuh723770b2013-03-10 13:26:20 -0700279 upper_open_region_.Restart(wrist_loop_->Y(0, 0));
280 lower_open_region_.Restart(wrist_loop_->Y(0, 0));
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800281 }
282
Austin Schuh1b864a12013-03-07 00:46:50 -0800283 // If the cRIO is gone for over 1/2 of a second, assume that it rebooted.
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800284 if (missing_position_count_ > 50) {
Brian Silverman43bb73e2013-03-17 13:39:47 -0700285 LOG(INFO, "assuming cRIO rebooted\n");
Austin Schuh6328daf2013-03-05 00:53:15 -0800286 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
287 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
288 last_bottom_disc_negedge_wait_count_ =
289 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800290 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800291 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Brian Silvermance86bac2013-03-31 19:07:24 -0700292 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800293 // 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;
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000306 if (last_top_disc_detect_) {
307 if (last_top_disc_posedge_count_ != position->top_disc_posedge_count) {
308 LOG(INFO, "Ignoring a top disc posedge\n");
309 }
Brian Silvermance86bac2013-03-31 19:07:24 -0700310 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
311 }
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000312 if (!last_top_disc_detect_) {
313 if (last_top_disc_negedge_count_ != position->top_disc_negedge_count) {
314 LOG(INFO, "Ignoring a top disc negedge\n");
315 }
Brian Silvermance86bac2013-03-31 19:07:24 -0700316 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
317 }
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800318 } else {
319 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800320 }
321 const double index_position = wrist_loop_->X_hat(0, 0);
322
Austin Schuh825bde92013-03-06 00:16:46 -0800323 if (position) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800324 // Reset the open region if we saw a negedge.
Austin Schuh723770b2013-03-10 13:26:20 -0700325 if (position->bottom_disc_negedge_wait_count !=
326 last_bottom_disc_negedge_wait_count_) {
327 // Saw a negedge, must be a new region.
328 lower_open_region_.Restart(position->bottom_disc_negedge_wait_position);
329 }
330 // Reset the open region if we saw a negedge.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800331 if (position->top_disc_negedge_count != last_top_disc_negedge_count_) {
332 // Saw a negedge, must be a new region.
Austin Schuh723770b2013-03-10 13:26:20 -0700333 upper_open_region_.Restart(position->top_disc_negedge_position);
334 }
335
336 // No disc. Expand the open region.
337 if (!position->bottom_disc_detect) {
338 lower_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800339 }
340
341 // No disc. Expand the open region.
342 if (!position->top_disc_detect) {
Austin Schuh723770b2013-03-10 13:26:20 -0700343 upper_open_region_.Expand(index_position);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800344 }
345
Austin Schuh825bde92013-03-06 00:16:46 -0800346 if (!position->top_disc_detect) {
347 // We don't see a disc. Verify that there are no discs that we should be
348 // seeing.
Austin Schuh1b864a12013-03-07 00:46:50 -0800349 // Assume that discs will move slow enough that we won't miss one as it
350 // goes by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800351
352 double cumulative_offset = 0.0;
353 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
354 frisbee != rend; ++frisbee) {
355 frisbee->OffsetDisc(cumulative_offset);
356 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh6b1ad2f2013-03-11 23:23:00 -0700357 wrist_loop_->X_hat(0, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800358 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800359 }
360 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800361
Austin Schuh825bde92013-03-06 00:16:46 -0800362 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000363 LOG(INFO, "Saw a posedge\n");
Austin Schuh1b864a12013-03-07 00:46:50 -0800364 const double index_position = wrist_loop_->X_hat(0, 0) -
365 position->index_position + position->top_disc_posedge_position;
Austin Schuh825bde92013-03-06 00:16:46 -0800366 // TODO(aschuh): Sanity check this number...
367 // Requires storing when the disc was last seen with the sensor off, and
368 // figuring out what to do if things go south.
369
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800370 // 1 if discs are going up, 0 if we have no clue, and -1 if they are going
371 // down.
372 int disc_direction = 0;
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000373 if (wrist_loop_->X_hat(1, 0) > 100.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800374 disc_direction = 1;
Austin Schuh81fb1bc2013-04-04 05:52:28 +0000375 } else if (wrist_loop_->X_hat(1, 0) < -100.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800376 disc_direction = -1;
377 } else {
378 // Save the upper and lower positions that we last saw a disc at.
379 // If there is a big buffer above, must be a disc from below.
380 // If there is a big buffer below, must be a disc from above.
381 // This should work to replace the velocity threshold above.
382
Austin Schuh723770b2013-03-10 13:26:20 -0700383 const double open_width = upper_open_region_.width();
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800384 const double relative_upper_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700385 (upper_open_region_.upper_bound() - index_position) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800386 const double relative_lower_open_precentage =
Austin Schuh723770b2013-03-10 13:26:20 -0700387 (index_position - upper_open_region_.lower_bound()) / open_width;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800388
389 if (ConvertIndexToDiscPosition(open_width) <
390 kTopDiscDetectMinSeperation * 0.9) {
391 LOG(ERROR, "Discs are way too close to each other. Doing nothing\n");
392 } else if (relative_upper_open_precentage > 0.75) {
393 // Looks like it is a disc going down from above since we are near
394 // the upper edge.
395 disc_direction = -1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700396 LOG(INFO, "Disc edge going down\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800397 } else if (relative_lower_open_precentage > 0.75) {
398 // Looks like it is a disc going up from below since we are near
399 // the lower edge.
400 disc_direction = 1;
Austin Schuha3e8e032013-03-10 18:43:14 -0700401 LOG(INFO, "Disc edge going up\n");
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800402 } else {
403 LOG(ERROR,
404 "Got an edge in the middle of what should be an open region.\n");
405 LOG(ERROR, "Open width: %f upper precentage %f %%\n",
406 open_width, relative_upper_open_precentage);
407 }
408 }
409
410 if (disc_direction > 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800411 // Moving up at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800412 // Find the highest disc that is below the top disc sensor.
413 // While we are at it, count the number above and log an error if there
414 // are too many.
415 if (frisbees_.size() == 0) {
416 Frisbee new_frisbee;
417 new_frisbee.has_been_indexed_ = true;
418 new_frisbee.index_start_position_ = index_position -
419 ConvertDiscPositionToIndex(kTopDiscDetectStart -
420 kIndexStartPosition);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800421 ++hopper_disc_count_;
422 ++total_disc_count_;
Austin Schuh1b864a12013-03-07 00:46:50 -0800423 frisbees_.push_front(new_frisbee);
424 LOG(WARNING, "Added a disc to the hopper at the top sensor\n");
425 }
426
427 int above_disc_count = 0;
428 double highest_position = 0;
429 Frisbee *highest_frisbee_below_sensor = NULL;
430 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
431 frisbee != rend; ++frisbee) {
432 const double disc_position = frisbee->absolute_position(
433 index_position);
434 // It is save to use the top position for the cuttoff, since the
435 // sensor being low will result in discs being pushed off of it.
436 if (disc_position >= kTopDiscDetectStop) {
437 ++above_disc_count;
438 } else if (!highest_frisbee_below_sensor ||
439 disc_position > highest_position) {
440 highest_frisbee_below_sensor = &*frisbee;
441 highest_position = disc_position;
442 }
443 }
Austin Schuh09e07082013-03-19 10:04:12 +0000444
445 if (!highest_frisbee_below_sensor) {
446 Frisbee new_frisbee;
447 new_frisbee.has_been_indexed_ = true;
448 new_frisbee.index_start_position_ = index_position -
449 ConvertDiscPositionToIndex(kTopDiscDetectStart -
450 kIndexStartPosition);
451 highest_position = kTopDiscDetectStart;
452 ++hopper_disc_count_;
453 ++total_disc_count_;
454 frisbees_.push_front(new_frisbee);
455 LOG(WARNING, "Added a disc to the hopper at the top sensor because the one we know about is up top\n");
456 }
457
Austin Schuh1b864a12013-03-07 00:46:50 -0800458 if (above_disc_count > 1) {
459 LOG(ERROR, "We have 2 discs above the top sensor.\n");
460 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800461 // We now have the disc. Shift all the ones below the sensor up by the
462 // computed delta.
463 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
464 highest_position - kTopDiscDetectStart);
465 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
466 frisbee != rend; ++frisbee) {
467 const double disc_position = frisbee->absolute_position(
468 index_position);
469 if (disc_position < kTopDiscDetectStop) {
Austin Schuh59004d32013-03-21 04:31:45 +0000470 LOG(INFO, "Moving disc down by %f meters, since it is at %f and top is [%f, %f]\n",
471 ConvertIndexToDiscPosition(disc_delta),
472 disc_position, kTopDiscDetectStart,
473 kTopDiscDetectStop);
Austin Schuh1b864a12013-03-07 00:46:50 -0800474 frisbee->OffsetDisc(disc_delta);
475 }
476 }
Brian Silverman43bb73e2013-03-17 13:39:47 -0700477 if (highest_frisbee_below_sensor) {
478 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
479 "Moving down by %f to %f\n", frisbees_.size(),
480 ConvertIndexToDiscPosition(disc_delta),
481 highest_frisbee_below_sensor->absolute_position(
482 wrist_loop_->X_hat(0, 0)));
483 } else {
484 LOG(INFO, "Currently have %d discs, saw posedge moving up. "
485 "Moving down by %f\n", frisbees_.size(),
486 ConvertIndexToDiscPosition(disc_delta));
487 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800488 } else if (disc_direction < 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800489 // Moving down at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800490 // There can only be 1 disc up top that would give us a posedge.
491 // Find it and place it at the one spot that it can be.
Brian Silverman94195052013-03-09 13:45:05 -0800492 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800493 Frisbee *min_frisbee = NULL;
494 MinDiscPosition(&min_disc_position, &min_frisbee);
495 if (!min_frisbee) {
496 // Uh, oh, we see a disc but there isn't one...
497 LOG(ERROR, "Saw a disc up top but there isn't one in the hopper\n");
498 } else {
499 const double disc_position = min_frisbee->absolute_position(
500 index_position);
501
502 const double disc_delta_meters = disc_position - kTopDiscDetectStop;
503 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
504 disc_delta_meters);
Austin Schuha3e8e032013-03-10 18:43:14 -0700505 LOG(INFO, "Posedge going down. Moving top disc down by %f\n",
506 disc_delta_meters);
Austin Schuh1b864a12013-03-07 00:46:50 -0800507 for (auto frisbee = frisbees_.begin(), end = frisbees_.end();
508 frisbee != end; ++frisbee) {
509 frisbee->OffsetDisc(disc_delta);
510 }
511 }
Austin Schuh825bde92013-03-06 00:16:46 -0800512 } else {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800513 LOG(ERROR, "Not sure how to handle the upper posedge, doing nothing\n");
Austin Schuh825bde92013-03-06 00:16:46 -0800514 }
515 }
516 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800517
Austin Schuhf8c52252013-03-03 02:25:49 -0800518 // Bool to track if it is safe for the goal to change yet.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700519 bool safe_to_change_state = true;
Brian Silvermanf0716e82013-03-17 23:36:11 -0700520 if (!position) {
521 // This fixes a nasty indexer bug.
522 // If we didn't get a position this cycle, we don't run the code below which
523 // checks the state of the disc detect sensor and whether all the discs are
524 // indexed. It is therefore not safe to change state and loose track of
525 // that disc.
526 safe_to_change_state = false;
527 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800528 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800529 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800530 // The goal should already be good, so sit tight with everything the same
531 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800532 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800533 case Goal::READY_LOWER:
534 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800535 {
Austin Schuhd78ab542013-03-01 22:22:19 -0800536 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800537 // Posedge of the disc entering the beam break.
538 if (position->bottom_disc_posedge_count !=
539 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800540 transfer_frisbee_.Reset();
541 transfer_frisbee_.bottom_posedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700542 LOG(INFO, "Posedge of bottom disc %f\n",
543 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800544 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800545 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800546 }
547
548 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800549 if (position->bottom_disc_negedge_count !=
550 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800551 transfer_frisbee_.bottom_negedge_time_ = now;
Austin Schuha3e8e032013-03-10 18:43:14 -0700552 LOG(INFO, "Negedge of bottom disc %f\n",
553 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
Austin Schuhd78ab542013-03-01 22:22:19 -0800554 frisbees_.push_front(transfer_frisbee_);
555 }
556
557 if (position->bottom_disc_detect) {
Brian Silvermanc5277542013-03-22 13:33:07 -0700558 intake_voltage = 0.0;
559 transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800560 // Must wait until the disc gets out before we can change state.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700561 safe_to_change_state = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800562
Austin Schuhf8c52252013-03-03 02:25:49 -0800563 // TODO(aschuh): A disc on the way through needs to start moving
564 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800565
566 Time elapsed_posedge_time = now -
567 transfer_frisbee_.bottom_posedge_time_;
568 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
569 // It has been too long. The disc must be jammed.
570 LOG(ERROR, "Been way too long. Jammed disc?\n");
Brian Silvermance86bac2013-03-31 19:07:24 -0700571 intake_voltage = -12.0;
Brian Silvermanf0716e82013-03-17 23:36:11 -0700572 transfer_voltage = -12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800573 }
574 }
575
Austin Schuhf8c52252013-03-03 02:25:49 -0800576 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800577 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800578 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800579 if (!frisbee->has_been_indexed_) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800580 if (last_bottom_disc_negedge_wait_count_ !=
581 position->bottom_disc_negedge_wait_count) {
582 // We have an index difference.
583 // Save the indexer position, and the time.
584 if (last_bottom_disc_negedge_wait_count_ + 1 !=
585 position->bottom_disc_negedge_wait_count) {
586 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
587 }
588
589 // Save the captured position as the position at which the disc
590 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800591 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800592 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800593 frisbee->index_start_position_ =
594 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800595 }
596 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800597 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800598 }
Austin Schuh59004d32013-03-21 04:31:45 +0000599 for (auto frisbee = frisbees_.begin();
600 frisbee != frisbees_.end(); ++frisbee) {
601 if (!frisbee->has_been_indexed_) {
Brian Silvermanc5277542013-03-22 13:33:07 -0700602 intake_voltage = 0.0;
603 transfer_voltage = 12.0;
Austin Schuh59004d32013-03-21 04:31:45 +0000604
605 // All discs must be indexed before it is safe to stop indexing.
606 safe_to_change_state = false;
607 }
608 }
609
610 // Figure out where the indexer should be to move the discs down to
611 // the right position.
612 double max_disc_position = 0;
613 if (MaxDiscPosition(&max_disc_position, NULL)) {
614 LOG(DEBUG, "There is a disc down here!\n");
615 // TODO(aschuh): Figure out what to do if grabbing the next one
616 // would cause things to jam into the loader.
617 // Say we aren't ready any more. Undefined behavior will result if
618 // that isn't observed.
619 double bottom_disc_position =
620 max_disc_position + ConvertDiscAngleToIndex(M_PI);
621 wrist_loop_->R << bottom_disc_position, 0.0;
622
623 // Verify that we are close enough to the goal so that we should be
624 // fine accepting the next disc.
625 double disc_error_meters = ConvertIndexToDiscPosition(
626 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
627 // We are ready for the next disc if the first one is in the first
628 // half circle of the indexer. It will take time for the disc to
629 // come into the indexer, so we will be able to move it out of the
630 // way in time.
631 // This choice also makes sure that we don't claim that we aren't
632 // ready between full speed intaking.
633 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
634 disc_error_meters < 0.04) {
635 // We are only ready if we aren't being asked to change state or
636 // are full.
637 status->ready_to_intake =
638 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
639 } else {
640 status->ready_to_intake = false;
641 }
642 } else {
643 // No discs! We are always ready for more if we aren't being
644 // asked to change state.
645 status->ready_to_intake = (safe_goal_ == goal_enum);
646 }
647
648 // Turn on the transfer roller if we are ready.
649 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
650 safe_goal_ == Goal::INTAKE) {
651 intake_voltage = transfer_voltage = 12.0;
652 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800653 }
Austin Schuh59004d32013-03-21 04:31:45 +0000654 LOG(DEBUG, "INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800655 break;
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700656 case Goal::REINITIALIZE:
657 LOG(WARNING, "Reinitializing the indexer\n");
658 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800659 case Goal::READY_SHOOTER:
660 case Goal::SHOOT:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700661 // Don't let us leave the shoot or preload state if there are 4 discs in
662 // the hopper.
663 if (hopper_disc_count_ >= 4 && goal_enum != Goal::SHOOT) {
664 safe_to_change_state = false;
665 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800666 // Check if we have any discs to shoot or load and handle them.
Brian Silverman94195052013-03-09 13:45:05 -0800667 double min_disc_position = 0;
Austin Schuh1b864a12013-03-07 00:46:50 -0800668 if (MinDiscPosition(&min_disc_position, NULL)) {
669 const double ready_disc_position = min_disc_position +
670 ConvertDiscPositionToIndex(kReadyToPreload - kIndexStartPosition);
Austin Schuhf8c52252013-03-03 02:25:49 -0800671
672 const double grabbed_disc_position =
673 min_disc_position +
674 ConvertDiscPositionToIndex(kReadyToLiftPosition -
Austin Schuh59004d32013-03-21 04:31:45 +0000675 kIndexStartPosition + 0.07);
Austin Schuhf8c52252013-03-03 02:25:49 -0800676
677 // Check the state of the loader FSM.
678 // If it is ready to load discs, position the disc so that it is ready
679 // to be grabbed.
680 // If it isn't ready, there is a disc in there. It needs to finish it's
681 // cycle first.
682 if (loader_state_ != LoaderState::READY) {
683 // We already have a disc in the loader.
684 // Stage the discs back a bit.
685 wrist_loop_->R << ready_disc_position, 0.0;
686
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800687 // Shoot if we are grabbed and being asked to shoot.
688 if (loader_state_ == LoaderState::GRABBED &&
689 safe_goal_ == Goal::SHOOT) {
690 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
691 }
692
Austin Schuhf8c52252013-03-03 02:25:49 -0800693 // Must wait until it has been grabbed to continue.
694 if (loader_state_ == LoaderState::GRABBING) {
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700695 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800696 }
697 } else {
698 // No disc up top right now.
699 wrist_loop_->R << grabbed_disc_position, 0.0;
700
701 // See if the disc has gotten pretty far up yet.
702 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
703 // Point of no return. We are committing to grabbing it now.
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700704 safe_to_change_state = false;
Austin Schuhf8c52252013-03-03 02:25:49 -0800705 const double robust_grabbed_disc_position =
706 (grabbed_disc_position -
707 ConvertDiscPositionToIndex(kGrabberLength));
708
709 // If close, start grabbing and/or shooting.
710 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
711 // Start the state machine.
712 if (safe_goal_ == Goal::SHOOT) {
713 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
714 } else {
715 loader_goal_ = LoaderGoal::GRAB;
716 }
717 // This frisbee is now gone. Take it out of the queue.
718 frisbees_.pop_back();
Austin Schuhf8c52252013-03-03 02:25:49 -0800719 }
720 }
721 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800722 } else {
723 if (loader_state_ != LoaderState::READY) {
724 // Shoot if we are grabbed and being asked to shoot.
725 if (loader_state_ == LoaderState::GRABBED &&
726 safe_goal_ == Goal::SHOOT) {
727 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
728 }
729 } else {
730 // Ok, no discs in sight. Spin the hopper up by 150% of it's full
731 // range and verify that we don't see anything.
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800732 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700733 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700734 lower_open_region_.lower_bound()) +
Austin Schuhf60861b2013-03-14 00:14:10 -0700735 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800736
737 wrist_loop_->R << hopper_clear_verification_position, 0.0;
738 if (::std::abs(wrist_loop_->X_hat(0, 0) -
739 hopper_clear_verification_position) <
740 ConvertDiscPositionToIndex(0.05)) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800741 // We are at the end of the range. There are no more discs here.
742 while (frisbees_.size() > 0) {
743 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700744 LOG(ERROR, "Upper is [%f %f]\n",
745 upper_open_region_.upper_bound(),
746 upper_open_region_.lower_bound());
747 LOG(ERROR, "Lower is [%f %f]\n",
748 lower_open_region_.upper_bound(),
749 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800750 frisbees_.pop_back();
751 --hopper_disc_count_;
752 --total_disc_count_;
753 }
754 if (hopper_disc_count_ != 0) {
755 LOG(ERROR,
756 "Emptied the hopper out but there are still discs there\n");
Brian Silvermanf0716e82013-03-17 23:36:11 -0700757 hopper_disc_count_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800758 }
759 }
760 }
761 }
762
763 {
764 const double hopper_clear_verification_position =
Austin Schuha3e8e032013-03-10 18:43:14 -0700765 ::std::max(upper_open_region_.lower_bound(),
Austin Schuh723770b2013-03-10 13:26:20 -0700766 lower_open_region_.lower_bound()) +
Austin Schuhf60861b2013-03-14 00:14:10 -0700767 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800768
769 if (wrist_loop_->X_hat(0, 0) >
770 hopper_clear_verification_position +
771 ConvertDiscPositionToIndex(0.05)) {
772 // We are at the end of the range. There are no more discs here.
773 while (frisbees_.size() > 0) {
774 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
Austin Schuha3e8e032013-03-10 18:43:14 -0700775 LOG(ERROR, "Upper is [%f %f]\n",
776 upper_open_region_.upper_bound(),
777 upper_open_region_.lower_bound());
778 LOG(ERROR, "Lower is [%f %f]\n",
779 lower_open_region_.upper_bound(),
780 lower_open_region_.lower_bound());
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800781 frisbees_.pop_back();
782 --hopper_disc_count_;
783 --total_disc_count_;
784 }
Brian Silvermanf0716e82013-03-17 23:36:11 -0700785 if (hopper_disc_count_ != 0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800786 LOG(ERROR,
Brian Silverman8efe23e2013-07-07 23:31:37 -0700787 "Emptied the hopper out but there are still %" PRId32 " discs there\n",
Austin Schuhf60861b2013-03-14 00:14:10 -0700788 hopper_disc_count_);
Brian Silvermanf0716e82013-03-17 23:36:11 -0700789 hopper_disc_count_ = 0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800790 }
791 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800792 }
793
Austin Schuha3e8e032013-03-10 18:43:14 -0700794 LOG(DEBUG, "READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800795 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800796 }
797
Austin Schuhd3d0fbf2013-03-14 00:37:00 -0700798 // Wait for a period of time to make sure that the disc gets sucked
799 // in properly. We need to do this regardless of what the indexer is doing.
800 for (auto frisbee = frisbees_.begin();
801 frisbee != frisbees_.end(); ++frisbee) {
802 if (now - frisbee->bottom_negedge_time_ < kTransferOffDelay) {
803 transfer_voltage = 12.0;
804 }
805 }
806
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700807 // If we have 4 discs, it is time to preload.
808 if (safe_to_change_state && hopper_disc_count_ >= 4) {
809 switch (safe_goal_) {
810 case Goal::HOLD:
811 case Goal::READY_LOWER:
812 case Goal::INTAKE:
813 safe_goal_ = Goal::READY_SHOOTER;
814 safe_to_change_state = false;
Brian Silverman8efe23e2013-07-07 23:31:37 -0700815 LOG(INFO, "We have %" PRId32 " discs, time to preload automatically\n",
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700816 hopper_disc_count_);
817 break;
818 case Goal::READY_SHOOTER:
819 case Goal::SHOOT:
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700820 case Goal::REINITIALIZE:
Austin Schuhae5fc8c2013-03-11 23:23:28 -0700821 break;
822 }
823 }
824
Austin Schuhf8c52252013-03-03 02:25:49 -0800825 // The only way out of the loader is to shoot the disc. The FSM can only go
826 // forwards.
827 switch (loader_state_) {
828 case LoaderState::READY:
Austin Schuha3e8e032013-03-10 18:43:14 -0700829 LOG(DEBUG, "Loader READY\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800830 // Open and down, ready to accept a disc.
831 loader_up_ = false;
832 disc_clamped_ = false;
833 disc_ejected_ = false;
834 if (loader_goal_ == LoaderGoal::GRAB ||
Brian Silverman9a1081b2013-04-05 13:50:44 -0700835 loader_goal_ == LoaderGoal::SHOOT_AND_RESET || goal->force_fire) {
836 if (goal->force_fire) {
837 LOG(INFO, "Told to force fire, moving on\n");
838 } else if (loader_goal_ == LoaderGoal::GRAB) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700839 LOG(INFO, "Told to GRAB, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800840 } else {
Austin Schuha3e8e032013-03-10 18:43:14 -0700841 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800842 }
843 loader_state_ = LoaderState::GRABBING;
844 loader_countdown_ = kGrabbingDelay;
845 } else {
846 break;
847 }
848 case LoaderState::GRABBING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700849 LOG(DEBUG, "Loader GRABBING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800850 // Closing the grabber.
851 loader_up_ = false;
852 disc_clamped_ = true;
853 disc_ejected_ = false;
854 if (loader_countdown_ > 0) {
855 --loader_countdown_;
856 break;
857 } else {
858 loader_state_ = LoaderState::GRABBED;
859 }
860 case LoaderState::GRABBED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700861 LOG(DEBUG, "Loader GRABBED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800862 // Grabber closed.
863 loader_up_ = false;
864 disc_clamped_ = true;
865 disc_ejected_ = false;
Brian Silverman9a1081b2013-04-05 13:50:44 -0700866 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET || goal->force_fire) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700867 shooter.status.FetchLatest();
868 if (shooter.status.get()) {
869 // TODO(aschuh): If we aren't shooting nicely, wait until the shooter
870 // is up to speed rather than just spinning.
Austin Schuhf60861b2013-03-14 00:14:10 -0700871 if (shooter.status->average_velocity > 130 && shooter.status->ready) {
Austin Schuha3e8e032013-03-10 18:43:14 -0700872 loader_state_ = LoaderState::LIFTING;
873 loader_countdown_ = kLiftingDelay;
874 LOG(INFO, "Told to SHOOT_AND_RESET, moving on\n");
875 } else {
876 LOG(WARNING, "Told to SHOOT_AND_RESET, shooter too slow at %f\n",
877 shooter.status->average_velocity);
878 break;
879 }
880 } else {
881 LOG(ERROR, "Told to SHOOT_AND_RESET, no shooter data, moving on.\n");
882 loader_state_ = LoaderState::LIFTING;
883 loader_countdown_ = kLiftingDelay;
884 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800885 } else if (loader_goal_ == LoaderGoal::READY) {
886 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800887 break;
888 } else {
889 break;
890 }
891 case LoaderState::LIFTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700892 LOG(DEBUG, "Loader LIFTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800893 // Lifting the disc.
894 loader_up_ = true;
895 disc_clamped_ = true;
896 disc_ejected_ = false;
897 if (loader_countdown_ > 0) {
898 --loader_countdown_;
899 break;
900 } else {
901 loader_state_ = LoaderState::LIFTED;
902 }
903 case LoaderState::LIFTED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700904 LOG(DEBUG, "Loader LIFTED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800905 // Disc lifted. Time to eject it out.
906 loader_up_ = true;
907 disc_clamped_ = true;
908 disc_ejected_ = false;
909 loader_state_ = LoaderState::SHOOTING;
910 loader_countdown_ = kShootingDelay;
911 case LoaderState::SHOOTING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700912 LOG(DEBUG, "Loader SHOOTING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800913 // Ejecting the disc into the shooter.
914 loader_up_ = true;
915 disc_clamped_ = false;
916 disc_ejected_ = true;
917 if (loader_countdown_ > 0) {
918 --loader_countdown_;
919 break;
920 } else {
921 loader_state_ = LoaderState::SHOOT;
922 }
923 case LoaderState::SHOOT:
Austin Schuha3e8e032013-03-10 18:43:14 -0700924 LOG(DEBUG, "Loader SHOOT\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800925 // The disc has been shot.
926 loader_up_ = true;
927 disc_clamped_ = false;
928 disc_ejected_ = true;
929 loader_state_ = LoaderState::LOWERING;
930 loader_countdown_ = kLoweringDelay;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800931 --hopper_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700932 ++shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800933 case LoaderState::LOWERING:
Austin Schuha3e8e032013-03-10 18:43:14 -0700934 LOG(DEBUG, "Loader LOWERING %d\n", loader_countdown_);
Austin Schuhf8c52252013-03-03 02:25:49 -0800935 // Lowering the loader back down.
936 loader_up_ = false;
937 disc_clamped_ = false;
938 disc_ejected_ = true;
939 if (loader_countdown_ > 0) {
940 --loader_countdown_;
941 break;
942 } else {
943 loader_state_ = LoaderState::LOWERED;
944 }
945 case LoaderState::LOWERED:
Austin Schuha3e8e032013-03-10 18:43:14 -0700946 LOG(DEBUG, "Loader LOWERED\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800947 // The indexer is lowered.
948 loader_up_ = false;
949 disc_clamped_ = false;
950 disc_ejected_ = false;
951 loader_state_ = LoaderState::READY;
952 // Once we have shot, we need to hang out in READY until otherwise
953 // notified.
954 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800955 break;
956 }
957
958 // Update the observer.
959 wrist_loop_->Update(position != NULL, output == NULL);
960
961 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800962 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800963 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -0800964 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -0800965 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
966 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
967 last_bottom_disc_negedge_wait_count_ =
968 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800969 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800970 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -0800971 }
972
Brian Silvermanb8d389f2013-03-19 22:54:06 -0700973 // Clear everything if we are supposed to re-initialize.
974 if (goal_enum == Goal::REINITIALIZE) {
975 safe_goal_ = Goal::REINITIALIZE;
976 no_prior_position_ = true;
977 hopper_disc_count_ = 0;
978 total_disc_count_ = 0;
979 shot_disc_count_ = 0;
980 loader_state_ = LoaderState::READY;
981 loader_goal_ = LoaderGoal::READY;
982 loader_countdown_ = 0;
983 loader_up_ = false;
984 disc_clamped_ = false;
985 disc_ejected_ = false;
986
987 intake_voltage = 0.0;
988 transfer_voltage = 0.0;
989 wrist_loop_->U(0, 0) = 0.0;
990 frisbees_.clear();
991 }
992
Austin Schuhd78ab542013-03-01 22:22:19 -0800993 status->hopper_disc_count = hopper_disc_count_;
994 status->total_disc_count = total_disc_count_;
Austin Schuh70be1ba2013-03-10 13:37:17 -0700995 status->shot_disc_count = shot_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800996 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800997
998 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800999 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -08001000 output->transfer_voltage = transfer_voltage;
Brian Silverman180e2b82013-04-08 14:29:56 -07001001 if (goal->override_index) {
1002 output->index_voltage = goal->index_voltage;
1003 } else {
1004 output->index_voltage = wrist_loop_->U(0, 0);
1005 }
Austin Schuhf8c52252013-03-03 02:25:49 -08001006 output->loader_up = loader_up_;
1007 output->disc_clamped = disc_clamped_;
1008 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -08001009 }
1010
Austin Schuhae5fc8c2013-03-11 23:23:28 -07001011 if (safe_to_change_state) {
Austin Schuhd78ab542013-03-01 22:22:19 -08001012 safe_goal_ = goal_enum;
1013 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -08001014 if (hopper_disc_count_ < 0) {
1015 LOG(ERROR, "NEGATIVE DISCS. VERY VERY BAD\n");
1016 }
Austin Schuhd78ab542013-03-01 22:22:19 -08001017}
1018
1019} // namespace control_loops
1020} // namespace frc971