blob: 83145c9a3dd47b0ddc65bd28a0e851b4445ce251 [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 Schuh1b864a12013-03-07 00:46:50 -080024 double disc_position = absolute_position(index_position);
Austin Schuh825bde92013-03-06 00:16:46 -080025 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.
Austin Schuhdff24e22013-03-06 00:41:21 -080029 double distance_to_above = IndexMotor::ConvertDiscPositionToIndex(
30 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStop));
31 double distance_to_below = IndexMotor::ConvertDiscPositionToIndex(
32 ::std::abs(disc_position - IndexMotor::kTopDiscDetectStart));
Austin Schuh825bde92013-03-06 00:16:46 -080033 if (::std::abs(index_velocity) < 100) {
34 if (distance_to_above < distance_to_below) {
Austin Schuhdff24e22013-03-06 00:41:21 -080035 printf("Moving disc to top slow.\n");
Austin Schuh825bde92013-03-06 00:16:46 -080036 // Move it up.
Austin Schuhdff24e22013-03-06 00:41:21 -080037 index_start_position_ -= distance_to_above;
38 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080039 } else {
Austin Schuhdff24e22013-03-06 00:41:21 -080040 printf("Moving disc to bottom slow.\n");
41 index_start_position_ += distance_to_below;
42 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080043 }
44 } else {
45 if (index_velocity > 0) {
46 // Now going up. If we didn't see it before, and we don't see it
47 // now but it should be in view, it must still be below. If it were
48 // above, it would be going further away from us.
Austin Schuh1b864a12013-03-07 00:46:50 -080049 printf("Moving fast up, shifting disc down. Disc was at %f\n",
50 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080051 index_start_position_ += distance_to_below;
Austin Schuh1b864a12013-03-07 00:46:50 -080052 printf("Moving fast up, shifting disc down. Disc now at %f\n",
53 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080054 return distance_to_below;
Austin Schuh825bde92013-03-06 00:16:46 -080055 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -080056 printf("Moving fast down, shifting disc up. Disc was at %f\n",
57 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080058 index_start_position_ -= distance_to_above;
Austin Schuh1b864a12013-03-07 00:46:50 -080059 printf("Moving fast down, shifting disc up. Disc now at %f\n",
60 absolute_position(index_position));
Austin Schuhdff24e22013-03-06 00:41:21 -080061 return -distance_to_above;
Austin Schuh825bde92013-03-06 00:16:46 -080062 }
63 }
64 }
Austin Schuhdff24e22013-03-06 00:41:21 -080065 return 0.0;
Austin Schuh825bde92013-03-06 00:16:46 -080066}
67
Austin Schuhd78ab542013-03-01 22:22:19 -080068IndexMotor::IndexMotor(control_loops::IndexLoop *my_index)
69 : aos::control_loops::ControlLoop<control_loops::IndexLoop>(my_index),
Austin Schuh93485832013-03-04 00:01:34 -080070 wrist_loop_(new IndexStateFeedbackLoop(MakeIndexLoop())),
Austin Schuhd78ab542013-03-01 22:22:19 -080071 hopper_disc_count_(0),
72 total_disc_count_(0),
Austin Schuhf8c52252013-03-03 02:25:49 -080073 safe_goal_(Goal::HOLD),
74 loader_goal_(LoaderGoal::READY),
75 loader_state_(LoaderState::READY),
Austin Schuhd78ab542013-03-01 22:22:19 -080076 loader_up_(false),
77 disc_clamped_(false),
78 disc_ejected_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080079 last_bottom_disc_detect_(false),
Austin Schuh825bde92013-03-06 00:16:46 -080080 last_top_disc_detect_(false),
Austin Schuhbcdb90c2013-03-03 23:24:58 -080081 no_prior_position_(true),
Austin Schuh7c0e2aa2013-03-09 02:01:16 -080082 missing_position_count_(0),
83 upper_open_index_position_(0.0),
84 upper_open_index_position_was_negedge_(false),
85 lower_open_index_position_(0.0),
86 lower_open_index_position_was_negedge_(false) {
Austin Schuhd78ab542013-03-01 22:22:19 -080087}
88
Austin Schuhf8c52252013-03-03 02:25:49 -080089/*static*/ const double IndexMotor::kTransferStartPosition = 0.0;
90/*static*/ const double IndexMotor::kIndexStartPosition = 0.2159;
91/*static*/ const double IndexMotor::kIndexFreeLength =
92 IndexMotor::ConvertDiscAngleToDiscPosition((360 * 2 + 14) * M_PI / 180);
93/*static*/ const double IndexMotor::kLoaderFreeStopPosition =
94 kIndexStartPosition + kIndexFreeLength;
Austin Schuh1b864a12013-03-07 00:46:50 -080095/*static*/ const double IndexMotor::kReadyToPreload =
96 kLoaderFreeStopPosition - ConvertDiscAngleToDiscPosition(M_PI / 6.0);
Austin Schuhf8c52252013-03-03 02:25:49 -080097/*static*/ const double IndexMotor::kReadyToLiftPosition =
98 kLoaderFreeStopPosition + 0.2921;
99/*static*/ const double IndexMotor::kGrabberLength = 0.03175;
100/*static*/ const double IndexMotor::kGrabberStartPosition =
101 kReadyToLiftPosition - kGrabberLength;
Austin Schuh6328daf2013-03-05 00:53:15 -0800102/*static*/ const double IndexMotor::kGrabberMovementVelocity = 0.7;
Austin Schuhf8c52252013-03-03 02:25:49 -0800103/*static*/ const double IndexMotor::kLifterStopPosition =
104 kReadyToLiftPosition + 0.161925;
105/*static*/ const double IndexMotor::kLifterMovementVelocity = 1.0;
106/*static*/ const double IndexMotor::kEjectorStopPosition =
107 kLifterStopPosition + 0.01;
108/*static*/ const double IndexMotor::kEjectorMovementVelocity = 1.0;
109/*static*/ const double IndexMotor::kBottomDiscDetectStart = -0.08;
110/*static*/ const double IndexMotor::kBottomDiscDetectStop = 0.200025;
Austin Schuh6328daf2013-03-05 00:53:15 -0800111/*static*/ const double IndexMotor::kBottomDiscIndexDelay = 0.01;
Austin Schuhf8c52252013-03-03 02:25:49 -0800112
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800113// TODO(aschuh): Verify these with the sensor actually on.
Austin Schuh825bde92013-03-06 00:16:46 -0800114/*static*/ const double IndexMotor::kTopDiscDetectStart =
115 (IndexMotor::kLoaderFreeStopPosition -
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800116 IndexMotor::ConvertDiscAngleToDiscPosition(49 * M_PI / 180));
Austin Schuh825bde92013-03-06 00:16:46 -0800117/*static*/ const double IndexMotor::kTopDiscDetectStop =
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800118 (IndexMotor::kLoaderFreeStopPosition +
119 IndexMotor::ConvertDiscAngleToDiscPosition(19 * M_PI / 180));
120
121// I measured the angle between 2 discs. That then gives me the distance
122// between 2 posedges (or negedges). Then subtract off the width of the
123// positive pulse, and that gives the width of the negative pulse.
124/*static*/ const double IndexMotor::kTopDiscDetectMinSeperation =
125 (IndexMotor::ConvertDiscAngleToDiscPosition(120 * M_PI / 180) -
126 (IndexMotor::kTopDiscDetectStop - IndexMotor::kTopDiscDetectStart));
Austin Schuhf8c52252013-03-03 02:25:49 -0800127
Austin Schuhd78ab542013-03-01 22:22:19 -0800128const /*static*/ double IndexMotor::kDiscRadius = 10.875 * 0.0254 / 2;
129const /*static*/ double IndexMotor::kRollerRadius = 2.0 * 0.0254 / 2;
Austin Schuhf8c52252013-03-03 02:25:49 -0800130const /*static*/ double IndexMotor::kTransferRollerRadius = 1.25 * 0.0254 / 2;
Austin Schuhd78ab542013-03-01 22:22:19 -0800131
Austin Schuhf8c52252013-03-03 02:25:49 -0800132/*static*/ const int IndexMotor::kGrabbingDelay = 5;
133/*static*/ const int IndexMotor::kLiftingDelay = 20;
134/*static*/ const int IndexMotor::kShootingDelay = 5;
135/*static*/ const int IndexMotor::kLoweringDelay = 20;
Austin Schuhd78ab542013-03-01 22:22:19 -0800136
Austin Schuh93485832013-03-04 00:01:34 -0800137// TODO(aschuh): Tune these.
138/*static*/ const double
139 IndexMotor::IndexStateFeedbackLoop::kMinMotionVoltage = 5.0;
140/*static*/ const double
141 IndexMotor::IndexStateFeedbackLoop::kNoMotionCuttoffCount = 30;
142
Austin Schuhd78ab542013-03-01 22:22:19 -0800143// Distance to move the indexer when grabbing a disc.
144const double kNextPosition = 10.0;
145
146/*static*/ double IndexMotor::ConvertDiscAngleToIndex(const double angle) {
147 return (angle * (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
148}
149
Austin Schuhf8c52252013-03-03 02:25:49 -0800150/*static*/ double IndexMotor::ConvertDiscAngleToDiscPosition(
151 const double angle) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800152 return angle * (kDiscRadius + kRollerRadius);
153}
154
Austin Schuhf8c52252013-03-03 02:25:49 -0800155/*static*/ double IndexMotor::ConvertDiscPositionToDiscAngle(
156 const double position) {
157 return position / (kDiscRadius + kRollerRadius);
158}
159
Austin Schuhd78ab542013-03-01 22:22:19 -0800160/*static*/ double IndexMotor::ConvertIndexToDiscAngle(const double angle) {
161 return (angle / (1 + (kDiscRadius * 2 + kRollerRadius) / kRollerRadius));
162}
163
164/*static*/ double IndexMotor::ConvertIndexToDiscPosition(const double angle) {
165 return IndexMotor::ConvertDiscAngleToDiscPosition(
166 ConvertIndexToDiscAngle(angle));
167}
168
Austin Schuhf8c52252013-03-03 02:25:49 -0800169/*static*/ double IndexMotor::ConvertTransferToDiscPosition(
170 const double angle) {
171 const double gear_ratio = (1 + (kDiscRadius * 2 + kTransferRollerRadius) /
172 kTransferRollerRadius);
173 return angle / gear_ratio * (kDiscRadius + kTransferRollerRadius);
174}
175
176/*static*/ double IndexMotor::ConvertDiscPositionToIndex(
177 const double position) {
178 return IndexMotor::ConvertDiscAngleToIndex(
179 ConvertDiscPositionToDiscAngle(position));
180}
181
Austin Schuh1b864a12013-03-07 00:46:50 -0800182bool IndexMotor::MinDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800183 bool found_start = false;
184 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800185 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800186 if (!found_start) {
187 if (frisbee.has_position()) {
188 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800189 if (found_disc) {
190 *found_disc = &frisbee;
191 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800192 found_start = true;
193 }
194 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800195 if (frisbee.position() <= *disc_position) {
196 *disc_position = frisbee.position();
197 if (found_disc) {
198 *found_disc = &frisbee;
199 }
200 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800201 }
202 }
203 return found_start;
204}
205
Austin Schuh1b864a12013-03-07 00:46:50 -0800206bool IndexMotor::MaxDiscPosition(double *disc_position, Frisbee **found_disc) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800207 bool found_start = false;
208 for (unsigned int i = 0; i < frisbees_.size(); ++i) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800209 Frisbee &frisbee = frisbees_[i];
Austin Schuhf8c52252013-03-03 02:25:49 -0800210 if (!found_start) {
211 if (frisbee.has_position()) {
212 *disc_position = frisbee.position();
Austin Schuh1b864a12013-03-07 00:46:50 -0800213 if (found_disc) {
214 *found_disc = &frisbee;
215 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800216 found_start = true;
217 }
218 } else {
Austin Schuh1b864a12013-03-07 00:46:50 -0800219 if (frisbee.position() > *disc_position) {
220 *disc_position = frisbee.position();
221 if (found_disc) {
222 *found_disc = &frisbee;
223 }
224 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800225 }
226 }
227 return found_start;
228}
229
Austin Schuh93485832013-03-04 00:01:34 -0800230void IndexMotor::IndexStateFeedbackLoop::CapU() {
231 // If the voltage has been low for a large number of cycles, cut the motor
232 // power. This is generally very bad controls practice since this isn't LTI,
233 // but we don't really care about tracking anything other than large step
234 // inputs, and the loader doesn't need to be that accurate.
235 if (::std::abs(U(0, 0)) < kMinMotionVoltage) {
236 ++low_voltage_count_;
237 if (low_voltage_count_ > kNoMotionCuttoffCount) {
238 printf("Limiting power from %f to 0\n", U(0, 0));
239 U(0, 0) = 0.0;
240 }
241 } else {
242 low_voltage_count_ = 0;
243 }
244
245 for (int i = 0; i < kNumOutputs; ++i) {
246 if (U[i] > plant.U_max[i]) {
247 U[i] = plant.U_max[i];
248 } else if (U[i] < plant.U_min[i]) {
249 U[i] = plant.U_min[i];
250 }
251 }
252}
253
254
Austin Schuhd78ab542013-03-01 22:22:19 -0800255// Positive angle is towards the shooter, and positive power is towards the
256// shooter.
257void IndexMotor::RunIteration(
258 const control_loops::IndexLoop::Goal *goal,
259 const control_loops::IndexLoop::Position *position,
260 control_loops::IndexLoop::Output *output,
261 control_loops::IndexLoop::Status *status) {
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800262 // Make goal easy to work with and sanity check it.
Austin Schuhd78ab542013-03-01 22:22:19 -0800263 Goal goal_enum = static_cast<Goal>(goal->goal_state);
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800264 if (goal->goal_state < 0 || goal->goal_state > 4) {
265 LOG(ERROR, "Goal state is %d which is out of range. Going to HOLD.\n",
266 goal->goal_state);
267 goal_enum = Goal::HOLD;
268 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800269
270 // Disable the motors now so that all early returns will return with the
271 // motors disabled.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800272 double intake_voltage = 0.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800273 double transfer_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800274 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800275 output->intake_voltage = 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800276 output->transfer_voltage = 0.0;
277 output->index_voltage = 0.0;
278 }
279
280 status->ready_to_intake = false;
281
Austin Schuhf8c52252013-03-03 02:25:49 -0800282 // Compute a safe index position that we can use.
Austin Schuhd78ab542013-03-01 22:22:19 -0800283 if (position) {
284 wrist_loop_->Y << position->index_position;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800285 // Set the goal to be the current position if this is the first time through
286 // so we don't always spin the indexer to the 0 position before starting.
287 if (no_prior_position_) {
288 wrist_loop_->R << wrist_loop_->Y(0, 0), 0.0;
289 no_prior_position_ = false;
Austin Schuh6328daf2013-03-05 00:53:15 -0800290 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
291 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
292 last_bottom_disc_negedge_wait_count_ =
293 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800294 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800295 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
296 // The open positions for the upper is right here and isn't a hard edge.
297 upper_open_index_position_ = wrist_loop_->Y(0, 0);
298 upper_open_index_position_was_negedge_ = false;
299 lower_open_index_position_ = wrist_loop_->Y(0, 0);
300 lower_open_index_position_was_negedge_ = false;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800301 }
302
Austin Schuh1b864a12013-03-07 00:46:50 -0800303 // If the cRIO is gone for over 1/2 of a second, assume that it rebooted.
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800304 if (missing_position_count_ > 50) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800305 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
306 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
307 last_bottom_disc_negedge_wait_count_ =
308 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800309 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800310 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
311 // We can't really trust the open range any more if the crio rebooted.
312 upper_open_index_position_ = wrist_loop_->Y(0, 0);
313 upper_open_index_position_was_negedge_ = false;
314 lower_open_index_position_ = wrist_loop_->Y(0, 0);
315 lower_open_index_position_was_negedge_ = false;
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800316 // Adjust the disc positions so that they don't have to move.
317 const double disc_offset =
318 position->index_position - wrist_loop_->X_hat(0, 0);
319 for (auto frisbee = frisbees_.begin();
320 frisbee != frisbees_.end(); ++frisbee) {
321 frisbee->OffsetDisc(disc_offset);
322 }
323 }
324 missing_position_count_ = 0;
325 } else {
326 ++missing_position_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800327 }
328 const double index_position = wrist_loop_->X_hat(0, 0);
329
Austin Schuh825bde92013-03-06 00:16:46 -0800330 if (position) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800331 // Reset the open region if we saw a negedge.
332 if (position->top_disc_negedge_count != last_top_disc_negedge_count_) {
333 // Saw a negedge, must be a new region.
334 upper_open_index_position_ = position->top_disc_negedge_position;
335 lower_open_index_position_ = position->top_disc_negedge_position;
336 upper_open_index_position_was_negedge_ = true;
337 lower_open_index_position_was_negedge_ = true;
338 }
339
340 // No disc. Expand the open region.
341 if (!position->top_disc_detect) {
342 // If it is higher than it was before, the end of the region is no longer
343 // determined by the negedge.
344 if (index_position > upper_open_index_position_) {
345 upper_open_index_position_ = index_position;
346 upper_open_index_position_was_negedge_ = false;
347 }
348 // If it is lower than it was before, the end of the region is no longer
349 // determined by the negedge.
350 if (index_position < lower_open_index_position_) {
351 lower_open_index_position_ = index_position;
352 lower_open_index_position_was_negedge_ = false;
353 }
354 }
355
Austin Schuh825bde92013-03-06 00:16:46 -0800356 if (!position->top_disc_detect) {
357 // We don't see a disc. Verify that there are no discs that we should be
358 // seeing.
Austin Schuh1b864a12013-03-07 00:46:50 -0800359 // Assume that discs will move slow enough that we won't miss one as it
360 // goes by. They will either pile up above or below the sensor.
Austin Schuhdff24e22013-03-06 00:41:21 -0800361
362 double cumulative_offset = 0.0;
363 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
364 frisbee != rend; ++frisbee) {
365 frisbee->OffsetDisc(cumulative_offset);
366 double amount_moved = frisbee->ObserveNoTopDiscSensor(
Austin Schuh825bde92013-03-06 00:16:46 -0800367 wrist_loop_->X_hat(0, 0), wrist_loop_->X_hat(1, 0));
Austin Schuhdff24e22013-03-06 00:41:21 -0800368 cumulative_offset += amount_moved;
Austin Schuh825bde92013-03-06 00:16:46 -0800369 }
370 }
Austin Schuh1b864a12013-03-07 00:46:50 -0800371
Austin Schuh825bde92013-03-06 00:16:46 -0800372 if (position->top_disc_posedge_count != last_top_disc_posedge_count_) {
Austin Schuh1b864a12013-03-07 00:46:50 -0800373 const double index_position = wrist_loop_->X_hat(0, 0) -
374 position->index_position + position->top_disc_posedge_position;
Austin Schuh825bde92013-03-06 00:16:46 -0800375 // TODO(aschuh): Sanity check this number...
376 // Requires storing when the disc was last seen with the sensor off, and
377 // figuring out what to do if things go south.
378
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800379 // 1 if discs are going up, 0 if we have no clue, and -1 if they are going
380 // down.
381 int disc_direction = 0;
Austin Schuh825bde92013-03-06 00:16:46 -0800382 if (wrist_loop_->X_hat(1, 0) > 50.0) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800383 disc_direction = 1;
384 } else if (wrist_loop_->X_hat(1, 0) < -50.0) {
385 disc_direction = -1;
386 } else {
387 // Save the upper and lower positions that we last saw a disc at.
388 // If there is a big buffer above, must be a disc from below.
389 // If there is a big buffer below, must be a disc from above.
390 // This should work to replace the velocity threshold above.
391
392 const double open_width =
393 upper_open_index_position_ - lower_open_index_position_;
394 const double relative_upper_open_precentage =
395 (upper_open_index_position_ - index_position) / open_width;
396 const double relative_lower_open_precentage =
397 (index_position - lower_open_index_position_) / open_width;
398 printf("Width %f upper %f lower %f\n",
399 open_width, relative_upper_open_precentage,
400 relative_lower_open_precentage);
401
402 if (ConvertIndexToDiscPosition(open_width) <
403 kTopDiscDetectMinSeperation * 0.9) {
404 LOG(ERROR, "Discs are way too close to each other. Doing nothing\n");
405 } else if (relative_upper_open_precentage > 0.75) {
406 // Looks like it is a disc going down from above since we are near
407 // the upper edge.
408 disc_direction = -1;
409 printf("Disc edge going down\n");
410 } else if (relative_lower_open_precentage > 0.75) {
411 // Looks like it is a disc going up from below since we are near
412 // the lower edge.
413 disc_direction = 1;
414 printf("Disc edge going up\n");
415 } else {
416 LOG(ERROR,
417 "Got an edge in the middle of what should be an open region.\n");
418 LOG(ERROR, "Open width: %f upper precentage %f %%\n",
419 open_width, relative_upper_open_precentage);
420 }
421 }
422
423 if (disc_direction > 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800424 // Moving up at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800425 // Find the highest disc that is below the top disc sensor.
426 // While we are at it, count the number above and log an error if there
427 // are too many.
428 if (frisbees_.size() == 0) {
429 Frisbee new_frisbee;
430 new_frisbee.has_been_indexed_ = true;
431 new_frisbee.index_start_position_ = index_position -
432 ConvertDiscPositionToIndex(kTopDiscDetectStart -
433 kIndexStartPosition);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800434 ++hopper_disc_count_;
435 ++total_disc_count_;
Austin Schuh1b864a12013-03-07 00:46:50 -0800436 frisbees_.push_front(new_frisbee);
437 LOG(WARNING, "Added a disc to the hopper at the top sensor\n");
438 }
439
440 int above_disc_count = 0;
441 double highest_position = 0;
442 Frisbee *highest_frisbee_below_sensor = NULL;
443 for (auto frisbee = frisbees_.rbegin(), rend = frisbees_.rend();
444 frisbee != rend; ++frisbee) {
445 const double disc_position = frisbee->absolute_position(
446 index_position);
447 // It is save to use the top position for the cuttoff, since the
448 // sensor being low will result in discs being pushed off of it.
449 if (disc_position >= kTopDiscDetectStop) {
450 ++above_disc_count;
451 } else if (!highest_frisbee_below_sensor ||
452 disc_position > highest_position) {
453 highest_frisbee_below_sensor = &*frisbee;
454 highest_position = disc_position;
455 }
456 }
457 if (above_disc_count > 1) {
458 LOG(ERROR, "We have 2 discs above the top sensor.\n");
459 }
460
461 // 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) {
470 frisbee->OffsetDisc(disc_delta);
471 }
472 }
473 printf("Currently have %d discs, saw posedge moving up. "
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800474 "Moving down by %f to %f\n", frisbees_.size(),
Austin Schuh1b864a12013-03-07 00:46:50 -0800475 ConvertIndexToDiscPosition(disc_delta),
476 highest_frisbee_below_sensor->absolute_position(
477 wrist_loop_->X_hat(0, 0)));
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800478 } else if (disc_direction < 0) {
Austin Schuh825bde92013-03-06 00:16:46 -0800479 // Moving down at a reasonable clip.
Austin Schuh1b864a12013-03-07 00:46:50 -0800480 // There can only be 1 disc up top that would give us a posedge.
481 // Find it and place it at the one spot that it can be.
482 double min_disc_position;
483 Frisbee *min_frisbee = NULL;
484 MinDiscPosition(&min_disc_position, &min_frisbee);
485 if (!min_frisbee) {
486 // Uh, oh, we see a disc but there isn't one...
487 LOG(ERROR, "Saw a disc up top but there isn't one in the hopper\n");
488 } else {
489 const double disc_position = min_frisbee->absolute_position(
490 index_position);
491
492 const double disc_delta_meters = disc_position - kTopDiscDetectStop;
493 const double disc_delta = IndexMotor::ConvertDiscPositionToIndex(
494 disc_delta_meters);
495 printf("Posedge going down. Moving top disc down by %f\n",
496 disc_delta_meters);
497 for (auto frisbee = frisbees_.begin(), end = frisbees_.end();
498 frisbee != end; ++frisbee) {
499 frisbee->OffsetDisc(disc_delta);
500 }
501 }
Austin Schuh825bde92013-03-06 00:16:46 -0800502 } else {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800503 LOG(ERROR, "Not sure how to handle the upper posedge, doing nothing\n");
Austin Schuh825bde92013-03-06 00:16:46 -0800504 }
505 }
506 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800507
Austin Schuhf8c52252013-03-03 02:25:49 -0800508 // Bool to track if it is safe for the goal to change yet.
Austin Schuhd78ab542013-03-01 22:22:19 -0800509 bool safe_to_change_state_ = true;
510 switch (safe_goal_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800511 case Goal::HOLD:
Austin Schuhd78ab542013-03-01 22:22:19 -0800512 // The goal should already be good, so sit tight with everything the same
513 // as it was.
Austin Schuhd78ab542013-03-01 22:22:19 -0800514 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800515 case Goal::READY_LOWER:
516 case Goal::INTAKE:
Austin Schuhd78ab542013-03-01 22:22:19 -0800517 {
518 Time now = Time::Now();
Austin Schuhd78ab542013-03-01 22:22:19 -0800519 if (position) {
Austin Schuh6328daf2013-03-05 00:53:15 -0800520 // Posedge of the disc entering the beam break.
521 if (position->bottom_disc_posedge_count !=
522 last_bottom_disc_posedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800523 transfer_frisbee_.Reset();
524 transfer_frisbee_.bottom_posedge_time_ = now;
525 printf("Posedge of bottom disc %f\n",
526 transfer_frisbee_.bottom_posedge_time_.ToSeconds());
527 ++hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800528 ++total_disc_count_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800529 }
530
531 // Disc exited the beam break now.
Austin Schuh6328daf2013-03-05 00:53:15 -0800532 if (position->bottom_disc_negedge_count !=
533 last_bottom_disc_negedge_count_) {
Austin Schuhd78ab542013-03-01 22:22:19 -0800534 transfer_frisbee_.bottom_negedge_time_ = now;
535 printf("Negedge of bottom disc %f\n",
536 transfer_frisbee_.bottom_negedge_time_.ToSeconds());
537 frisbees_.push_front(transfer_frisbee_);
538 }
539
540 if (position->bottom_disc_detect) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800541 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800542 // Must wait until the disc gets out before we can change state.
543 safe_to_change_state_ = false;
544
Austin Schuhf8c52252013-03-03 02:25:49 -0800545 // TODO(aschuh): A disc on the way through needs to start moving
546 // the indexer if it isn't already moving. Maybe?
Austin Schuhd78ab542013-03-01 22:22:19 -0800547
548 Time elapsed_posedge_time = now -
549 transfer_frisbee_.bottom_posedge_time_;
550 if (elapsed_posedge_time >= Time::InSeconds(0.3)) {
551 // It has been too long. The disc must be jammed.
552 LOG(ERROR, "Been way too long. Jammed disc?\n");
553 printf("Been way too long. Jammed disc?\n");
554 }
555 }
556
Austin Schuhf8c52252013-03-03 02:25:49 -0800557 // Check all non-indexed discs and see if they should be indexed.
Austin Schuhb6d898b2013-03-03 15:34:35 -0800558 for (auto frisbee = frisbees_.begin();
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800559 frisbee != frisbees_.end(); ++frisbee) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800560 if (!frisbee->has_been_indexed_) {
561 intake_voltage = transfer_voltage = 12.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800562
Austin Schuh6328daf2013-03-05 00:53:15 -0800563 if (last_bottom_disc_negedge_wait_count_ !=
564 position->bottom_disc_negedge_wait_count) {
565 // We have an index difference.
566 // Save the indexer position, and the time.
567 if (last_bottom_disc_negedge_wait_count_ + 1 !=
568 position->bottom_disc_negedge_wait_count) {
569 LOG(ERROR, "Funny, we got 2 edges since we last checked.\n");
570 }
571
572 // Save the captured position as the position at which the disc
573 // touched the indexer.
Austin Schuhd78ab542013-03-01 22:22:19 -0800574 LOG(INFO, "Grabbed on the index now at %f\n", index_position);
575 printf("Grabbed on the index now at %f\n", index_position);
Austin Schuhb6d898b2013-03-03 15:34:35 -0800576 frisbee->has_been_indexed_ = true;
Austin Schuh6328daf2013-03-05 00:53:15 -0800577 frisbee->index_start_position_ =
578 position->bottom_disc_negedge_wait_position;
Austin Schuhd78ab542013-03-01 22:22:19 -0800579 }
580 }
Austin Schuhb6d898b2013-03-03 15:34:35 -0800581 if (!frisbee->has_been_indexed_) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800582 // All discs must be indexed before it is safe to stop indexing.
Austin Schuhd78ab542013-03-01 22:22:19 -0800583 safe_to_change_state_ = false;
584 }
585 }
586
Austin Schuhf8c52252013-03-03 02:25:49 -0800587 // Figure out where the indexer should be to move the discs down to
588 // the right position.
589 double max_disc_position;
Austin Schuh1b864a12013-03-07 00:46:50 -0800590 if (MaxDiscPosition(&max_disc_position, NULL)) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800591 printf("There is a disc down here!\n");
592 // TODO(aschuh): Figure out what to do if grabbing the next one
593 // would cause things to jam into the loader.
594 // Say we aren't ready any more. Undefined behavior will result if
595 // that isn't observed.
596 double bottom_disc_position =
597 max_disc_position + ConvertDiscAngleToIndex(M_PI);
598 wrist_loop_->R << bottom_disc_position, 0.0;
Austin Schuhd78ab542013-03-01 22:22:19 -0800599
Austin Schuhf8c52252013-03-03 02:25:49 -0800600 // Verify that we are close enough to the goal so that we should be
601 // fine accepting the next disc.
602 double disc_error_meters = ConvertIndexToDiscPosition(
603 wrist_loop_->X_hat(0, 0) - bottom_disc_position);
604 // We are ready for the next disc if the first one is in the first
605 // half circle of the indexer. It will take time for the disc to
606 // come into the indexer, so we will be able to move it out of the
607 // way in time.
608 // This choice also makes sure that we don't claim that we aren't
609 // ready between full speed intaking.
610 if (-ConvertDiscAngleToIndex(M_PI) < disc_error_meters &&
611 disc_error_meters < 0.04) {
612 // We are only ready if we aren't being asked to change state or
613 // are full.
614 status->ready_to_intake =
615 (safe_goal_ == goal_enum) && hopper_disc_count_ < 4;
616 } else {
617 status->ready_to_intake = false;
Austin Schuhd78ab542013-03-01 22:22:19 -0800618 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800619 } else {
620 // No discs! We are always ready for more if we aren't being
621 // asked to change state.
622 status->ready_to_intake = (safe_goal_ == goal_enum);
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800623 printf("Ready to intake, zero discs. %d %d %d\n",
624 status->ready_to_intake, hopper_disc_count_, safe_goal_);
Austin Schuhd78ab542013-03-01 22:22:19 -0800625 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800626
627 // Turn on the transfer roller if we are ready.
628 if (status->ready_to_intake && hopper_disc_count_ < 4 &&
629 safe_goal_ == Goal::INTAKE) {
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800630 printf("Go\n");
Austin Schuhb6d898b2013-03-03 15:34:35 -0800631 intake_voltage = transfer_voltage = 12.0;
Austin Schuhf8c52252013-03-03 02:25:49 -0800632 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800633 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800634 printf("INTAKE\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800635 }
636 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800637 case Goal::READY_SHOOTER:
638 case Goal::SHOOT:
639 // Check if we have any discs to shoot or load and handle them.
640 double min_disc_position;
Austin Schuh1b864a12013-03-07 00:46:50 -0800641 if (MinDiscPosition(&min_disc_position, NULL)) {
642 const double ready_disc_position = min_disc_position +
643 ConvertDiscPositionToIndex(kReadyToPreload - kIndexStartPosition);
Austin Schuhf8c52252013-03-03 02:25:49 -0800644
645 const double grabbed_disc_position =
646 min_disc_position +
647 ConvertDiscPositionToIndex(kReadyToLiftPosition -
648 kIndexStartPosition + 0.03);
649
650 // Check the state of the loader FSM.
651 // If it is ready to load discs, position the disc so that it is ready
652 // to be grabbed.
653 // If it isn't ready, there is a disc in there. It needs to finish it's
654 // cycle first.
655 if (loader_state_ != LoaderState::READY) {
656 // We already have a disc in the loader.
657 // Stage the discs back a bit.
658 wrist_loop_->R << ready_disc_position, 0.0;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800659 printf("Loader not ready but asked to shoot\n");
Austin Schuhf8c52252013-03-03 02:25:49 -0800660
Austin Schuhbcdb90c2013-03-03 23:24:58 -0800661 // Shoot if we are grabbed and being asked to shoot.
662 if (loader_state_ == LoaderState::GRABBED &&
663 safe_goal_ == Goal::SHOOT) {
664 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
665 }
666
Austin Schuhf8c52252013-03-03 02:25:49 -0800667 // Must wait until it has been grabbed to continue.
668 if (loader_state_ == LoaderState::GRABBING) {
669 safe_to_change_state_ = false;
670 }
671 } else {
672 // No disc up top right now.
673 wrist_loop_->R << grabbed_disc_position, 0.0;
674
675 // See if the disc has gotten pretty far up yet.
676 if (wrist_loop_->X_hat(0, 0) > ready_disc_position) {
677 // Point of no return. We are committing to grabbing it now.
678 safe_to_change_state_ = false;
679 const double robust_grabbed_disc_position =
680 (grabbed_disc_position -
681 ConvertDiscPositionToIndex(kGrabberLength));
682
683 // If close, start grabbing and/or shooting.
684 if (wrist_loop_->X_hat(0, 0) > robust_grabbed_disc_position) {
685 // Start the state machine.
686 if (safe_goal_ == Goal::SHOOT) {
687 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
688 } else {
689 loader_goal_ = LoaderGoal::GRAB;
690 }
691 // This frisbee is now gone. Take it out of the queue.
692 frisbees_.pop_back();
Austin Schuhf8c52252013-03-03 02:25:49 -0800693 }
694 }
695 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800696 } else {
697 if (loader_state_ != LoaderState::READY) {
698 // Shoot if we are grabbed and being asked to shoot.
699 if (loader_state_ == LoaderState::GRABBED &&
700 safe_goal_ == Goal::SHOOT) {
701 loader_goal_ = LoaderGoal::SHOOT_AND_RESET;
702 }
703 } else {
704 // Ok, no discs in sight. Spin the hopper up by 150% of it's full
705 // range and verify that we don't see anything.
706 printf("Moving the indexer to verify that it is clear\n");
707 const double hopper_clear_verification_position =
708 lower_open_index_position_ +
709 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
710
711 wrist_loop_->R << hopper_clear_verification_position, 0.0;
712 if (::std::abs(wrist_loop_->X_hat(0, 0) -
713 hopper_clear_verification_position) <
714 ConvertDiscPositionToIndex(0.05)) {
715 printf("Should be empty\n");
716 // We are at the end of the range. There are no more discs here.
717 while (frisbees_.size() > 0) {
718 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
719 frisbees_.pop_back();
720 --hopper_disc_count_;
721 --total_disc_count_;
722 }
723 if (hopper_disc_count_ != 0) {
724 LOG(ERROR,
725 "Emptied the hopper out but there are still discs there\n");
726 }
727 }
728 }
729 }
730
731 {
732 const double hopper_clear_verification_position =
733 lower_open_index_position_ +
734 ConvertDiscPositionToIndex(kIndexFreeLength) * 1.5;
735
736 if (wrist_loop_->X_hat(0, 0) >
737 hopper_clear_verification_position +
738 ConvertDiscPositionToIndex(0.05)) {
739 // We are at the end of the range. There are no more discs here.
740 while (frisbees_.size() > 0) {
741 LOG(ERROR, "Dropping an extra disc since it can't exist\n");
742 frisbees_.pop_back();
743 --hopper_disc_count_;
744 --total_disc_count_;
745 }
746 if (hopper_disc_count_ != 0) {
747 LOG(ERROR,
748 "Emptied the hopper out but there are still discs there\n");
749 }
750 }
Austin Schuhf8c52252013-03-03 02:25:49 -0800751 }
752
753 printf("READY_SHOOTER or SHOOT\n");
Austin Schuhd78ab542013-03-01 22:22:19 -0800754 break;
Austin Schuhf8c52252013-03-03 02:25:49 -0800755 }
756
757 // The only way out of the loader is to shoot the disc. The FSM can only go
758 // forwards.
759 switch (loader_state_) {
760 case LoaderState::READY:
761 printf("Loader READY\n");
762 // Open and down, ready to accept a disc.
763 loader_up_ = false;
764 disc_clamped_ = false;
765 disc_ejected_ = false;
766 if (loader_goal_ == LoaderGoal::GRAB ||
767 loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
768 if (loader_goal_ == LoaderGoal::GRAB) {
769 printf("Told to GRAB, moving on\n");
770 } else {
771 printf("Told to SHOOT_AND_RESET, moving on\n");
772 }
773 loader_state_ = LoaderState::GRABBING;
774 loader_countdown_ = kGrabbingDelay;
775 } else {
776 break;
777 }
778 case LoaderState::GRABBING:
779 printf("Loader GRABBING %d\n", loader_countdown_);
780 // Closing the grabber.
781 loader_up_ = false;
782 disc_clamped_ = true;
783 disc_ejected_ = false;
784 if (loader_countdown_ > 0) {
785 --loader_countdown_;
786 break;
787 } else {
788 loader_state_ = LoaderState::GRABBED;
789 }
790 case LoaderState::GRABBED:
791 printf("Loader GRABBED\n");
792 // Grabber closed.
793 loader_up_ = false;
794 disc_clamped_ = true;
795 disc_ejected_ = false;
796 if (loader_goal_ == LoaderGoal::SHOOT_AND_RESET) {
797 // TODO(aschuh): Only shoot if the shooter is up to speed.
798 // Seems like that would have us shooting a bit later than we could be,
799 // but it also probably spins back up real fast.
800 loader_state_ = LoaderState::LIFTING;
801 loader_countdown_ = kLiftingDelay;
802 printf("Told to SHOOT_AND_RESET, moving on\n");
803 } else if (loader_goal_ == LoaderGoal::READY) {
804 LOG(ERROR, "Can't go to ready when we have something grabbed.\n");
805 printf("Can't go to ready when we have something grabbed.\n");
806 break;
807 } else {
808 break;
809 }
810 case LoaderState::LIFTING:
811 printf("Loader LIFTING %d\n", loader_countdown_);
812 // Lifting the disc.
813 loader_up_ = true;
814 disc_clamped_ = true;
815 disc_ejected_ = false;
816 if (loader_countdown_ > 0) {
817 --loader_countdown_;
818 break;
819 } else {
820 loader_state_ = LoaderState::LIFTED;
821 }
822 case LoaderState::LIFTED:
823 printf("Loader LIFTED\n");
824 // Disc lifted. Time to eject it out.
825 loader_up_ = true;
826 disc_clamped_ = true;
827 disc_ejected_ = false;
828 loader_state_ = LoaderState::SHOOTING;
829 loader_countdown_ = kShootingDelay;
830 case LoaderState::SHOOTING:
831 printf("Loader SHOOTING %d\n", loader_countdown_);
832 // Ejecting the disc into the shooter.
833 loader_up_ = true;
834 disc_clamped_ = false;
835 disc_ejected_ = true;
836 if (loader_countdown_ > 0) {
837 --loader_countdown_;
838 break;
839 } else {
840 loader_state_ = LoaderState::SHOOT;
841 }
842 case LoaderState::SHOOT:
843 printf("Loader SHOOT\n");
844 // The disc has been shot.
845 loader_up_ = true;
846 disc_clamped_ = false;
847 disc_ejected_ = true;
848 loader_state_ = LoaderState::LOWERING;
849 loader_countdown_ = kLoweringDelay;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800850 --hopper_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800851 case LoaderState::LOWERING:
852 printf("Loader LOWERING %d\n", loader_countdown_);
853 // Lowering the loader back down.
854 loader_up_ = false;
855 disc_clamped_ = false;
856 disc_ejected_ = true;
857 if (loader_countdown_ > 0) {
858 --loader_countdown_;
859 break;
860 } else {
861 loader_state_ = LoaderState::LOWERED;
862 }
863 case LoaderState::LOWERED:
864 printf("Loader LOWERED\n");
865 // The indexer is lowered.
866 loader_up_ = false;
867 disc_clamped_ = false;
868 disc_ejected_ = false;
869 loader_state_ = LoaderState::READY;
870 // Once we have shot, we need to hang out in READY until otherwise
871 // notified.
872 loader_goal_ = LoaderGoal::READY;
Austin Schuhd78ab542013-03-01 22:22:19 -0800873 break;
874 }
875
876 // Update the observer.
877 wrist_loop_->Update(position != NULL, output == NULL);
878
879 if (position) {
Austin Schuhf8c52252013-03-03 02:25:49 -0800880 LOG(DEBUG, "pos=%f\n", position->index_position);
Austin Schuhd78ab542013-03-01 22:22:19 -0800881 last_bottom_disc_detect_ = position->bottom_disc_detect;
Austin Schuh825bde92013-03-06 00:16:46 -0800882 last_top_disc_detect_ = position->top_disc_detect;
Austin Schuh6328daf2013-03-05 00:53:15 -0800883 last_bottom_disc_posedge_count_ = position->bottom_disc_posedge_count;
884 last_bottom_disc_negedge_count_ = position->bottom_disc_negedge_count;
885 last_bottom_disc_negedge_wait_count_ =
886 position->bottom_disc_negedge_wait_count;
Austin Schuh825bde92013-03-06 00:16:46 -0800887 last_top_disc_posedge_count_ = position->top_disc_posedge_count;
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800888 last_top_disc_negedge_count_ = position->top_disc_negedge_count;
Austin Schuhd78ab542013-03-01 22:22:19 -0800889 }
890
891 status->hopper_disc_count = hopper_disc_count_;
892 status->total_disc_count = total_disc_count_;
Austin Schuhf8c52252013-03-03 02:25:49 -0800893 status->preloaded = (loader_state_ != LoaderState::READY);
Austin Schuhd78ab542013-03-01 22:22:19 -0800894
895 if (output) {
Austin Schuhb6d898b2013-03-03 15:34:35 -0800896 output->intake_voltage = intake_voltage;
Austin Schuhf8c52252013-03-03 02:25:49 -0800897 output->transfer_voltage = transfer_voltage;
Austin Schuhd78ab542013-03-01 22:22:19 -0800898 output->index_voltage = wrist_loop_->U(0, 0);
Austin Schuhf8c52252013-03-03 02:25:49 -0800899 output->loader_up = loader_up_;
900 output->disc_clamped = disc_clamped_;
901 output->disc_ejected = disc_ejected_;
Austin Schuhd78ab542013-03-01 22:22:19 -0800902 }
903
904 if (safe_to_change_state_) {
905 safe_goal_ = goal_enum;
906 }
Austin Schuh7c0e2aa2013-03-09 02:01:16 -0800907 if (hopper_disc_count_ < 0) {
908 LOG(ERROR, "NEGATIVE DISCS. VERY VERY BAD\n");
909 }
Austin Schuhd78ab542013-03-01 22:22:19 -0800910}
911
912} // namespace control_loops
913} // namespace frc971