blob: bf0acb906123047a008696a48723cf511fdf931e [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 Schuhdff24e22013-03-06 00:41:21 -080021double IndexMotor::Frisbee::ObserveNoTopDiscSensor(
Austin Schuh825bde92013-03-06 00:16:46 -080022 double index_position, double index_velocity) {
Austin Schuhdff24e22013-03-06 00:41:21 -080023 // The absolute disc position in meters.
Austin Schuh825bde92013-03-06 00:16:46 -080024 double disc_position = IndexMotor::ConvertIndexToDiscPosition(
Austin Schuhdff24e22013-03-06 00:41:21 -080025 index_position - index_start_position_) + IndexMotor::kIndexStartPosition;
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 Schuh825bde92013-03-06 00:16:46 -080034 if (::std::abs(index_velocity) < 100) {
35 if (distance_to_above < distance_to_below) {
Austin Schuhdff24e22013-03-06 00:41:21 -080036 printf("Moving disc to top slow.\n");
Austin Schuh825bde92013-03-06 00:16:46 -080037 // Move it up.
Austin Schuhdff24e22013-03-06 00:41:21 -080038 index_start_position_ -= distance_to_above;
39 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080040 } else {
Austin Schuhdff24e22013-03-06 00:41:21 -080041 printf("Moving disc to bottom slow.\n");
42 index_start_position_ += distance_to_below;
43 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080044 }
45 } else {
46 if (index_velocity > 0) {
47 // Now going up. If we didn't see it before, and we don't see it
48 // now but it should be in view, it must still be below. If it were
49 // above, it would be going further away from us.
Austin Schuhdff24e22013-03-06 00:41:21 -080050 printf("Moving fast up, shifting disc up\n");
51 index_start_position_ += distance_to_below;
52 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080053 } else {
Austin Schuhdff24e22013-03-06 00:41:21 -080054 printf("Moving fast down, shifting disc down\n");
55 index_start_position_ -= distance_to_above;
56 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080057 }
58 }
59 }
Austin Schuhdff24e22013-03-06 00:41:21 -080060 return 0.0;
Austin Schuh825bde92013-03-06 00:16:46 -080061}
62
Austin Schuhd78ab542013-03-01 22:22:19 -080063IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
64 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
Austin Schuh93485832013-03-04 00:01:34 -080065 wrist_loop_(new IndexStateFeedbackLoop(MakeIndexLoop())),
Austin Schuhd78ab542013-03-01 22:22:19 -080066 hopper_disc_count_(0),
67 total_disc_count_(0),
Austin Schuhf8c52252013-03-03 02:25:49 -080068 safe_goal_(Goal::HOLD),
69 loader_goal_(LoaderGoal::READY),
70 loader_state_(LoaderState::READY),
Austin Schuhd78ab542013-03-01 22:22:19 -080071 loader_up_(false),
72 disc_clamped_(false),
73 disc_ejected_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080074 last_bottom_disc_detect_(false),
Austin Schuh825bde92013-03-06 00:16:46 -080075 last_top_disc_detect_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080076 no_prior_position_(true),
77 missing_position_count_(0) {
Austin Schuhd78ab542013-03-01 22:22:19 -080078}
79
Austin Schuhf8c52252013-03-03 02:25:49 -080080/*static*/ const double IndexMotor::kTransferStartPosition = 0.0;
81/*static*/ const double IndexMotor::kIndexStartPosition = 0.2159;
82/*static*/ const double IndexMotor::kIndexFreeLength =
83 IndexMotor::ConvertDiscAngleToDiscPosition((360 * 2 + 14) * M_PI / 180);
84/*static*/ const double IndexMotor::kLoaderFreeStopPosition =
85 kIndexStartPosition + kIndexFreeLength;
86/*static*/ const double IndexMotor::kReadyToLiftPosition =
87 kLoaderFreeStopPosition + 0.2921;
88/*static*/ const double IndexMotor::kGrabberLength = 0.03175;
89/*static*/ const double IndexMotor::kGrabberStartPosition =
90 kReadyToLiftPosition - kGrabberLength;
Austin Schuh6328daf2013-03-05 00:53:15 -080091/*static*/ const double IndexMotor::kGrabberMovementVelocity = 0.7;
Austin Schuhf8c52252013-03-03 02:25:49 -080092/*static*/ const double IndexMotor::kLifterStopPosition =
93 kReadyToLiftPosition + 0.161925;
94/*static*/ const double IndexMotor::kLifterMovementVelocity = 1.0;
95/*static*/ const double IndexMotor::kEjectorStopPosition =
96 kLifterStopPosition + 0.01;
97/*static*/ const double IndexMotor::kEjectorMovementVelocity = 1.0;
98/*static*/ const double IndexMotor::kBottomDiscDetectStart = -0.08;
99/*static*/ const double IndexMotor::kBottomDiscDetectStop = 0.200025;
Austin Schuh6328daf2013-03-05 00:53:15 -0800100/*static*/ const double IndexMotor::kBottomDiscIndexDelay = 0.01;
Austin Schuhf8c52252013-03-03 02:25:49 -0800101
102// TODO(aschuh): Figure these out.
Austin Schuh825bde92013-03-06 00:16:46 -0800103/*static*/ const double IndexMotor::kTopDiscDetectStart =
104 (IndexMotor::kLoaderFreeStopPosition -
105 IndexMotor::ConvertDiscAngleToDiscPosition(60 * M_PI / 180));
106// This is a guess for the width of the disc radially. It should be close to 11
107// inches but a bit below.
108/*static*/ const double IndexMotor::kTopDiscDetectStop =
109 IndexMotor::kTopDiscDetectStart + 10 * 0.0254;
Austin Schuhf8c52252013-03-03 02:25:49 -0800110
Austin Schuhd78ab542013-03-01 22:22:19 -0800111const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
112const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
Austin Schuhf8c52252013-03-03 02:25:49 -0800113const /*static*/ double IndexMotor::kTransferRollerRadius = 1.25 * 0.0254 / 2;
Austin Schuhd78ab542013-03-01 22:22:19 -0800114
Austin Schuhf8c52252013-03-03 02:25:49 -0800115/*static*/ const int IndexMotor::kGrabbingDelay = 5;
116/*static*/ const int IndexMotor::kLiftingDelay = 20;
117/*static*/ const int IndexMotor::kShootingDelay = 5;
118/*static*/ const int IndexMotor::kLoweringDelay = 20;
Austin Schuhd78ab542013-03-01 22:22:19 -0800119
Austin Schuh93485832013-03-04 00:01:34 -0800120// TODO(aschuh): Tune these.
121/*static*/ const double
122 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 5.0;
123/*static*/ const double
124 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 30;
125
Austin Schuhd78ab542013-03-01 22:22:19 -0800126// Distance to move the indexer when grabbing a disc.
127const double kNextPosition = 10.0;
128
129/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
130 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
131}
132
Austin Schuhf8c52252013-03-03 02:25:49 -0800133/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
134 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800135 return angle * (kDiscRadius + kRollerRadius);
136}
137
Austin Schuhf8c52252013-03-03 02:25:49 -0800138/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
139 const double position) {
140 return position / (kDiscRadius + kRollerRadius);
141}
142
Austin Schuhd78ab542013-03-01 22:22:19 -0800143/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
144 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
145}
146
147/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
148 return IndexMotor::ConvertDiscAngleToDiscPosition(
149 ConvertIndexToDiscAngle(angle));
150}
151
Austin Schuhf8c52252013-03-03 02:25:49 -0800152/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
153 const double angle) {
154 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
155 kTransferRollerRadius);
156 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
157}
158
159/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
160 const double position) {
161 return IndexMotor::ConvertDiscAngleToIndex(
162 ConvertDiscPositionToDiscAngle(position));
163}
164
165bool IndexMotor::MinDiscPosition(double *disc_position) {
166 bool found_start = false;
167 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
168 const Frisbee &frisbee = frisbees_[i];
169 if (!found_start) {
170 if (frisbee.has_position()) {
171 *disc_position = frisbee.position();
172 found_start = true;
173 }
174 } else {
175 *disc_position = ::std::min(frisbee.position(),
176 *disc_position);
177 }
178 }
179 return found_start;
180}
181
182bool IndexMotor::MaxDiscPosition(double *disc_position) {
183 bool found_start = false;
184 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
185 const Frisbee &frisbee = frisbees_[i];
186 if (!found_start) {
187 if (frisbee.has_position()) {
188 *disc_position = frisbee.position();
189 found_start = true;
190 }
191 } else {
192 *disc_position = ::std::max(frisbee.position(),
193 *disc_position);
194 }
195 }
196 return found_start;
197}
198
Austin Schuh93485832013-03-04 00:01:34 -0800199void IndexMotor::IndexStateFeedbackLoop::CapU() {
200 // If the voltage has been low for a large number of cycles, cut the motor
201 // power. This is generally very bad controls practice since this isn't LTI,
202 // but we don't really care about tracking anything other than large step
203 // inputs, and the loader doesn't need to be that accurate.
204 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
205 ++low_voltage_count_;
206 if (low_voltage_count_ > kNoMotionCuttoffCount) {
207 printf("Limiting power from %f to 0\n", U(0, 0));
208 U(0, 0) = 0.0;
209 }
210 } else {
211 low_voltage_count_ = 0;
212 }
213
214 for (int i = 0; i < kNumOutputs; ++i) {
215 if (U[i] > plant.U_max[i]) {
216 U[i] = plant.U_max[i];
217 } else if (U[i] < plant.U_min[i]) {
218 U[i] = plant.U_min[i];
219 }
220 }
221}
222
223
Austin Schuhd78ab542013-03-01 22:22:19 -0800224// Positive angle is towards the shooter, and positive power is towards the
225// shooter.
226void IndexMotor::RunIteration(
227 const control_loops::IndexLoop::Goal *goal,
228 const control_loops::IndexLoop::Position *position,
229 control_loops::IndexLoop::Output *output,
230 control_loops::IndexLoop::Status *status) {
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800231 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800232 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800233 if (goal->goal_state < 0 || goal->goal_state > 4) {
234 LOG(ERROR, "Goal state is %d which is out of range. Going to HOLD.\n",
235 goal->goal_state);
236 goal_enum = Goal::HOLD;
237 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800238
239 // Disable the motors now so that all early returns will return with the
240 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800241 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800242 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800243 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800244 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800245 output->transfer_voltage = 0.0;
246 output->index_voltage = 0.0;
247 }
248
249 status->ready_to_intake = false;
250
Austin Schuhf8c52252013-03-03 02:25:49 -0800251 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800252 if (position) {
253 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800254 // Set the goal to be the current position if this is the first time through
255 // so we don't always spin the indexer to the 0 position before starting.
256 if (no_prior_position_) {
257 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
258 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800259 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
260 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
261 last_bottom_disc_negedge_wait_count_ =
262 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800263 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800264 }
265
266 // If the cRIO is gone for 1/2 of a second, assume that it rebooted.
267 if (missing_position_count_ > 50) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800268 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
269 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
270 last_bottom_disc_negedge_wait_count_ =
271 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800272 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800273 // Adjust the disc positions so that they don't have to move.
274 const double disc_offset =
275 position->index_position - wrist_loop_->X_hat(0, 0);
276 for (auto frisbee = frisbees_.begin();
277 frisbee != frisbees_.end(); ++frisbee) {
278 frisbee->OffsetDisc(disc_offset);
279 }
280 }
281 missing_position_count_ = 0;
282 } else {
283 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800284 }
285 const double index_position = wrist_loop_->X_hat(0, 0);
286
Austin Schuh825bde92013-03-06 00:16:46 -0800287 if (position) {
288 if (!position->top_disc_detect) {
289 // We don't see a disc. Verify that there are no discs that we should be
290 // seeing.
291 // Assume that discs will move slow enough that we won't one as it goes
292 // by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800293
294 double cumulative_offset = 0.0;
295 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
296 frisbee != rend; ++frisbee) {
297 frisbee->OffsetDisc(cumulative_offset);
298 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh825bde92013-03-06 00:16:46 -0800299 wrist_loop_->X_hat(0, 0), wrist_loop_->X_hat(1, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800300 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800301 }
302 }
303 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
304 // TODO(aschuh): Sanity check this number...
305 // Requires storing when the disc was last seen with the sensor off, and
306 // figuring out what to do if things go south.
307
308 // Find a disc that we should be seeing. There are 3 cases...
309 // 1) The top most disc is going up by the sensor.
310 // 2) There is 1 disc almost in the loader, and past the sensor.
311 // This is the next disc.
312 // 3) The top most disc is coming back down and we are seeing it.
313 if (wrist_loop_->X_hat(1, 0) > 50.0) {
314 // Moving up at a reasonable clip.
315 // TODO(aschuh): Do something!
316 } else if (wrist_loop_->X_hat(1, 0) < -50.0) {
317 // Moving down at a reasonable clip.
318 // Find the top disc and use that.
319 // TODO(aschuh): Do something!
320 } else {
321 // TODO(aschuh): Do something!
322 }
323 }
324 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800325
Austin Schuhf8c52252013-03-03 02:25:49 -0800326 // Bool to track if it is safe for the goal to change yet.
Austin Schuhd78ab542013-03-01 22:22:19 -0800327 bool safe_to_change_state_ = true;
328 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800329 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800330 // The goal should already be good, so sit tight with everything the same
331 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800332 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800333 case Goal::READY_LOWER:
334 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800335 {
336 Time now = Time::Now();
Austin Schuhd78ab542013-03-01 22:22:19 -0800337 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800338 // Posedge of the disc entering the beam break.
339 if (position->bottom_disc_posedge_count !=
340 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800341 transfer_frisbee_.Reset();
342 transfer_frisbee_.bottom_posedge_time_ = now;
343 printf("Posedge of bottom disc %f\n",
344 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
345 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800346 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800347 }
348
349 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800350 if (position->bottom_disc_negedge_count !=
351 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800352 transfer_frisbee_.bottom_negedge_time_ = now;
353 printf("Negedge of bottom disc %f\n",
354 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
355 frisbees_.push_front(transfer_frisbee_);
356 }
357
358 if (position->bottom_disc_detect) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800359 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800360 // Must wait until the disc gets out before we can change state.
361 safe_to_change_state_ = false;
362
Austin Schuhf8c52252013-03-03 02:25:49 -0800363 // TODO(aschuh): A disc on the way through needs to start moving
364 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800365
366 Time elapsed_posedge_time = now -
367 transfer_frisbee_.bottom_posedge_time_;
368 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
369 // It has been too long. The disc must be jammed.
370 LOG(ERROR, "Been way too long. Jammed disc?\n");
371 printf("Been way too long. Jammed disc?\n");
372 }
373 }
374
Austin Schuhf8c52252013-03-03 02:25:49 -0800375 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800376 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800377 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800378 if (!frisbee->has_been_indexed_) {
379 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800380
Austin Schuh6328daf2013-03-05 00:53:15 -0800381 if (last_bottom_disc_negedge_wait_count_ !=
382 position->bottom_disc_negedge_wait_count) {
383 // We have an index difference.
384 // Save the indexer position, and the time.
385 if (last_bottom_disc_negedge_wait_count_ + 1 !=
386 position->bottom_disc_negedge_wait_count) {
387 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
388 }
389
390 // Save the captured position as the position at which the disc
391 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800392 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
393 printf("Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800394 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800395 frisbee->index_start_position_ =
396 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800397 }
398 }
Austin Schuhb6d898b2013-03-03 15:34:35 -0800399 if (!frisbee->has_been_indexed_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800400 // All discs must be indexed before it is safe to stop indexing.
Austin Schuhd78ab542013-03-01 22:22:19 -0800401 safe_to_change_state_ = false;
402 }
403 }
404
Austin Schuhf8c52252013-03-03 02:25:49 -0800405 // Figure out where the indexer should be to move the discs down to
406 // the right position.
407 double max_disc_position;
408 if (MaxDiscPosition(&max_disc_position)) {
409 printf("There is a disc down here!\n");
410 // TODO(aschuh): Figure out what to do if grabbing the next one
411 // would cause things to jam into the loader.
412 // Say we aren't ready any more. Undefined behavior will result if
413 // that isn't observed.
414 double bottom_disc_position =
415 max_disc_position + ConvertDiscAngleToIndex(M_PI);
416 wrist_loop_->R << bottom_disc_position, 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800417
Austin Schuhf8c52252013-03-03 02:25:49 -0800418 // Verify that we are close enough to the goal so that we should be
419 // fine accepting the next disc.
420 double disc_error_meters = ConvertIndexToDiscPosition(
421 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
422 // We are ready for the next disc if the first one is in the first
423 // half circle of the indexer. It will take time for the disc to
424 // come into the indexer, so we will be able to move it out of the
425 // way in time.
426 // This choice also makes sure that we don't claim that we aren't
427 // ready between full speed intaking.
428 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
429 disc_error_meters < 0.04) {
430 // We are only ready if we aren't being asked to change state or
431 // are full.
432 status->ready_to_intake =
433 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
434 } else {
435 status->ready_to_intake = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800436 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800437 } else {
438 // No discs! We are always ready for more if we aren't being
439 // asked to change state.
440 status->ready_to_intake = (safe_goal_ == goal_enum);
Austin Schuhd78ab542013-03-01 22:22:19 -0800441 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800442
443 // Turn on the transfer roller if we are ready.
444 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
445 safe_goal_ == Goal::INTAKE) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800446 intake_voltage = transfer_voltage = 12.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800447 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800448 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800449 printf("INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800450 }
451 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800452 case Goal::READY_SHOOTER:
453 case Goal::SHOOT:
454 // Check if we have any discs to shoot or load and handle them.
455 double min_disc_position;
456 if (MinDiscPosition(&min_disc_position)) {
457 const double ready_disc_position =
458 min_disc_position + ConvertDiscPositionToIndex(kIndexFreeLength) -
459 ConvertDiscAngleToIndex(M_PI / 6.0);
460
461 const double grabbed_disc_position =
462 min_disc_position +
463 ConvertDiscPositionToIndex(kReadyToLiftPosition -
464 kIndexStartPosition + 0.03);
465
466 // Check the state of the loader FSM.
467 // If it is ready to load discs, position the disc so that it is ready
468 // to be grabbed.
469 // If it isn't ready, there is a disc in there. It needs to finish it's
470 // cycle first.
471 if (loader_state_ != LoaderState::READY) {
472 // We already have a disc in the loader.
473 // Stage the discs back a bit.
474 wrist_loop_->R << ready_disc_position, 0.0;
475
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800476 // Shoot if we are grabbed and being asked to shoot.
477 if (loader_state_ == LoaderState::GRABBED &&
478 safe_goal_ == Goal::SHOOT) {
479 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
480 }
481
Austin Schuhf8c52252013-03-03 02:25:49 -0800482 // Must wait until it has been grabbed to continue.
483 if (loader_state_ == LoaderState::GRABBING) {
484 safe_to_change_state_ = false;
485 }
486 } else {
487 // No disc up top right now.
488 wrist_loop_->R << grabbed_disc_position, 0.0;
489
490 // See if the disc has gotten pretty far up yet.
491 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
492 // Point of no return. We are committing to grabbing it now.
493 safe_to_change_state_ = false;
494 const double robust_grabbed_disc_position =
495 (grabbed_disc_position -
496 ConvertDiscPositionToIndex(kGrabberLength));
497
498 // If close, start grabbing and/or shooting.
499 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
500 // Start the state machine.
501 if (safe_goal_ == Goal::SHOOT) {
502 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
503 } else {
504 loader_goal_ = LoaderGoal::GRAB;
505 }
506 // This frisbee is now gone. Take it out of the queue.
507 frisbees_.pop_back();
508 --hopper_disc_count_;
509 }
510 }
511 }
512 }
513
514 printf("READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800515 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800516 }
517
518 // The only way out of the loader is to shoot the disc. The FSM can only go
519 // forwards.
520 switch (loader_state_) {
521 case LoaderState::READY:
522 printf("Loader READY\n");
523 // Open and down, ready to accept a disc.
524 loader_up_ = false;
525 disc_clamped_ = false;
526 disc_ejected_ = false;
527 if (loader_goal_ == LoaderGoal::GRAB ||
528 loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
529 if (loader_goal_ == LoaderGoal::GRAB) {
530 printf("Told to GRAB, moving on\n");
531 } else {
532 printf("Told to SHOOT_AND_RESET, moving on\n");
533 }
534 loader_state_ = LoaderState::GRABBING;
535 loader_countdown_ = kGrabbingDelay;
536 } else {
537 break;
538 }
539 case LoaderState::GRABBING:
540 printf("Loader GRABBING %d\n", loader_countdown_);
541 // Closing the grabber.
542 loader_up_ = false;
543 disc_clamped_ = true;
544 disc_ejected_ = false;
545 if (loader_countdown_ > 0) {
546 --loader_countdown_;
547 break;
548 } else {
549 loader_state_ = LoaderState::GRABBED;
550 }
551 case LoaderState::GRABBED:
552 printf("Loader GRABBED\n");
553 // Grabber closed.
554 loader_up_ = false;
555 disc_clamped_ = true;
556 disc_ejected_ = false;
557 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
558 // TODO(aschuh): Only shoot if the shooter is up to speed.
559 // Seems like that would have us shooting a bit later than we could be,
560 // but it also probably spins back up real fast.
561 loader_state_ = LoaderState::LIFTING;
562 loader_countdown_ = kLiftingDelay;
563 printf("Told to SHOOT_AND_RESET, moving on\n");
564 } else if (loader_goal_ == LoaderGoal::READY) {
565 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
566 printf("Can't go to ready when we have something grabbed.\n");
567 break;
568 } else {
569 break;
570 }
571 case LoaderState::LIFTING:
572 printf("Loader LIFTING %d\n", loader_countdown_);
573 // Lifting the disc.
574 loader_up_ = true;
575 disc_clamped_ = true;
576 disc_ejected_ = false;
577 if (loader_countdown_ > 0) {
578 --loader_countdown_;
579 break;
580 } else {
581 loader_state_ = LoaderState::LIFTED;
582 }
583 case LoaderState::LIFTED:
584 printf("Loader LIFTED\n");
585 // Disc lifted. Time to eject it out.
586 loader_up_ = true;
587 disc_clamped_ = true;
588 disc_ejected_ = false;
589 loader_state_ = LoaderState::SHOOTING;
590 loader_countdown_ = kShootingDelay;
591 case LoaderState::SHOOTING:
592 printf("Loader SHOOTING %d\n", loader_countdown_);
593 // Ejecting the disc into the shooter.
594 loader_up_ = true;
595 disc_clamped_ = false;
596 disc_ejected_ = true;
597 if (loader_countdown_ > 0) {
598 --loader_countdown_;
599 break;
600 } else {
601 loader_state_ = LoaderState::SHOOT;
602 }
603 case LoaderState::SHOOT:
604 printf("Loader SHOOT\n");
605 // The disc has been shot.
606 loader_up_ = true;
607 disc_clamped_ = false;
608 disc_ejected_ = true;
609 loader_state_ = LoaderState::LOWERING;
610 loader_countdown_ = kLoweringDelay;
611 case LoaderState::LOWERING:
612 printf("Loader LOWERING %d\n", loader_countdown_);
613 // Lowering the loader back down.
614 loader_up_ = false;
615 disc_clamped_ = false;
616 disc_ejected_ = true;
617 if (loader_countdown_ > 0) {
618 --loader_countdown_;
619 break;
620 } else {
621 loader_state_ = LoaderState::LOWERED;
622 }
623 case LoaderState::LOWERED:
624 printf("Loader LOWERED\n");
625 // The indexer is lowered.
626 loader_up_ = false;
627 disc_clamped_ = false;
628 disc_ejected_ = false;
629 loader_state_ = LoaderState::READY;
630 // Once we have shot, we need to hang out in READY until otherwise
631 // notified.
632 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800633 break;
634 }
635
636 // Update the observer.
637 wrist_loop_->Update(position != NULL, output == NULL);
638
639 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800640 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800641 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -0800642 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -0800643 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
644 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
645 last_bottom_disc_negedge_wait_count_ =
646 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800647 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -0800648 }
649
650 status->hopper_disc_count = hopper_disc_count_;
651 status->total_disc_count = total_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800652 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800653
654 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800655 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -0800656 output->transfer_voltage = transfer_voltage;
Austin Schuhd78ab542013-03-01 22:22:19 -0800657 output->index_voltage = wrist_loop_->U(0, 0);
Austin Schuhf8c52252013-03-03 02:25:49 -0800658 output->loader_up = loader_up_;
659 output->disc_clamped = disc_clamped_;
660 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800661 }
662
663 if (safe_to_change_state_) {
664 safe_goal_ = goal_enum;
665 }
666}
667
668} // namespace control_loops
669} // namespace frc971