blob: 040cef38154e14448cdd7ebdea67767d58f44231 [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#include "y2014/control_loops/claw/claw.h"
2
3#include <algorithm>
4
5#include "aos/common/controls/control_loops.q.h"
6#include "aos/common/logging/logging.h"
7#include "aos/common/logging/queue_logging.h"
8#include "aos/common/logging/matrix_logging.h"
9#include "aos/common/commonmath.h"
10
11#include "y2014/constants.h"
12#include "y2014/control_loops/claw/claw_motor_plant.h"
13
14// Zeroing plan.
15// There are 2 types of zeros. Enabled and disabled ones.
16// Disabled ones are only valid during auto mode, and can be used to speed up
17// the enabled zero process. We need to re-zero during teleop in case the auto
18// zero was poor and causes us to miss all our shots.
19//
20// We need to be able to zero manually while disabled by moving the joint over
21// the zeros.
22// Zero on the down edge when disabled (gravity in the direction of motion)
23//
24// When enabled, zero on the up edge (gravity opposing the direction of motion)
25// The enabled sequence needs to work as follows. We can crash the claw if we
26// bring them too close to each other or too far from each other. The only safe
27// thing to do is to move them in unison.
28//
29// Start by moving them both towards the front of the bot to either find either
30// the middle hall effect on either jaw, or the front hall effect on the bottom
31// jaw. Any edge that isn't the desired edge will provide an approximate edge
32// location that can be used for the fine tuning step.
33// Once an edge is found on the front claw, move back the other way with both
34// claws until an edge is found for the other claw.
35// Now that we have an approximate zero, we can robustify the limits to keep
36// both claws safe. Then, we can move both claws to a position that is the
37// correct side of the zero and go zero.
38
39// Valid region plan.
40// Difference between the arms has a range, and the values of each arm has a
41// range.
42// If a claw runs up against a static limit, don't let the goal change outside
43// the limit.
44// If a claw runs up against a movable limit, move both claws outwards to get
45// out of the condition.
46
Austin Schuh24957102015-11-28 16:04:40 -080047namespace y2014 {
Brian Silverman17f503e2015-08-02 18:17:18 -070048namespace control_loops {
49
Austin Schuh24957102015-11-28 16:04:40 -080050using ::frc971::HallEffectTracker;
Austin Schuh0e997732015-11-08 15:14:53 -080051using ::y2014::control_loops::claw::kDt;
Austin Schuh24957102015-11-28 16:04:40 -080052using ::frc971::control_loops::DoCoerceGoal;
53using ::frc971::control_loops::ClawPositionToLog;
Austin Schuh0e997732015-11-08 15:14:53 -080054
Brian Silverman17f503e2015-08-02 18:17:18 -070055static const double kZeroingVoltage = 4.0;
56static const double kMaxVoltage = 12.0;
57const double kRezeroThreshold = 0.07;
58
59ClawLimitedLoop::ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> &&loop)
60 : StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
61 uncapped_average_voltage_(0.0),
62 is_zeroing_(true),
63 U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0,
64 -1, 0,
65 0, 1,
66 0, -1).finished(),
67 (Eigen::Matrix<double, 4, 1>() << kMaxVoltage, kMaxVoltage,
68 kMaxVoltage, kMaxVoltage).finished()),
69 U_Poly_zeroing_((Eigen::Matrix<double, 4, 2>() << 1, 0,
70 -1, 0,
71 0, 1,
72 0, -1).finished(),
73 (Eigen::Matrix<double, 4, 1>() <<
74 kZeroingVoltage, kZeroingVoltage,
75 kZeroingVoltage, kZeroingVoltage).finished()) {
76 ::aos::controls::HPolytope<0>::Init();
77}
78
79// Caps the voltage prioritizing reducing velocity error over reducing
80// positional error.
81// Uses the polytope libararies which we used to just use for the drivetrain.
82// Uses a region representing the maximum voltage and then transforms it such
83// that the points represent different amounts of positional error and
84// constrains the region such that, if at all possible, it will maintain its
85// current efforts to reduce velocity error.
86void ClawLimitedLoop::CapU() {
87 const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
88
89 double u_top = U(1, 0);
90 double u_bottom = U(0, 0);
91
92 uncapped_average_voltage_ = (u_top + u_bottom) / 2;
93
94 double max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
95
96 if (::std::abs(u_bottom) > max_voltage || ::std::abs(u_top) > max_voltage) {
97 LOG_MATRIX(DEBUG, "U at start", U());
98 // H * U <= k
99 // U = UPos + UVel
100 // H * (UPos + UVel) <= k
101 // H * UPos <= k - H * UVel
102
103 // Now, we can do a coordinate transformation and say the following.
104
105 // UPos = position_K * position_error
106 // (H * position_K) * position_error <= k - H * UVel
107
108 Eigen::Matrix<double, 2, 2> position_K;
109 position_K << K(0, 0), K(0, 1),
110 K(1, 0), K(1, 1);
111 Eigen::Matrix<double, 2, 2> velocity_K;
112 velocity_K << K(0, 2), K(0, 3),
113 K(1, 2), K(1, 3);
114
115 Eigen::Matrix<double, 2, 1> position_error;
116 position_error << error(0, 0), error(1, 0);
117 Eigen::Matrix<double, 2, 1> velocity_error;
118 velocity_error << error(2, 0), error(3, 0);
119 LOG_MATRIX(DEBUG, "error", error);
120
121 const auto &poly = is_zeroing_ ? U_Poly_zeroing_ : U_Poly_;
122 const Eigen::Matrix<double, 4, 2> pos_poly_H = poly.H() * position_K;
123 const Eigen::Matrix<double, 4, 1> pos_poly_k =
124 poly.k() - poly.H() * velocity_K * velocity_error;
125 const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
126
127 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
128 {
129 const auto &P = position_error;
130
131 // This line was at 45 degrees but is now at some angle steeper than the
132 // straight one between the points.
133 Eigen::Matrix<double, 1, 2> angle_45;
134 // If the top claw is above its soft upper limit, make the line actually
135 // 45 degrees to avoid smashing it into the limit in an attempt to fix the
136 // separation error faster than the bottom position one.
137 if (X_hat(0, 0) + X_hat(1, 0) >
138 constants::GetValues().claw.upper_claw.upper_limit) {
139 angle_45 << 1, 1;
140 } else {
141 // Fixing separation error half as fast as positional error works well
142 // because it means they both close evenly.
143 angle_45 << ::std::sqrt(3), 1;
144 }
145 Eigen::Matrix<double, 1, 2> L45_quadrant;
146 L45_quadrant << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
147 const auto L45 = L45_quadrant.cwiseProduct(angle_45);
148 const double w45 = 0;
149
150 Eigen::Matrix<double, 1, 2> LH;
151 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
152 LH << 0, 1;
153 } else {
154 LH << 1, 0;
155 }
156 const double wh = LH.dot(P);
157
158 Eigen::Matrix<double, 2, 2> standard;
159 standard << L45, LH;
160 Eigen::Matrix<double, 2, 1> W;
161 W << w45, wh;
162 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
163
164 bool is_inside_h;
165 const auto adjusted_pos_error_h =
166 DoCoerceGoal(pos_poly, LH, wh, position_error, &is_inside_h);
167 const auto adjusted_pos_error_45 =
168 DoCoerceGoal(pos_poly, L45, w45, intersection, nullptr);
169 if (pos_poly.IsInside(intersection)) {
170 adjusted_pos_error = adjusted_pos_error_h;
171 } else {
172 if (is_inside_h) {
173 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
174 adjusted_pos_error = adjusted_pos_error_h;
175 } else {
176 adjusted_pos_error = adjusted_pos_error_45;
177 }
178 } else {
179 adjusted_pos_error = adjusted_pos_error_45;
180 }
181 }
182 }
183
184 LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
185 mutable_U() = velocity_K * velocity_error + position_K * adjusted_pos_error;
186 LOG_MATRIX(DEBUG, "U is now", U());
187
188 {
189 const auto values = constants::GetValues().claw;
190 if (top_known_) {
191 if (X_hat(0, 0) + X_hat(1, 0) > values.upper_claw.upper_limit && U(1, 0) > 0) {
192 LOG(WARNING, "upper claw too high and moving up\n");
193 mutable_U(1, 0) = 0;
194 } else if (X_hat(0, 0) + X_hat(1, 0) < values.upper_claw.lower_limit &&
195 U(1, 0) < 0) {
196 LOG(WARNING, "upper claw too low and moving down\n");
197 mutable_U(1, 0) = 0;
198 }
199 }
200 if (bottom_known_) {
201 if (X_hat(0, 0) > values.lower_claw.upper_limit && U(0, 0) > 0) {
202 LOG(WARNING, "lower claw too high and moving up\n");
203 mutable_U(0, 0) = 0;
204 } else if (X_hat(0, 0) < values.lower_claw.lower_limit && U(0, 0) < 0) {
205 LOG(WARNING, "lower claw too low and moving down\n");
206 mutable_U(0, 0) = 0;
207 }
208 }
209 }
210 }
211}
212
213ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
214 ClawMotor *motor)
215 : offset_(0.0),
216 name_(name),
217 motor_(motor),
218 zeroing_state_(UNKNOWN_POSITION),
Brian Silverman17f503e2015-08-02 18:17:18 -0700219 encoder_(0.0),
220 last_encoder_(0.0) {}
221
Austin Schuh24957102015-11-28 16:04:40 -0800222void ZeroedStateFeedbackLoop::SetPositionValues(
223 const ::frc971::control_loops::HalfClawPosition &claw) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700224 front_.Update(claw.front);
225 calibration_.Update(claw.calibration);
226 back_.Update(claw.back);
227
228 bool any_sensor_triggered = any_triggered();
229 if (any_sensor_triggered && any_triggered_last_) {
230 // We are still on the hall effect and nothing has changed.
231 min_hall_effect_on_angle_ =
232 ::std::min(min_hall_effect_on_angle_, claw.position);
233 max_hall_effect_on_angle_ =
234 ::std::max(max_hall_effect_on_angle_, claw.position);
235 } else if (!any_sensor_triggered && !any_triggered_last_) {
236 // We are still off the hall effect and nothing has changed.
237 min_hall_effect_off_angle_ =
238 ::std::min(min_hall_effect_off_angle_, claw.position);
239 max_hall_effect_off_angle_ =
240 ::std::max(max_hall_effect_off_angle_, claw.position);
Brian Silverman17f503e2015-08-02 18:17:18 -0700241 }
242
Brian Silvermand3efb182015-05-13 23:04:29 -0400243 if (front_.is_posedge()) {
244 // Saw a posedge on the hall effect. Reset the limits.
245 min_hall_effect_on_angle_ =
246 ::std::min(claw.front.posedge_value, claw.position);
247 max_hall_effect_on_angle_ =
248 ::std::max(claw.front.posedge_value, claw.position);
249 }
250 if (calibration_.is_posedge()) {
251 // Saw a posedge on the hall effect. Reset the limits.
252 min_hall_effect_on_angle_ =
253 ::std::min(claw.calibration.posedge_value, claw.position);
254 max_hall_effect_on_angle_ =
255 ::std::max(claw.calibration.posedge_value, claw.position);
256 }
257 if (back_.is_posedge()) {
258 // Saw a posedge on the hall effect. Reset the limits.
259 min_hall_effect_on_angle_ =
260 ::std::min(claw.back.posedge_value, claw.position);
261 max_hall_effect_on_angle_ =
262 ::std::max(claw.back.posedge_value, claw.position);
263 }
264
265 if (front_.is_negedge()) {
266 // Saw a negedge on the hall effect. Reset the limits.
267 min_hall_effect_off_angle_ =
268 ::std::min(claw.front.negedge_value, claw.position);
269 max_hall_effect_off_angle_ =
270 ::std::max(claw.front.negedge_value, claw.position);
271 }
272 if (calibration_.is_negedge()) {
273 // Saw a negedge on the hall effect. Reset the limits.
274 min_hall_effect_off_angle_ =
275 ::std::min(claw.calibration.negedge_value, claw.position);
276 max_hall_effect_off_angle_ =
277 ::std::max(claw.calibration.negedge_value, claw.position);
278 }
279 if (back_.is_negedge()) {
280 // Saw a negedge on the hall effect. Reset the limits.
281 min_hall_effect_off_angle_ =
282 ::std::min(claw.back.negedge_value, claw.position);
283 max_hall_effect_off_angle_ =
284 ::std::max(claw.back.negedge_value, claw.position);
285 }
286
Brian Silverman17f503e2015-08-02 18:17:18 -0700287 last_encoder_ = encoder_;
288 if (front().value() || calibration().value() || back().value()) {
289 last_on_encoder_ = encoder_;
290 } else {
291 last_off_encoder_ = encoder_;
292 }
293 encoder_ = claw.position;
294 any_triggered_last_ = any_sensor_triggered;
295}
296
Austin Schuh24957102015-11-28 16:04:40 -0800297void ZeroedStateFeedbackLoop::Reset(
298 const ::frc971::control_loops::HalfClawPosition &claw) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700299 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
300
301 front_.Reset(claw.front);
302 calibration_.Reset(claw.calibration);
303 back_.Reset(claw.back);
304 // close up the min and max edge positions as they are no longer valid and
305 // will be expanded in future iterations
306 min_hall_effect_on_angle_ = claw.position;
307 max_hall_effect_on_angle_ = claw.position;
308 min_hall_effect_off_angle_ = claw.position;
309 max_hall_effect_off_angle_ = claw.position;
310 any_triggered_last_ = any_triggered();
311}
312
313bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
314 const constants::Values::Claws::Claw &claw_values,
315 JointZeroingState zeroing_state) {
316 double edge_encoder;
317 double edge_angle;
318 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
319 LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
320 SetCalibration(edge_encoder, edge_angle);
321 set_zeroing_state(zeroing_state);
322 return true;
323 }
324 return false;
325}
326
327void TopZeroedStateFeedbackLoop::HandleCalibrationError(
328 const constants::Values::Claws::Claw &claw_values) {
329 double edge_encoder;
330 double edge_angle;
331 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
332 const double calibration_error =
333 ComputeCalibrationChange(edge_encoder, edge_angle);
334 LOG(INFO, "Top calibration error is %f\n", calibration_error);
335 if (::std::abs(calibration_error) > kRezeroThreshold) {
336 LOG(WARNING, "rezeroing top\n");
337 SetCalibration(edge_encoder, edge_angle);
338 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
339 }
340 }
341}
342
343
344void BottomZeroedStateFeedbackLoop::HandleCalibrationError(
345 const constants::Values::Claws::Claw &claw_values) {
346 double edge_encoder;
347 double edge_angle;
348 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
349 const double calibration_error =
350 ComputeCalibrationChange(edge_encoder, edge_angle);
351 LOG(INFO, "Bottom calibration error is %f\n", calibration_error);
352 if (::std::abs(calibration_error) > kRezeroThreshold) {
353 LOG(WARNING, "rezeroing bottom\n");
354 SetCalibration(edge_encoder, edge_angle);
355 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
356 }
357 }
358}
359
360bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
361 const constants::Values::Claws::Claw &claw_values,
362 JointZeroingState zeroing_state) {
363 double edge_encoder;
364 double edge_angle;
365 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
366 LOG(INFO, "Calibration edge.\n");
367 SetCalibration(edge_encoder, edge_angle);
368 set_zeroing_state(zeroing_state);
369 return true;
370 }
371 return false;
372}
373
Austin Schuh24957102015-11-28 16:04:40 -0800374ClawMotor::ClawMotor(::frc971::control_loops::ClawQueue *my_claw)
375 : aos::controls::ControlLoop<::frc971::control_loops::ClawQueue>(my_claw),
Brian Silverman17f503e2015-08-02 18:17:18 -0700376 has_top_claw_goal_(false),
377 top_claw_goal_(0.0),
378 top_claw_(this),
379 has_bottom_claw_goal_(false),
380 bottom_claw_goal_(0.0),
381 bottom_claw_(this),
Austin Schuhedc317c2015-11-08 14:07:42 -0800382 claw_(::y2014::control_loops::claw::MakeClawLoop()),
Brian Silverman17f503e2015-08-02 18:17:18 -0700383 was_enabled_(false),
384 doing_calibration_fine_tune_(false),
385 capped_goal_(false),
386 mode_(UNKNOWN_LOCATION) {}
387
388const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
389
390bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
391 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
392 const HallEffectTracker &sensorB) {
393 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
394 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
395 this_sensor.value() && !this_sensor.last_value()) {
396 posedge_filter_ = &this_sensor;
397 } else if (posedge_filter_ == &this_sensor &&
398 !this_sensor.posedge_count_changed() &&
399 !sensorA.posedge_count_changed() &&
400 !sensorB.posedge_count_changed() && this_sensor.value()) {
401 posedge_filter_ = nullptr;
402 return true;
403 } else if (posedge_filter_ == &this_sensor) {
404 posedge_filter_ = nullptr;
405 }
406 return false;
407}
408
409bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
410 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
411 const HallEffectTracker &sensorB) {
412 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
413 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
414 !this_sensor.value() && this_sensor.last_value()) {
415 negedge_filter_ = &this_sensor;
416 } else if (negedge_filter_ == &this_sensor &&
417 !this_sensor.negedge_count_changed() &&
418 !sensorA.negedge_count_changed() &&
419 !sensorB.negedge_count_changed() && !this_sensor.value()) {
420 negedge_filter_ = nullptr;
421 return true;
422 } else if (negedge_filter_ == &this_sensor) {
423 negedge_filter_ = nullptr;
424 }
425 return false;
426}
427
428bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
429 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
430 double *edge_angle, const HallEffectTracker &this_sensor,
431 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
432 const char *hall_effect_name) {
433 bool found_edge = false;
434
435 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
436 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
437 LOG(WARNING, "%s: Uncertain which side, rejecting posedge\n", name_);
438 } else {
439 const double average_last_encoder =
440 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400441 if (this_sensor.posedge_value() < average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700442 *edge_angle = angles.upper_decreasing_angle;
443 LOG(INFO, "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
Brian Silvermand3efb182015-05-13 23:04:29 -0400444 name_, hall_effect_name, *edge_angle, this_sensor.posedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700445 average_last_encoder);
446 } else {
447 *edge_angle = angles.lower_angle;
448 LOG(INFO, "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
Brian Silvermand3efb182015-05-13 23:04:29 -0400449 name_, hall_effect_name, *edge_angle, this_sensor.posedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700450 average_last_encoder);
451 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400452 *edge_encoder = this_sensor.posedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700453 found_edge = true;
454 }
455 }
456
457 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
458 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
459 LOG(WARNING, "%s: Uncertain which side, rejecting negedge\n", name_);
460 } else {
461 const double average_last_encoder =
462 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400463 if (this_sensor.negedge_value() > average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700464 *edge_angle = angles.upper_angle;
465 LOG(INFO, "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
Brian Silvermand3efb182015-05-13 23:04:29 -0400466 name_, hall_effect_name, *edge_angle, this_sensor.negedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700467 average_last_encoder);
468 } else {
469 *edge_angle = angles.lower_decreasing_angle;
470 LOG(INFO, "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
Brian Silvermand3efb182015-05-13 23:04:29 -0400471 name_, hall_effect_name, *edge_angle, this_sensor.negedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700472 average_last_encoder);
473 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400474 *edge_encoder = this_sensor.negedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700475 found_edge = true;
476 }
477 }
478
479 return found_edge;
480}
481
482bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
483 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
484 double *edge_angle) {
485 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
486 calibration_, back_, "front")) {
487 return true;
488 }
489 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
490 calibration_, front_, back_, "calibration")) {
491 return true;
492 }
493 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
494 calibration_, front_, "back")) {
495 return true;
496 }
497 return false;
498}
499
500void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
501 double edge_angle) {
502 double old_offset = offset_;
503 offset_ = edge_angle - edge_encoder;
504 const double doffset = offset_ - old_offset;
505 motor_->ChangeTopOffset(doffset);
506}
507
508double TopZeroedStateFeedbackLoop::ComputeCalibrationChange(double edge_encoder,
509 double edge_angle) {
510 const double offset = edge_angle - edge_encoder;
511 const double doffset = offset - offset_;
512 return doffset;
513}
514
515void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
516 double edge_angle) {
517 double old_offset = offset_;
518 offset_ = edge_angle - edge_encoder;
519 const double doffset = offset_ - old_offset;
520 motor_->ChangeBottomOffset(doffset);
521}
522
523double BottomZeroedStateFeedbackLoop::ComputeCalibrationChange(
524 double edge_encoder, double edge_angle) {
525 const double offset = edge_angle - edge_encoder;
526 const double doffset = offset - offset_;
527 return doffset;
528}
529
530void ClawMotor::ChangeTopOffset(double doffset) {
531 claw_.ChangeTopOffset(doffset);
532 if (has_top_claw_goal_) {
533 top_claw_goal_ += doffset;
534 }
535}
536
537void ClawMotor::ChangeBottomOffset(double doffset) {
538 claw_.ChangeBottomOffset(doffset);
539 if (has_bottom_claw_goal_) {
540 bottom_claw_goal_ += doffset;
541 }
542}
543
544void ClawLimitedLoop::ChangeTopOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700545 mutable_X_hat()(1, 0) += doffset;
546 LOG(INFO, "Changing top offset by %f\n", doffset);
547}
548void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700549 mutable_X_hat()(0, 0) += doffset;
550 mutable_X_hat()(1, 0) -= doffset;
551 LOG(INFO, "Changing bottom offset by %f\n", doffset);
552}
553
554void LimitClawGoal(double *bottom_goal, double *top_goal,
Austin Schuh24957102015-11-28 16:04:40 -0800555 const constants::Values &values) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700556 // first update position based on angle limit
557 const double separation = *top_goal - *bottom_goal;
558 if (separation > values.claw.soft_max_separation) {
559 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
560 const double dsep = (separation - values.claw.soft_max_separation) / 2.0;
561 *bottom_goal += dsep;
562 *top_goal -= dsep;
563 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
564 }
565 if (separation < values.claw.soft_min_separation) {
566 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
567 const double dsep = (separation - values.claw.soft_min_separation) / 2.0;
568 *bottom_goal += dsep;
569 *top_goal -= dsep;
570 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
571 }
572
573 // now move both goals in unison
574 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
575 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
576 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
577 *bottom_goal = values.claw.lower_claw.lower_limit;
578 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
579 }
580 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
581 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
582 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
583 *bottom_goal = values.claw.lower_claw.upper_limit;
584 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
585 }
586
587 if (*top_goal < values.claw.upper_claw.lower_limit) {
588 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
589 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
590 *top_goal = values.claw.upper_claw.lower_limit;
591 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
592 }
593 if (*top_goal > values.claw.upper_claw.upper_limit) {
594 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
595 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
596 *top_goal = values.claw.upper_claw.upper_limit;
597 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
598 }
599}
600
601bool ClawMotor::is_ready() const {
602 return (
603 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
604 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
605 (((::aos::joystick_state.get() == NULL)
606 ? true
607 : ::aos::joystick_state->autonomous) &&
608 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
609 top_claw_.zeroing_state() ==
610 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
611 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
612 bottom_claw_.zeroing_state() ==
613 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
614}
615
616bool ClawMotor::is_zeroing() const { return !is_ready(); }
617
618// Positive angle is up, and positive power is up.
Austin Schuh24957102015-11-28 16:04:40 -0800619void ClawMotor::RunIteration(
620 const ::frc971::control_loops::ClawQueue::Goal *goal,
621 const ::frc971::control_loops::ClawQueue::Position *position,
622 ::frc971::control_loops::ClawQueue::Output *output,
623 ::frc971::control_loops::ClawQueue::Status *status) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700624 // Disable the motors now so that all early returns will return with the
625 // motors disabled.
626 if (output) {
627 output->top_claw_voltage = 0;
628 output->bottom_claw_voltage = 0;
629 output->intake_voltage = 0;
630 output->tusk_voltage = 0;
631 }
632
633 if (goal) {
634 if (::std::isnan(goal->bottom_angle) ||
635 ::std::isnan(goal->separation_angle) || ::std::isnan(goal->intake) ||
636 ::std::isnan(goal->centering)) {
637 return;
638 }
639 }
640
641 if (WasReset()) {
642 top_claw_.Reset(position->top);
643 bottom_claw_.Reset(position->bottom);
644 }
645
Austin Schuh24957102015-11-28 16:04:40 -0800646 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700647
648 if (position) {
649 Eigen::Matrix<double, 2, 1> Y;
650 Y << position->bottom.position + bottom_claw_.offset(),
651 position->top.position + top_claw_.offset();
652 claw_.Correct(Y);
653
654 top_claw_.SetPositionValues(position->top);
655 bottom_claw_.SetPositionValues(position->bottom);
656
657 if (!has_top_claw_goal_) {
658 has_top_claw_goal_ = true;
659 top_claw_goal_ = top_claw_.absolute_position();
660 initial_separation_ =
661 top_claw_.absolute_position() - bottom_claw_.absolute_position();
662 }
663 if (!has_bottom_claw_goal_) {
664 has_bottom_claw_goal_ = true;
665 bottom_claw_goal_ = bottom_claw_.absolute_position();
666 initial_separation_ =
667 top_claw_.absolute_position() - bottom_claw_.absolute_position();
668 }
669 LOG_STRUCT(DEBUG, "absolute position",
670 ClawPositionToLog(top_claw_.absolute_position(),
671 bottom_claw_.absolute_position()));
672 }
673
674 bool autonomous, enabled;
675 if (::aos::joystick_state.get() == nullptr) {
676 autonomous = true;
677 enabled = false;
678 } else {
679 autonomous = ::aos::joystick_state->autonomous;
680 enabled = ::aos::joystick_state->enabled;
681 }
682
683 double bottom_claw_velocity_ = 0.0;
684 double top_claw_velocity_ = 0.0;
685
686 if (goal != NULL &&
687 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
688 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
689 (autonomous &&
690 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
691 top_claw_.zeroing_state() ==
692 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
693 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
694 bottom_claw_.zeroing_state() ==
695 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))))) {
696 // Ready to use the claw.
697 // Limit the goals here.
698 bottom_claw_goal_ = goal->bottom_angle;
699 top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
700 has_bottom_claw_goal_ = true;
701 has_top_claw_goal_ = true;
702 doing_calibration_fine_tune_ = false;
703 mode_ = READY;
704
705 bottom_claw_.HandleCalibrationError(values.claw.lower_claw);
706 top_claw_.HandleCalibrationError(values.claw.upper_claw);
707 } else if (top_claw_.zeroing_state() !=
708 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
709 bottom_claw_.zeroing_state() !=
710 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
711 // Time to fine tune the zero.
712 // Limit the goals here.
713 if (!enabled) {
714 // If we are disabled, start the fine tune process over again.
715 doing_calibration_fine_tune_ = false;
716 }
717 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
718 // always get the bottom claw to calibrated first
719 LOG(DEBUG, "Calibrating the bottom of the claw\n");
720 if (!doing_calibration_fine_tune_) {
721 if (::std::abs(bottom_absolute_position() -
722 values.claw.start_fine_tune_pos) <
723 values.claw.claw_unimportant_epsilon) {
724 doing_calibration_fine_tune_ = true;
Austin Schuh0e997732015-11-08 15:14:53 -0800725 bottom_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700726 top_claw_velocity_ = bottom_claw_velocity_ =
727 values.claw.claw_zeroing_speed;
728 LOG(DEBUG, "Ready to fine tune the bottom\n");
729 mode_ = FINE_TUNE_BOTTOM;
730 } else {
731 // send bottom to zeroing start
732 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
733 LOG(DEBUG, "Going to the start position for the bottom\n");
734 mode_ = PREP_FINE_TUNE_BOTTOM;
735 }
736 } else {
737 mode_ = FINE_TUNE_BOTTOM;
Austin Schuh0e997732015-11-08 15:14:53 -0800738 bottom_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700739 top_claw_velocity_ = bottom_claw_velocity_ =
740 values.claw.claw_zeroing_speed;
741 if (top_claw_.front_or_back_triggered() ||
742 bottom_claw_.front_or_back_triggered()) {
743 // We shouldn't hit a limit, but if we do, go back to the zeroing
744 // point and try again.
745 doing_calibration_fine_tune_ = false;
746 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
747 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
748 LOG(DEBUG, "Found a limit, starting over.\n");
749 mode_ = PREP_FINE_TUNE_BOTTOM;
750 }
751
752 if (position && bottom_claw_.SawFilteredPosedge(
753 bottom_claw_.calibration(), bottom_claw_.front(),
754 bottom_claw_.back())) {
755 // do calibration
756 bottom_claw_.SetCalibration(
Brian Silvermand3efb182015-05-13 23:04:29 -0400757 position->bottom.calibration.posedge_value,
Brian Silverman17f503e2015-08-02 18:17:18 -0700758 values.claw.lower_claw.calibration.lower_angle);
759 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
760 // calibrated so we are done fine tuning bottom
761 doing_calibration_fine_tune_ = false;
762 LOG(DEBUG, "Calibrated the bottom correctly!\n");
763 } else if (bottom_claw_.calibration().last_value()) {
764 LOG(DEBUG, "Aborting bottom fine tune because sensor triggered\n");
765 doing_calibration_fine_tune_ = false;
766 bottom_claw_.set_zeroing_state(
767 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
768 } else {
769 LOG(DEBUG, "Fine tuning\n");
770 }
771 }
772 // now set the top claw to track
773
774 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
775 } else {
776 // bottom claw must be calibrated, start on the top
777 if (!doing_calibration_fine_tune_) {
778 if (::std::abs(top_absolute_position() -
779 values.claw.start_fine_tune_pos) <
780 values.claw.claw_unimportant_epsilon) {
781 doing_calibration_fine_tune_ = true;
Austin Schuh0e997732015-11-08 15:14:53 -0800782 top_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700783 top_claw_velocity_ = bottom_claw_velocity_ =
784 values.claw.claw_zeroing_speed;
785 LOG(DEBUG, "Ready to fine tune the top\n");
786 mode_ = FINE_TUNE_TOP;
787 } else {
788 // send top to zeroing start
789 top_claw_goal_ = values.claw.start_fine_tune_pos;
790 LOG(DEBUG, "Going to the start position for the top\n");
791 mode_ = PREP_FINE_TUNE_TOP;
792 }
793 } else {
794 mode_ = FINE_TUNE_TOP;
Austin Schuh0e997732015-11-08 15:14:53 -0800795 top_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700796 top_claw_velocity_ = bottom_claw_velocity_ =
797 values.claw.claw_zeroing_speed;
798 if (top_claw_.front_or_back_triggered() ||
799 bottom_claw_.front_or_back_triggered()) {
800 // this should not happen, but now we know it won't
801 doing_calibration_fine_tune_ = false;
802 top_claw_goal_ = values.claw.start_fine_tune_pos;
803 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
804 LOG(DEBUG, "Found a limit, starting over.\n");
805 mode_ = PREP_FINE_TUNE_TOP;
806 }
807
808 if (position &&
809 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
810 top_claw_.front(), top_claw_.back())) {
811 // do calibration
812 top_claw_.SetCalibration(
Brian Silvermand3efb182015-05-13 23:04:29 -0400813 position->top.calibration.posedge_value,
Brian Silverman17f503e2015-08-02 18:17:18 -0700814 values.claw.upper_claw.calibration.lower_angle);
815 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
816 // calibrated so we are done fine tuning top
817 doing_calibration_fine_tune_ = false;
818 LOG(DEBUG, "Calibrated the top correctly!\n");
819 } else if (top_claw_.calibration().last_value()) {
820 LOG(DEBUG, "Aborting top fine tune because sensor triggered\n");
821 doing_calibration_fine_tune_ = false;
822 top_claw_.set_zeroing_state(
823 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
824 }
825 }
826 // now set the bottom claw to track
827 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
828 }
829 } else {
830 doing_calibration_fine_tune_ = false;
831 if (!was_enabled_ && enabled) {
832 if (position) {
833 top_claw_goal_ = position->top.position + top_claw_.offset();
834 bottom_claw_goal_ = position->bottom.position + bottom_claw_.offset();
835 initial_separation_ =
836 position->top.position - position->bottom.position;
837 } else {
838 has_top_claw_goal_ = false;
839 has_bottom_claw_goal_ = false;
840 }
841 }
842
843 if ((bottom_claw_.zeroing_state() !=
844 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
845 bottom_claw_.front().value() || top_claw_.front().value()) &&
846 !top_claw_.back().value() && !bottom_claw_.back().value()) {
847 if (enabled) {
848 // Time to slowly move back up to find any position to narrow down the
849 // zero.
Austin Schuh0e997732015-11-08 15:14:53 -0800850 top_claw_goal_ += values.claw.claw_zeroing_off_speed * kDt;
851 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700852 top_claw_velocity_ = bottom_claw_velocity_ =
853 values.claw.claw_zeroing_off_speed;
854 LOG(DEBUG, "Bottom is known.\n");
855 }
856 } else {
857 // We don't know where either claw is. Slowly start moving down to find
858 // any hall effect.
859 if (enabled) {
Austin Schuh0e997732015-11-08 15:14:53 -0800860 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * kDt;
861 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700862 top_claw_velocity_ = bottom_claw_velocity_ =
863 -values.claw.claw_zeroing_off_speed;
864 LOG(DEBUG, "Both are unknown.\n");
865 }
866 }
867
868 if (position) {
869 if (enabled) {
870 top_claw_.SetCalibrationOnEdge(
871 values.claw.upper_claw,
872 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
873 bottom_claw_.SetCalibrationOnEdge(
874 values.claw.lower_claw,
875 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
876 } else {
877 // TODO(austin): Only calibrate on the predetermined edge.
878 // We might be able to just ignore this since the backlash is soooo
879 // low.
880 // :)
881 top_claw_.SetCalibrationOnEdge(
882 values.claw.upper_claw,
883 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
884 bottom_claw_.SetCalibrationOnEdge(
885 values.claw.lower_claw,
886 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
887 }
888 }
889 mode_ = UNKNOWN_LOCATION;
890 }
891
892 // Limit the goals if both claws have been (mostly) found.
893 if (mode_ != UNKNOWN_LOCATION) {
894 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
895 }
896
897 claw_.set_positions_known(
898 top_claw_.zeroing_state() != ZeroedStateFeedbackLoop::UNKNOWN_POSITION,
899 bottom_claw_.zeroing_state() !=
900 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
901 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
902 claw_.mutable_R() << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
903 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
904 LOG_MATRIX(DEBUG, "actual goal", claw_.R());
905
906 // Only cap power when one of the halves of the claw is moving slowly and
907 // could wind up.
908 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
909 mode_ == FINE_TUNE_BOTTOM);
910 claw_.Update(output == nullptr);
911 } else {
912 claw_.Update(true);
913 }
914
915 capped_goal_ = false;
916 switch (mode_) {
917 case READY:
918 case PREP_FINE_TUNE_TOP:
919 case PREP_FINE_TUNE_BOTTOM:
920 break;
921 case FINE_TUNE_BOTTOM:
922 case FINE_TUNE_TOP:
923 case UNKNOWN_LOCATION: {
924 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
925 double dx_bot = (claw_.U_uncapped(0, 0) -
926 values.claw.max_zeroing_voltage) /
927 claw_.K(0, 0);
928 double dx_top = (claw_.U_uncapped(1, 0) -
929 values.claw.max_zeroing_voltage) /
930 claw_.K(0, 0);
931 double dx = ::std::max(dx_top, dx_bot);
932 bottom_claw_goal_ -= dx;
933 top_claw_goal_ -= dx;
934 Eigen::Matrix<double, 4, 1> R;
935 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0),
936 claw_.R(3, 0);
937 claw_.mutable_U() = claw_.K() * (R - claw_.X_hat());
938 capped_goal_ = true;
939 LOG(DEBUG, "Moving the goal by %f to prevent windup."
940 " Uncapped is %f, max is %f, difference is %f\n",
941 dx,
942 claw_.uncapped_average_voltage(), values.claw.max_zeroing_voltage,
943 (claw_.uncapped_average_voltage() -
944 values.claw.max_zeroing_voltage));
945 } else if (claw_.uncapped_average_voltage() <
946 -values.claw.max_zeroing_voltage) {
947 double dx_bot = (claw_.U_uncapped(0, 0) +
948 values.claw.max_zeroing_voltage) /
949 claw_.K(0, 0);
950 double dx_top = (claw_.U_uncapped(1, 0) +
951 values.claw.max_zeroing_voltage) /
952 claw_.K(0, 0);
953 double dx = ::std::min(dx_top, dx_bot);
954 bottom_claw_goal_ -= dx;
955 top_claw_goal_ -= dx;
956 Eigen::Matrix<double, 4, 1> R;
957 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0),
958 claw_.R(3, 0);
959 claw_.mutable_U() = claw_.K() * (R - claw_.X_hat());
960 capped_goal_ = true;
961 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
962 }
963 } break;
964 }
965
966 if (output) {
967 if (goal) {
968 //setup the intake
969 output->intake_voltage =
970 (goal->intake > 12.0) ? 12 : (goal->intake < -12.0) ? -12.0
971 : goal->intake;
972 output->tusk_voltage = goal->centering;
973 output->tusk_voltage =
974 (goal->centering > 12.0) ? 12 : (goal->centering < -12.0)
975 ? -12.0
976 : goal->centering;
977 }
978 output->top_claw_voltage = claw_.U(1, 0);
979 output->bottom_claw_voltage = claw_.U(0, 0);
980
981 if (output->top_claw_voltage > kMaxVoltage) {
982 output->top_claw_voltage = kMaxVoltage;
983 } else if (output->top_claw_voltage < -kMaxVoltage) {
984 output->top_claw_voltage = -kMaxVoltage;
985 }
986
987 if (output->bottom_claw_voltage > kMaxVoltage) {
988 output->bottom_claw_voltage = kMaxVoltage;
989 } else if (output->bottom_claw_voltage < -kMaxVoltage) {
990 output->bottom_claw_voltage = -kMaxVoltage;
991 }
992 }
993
994 status->bottom = bottom_absolute_position();
995 status->separation = top_absolute_position() - bottom_absolute_position();
996 status->bottom_velocity = claw_.X_hat(2, 0);
997 status->separation_velocity = claw_.X_hat(3, 0);
998
999 if (goal) {
1000 bool bottom_done =
1001 ::std::abs(bottom_absolute_position() - goal->bottom_angle) < 0.020;
1002 bool bottom_velocity_done = ::std::abs(status->bottom_velocity) < 0.2;
1003 bool separation_done =
1004 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
1005 goal->separation_angle) < 0.020;
1006 bool separation_done_with_ball =
1007 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
1008 goal->separation_angle) < 0.06;
1009 status->done = is_ready() && separation_done && bottom_done && bottom_velocity_done;
1010 status->done_with_ball =
1011 is_ready() && separation_done_with_ball && bottom_done && bottom_velocity_done;
1012 } else {
1013 status->done = status->done_with_ball = false;
1014 }
1015
1016 status->zeroed = is_ready();
1017 status->zeroed_for_auto =
1018 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
1019 top_claw_.zeroing_state() ==
1020 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
1021 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
1022 bottom_claw_.zeroing_state() ==
1023 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
1024
1025 was_enabled_ = enabled;
1026}
1027
1028} // namespace control_loops
Austin Schuh24957102015-11-28 16:04:40 -08001029} // namespace y2014