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