blob: 70c179fed13514a16f2a66bbd33176423896926e [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"
12
13#include "frc971/constants.h"
Austin Schuh00558222013-03-03 14:16:16 -080014#include "frc971/control_loops/index/index_motor_plant.h"
Austin Schuhd78ab542013-03-01 22:22:19 -080015
16using ::aos::time::Time;
17
18namespace frc971 {
19namespace control_loops {
20
Austin Schuh825bde92013-03-06 00:16:46 -080021void IndexMotor::Frisbee::ObserveNoTopDiscSensor(
22 double index_position, double index_velocity) {
23 double disc_position = IndexMotor::ConvertIndexToDiscPosition(
24 index_position - index_start_position_);
25 if (IndexMotor::kTopDiscDetectStart <= disc_position &&
26 disc_position <= IndexMotor::kTopDiscDetectStop) {
27 // Whoops, this shouldn't be happening.
28 // Move the disc off the way that makes most sense.
29 double distance_to_above = ::std::abs(
30 disc_position - IndexMotor::kTopDiscDetectStop);
31 double distance_to_below = ::std::abs(
32 disc_position - IndexMotor::kTopDiscDetectStart);
33 if (::std::abs(index_velocity) < 100) {
34 if (distance_to_above < distance_to_below) {
35 // Move it up.
36 index_start_position_ += distance_to_above;
37 } else {
38 index_start_position_ -= distance_to_below;
39 }
40 } else {
41 if (index_velocity > 0) {
42 // Now going up. If we didn't see it before, and we don't see it
43 // now but it should be in view, it must still be below. If it were
44 // above, it would be going further away from us.
45 index_start_position_ -= distance_to_below;
46 } else {
47 index_start_position_ += distance_to_above;
48 }
49 }
50 }
51}
52
Austin Schuhd78ab542013-03-01 22:22:19 -080053IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
54 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
Austin Schuh93485832013-03-04 00:01:34 -080055 wrist_loop_(new IndexStateFeedbackLoop(MakeIndexLoop())),
Austin Schuhd78ab542013-03-01 22:22:19 -080056 hopper_disc_count_(0),
57 total_disc_count_(0),
Austin Schuhf8c52252013-03-03 02:25:49 -080058 safe_goal_(Goal::HOLD),
59 loader_goal_(LoaderGoal::READY),
60 loader_state_(LoaderState::READY),
Austin Schuhd78ab542013-03-01 22:22:19 -080061 loader_up_(false),
62 disc_clamped_(false),
63 disc_ejected_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080064 last_bottom_disc_detect_(false),
Austin Schuh825bde92013-03-06 00:16:46 -080065 last_top_disc_detect_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080066 no_prior_position_(true),
67 missing_position_count_(0) {
Austin Schuhd78ab542013-03-01 22:22:19 -080068}
69
Austin Schuhf8c52252013-03-03 02:25:49 -080070/*static*/ const double IndexMotor::kTransferStartPosition = 0.0;
71/*static*/ const double IndexMotor::kIndexStartPosition = 0.2159;
72/*static*/ const double IndexMotor::kIndexFreeLength =
73 IndexMotor::ConvertDiscAngleToDiscPosition((360 * 2 + 14) * M_PI / 180);
74/*static*/ const double IndexMotor::kLoaderFreeStopPosition =
75 kIndexStartPosition + kIndexFreeLength;
76/*static*/ const double IndexMotor::kReadyToLiftPosition =
77 kLoaderFreeStopPosition + 0.2921;
78/*static*/ const double IndexMotor::kGrabberLength = 0.03175;
79/*static*/ const double IndexMotor::kGrabberStartPosition =
80 kReadyToLiftPosition - kGrabberLength;
Austin Schuh6328daf2013-03-05 00:53:15 -080081/*static*/ const double IndexMotor::kGrabberMovementVelocity = 0.7;
Austin Schuhf8c52252013-03-03 02:25:49 -080082/*static*/ const double IndexMotor::kLifterStopPosition =
83 kReadyToLiftPosition + 0.161925;
84/*static*/ const double IndexMotor::kLifterMovementVelocity = 1.0;
85/*static*/ const double IndexMotor::kEjectorStopPosition =
86 kLifterStopPosition + 0.01;
87/*static*/ const double IndexMotor::kEjectorMovementVelocity = 1.0;
88/*static*/ const double IndexMotor::kBottomDiscDetectStart = -0.08;
89/*static*/ const double IndexMotor::kBottomDiscDetectStop = 0.200025;
Austin Schuh6328daf2013-03-05 00:53:15 -080090/*static*/ const double IndexMotor::kBottomDiscIndexDelay = 0.01;
Austin Schuhf8c52252013-03-03 02:25:49 -080091
92// TODO(aschuh): Figure these out.
Austin Schuh825bde92013-03-06 00:16:46 -080093/*static*/ const double IndexMotor::kTopDiscDetectStart =
94 (IndexMotor::kLoaderFreeStopPosition -
95 IndexMotor::ConvertDiscAngleToDiscPosition(60 * M_PI / 180));
96// This is a guess for the width of the disc radially. It should be close to 11
97// inches but a bit below.
98/*static*/ const double IndexMotor::kTopDiscDetectStop =
99 IndexMotor::kTopDiscDetectStart + 10 * 0.0254;
Austin Schuhf8c52252013-03-03 02:25:49 -0800100
Austin Schuhd78ab542013-03-01 22:22:19 -0800101const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
102const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
Austin Schuhf8c52252013-03-03 02:25:49 -0800103const /*static*/ double IndexMotor::kTransferRollerRadius = 1.25 * 0.0254 / 2;
Austin Schuhd78ab542013-03-01 22:22:19 -0800104
Austin Schuhf8c52252013-03-03 02:25:49 -0800105/*static*/ const int IndexMotor::kGrabbingDelay = 5;
106/*static*/ const int IndexMotor::kLiftingDelay = 20;
107/*static*/ const int IndexMotor::kShootingDelay = 5;
108/*static*/ const int IndexMotor::kLoweringDelay = 20;
Austin Schuhd78ab542013-03-01 22:22:19 -0800109
Austin Schuh93485832013-03-04 00:01:34 -0800110// TODO(aschuh): Tune these.
111/*static*/ const double
112 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 5.0;
113/*static*/ const double
114 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 30;
115
Austin Schuhd78ab542013-03-01 22:22:19 -0800116// Distance to move the indexer when grabbing a disc.
117const double kNextPosition = 10.0;
118
119/*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
155bool IndexMotor::MinDiscPosition(double *disc_position) {
156 bool found_start = false;
157 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
158 const Frisbee &frisbee = frisbees_[i];
159 if (!found_start) {
160 if (frisbee.has_position()) {
161 *disc_position = frisbee.position();
162 found_start = true;
163 }
164 } else {
165 *disc_position = ::std::min(frisbee.position(),
166 *disc_position);
167 }
168 }
169 return found_start;
170}
171
172bool IndexMotor::MaxDiscPosition(double *disc_position) {
173 bool found_start = false;
174 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
175 const Frisbee &frisbee = frisbees_[i];
176 if (!found_start) {
177 if (frisbee.has_position()) {
178 *disc_position = frisbee.position();
179 found_start = true;
180 }
181 } else {
182 *disc_position = ::std::max(frisbee.position(),
183 *disc_position);
184 }
185 }
186 return found_start;
187}
188
Austin Schuh93485832013-03-04 00:01:34 -0800189void IndexMotor::IndexStateFeedbackLoop::CapU() {
190 // If the voltage has been low for a large number of cycles, cut the motor
191 // power. This is generally very bad controls practice since this isn't LTI,
192 // but we don't really care about tracking anything other than large step
193 // inputs, and the loader doesn't need to be that accurate.
194 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
195 ++low_voltage_count_;
196 if (low_voltage_count_ > kNoMotionCuttoffCount) {
197 printf("Limiting power from %f to 0\n", U(0, 0));
198 U(0, 0) = 0.0;
199 }
200 } else {
201 low_voltage_count_ = 0;
202 }
203
204 for (int i = 0; i < kNumOutputs; ++i) {
205 if (U[i] > plant.U_max[i]) {
206 U[i] = plant.U_max[i];
207 } else if (U[i] < plant.U_min[i]) {
208 U[i] = plant.U_min[i];
209 }
210 }
211}
212
213
Austin Schuhd78ab542013-03-01 22:22:19 -0800214// Positive angle is towards the shooter, and positive power is towards the
215// shooter.
216void IndexMotor::RunIteration(
217 const control_loops::IndexLoop::Goal *goal,
218 const control_loops::IndexLoop::Position *position,
219 control_loops::IndexLoop::Output *output,
220 control_loops::IndexLoop::Status *status) {
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800221 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800222 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800223 if (goal->goal_state < 0 || goal->goal_state > 4) {
224 LOG(ERROR, "Goal state is %d which is out of range. Going to HOLD.\n",
225 goal->goal_state);
226 goal_enum = Goal::HOLD;
227 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800228
229 // Disable the motors now so that all early returns will return with the
230 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800231 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800232 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800233 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800234 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800235 output->transfer_voltage = 0.0;
236 output->index_voltage = 0.0;
237 }
238
239 status->ready_to_intake = false;
240
Austin Schuhf8c52252013-03-03 02:25:49 -0800241 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800242 if (position) {
243 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800244 // Set the goal to be the current position if this is the first time through
245 // so we don't always spin the indexer to the 0 position before starting.
246 if (no_prior_position_) {
247 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
248 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800249 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
250 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
251 last_bottom_disc_negedge_wait_count_ =
252 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800253 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800254 }
255
256 // If the cRIO is gone for 1/2 of a second, assume that it rebooted.
257 if (missing_position_count_ > 50) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800258 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
259 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
260 last_bottom_disc_negedge_wait_count_ =
261 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800262 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800263 // Adjust the disc positions so that they don't have to move.
264 const double disc_offset =
265 position->index_position - wrist_loop_->X_hat(0, 0);
266 for (auto frisbee = frisbees_.begin();
267 frisbee != frisbees_.end(); ++frisbee) {
268 frisbee->OffsetDisc(disc_offset);
269 }
270 }
271 missing_position_count_ = 0;
272 } else {
273 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800274 }
275 const double index_position = wrist_loop_->X_hat(0, 0);
276
Austin Schuh825bde92013-03-06 00:16:46 -0800277 if (position) {
278 if (!position->top_disc_detect) {
279 // We don't see a disc. Verify that there are no discs that we should be
280 // seeing.
281 // Assume that discs will move slow enough that we won't one as it goes
282 // by. They will either pile up above or below the sensor.
283 for (auto frisbee = frisbees_.begin();
284 frisbee != frisbees_.end(); ++frisbee) {
285 frisbee->ObserveNoTopDiscSensor(
286 wrist_loop_->X_hat(0, 0), wrist_loop_->X_hat(1, 0));
287 }
288 }
289 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
290 // TODO(aschuh): Sanity check this number...
291 // Requires storing when the disc was last seen with the sensor off, and
292 // figuring out what to do if things go south.
293
294 // Find a disc that we should be seeing. There are 3 cases...
295 // 1) The top most disc is going up by the sensor.
296 // 2) There is 1 disc almost in the loader, and past the sensor.
297 // This is the next disc.
298 // 3) The top most disc is coming back down and we are seeing it.
299 if (wrist_loop_->X_hat(1, 0) > 50.0) {
300 // Moving up at a reasonable clip.
301 // TODO(aschuh): Do something!
302 } else if (wrist_loop_->X_hat(1, 0) < -50.0) {
303 // Moving down at a reasonable clip.
304 // Find the top disc and use that.
305 // TODO(aschuh): Do something!
306 } else {
307 // TODO(aschuh): Do something!
308 }
309 }
310 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800311
Austin Schuhf8c52252013-03-03 02:25:49 -0800312 // Bool to track if it is safe for the goal to change yet.
Austin Schuhd78ab542013-03-01 22:22:19 -0800313 bool safe_to_change_state_ = true;
314 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800315 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800316 // The goal should already be good, so sit tight with everything the same
317 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800318 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800319 case Goal::READY_LOWER:
320 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800321 {
322 Time now = Time::Now();
Austin Schuhd78ab542013-03-01 22:22:19 -0800323 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800324 // Posedge of the disc entering the beam break.
325 if (position->bottom_disc_posedge_count !=
326 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800327 transfer_frisbee_.Reset();
328 transfer_frisbee_.bottom_posedge_time_ = now;
329 printf("Posedge of bottom disc %f\n",
330 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
331 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800332 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800333 }
334
335 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800336 if (position->bottom_disc_negedge_count !=
337 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800338 transfer_frisbee_.bottom_negedge_time_ = now;
339 printf("Negedge of bottom disc %f\n",
340 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
341 frisbees_.push_front(transfer_frisbee_);
342 }
343
344 if (position->bottom_disc_detect) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800345 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800346 // Must wait until the disc gets out before we can change state.
347 safe_to_change_state_ = false;
348
Austin Schuhf8c52252013-03-03 02:25:49 -0800349 // TODO(aschuh): A disc on the way through needs to start moving
350 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800351
352 Time elapsed_posedge_time = now -
353 transfer_frisbee_.bottom_posedge_time_;
354 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
355 // It has been too long. The disc must be jammed.
356 LOG(ERROR, "Been way too long. Jammed disc?\n");
357 printf("Been way too long. Jammed disc?\n");
358 }
359 }
360
Austin Schuhf8c52252013-03-03 02:25:49 -0800361 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800362 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800363 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800364 if (!frisbee->has_been_indexed_) {
365 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800366
Austin Schuh6328daf2013-03-05 00:53:15 -0800367 if (last_bottom_disc_negedge_wait_count_ !=
368 position->bottom_disc_negedge_wait_count) {
369 // We have an index difference.
370 // Save the indexer position, and the time.
371 if (last_bottom_disc_negedge_wait_count_ + 1 !=
372 position->bottom_disc_negedge_wait_count) {
373 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
374 }
375
376 // Save the captured position as the position at which the disc
377 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800378 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
379 printf("Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800380 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800381 frisbee->index_start_position_ =
382 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800383 }
384 }
Austin Schuhb6d898b2013-03-03 15:34:35 -0800385 if (!frisbee->has_been_indexed_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800386 // All discs must be indexed before it is safe to stop indexing.
Austin Schuhd78ab542013-03-01 22:22:19 -0800387 safe_to_change_state_ = false;
388 }
389 }
390
Austin Schuhf8c52252013-03-03 02:25:49 -0800391 // Figure out where the indexer should be to move the discs down to
392 // the right position.
393 double max_disc_position;
394 if (MaxDiscPosition(&max_disc_position)) {
395 printf("There is a disc down here!\n");
396 // TODO(aschuh): Figure out what to do if grabbing the next one
397 // would cause things to jam into the loader.
398 // Say we aren't ready any more. Undefined behavior will result if
399 // that isn't observed.
400 double bottom_disc_position =
401 max_disc_position + ConvertDiscAngleToIndex(M_PI);
402 wrist_loop_->R << bottom_disc_position, 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800403
Austin Schuhf8c52252013-03-03 02:25:49 -0800404 // Verify that we are close enough to the goal so that we should be
405 // fine accepting the next disc.
406 double disc_error_meters = ConvertIndexToDiscPosition(
407 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
408 // We are ready for the next disc if the first one is in the first
409 // half circle of the indexer. It will take time for the disc to
410 // come into the indexer, so we will be able to move it out of the
411 // way in time.
412 // This choice also makes sure that we don't claim that we aren't
413 // ready between full speed intaking.
414 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
415 disc_error_meters < 0.04) {
416 // We are only ready if we aren't being asked to change state or
417 // are full.
418 status->ready_to_intake =
419 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
420 } else {
421 status->ready_to_intake = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800422 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800423 } else {
424 // No discs! We are always ready for more if we aren't being
425 // asked to change state.
426 status->ready_to_intake = (safe_goal_ == goal_enum);
Austin Schuhd78ab542013-03-01 22:22:19 -0800427 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800428
429 // Turn on the transfer roller if we are ready.
430 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
431 safe_goal_ == Goal::INTAKE) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800432 intake_voltage = transfer_voltage = 12.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800433 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800434 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800435 printf("INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800436 }
437 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800438 case Goal::READY_SHOOTER:
439 case Goal::SHOOT:
440 // Check if we have any discs to shoot or load and handle them.
441 double min_disc_position;
442 if (MinDiscPosition(&min_disc_position)) {
443 const double ready_disc_position =
444 min_disc_position + ConvertDiscPositionToIndex(kIndexFreeLength) -
445 ConvertDiscAngleToIndex(M_PI / 6.0);
446
447 const double grabbed_disc_position =
448 min_disc_position +
449 ConvertDiscPositionToIndex(kReadyToLiftPosition -
450 kIndexStartPosition + 0.03);
451
452 // Check the state of the loader FSM.
453 // If it is ready to load discs, position the disc so that it is ready
454 // to be grabbed.
455 // If it isn't ready, there is a disc in there. It needs to finish it's
456 // cycle first.
457 if (loader_state_ != LoaderState::READY) {
458 // We already have a disc in the loader.
459 // Stage the discs back a bit.
460 wrist_loop_->R << ready_disc_position, 0.0;
461
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800462 // Shoot if we are grabbed and being asked to shoot.
463 if (loader_state_ == LoaderState::GRABBED &&
464 safe_goal_ == Goal::SHOOT) {
465 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
466 }
467
Austin Schuhf8c52252013-03-03 02:25:49 -0800468 // Must wait until it has been grabbed to continue.
469 if (loader_state_ == LoaderState::GRABBING) {
470 safe_to_change_state_ = false;
471 }
472 } else {
473 // No disc up top right now.
474 wrist_loop_->R << grabbed_disc_position, 0.0;
475
476 // See if the disc has gotten pretty far up yet.
477 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
478 // Point of no return. We are committing to grabbing it now.
479 safe_to_change_state_ = false;
480 const double robust_grabbed_disc_position =
481 (grabbed_disc_position -
482 ConvertDiscPositionToIndex(kGrabberLength));
483
484 // If close, start grabbing and/or shooting.
485 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
486 // Start the state machine.
487 if (safe_goal_ == Goal::SHOOT) {
488 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
489 } else {
490 loader_goal_ = LoaderGoal::GRAB;
491 }
492 // This frisbee is now gone. Take it out of the queue.
493 frisbees_.pop_back();
494 --hopper_disc_count_;
495 }
496 }
497 }
498 }
499
500 printf("READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800501 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800502 }
503
504 // The only way out of the loader is to shoot the disc. The FSM can only go
505 // forwards.
506 switch (loader_state_) {
507 case LoaderState::READY:
508 printf("Loader READY\n");
509 // Open and down, ready to accept a disc.
510 loader_up_ = false;
511 disc_clamped_ = false;
512 disc_ejected_ = false;
513 if (loader_goal_ == LoaderGoal::GRAB ||
514 loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
515 if (loader_goal_ == LoaderGoal::GRAB) {
516 printf("Told to GRAB, moving on\n");
517 } else {
518 printf("Told to SHOOT_AND_RESET, moving on\n");
519 }
520 loader_state_ = LoaderState::GRABBING;
521 loader_countdown_ = kGrabbingDelay;
522 } else {
523 break;
524 }
525 case LoaderState::GRABBING:
526 printf("Loader GRABBING %d\n", loader_countdown_);
527 // Closing the grabber.
528 loader_up_ = false;
529 disc_clamped_ = true;
530 disc_ejected_ = false;
531 if (loader_countdown_ > 0) {
532 --loader_countdown_;
533 break;
534 } else {
535 loader_state_ = LoaderState::GRABBED;
536 }
537 case LoaderState::GRABBED:
538 printf("Loader GRABBED\n");
539 // Grabber closed.
540 loader_up_ = false;
541 disc_clamped_ = true;
542 disc_ejected_ = false;
543 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
544 // TODO(aschuh): Only shoot if the shooter is up to speed.
545 // Seems like that would have us shooting a bit later than we could be,
546 // but it also probably spins back up real fast.
547 loader_state_ = LoaderState::LIFTING;
548 loader_countdown_ = kLiftingDelay;
549 printf("Told to SHOOT_AND_RESET, moving on\n");
550 } else if (loader_goal_ == LoaderGoal::READY) {
551 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
552 printf("Can't go to ready when we have something grabbed.\n");
553 break;
554 } else {
555 break;
556 }
557 case LoaderState::LIFTING:
558 printf("Loader LIFTING %d\n", loader_countdown_);
559 // Lifting the disc.
560 loader_up_ = true;
561 disc_clamped_ = true;
562 disc_ejected_ = false;
563 if (loader_countdown_ > 0) {
564 --loader_countdown_;
565 break;
566 } else {
567 loader_state_ = LoaderState::LIFTED;
568 }
569 case LoaderState::LIFTED:
570 printf("Loader LIFTED\n");
571 // Disc lifted. Time to eject it out.
572 loader_up_ = true;
573 disc_clamped_ = true;
574 disc_ejected_ = false;
575 loader_state_ = LoaderState::SHOOTING;
576 loader_countdown_ = kShootingDelay;
577 case LoaderState::SHOOTING:
578 printf("Loader SHOOTING %d\n", loader_countdown_);
579 // Ejecting the disc into the shooter.
580 loader_up_ = true;
581 disc_clamped_ = false;
582 disc_ejected_ = true;
583 if (loader_countdown_ > 0) {
584 --loader_countdown_;
585 break;
586 } else {
587 loader_state_ = LoaderState::SHOOT;
588 }
589 case LoaderState::SHOOT:
590 printf("Loader SHOOT\n");
591 // The disc has been shot.
592 loader_up_ = true;
593 disc_clamped_ = false;
594 disc_ejected_ = true;
595 loader_state_ = LoaderState::LOWERING;
596 loader_countdown_ = kLoweringDelay;
597 case LoaderState::LOWERING:
598 printf("Loader LOWERING %d\n", loader_countdown_);
599 // Lowering the loader back down.
600 loader_up_ = false;
601 disc_clamped_ = false;
602 disc_ejected_ = true;
603 if (loader_countdown_ > 0) {
604 --loader_countdown_;
605 break;
606 } else {
607 loader_state_ = LoaderState::LOWERED;
608 }
609 case LoaderState::LOWERED:
610 printf("Loader LOWERED\n");
611 // The indexer is lowered.
612 loader_up_ = false;
613 disc_clamped_ = false;
614 disc_ejected_ = false;
615 loader_state_ = LoaderState::READY;
616 // Once we have shot, we need to hang out in READY until otherwise
617 // notified.
618 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800619 break;
620 }
621
622 // Update the observer.
623 wrist_loop_->Update(position != NULL, output == NULL);
624
625 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800626 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800627 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -0800628 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -0800629 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
630 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
631 last_bottom_disc_negedge_wait_count_ =
632 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800633 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -0800634 }
635
636 status->hopper_disc_count = hopper_disc_count_;
637 status->total_disc_count = total_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800638 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800639
640 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800641 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -0800642 output->transfer_voltage = transfer_voltage;
Austin Schuhd78ab542013-03-01 22:22:19 -0800643 output->index_voltage = wrist_loop_->U(0, 0);
Austin Schuhf8c52252013-03-03 02:25:49 -0800644 output->loader_up = loader_up_;
645 output->disc_clamped = disc_clamped_;
646 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800647 }
648
649 if (safe_to_change_state_) {
650 safe_goal_ = goal_enum;
651 }
652}
653
654} // namespace control_loops
655} // namespace frc971