blob: 634468176d75714d7f87f1c69695d2f2084412c6 [file] [log] [blame]
Austin Schuh3bb9a442014-02-02 16:01:45 -08001#include "frc971/control_loops/claw/claw.h"
2
3#include <stdio.h>
4
5#include <algorithm>
6
7#include "aos/common/control_loop/control_loops.q.h"
8#include "aos/common/logging/logging.h"
9
10#include "frc971/constants.h"
Austin Schuhcda86af2014-02-16 16:16:39 -080011#include "frc971/control_loops/claw/claw_motor_plant.h"
12
Austin Schuh3bb9a442014-02-02 16:01:45 -080013// Zeroing plan.
14// There are 2 types of zeros. Enabled and disabled ones.
15// Disabled ones are only valid during auto mode, and can be used to speed up
16// the enabled zero process. We need to re-zero during teleop in case the auto
17// zero was poor and causes us to miss all our shots.
18//
19// We need to be able to zero manually while disabled by moving the joint over
20// the zeros.
21// Zero on the down edge when disabled (gravity in the direction of motion)
22//
23// When enabled, zero on the up edge (gravity opposing the direction of motion)
24// The enabled sequence needs to work as follows. We can crash the claw if we
25// bring them too close to each other or too far from each other. The only safe
26// thing to do is to move them in unison.
27//
28// Start by moving them both towards the front of the bot to either find either
29// the middle hall effect on either jaw, or the front hall effect on the bottom
30// jaw. Any edge that isn't the desired edge will provide an approximate edge
31// location that can be used for the fine tuning step.
32// Once an edge is found on the front claw, move back the other way with both
33// claws until an edge is found for the other claw.
34// Now that we have an approximate zero, we can robustify the limits to keep
35// both claws safe. Then, we can move both claws to a position that is the
36// correct side of the zero and go zero.
37
38// Valid region plan.
Ben Fredrickson81ba2d52014-03-02 08:21:46 +000039// Difference between the arms has a range, and the values of each arm has a
40// range.
Austin Schuh3bb9a442014-02-02 16:01:45 -080041// If a claw runs up against a static limit, don't let the goal change outside
42// the limit.
43// If a claw runs up against a movable limit, move both claws outwards to get
44// out of the condition.
45
46namespace frc971 {
47namespace control_loops {
48
Austin Schuh01c652b2014-02-21 23:13:42 -080049static const double kZeroingVoltage = 4.0;
50static const double kMaxVoltage = 12.0;
Austin Schuhf84a1302014-02-19 00:23:30 -080051
Austin Schuh27b8fb12014-02-22 15:10:05 -080052ClawLimitedLoop::ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> loop)
53 : StateFeedbackLoop<4, 2, 2>(loop),
54 uncapped_average_voltage_(0.0),
55 is_zeroing_(true) {}
56
Austin Schuhcda86af2014-02-16 16:16:39 -080057void ClawLimitedLoop::CapU() {
Austin Schuh4cb047f2014-02-16 21:10:19 -080058 uncapped_average_voltage_ = U(0, 0) + U(1, 0) / 2.0;
59 if (is_zeroing_) {
Austin Schuhf84a1302014-02-19 00:23:30 -080060 LOG(DEBUG, "zeroing\n");
Austin Schuh4cb047f2014-02-16 21:10:19 -080061 const frc971::constants::Values &values = constants::GetValues();
62 if (uncapped_average_voltage_ > values.claw.max_zeroing_voltage) {
63 const double difference =
64 uncapped_average_voltage_ - values.claw.max_zeroing_voltage;
65 U(0, 0) -= difference;
Austin Schuh4cb047f2014-02-16 21:10:19 -080066 } else if (uncapped_average_voltage_ < -values.claw.max_zeroing_voltage) {
67 const double difference =
68 -uncapped_average_voltage_ - values.claw.max_zeroing_voltage;
69 U(0, 0) += difference;
Austin Schuh4cb047f2014-02-16 21:10:19 -080070 }
71 }
72
73 double max_value =
74 ::std::max(::std::abs(U(0, 0)), ::std::abs(U(1, 0) + U(0, 0)));
75
Austin Schuh01c652b2014-02-21 23:13:42 -080076 const double k_max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
77 if (max_value > k_max_voltage) {
Austin Schuhcda86af2014-02-16 16:16:39 -080078 LOG(DEBUG, "Capping U because max is %f\n", max_value);
Austin Schuh01c652b2014-02-21 23:13:42 -080079 U = U * k_max_voltage / max_value;
Austin Schuhcda86af2014-02-16 16:16:39 -080080 LOG(DEBUG, "Capping U is now %f %f\n", U(0, 0), U(1, 0));
Austin Schuh4b7b5d02014-02-10 21:20:34 -080081 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -080082}
83
Austin Schuh27b8fb12014-02-22 15:10:05 -080084ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
85 ClawMotor *motor)
86 : offset_(0.0),
87 name_(name),
88 motor_(motor),
89 zeroing_state_(UNKNOWN_POSITION),
90 posedge_value_(0.0),
91 negedge_value_(0.0),
92 encoder_(0.0),
93 last_encoder_(0.0) {}
94
95void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition &claw) {
96 front_.Update(claw.front);
97 calibration_.Update(claw.calibration);
98 back_.Update(claw.back);
99
100 bool any_sensor_triggered = any_triggered();
101 if (any_sensor_triggered && any_triggered_last_) {
102 // We are still on the hall effect and nothing has changed.
103 min_hall_effect_on_angle_ =
104 ::std::min(min_hall_effect_on_angle_, claw.position);
105 max_hall_effect_on_angle_ =
106 ::std::max(max_hall_effect_on_angle_, claw.position);
107 } else if (!any_sensor_triggered && !any_triggered_last_) {
108 // We are still off the hall effect and nothing has changed.
109 min_hall_effect_off_angle_ =
110 ::std::min(min_hall_effect_off_angle_, claw.position);
111 max_hall_effect_off_angle_ =
112 ::std::max(max_hall_effect_off_angle_, claw.position);
113 } else if (any_sensor_triggered && !any_triggered_last_) {
114 // Saw a posedge on the hall effect. Reset the limits.
115 min_hall_effect_on_angle_ = ::std::min(claw.posedge_value, claw.position);
116 max_hall_effect_on_angle_ = ::std::max(claw.posedge_value, claw.position);
117 } else if (!any_sensor_triggered && any_triggered_last_) {
118 // Saw a negedge on the hall effect. Reset the limits.
119 min_hall_effect_off_angle_ = ::std::min(claw.negedge_value, claw.position);
120 max_hall_effect_off_angle_ = ::std::max(claw.negedge_value, claw.position);
121 }
122
123 posedge_value_ = claw.posedge_value;
124 negedge_value_ = claw.negedge_value;
125 last_encoder_ = encoder_;
126 if (front().value() || calibration().value() || back().value()) {
127 last_on_encoder_ = encoder_;
128 } else {
129 last_off_encoder_ = encoder_;
130 }
131 encoder_ = claw.position;
132 any_triggered_last_ = any_sensor_triggered;
133}
134
135void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition &claw) {
136 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
137
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000138 front_.Reset(claw.front);
139 calibration_.Reset(claw.calibration);
140 back_.Reset(claw.back);
Austin Schuh27b8fb12014-02-22 15:10:05 -0800141 // close up the min and max edge positions as they are no longer valid and
142 // will be expanded in future iterations
143 min_hall_effect_on_angle_ = claw.position;
144 max_hall_effect_on_angle_ = claw.position;
145 min_hall_effect_off_angle_ = claw.position;
146 max_hall_effect_off_angle_ = claw.position;
147 any_triggered_last_ = any_triggered();
148}
149
150bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
151 const constants::Values::Claws::Claw &claw_values,
152 JointZeroingState zeroing_state) {
153 double edge_encoder;
154 double edge_angle;
155 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
156 LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
157 SetCalibration(edge_encoder, edge_angle);
158 set_zeroing_state(zeroing_state);
159 return true;
160 }
161 return false;
162}
163
164bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
165 const constants::Values::Claws::Claw &claw_values,
166 JointZeroingState zeroing_state) {
167 double edge_encoder;
168 double edge_angle;
169 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
170 LOG(INFO, "Calibration edge.\n");
171 SetCalibration(edge_encoder, edge_angle);
172 set_zeroing_state(zeroing_state);
173 return true;
174 }
175 return false;
176}
177
Austin Schuhcc0bf312014-02-09 00:39:29 -0800178ClawMotor::ClawMotor(control_loops::ClawGroup *my_claw)
179 : aos::control_loops::ControlLoop<control_loops::ClawGroup>(my_claw),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800180 has_top_claw_goal_(false),
181 top_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800182 top_claw_(this),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800183 has_bottom_claw_goal_(false),
184 bottom_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800185 bottom_claw_(this),
186 claw_(MakeClawLoop()),
Ben Fredrickson9b388422014-02-13 06:15:31 +0000187 was_enabled_(false),
Austin Schuh4cb047f2014-02-16 21:10:19 -0800188 doing_calibration_fine_tune_(false),
Austin Schuhe7f90d12014-02-17 00:48:25 -0800189 capped_goal_(false),
190 mode_(UNKNOWN_LOCATION) {}
Austin Schuh3bb9a442014-02-02 16:01:45 -0800191
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800192const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800193
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000194bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
195 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
196 const HallEffectTracker &sensorB) {
197 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
198 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
199 this_sensor.value() && !this_sensor.last_value()) {
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000200 posedge_filter_ = &this_sensor;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000201 } else if (posedge_filter_ == &this_sensor &&
202 !this_sensor.posedge_count_changed() &&
203 !sensorA.posedge_count_changed() &&
204 !sensorB.posedge_count_changed() && this_sensor.value()) {
205 posedge_filter_ = nullptr;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000206 return true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000207 } else if (posedge_filter_ == &this_sensor) {
208 posedge_filter_ = nullptr;
209 }
210 return false;
211}
212
213bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
214 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
215 const HallEffectTracker &sensorB) {
216 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
217 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
218 !this_sensor.value() && this_sensor.last_value()) {
219 negedge_filter_ = &this_sensor;
220 } else if (negedge_filter_ == &this_sensor &&
221 !this_sensor.negedge_count_changed() &&
222 !sensorA.negedge_count_changed() &&
223 !sensorB.negedge_count_changed() && !this_sensor.value()) {
224 negedge_filter_ = nullptr;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000225 return true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000226 } else if (negedge_filter_ == &this_sensor) {
227 negedge_filter_ = nullptr;
228 }
229 return false;
230}
231
Brian Silvermane0a95462014-02-17 00:41:09 -0800232bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
233 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000234 double *edge_angle, const HallEffectTracker &this_sensor,
235 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
Brian Silvermane0a95462014-02-17 00:41:09 -0800236 const char *hall_effect_name) {
Austin Schuhf84a1302014-02-19 00:23:30 -0800237 bool found_edge = false;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800238
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000239 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800240 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000241 LOG(INFO, "%s: Uncertain which side, rejecting posedge\n", name_);
Brian Silvermane0a95462014-02-17 00:41:09 -0800242 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800243 const double average_last_encoder =
244 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
245 if (posedge_value_ < average_last_encoder) {
246 *edge_angle = angles.upper_decreasing_angle;
247 LOG(INFO, "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
248 name_, hall_effect_name, *edge_angle, posedge_value_,
249 average_last_encoder);
250 } else {
251 *edge_angle = angles.lower_angle;
252 LOG(INFO, "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
253 name_, hall_effect_name, *edge_angle, posedge_value_,
254 average_last_encoder);
255 }
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000256 *edge_encoder = posedge_value_;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800257 found_edge = true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000258 }
259 }
260
261 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
262 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000263 LOG(INFO, "%s: Uncertain which side, rejecting negedge\n", name_);
Brian Silvermane0a95462014-02-17 00:41:09 -0800264 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800265 const double average_last_encoder =
266 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
267 if (negedge_value_ > average_last_encoder) {
268 *edge_angle = angles.upper_angle;
269 LOG(INFO, "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
270 name_, hall_effect_name, *edge_angle, negedge_value_,
271 average_last_encoder);
272 } else {
273 *edge_angle = angles.lower_decreasing_angle;
274 LOG(INFO, "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
275 name_, hall_effect_name, *edge_angle, negedge_value_,
276 average_last_encoder);
277 }
278 *edge_encoder = negedge_value_;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000279 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800280 }
Austin Schuh27b8fb12014-02-22 15:10:05 -0800281 }
282
Austin Schuhf84a1302014-02-19 00:23:30 -0800283 return found_edge;
Brian Silvermane0a95462014-02-17 00:41:09 -0800284}
285
Austin Schuhf9286cd2014-02-11 00:51:09 -0800286bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800287 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
Austin Schuhf9286cd2014-02-11 00:51:09 -0800288 double *edge_angle) {
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000289 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
290 calibration_, back_, "front")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800291 return true;
292 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800293 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000294 calibration_, front_, back_, "calibration")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800295 return true;
296 }
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000297 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
298 calibration_, front_, "back")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800299 return true;
300 }
301 return false;
302}
303
Austin Schuhcda86af2014-02-16 16:16:39 -0800304void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
305 double edge_angle) {
306 double old_offset = offset_;
307 offset_ = edge_angle - edge_encoder;
308 const double doffset = offset_ - old_offset;
309 motor_->ChangeTopOffset(doffset);
310}
311
312void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
313 double edge_angle) {
314 double old_offset = offset_;
315 offset_ = edge_angle - edge_encoder;
316 const double doffset = offset_ - old_offset;
317 motor_->ChangeBottomOffset(doffset);
318}
319
320void ClawMotor::ChangeTopOffset(double doffset) {
321 claw_.ChangeTopOffset(doffset);
322 if (has_top_claw_goal_) {
323 top_claw_goal_ += doffset;
324 }
325}
326
327void ClawMotor::ChangeBottomOffset(double doffset) {
328 claw_.ChangeBottomOffset(doffset);
329 if (has_bottom_claw_goal_) {
330 bottom_claw_goal_ += doffset;
331 }
332}
333
334void ClawLimitedLoop::ChangeTopOffset(double doffset) {
335 Y_(1, 0) += doffset;
336 X_hat(1, 0) += doffset;
337 LOG(INFO, "Changing top offset by %f\n", doffset);
338}
339void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
340 Y_(0, 0) += doffset;
341 X_hat(0, 0) += doffset;
342 X_hat(1, 0) -= doffset;
343 LOG(INFO, "Changing bottom offset by %f\n", doffset);
344}
joe7376ff52014-02-16 18:28:42 -0800345
Austin Schuh069143b2014-02-17 02:46:26 -0800346void LimitClawGoal(double *bottom_goal, double *top_goal,
347 const frc971::constants::Values &values) {
348 // first update position based on angle limit
349
350 const double separation = *top_goal - *bottom_goal;
351 if (separation > values.claw.claw_max_separation) {
Austin Schuh069143b2014-02-17 02:46:26 -0800352 const double dsep = (separation - values.claw.claw_max_separation) / 2.0;
353 *bottom_goal += dsep;
354 *top_goal -= dsep;
355 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
356 }
357 if (separation < values.claw.claw_min_separation) {
Austin Schuh069143b2014-02-17 02:46:26 -0800358 const double dsep = (separation - values.claw.claw_min_separation) / 2.0;
359 *bottom_goal += dsep;
360 *top_goal -= dsep;
361 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
362 }
363
364 // now move both goals in unison
365 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
366 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
367 *bottom_goal = values.claw.lower_claw.lower_limit;
368 }
369 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
370 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
371 *bottom_goal = values.claw.lower_claw.upper_limit;
372 }
373
374 if (*top_goal < values.claw.upper_claw.lower_limit) {
375 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
376 *top_goal = values.claw.upper_claw.lower_limit;
377 }
378 if (*top_goal > values.claw.upper_claw.upper_limit) {
379 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
380 *top_goal = values.claw.upper_claw.upper_limit;
381 }
382}
Austin Schuhcda86af2014-02-16 16:16:39 -0800383
Austin Schuhe7f90d12014-02-17 00:48:25 -0800384bool ClawMotor::is_ready() const {
385 return (
386 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
387 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
388 (::aos::robot_state->autonomous &&
389 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
390 top_claw_.zeroing_state() ==
391 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
392 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
393 bottom_claw_.zeroing_state() ==
394 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
395}
396
397bool ClawMotor::is_zeroing() const { return !is_ready(); }
398
Austin Schuh3bb9a442014-02-02 16:01:45 -0800399// Positive angle is up, and positive power is up.
Austin Schuhcc0bf312014-02-09 00:39:29 -0800400void ClawMotor::RunIteration(const control_loops::ClawGroup::Goal *goal,
401 const control_loops::ClawGroup::Position *position,
402 control_loops::ClawGroup::Output *output,
James Kuszmaul9ead1de2014-02-28 21:24:39 -0800403 control_loops::ClawGroup::Status *status) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800404 constexpr double dt = 0.01;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800405
406 // Disable the motors now so that all early returns will return with the
407 // motors disabled.
408 if (output) {
409 output->top_claw_voltage = 0;
410 output->bottom_claw_voltage = 0;
411 output->intake_voltage = 0;
Ben Fredrickson61893d52014-03-02 09:43:23 +0000412 output->tusk_voltage = 0;
413 }
414
415 if (::std::isnan(goal->bottom_angle) ||
416 ::std::isnan(goal->separation_angle) || ::std::isnan(goal->intake) ||
417 ::std::isnan(goal->centering)) {
418 return;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800419 }
420
Austin Schuh1a499942014-02-17 01:51:58 -0800421 if (reset()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800422 top_claw_.Reset(position->top);
423 bottom_claw_.Reset(position->bottom);
Austin Schuh1a499942014-02-17 01:51:58 -0800424 }
425
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800426 if (::aos::robot_state.get() == nullptr) {
427 return;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800428 }
429
Austin Schuhf9286cd2014-02-11 00:51:09 -0800430 const frc971::constants::Values &values = constants::GetValues();
431
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800432 if (position) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800433 Eigen::Matrix<double, 2, 1> Y;
434 Y << position->bottom.position + bottom_claw_.offset(),
435 position->top.position + top_claw_.offset();
436 claw_.Correct(Y);
437
Austin Schuhf9286cd2014-02-11 00:51:09 -0800438 top_claw_.SetPositionValues(position->top);
439 bottom_claw_.SetPositionValues(position->bottom);
440
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800441 if (!has_top_claw_goal_) {
442 has_top_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800443 top_claw_goal_ = top_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800444 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800445 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800446 }
447 if (!has_bottom_claw_goal_) {
448 has_bottom_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800449 bottom_claw_goal_ = bottom_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800450 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800451 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800452 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800453 LOG(DEBUG, "Claw position is (top: %f bottom: %f\n",
454 top_claw_.absolute_position(), bottom_claw_.absolute_position());
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800455 }
456
Austin Schuh069143b2014-02-17 02:46:26 -0800457 const bool autonomous = ::aos::robot_state->autonomous;
458 const bool enabled = ::aos::robot_state->enabled;
459
460 double bottom_claw_velocity_ = 0.0;
461 double top_claw_velocity_ = 0.0;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800462
463 if ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
464 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
465 (autonomous &&
466 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
467 top_claw_.zeroing_state() ==
468 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
469 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
470 bottom_claw_.zeroing_state() ==
471 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION)))) {
472 // Ready to use the claw.
473 // Limit the goals here.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800474 bottom_claw_goal_ = goal->bottom_angle;
Brian Silverman7c021c42014-02-17 15:15:56 -0800475 top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
Austin Schuhcda86af2014-02-16 16:16:39 -0800476 has_bottom_claw_goal_ = true;
477 has_top_claw_goal_ = true;
478 doing_calibration_fine_tune_ = false;
479
Austin Schuhe7f90d12014-02-17 00:48:25 -0800480 mode_ = READY;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800481 } else if (top_claw_.zeroing_state() !=
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000482 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800483 bottom_claw_.zeroing_state() !=
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000484 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800485 // Time to fine tune the zero.
486 // Limit the goals here.
Austin Schuh0c733422014-02-17 01:17:12 -0800487 if (!enabled) {
488 // If we are disabled, start the fine tune process over again.
489 doing_calibration_fine_tune_ = false;
490 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800491 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800492 // always get the bottom claw to calibrated first
493 LOG(DEBUG, "Calibrating the bottom of the claw\n");
494 if (!doing_calibration_fine_tune_) {
495 if (::std::abs(bottom_absolute_position() -
Austin Schuhd27931c2014-02-16 19:18:20 -0800496 values.claw.start_fine_tune_pos) <
497 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800498 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800499 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800500 top_claw_velocity_ = bottom_claw_velocity_ =
501 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800502 LOG(DEBUG, "Ready to fine tune the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800503 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800504 } else {
505 // send bottom to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800506 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800507 LOG(DEBUG, "Going to the start position for the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800508 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800509 }
510 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800511 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhd27931c2014-02-16 19:18:20 -0800512 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800513 top_claw_velocity_ = bottom_claw_velocity_ =
514 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800515 if (top_claw_.front_or_back_triggered() ||
516 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800517 // We shouldn't hit a limit, but if we do, go back to the zeroing
518 // point and try again.
519 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800520 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800521 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800522 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800523 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800524 }
Austin Schuh288c8c32014-02-16 17:20:17 -0800525
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000526 if (position && bottom_claw_.SawFilteredPosedge(
527 bottom_claw_.calibration(), bottom_claw_.front(),
528 bottom_claw_.back())) {
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000529 // do calibration
530 bottom_claw_.SetCalibration(
531 position->bottom.posedge_value,
532 values.claw.lower_claw.calibration.lower_angle);
533 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
534 // calibrated so we are done fine tuning bottom
535 doing_calibration_fine_tune_ = false;
536 LOG(DEBUG, "Calibrated the bottom correctly!\n");
537 } else if (bottom_claw_.calibration().last_value()) {
538 doing_calibration_fine_tune_ = false;
539 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
540 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
541 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800542 } else {
543 LOG(DEBUG, "Fine tuning\n");
544 }
545 }
546 // now set the top claw to track
547
Austin Schuhd27931c2014-02-16 19:18:20 -0800548 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800549 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800550 // bottom claw must be calibrated, start on the top
551 if (!doing_calibration_fine_tune_) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800552 if (::std::abs(top_absolute_position() -
553 values.claw.start_fine_tune_pos) <
554 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800555 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800556 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800557 top_claw_velocity_ = bottom_claw_velocity_ =
558 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800559 LOG(DEBUG, "Ready to fine tune the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800560 mode_ = FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800561 } else {
562 // send top to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800563 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800564 LOG(DEBUG, "Going to the start position for the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800565 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800566 }
567 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800568 mode_ = FINE_TUNE_TOP;
Austin Schuhd27931c2014-02-16 19:18:20 -0800569 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800570 top_claw_velocity_ = bottom_claw_velocity_ =
571 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800572 if (top_claw_.front_or_back_triggered() ||
573 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800574 // this should not happen, but now we know it won't
575 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800576 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800577 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800578 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800579 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800580 }
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000581
582 if (position &&
583 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
584 top_claw_.front(), top_claw_.back())) {
585 // do calibration
586 top_claw_.SetCalibration(
587 position->top.posedge_value,
588 values.claw.upper_claw.calibration.lower_angle);
589 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
590 // calibrated so we are done fine tuning top
591 doing_calibration_fine_tune_ = false;
592 LOG(DEBUG, "Calibrated the top correctly!\n");
593 } else if (top_claw_.calibration().last_value()) {
594 doing_calibration_fine_tune_ = false;
595 top_claw_goal_ = values.claw.start_fine_tune_pos;
596 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
597 mode_ = PREP_FINE_TUNE_TOP;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000598 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800599 }
600 // now set the bottom claw to track
Austin Schuhd27931c2014-02-16 19:18:20 -0800601 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800602 }
603 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800604 doing_calibration_fine_tune_ = false;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800605 if (!was_enabled_ && enabled) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800606 if (position) {
607 top_claw_goal_ = position->top.position;
608 bottom_claw_goal_ = position->bottom.position;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800609 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800610 position->top.position - position->bottom.position;
Austin Schuhf9286cd2014-02-11 00:51:09 -0800611 } else {
612 has_top_claw_goal_ = false;
613 has_bottom_claw_goal_ = false;
614 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800615 }
Austin Schuhf9286cd2014-02-11 00:51:09 -0800616
Austin Schuh4cb047f2014-02-16 21:10:19 -0800617 if ((bottom_claw_.zeroing_state() !=
618 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
Brian Silvermane0a95462014-02-17 00:41:09 -0800619 bottom_claw_.front().value() || top_claw_.front().value()) &&
620 !top_claw_.back().value() && !bottom_claw_.back().value()) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800621 if (enabled) {
622 // Time to slowly move back up to find any position to narrow down the
623 // zero.
Austin Schuhd27931c2014-02-16 19:18:20 -0800624 top_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
625 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800626 top_claw_velocity_ = bottom_claw_velocity_ =
627 values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800628 LOG(DEBUG, "Bottom is known.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800629 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800630 } else {
631 // We don't know where either claw is. Slowly start moving down to find
632 // any hall effect.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800633 if (enabled) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800634 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
635 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800636 top_claw_velocity_ = bottom_claw_velocity_ =
637 -values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800638 LOG(DEBUG, "Both are unknown.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800639 }
640 }
641
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000642 if (position) {
643 if (enabled) {
644 top_claw_.SetCalibrationOnEdge(
645 values.claw.upper_claw,
646 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
647 bottom_claw_.SetCalibrationOnEdge(
648 values.claw.lower_claw,
649 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
650 } else {
651 // TODO(austin): Only calibrate on the predetermined edge.
652 // We might be able to just ignore this since the backlash is soooo
653 // low.
654 // :)
655 top_claw_.SetCalibrationOnEdge(
656 values.claw.upper_claw,
657 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
658 bottom_claw_.SetCalibrationOnEdge(
659 values.claw.lower_claw,
660 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
661 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800662 }
Austin Schuhe7f90d12014-02-17 00:48:25 -0800663 mode_ = UNKNOWN_LOCATION;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800664 }
665
Austin Schuh069143b2014-02-17 02:46:26 -0800666 // Limit the goals if both claws have been (mostly) found.
667 if (mode_ != UNKNOWN_LOCATION) {
668 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
669 }
670
Austin Schuhf9286cd2014-02-11 00:51:09 -0800671 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
Austin Schuh069143b2014-02-17 02:46:26 -0800672 claw_.R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
673 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Austin Schuhcda86af2014-02-16 16:16:39 -0800674 double separation = -971;
675 if (position != nullptr) {
676 separation = position->top.position - position->bottom.position;
677 }
678 LOG(DEBUG, "Goal is %f (bottom) %f, separation is %f\n", claw_.R(0, 0),
679 claw_.R(1, 0), separation);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800680
Austin Schuh01c652b2014-02-21 23:13:42 -0800681 // Only cap power when one of the halves of the claw is moving slowly and
682 // could wind up.
Austin Schuhe7f90d12014-02-17 00:48:25 -0800683 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
684 mode_ == FINE_TUNE_BOTTOM);
Austin Schuhcda86af2014-02-16 16:16:39 -0800685 claw_.Update(output == nullptr);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800686 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800687 claw_.Update(true);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800688 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800689
Austin Schuh4cb047f2014-02-16 21:10:19 -0800690 capped_goal_ = false;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800691 switch (mode_) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800692 case READY:
Austin Schuhe7f90d12014-02-17 00:48:25 -0800693 case PREP_FINE_TUNE_TOP:
694 case PREP_FINE_TUNE_BOTTOM:
Austin Schuhcda86af2014-02-16 16:16:39 -0800695 break;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800696 case FINE_TUNE_BOTTOM:
697 case FINE_TUNE_TOP:
Austin Schuh4cb047f2014-02-16 21:10:19 -0800698 case UNKNOWN_LOCATION: {
699 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
700 double dx = (claw_.uncapped_average_voltage() -
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000701 values.claw.max_zeroing_voltage) / claw_.K(0, 0);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800702 bottom_claw_goal_ -= dx;
703 top_claw_goal_ -= dx;
Austin Schuhcda86af2014-02-16 16:16:39 -0800704 capped_goal_ = true;
Austin Schuh4cb047f2014-02-16 21:10:19 -0800705 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Austin Schuhe7f90d12014-02-17 00:48:25 -0800706 LOG(DEBUG, "Uncapped is %f, max is %f, difference is %f\n",
707 claw_.uncapped_average_voltage(), values.claw.max_zeroing_voltage,
708 (claw_.uncapped_average_voltage() -
709 values.claw.max_zeroing_voltage));
Austin Schuh4cb047f2014-02-16 21:10:19 -0800710 } else if (claw_.uncapped_average_voltage() <
711 -values.claw.max_zeroing_voltage) {
712 double dx = (claw_.uncapped_average_voltage() +
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000713 values.claw.max_zeroing_voltage) / claw_.K(0, 0);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800714 bottom_claw_goal_ -= dx;
715 top_claw_goal_ -= dx;
Austin Schuhcda86af2014-02-16 16:16:39 -0800716 capped_goal_ = true;
Austin Schuh4cb047f2014-02-16 21:10:19 -0800717 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Austin Schuhcda86af2014-02-16 16:16:39 -0800718 }
Austin Schuh4cb047f2014-02-16 21:10:19 -0800719 } break;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800720 }
721
722 if (output) {
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000723 if (goal) {
724 //setup the intake
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000725 output->intake_voltage =
726 (goal->intake > 12.0) ? 12 : (goal->intake < -12.0) ? -12.0
727 : goal->intake;
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000728 output->tusk_voltage = goal->centering;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000729 output->tusk_voltage =
730 (goal->centering > 12.0) ? 12 : (goal->centering < -12.0)
731 ? -12.0
732 : goal->centering;
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000733 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800734 output->top_claw_voltage = claw_.U(1, 0) + claw_.U(0, 0);
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000735 output->bottom_claw_voltage = claw_.U(0, 0);
Austin Schuhf84a1302014-02-19 00:23:30 -0800736
737 if (output->top_claw_voltage > kMaxVoltage) {
738 output->top_claw_voltage = kMaxVoltage;
739 } else if (output->top_claw_voltage < -kMaxVoltage) {
740 output->top_claw_voltage = -kMaxVoltage;
741 }
742
743 if (output->bottom_claw_voltage > kMaxVoltage) {
744 output->bottom_claw_voltage = kMaxVoltage;
745 } else if (output->bottom_claw_voltage < -kMaxVoltage) {
746 output->bottom_claw_voltage = -kMaxVoltage;
747 }
Austin Schuh3bb9a442014-02-02 16:01:45 -0800748 }
James Kuszmaul4abaf482014-02-26 21:16:35 -0800749
750 bool bottom_done =
751 ::std::abs(bottom_absolute_position() - goal->bottom_angle) < 0.005;
752 bool separation_done =
753 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000754 goal->separation_angle) < 0.005;
755 bool separation_done_with_ball =
756 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
757 goal->separation_angle) < 0.06;
James Kuszmaul4abaf482014-02-26 21:16:35 -0800758 status->done = is_ready() && separation_done && bottom_done;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000759 status->done_with_ball =
760 is_ready() && separation_done_with_ball && bottom_done;
James Kuszmaul4abaf482014-02-26 21:16:35 -0800761
762 status->bottom = bottom_absolute_position();
763 status->separation = top_absolute_position() - bottom_absolute_position();
764 status->bottom_velocity = claw_.X_hat(2, 0);
765 status->separation_velocity = claw_.X_hat(3, 0);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800766
767 was_enabled_ = ::aos::robot_state->enabled;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800768}
769
770} // namespace control_loops
771} // namespace frc971
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000772