blob: 1c93cf5a6d7f0adc375dfceb34ba3d1042f74ac0 [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),
Brian Silverman17f503e2015-08-02 18:17:18 -0700214 encoder_(0.0),
215 last_encoder_(0.0) {}
216
217void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition &claw) {
218 front_.Update(claw.front);
219 calibration_.Update(claw.calibration);
220 back_.Update(claw.back);
221
222 bool any_sensor_triggered = any_triggered();
223 if (any_sensor_triggered && any_triggered_last_) {
224 // We are still on the hall effect and nothing has changed.
225 min_hall_effect_on_angle_ =
226 ::std::min(min_hall_effect_on_angle_, claw.position);
227 max_hall_effect_on_angle_ =
228 ::std::max(max_hall_effect_on_angle_, claw.position);
229 } else if (!any_sensor_triggered && !any_triggered_last_) {
230 // We are still off the hall effect and nothing has changed.
231 min_hall_effect_off_angle_ =
232 ::std::min(min_hall_effect_off_angle_, claw.position);
233 max_hall_effect_off_angle_ =
234 ::std::max(max_hall_effect_off_angle_, claw.position);
Brian Silverman17f503e2015-08-02 18:17:18 -0700235 }
236
Brian Silvermand3efb182015-05-13 23:04:29 -0400237 if (front_.is_posedge()) {
238 // Saw a posedge on the hall effect. Reset the limits.
239 min_hall_effect_on_angle_ =
240 ::std::min(claw.front.posedge_value, claw.position);
241 max_hall_effect_on_angle_ =
242 ::std::max(claw.front.posedge_value, claw.position);
243 }
244 if (calibration_.is_posedge()) {
245 // Saw a posedge on the hall effect. Reset the limits.
246 min_hall_effect_on_angle_ =
247 ::std::min(claw.calibration.posedge_value, claw.position);
248 max_hall_effect_on_angle_ =
249 ::std::max(claw.calibration.posedge_value, claw.position);
250 }
251 if (back_.is_posedge()) {
252 // Saw a posedge on the hall effect. Reset the limits.
253 min_hall_effect_on_angle_ =
254 ::std::min(claw.back.posedge_value, claw.position);
255 max_hall_effect_on_angle_ =
256 ::std::max(claw.back.posedge_value, claw.position);
257 }
258
259 if (front_.is_negedge()) {
260 // Saw a negedge on the hall effect. Reset the limits.
261 min_hall_effect_off_angle_ =
262 ::std::min(claw.front.negedge_value, claw.position);
263 max_hall_effect_off_angle_ =
264 ::std::max(claw.front.negedge_value, claw.position);
265 }
266 if (calibration_.is_negedge()) {
267 // Saw a negedge on the hall effect. Reset the limits.
268 min_hall_effect_off_angle_ =
269 ::std::min(claw.calibration.negedge_value, claw.position);
270 max_hall_effect_off_angle_ =
271 ::std::max(claw.calibration.negedge_value, claw.position);
272 }
273 if (back_.is_negedge()) {
274 // Saw a negedge on the hall effect. Reset the limits.
275 min_hall_effect_off_angle_ =
276 ::std::min(claw.back.negedge_value, claw.position);
277 max_hall_effect_off_angle_ =
278 ::std::max(claw.back.negedge_value, claw.position);
279 }
280
Brian Silverman17f503e2015-08-02 18:17:18 -0700281 last_encoder_ = encoder_;
282 if (front().value() || calibration().value() || back().value()) {
283 last_on_encoder_ = encoder_;
284 } else {
285 last_off_encoder_ = encoder_;
286 }
287 encoder_ = claw.position;
288 any_triggered_last_ = any_sensor_triggered;
289}
290
291void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition &claw) {
292 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
293
294 front_.Reset(claw.front);
295 calibration_.Reset(claw.calibration);
296 back_.Reset(claw.back);
297 // close up the min and max edge positions as they are no longer valid and
298 // will be expanded in future iterations
299 min_hall_effect_on_angle_ = claw.position;
300 max_hall_effect_on_angle_ = claw.position;
301 min_hall_effect_off_angle_ = claw.position;
302 max_hall_effect_off_angle_ = claw.position;
303 any_triggered_last_ = any_triggered();
304}
305
306bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
307 const constants::Values::Claws::Claw &claw_values,
308 JointZeroingState zeroing_state) {
309 double edge_encoder;
310 double edge_angle;
311 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
312 LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
313 SetCalibration(edge_encoder, edge_angle);
314 set_zeroing_state(zeroing_state);
315 return true;
316 }
317 return false;
318}
319
320void TopZeroedStateFeedbackLoop::HandleCalibrationError(
321 const constants::Values::Claws::Claw &claw_values) {
322 double edge_encoder;
323 double edge_angle;
324 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
325 const double calibration_error =
326 ComputeCalibrationChange(edge_encoder, edge_angle);
327 LOG(INFO, "Top calibration error is %f\n", calibration_error);
328 if (::std::abs(calibration_error) > kRezeroThreshold) {
329 LOG(WARNING, "rezeroing top\n");
330 SetCalibration(edge_encoder, edge_angle);
331 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
332 }
333 }
334}
335
336
337void BottomZeroedStateFeedbackLoop::HandleCalibrationError(
338 const constants::Values::Claws::Claw &claw_values) {
339 double edge_encoder;
340 double edge_angle;
341 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
342 const double calibration_error =
343 ComputeCalibrationChange(edge_encoder, edge_angle);
344 LOG(INFO, "Bottom calibration error is %f\n", calibration_error);
345 if (::std::abs(calibration_error) > kRezeroThreshold) {
346 LOG(WARNING, "rezeroing bottom\n");
347 SetCalibration(edge_encoder, edge_angle);
348 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
349 }
350 }
351}
352
353bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
354 const constants::Values::Claws::Claw &claw_values,
355 JointZeroingState zeroing_state) {
356 double edge_encoder;
357 double edge_angle;
358 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
359 LOG(INFO, "Calibration edge.\n");
360 SetCalibration(edge_encoder, edge_angle);
361 set_zeroing_state(zeroing_state);
362 return true;
363 }
364 return false;
365}
366
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400367ClawMotor::ClawMotor(control_loops::ClawQueue *my_claw)
368 : aos::controls::ControlLoop<control_loops::ClawQueue>(my_claw),
Brian Silverman17f503e2015-08-02 18:17:18 -0700369 has_top_claw_goal_(false),
370 top_claw_goal_(0.0),
371 top_claw_(this),
372 has_bottom_claw_goal_(false),
373 bottom_claw_goal_(0.0),
374 bottom_claw_(this),
Austin Schuhedc317c2015-11-08 14:07:42 -0800375 claw_(::y2014::control_loops::claw::MakeClawLoop()),
Brian Silverman17f503e2015-08-02 18:17:18 -0700376 was_enabled_(false),
377 doing_calibration_fine_tune_(false),
378 capped_goal_(false),
379 mode_(UNKNOWN_LOCATION) {}
380
381const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
382
383bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
384 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
385 const HallEffectTracker &sensorB) {
386 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
387 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
388 this_sensor.value() && !this_sensor.last_value()) {
389 posedge_filter_ = &this_sensor;
390 } else if (posedge_filter_ == &this_sensor &&
391 !this_sensor.posedge_count_changed() &&
392 !sensorA.posedge_count_changed() &&
393 !sensorB.posedge_count_changed() && this_sensor.value()) {
394 posedge_filter_ = nullptr;
395 return true;
396 } else if (posedge_filter_ == &this_sensor) {
397 posedge_filter_ = nullptr;
398 }
399 return false;
400}
401
402bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
403 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
404 const HallEffectTracker &sensorB) {
405 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
406 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
407 !this_sensor.value() && this_sensor.last_value()) {
408 negedge_filter_ = &this_sensor;
409 } else if (negedge_filter_ == &this_sensor &&
410 !this_sensor.negedge_count_changed() &&
411 !sensorA.negedge_count_changed() &&
412 !sensorB.negedge_count_changed() && !this_sensor.value()) {
413 negedge_filter_ = nullptr;
414 return true;
415 } else if (negedge_filter_ == &this_sensor) {
416 negedge_filter_ = nullptr;
417 }
418 return false;
419}
420
421bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
422 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
423 double *edge_angle, const HallEffectTracker &this_sensor,
424 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
425 const char *hall_effect_name) {
426 bool found_edge = false;
427
428 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
429 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
430 LOG(WARNING, "%s: Uncertain which side, rejecting posedge\n", name_);
431 } else {
432 const double average_last_encoder =
433 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400434 if (this_sensor.posedge_value() < average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700435 *edge_angle = angles.upper_decreasing_angle;
436 LOG(INFO, "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
Brian Silvermand3efb182015-05-13 23:04:29 -0400437 name_, hall_effect_name, *edge_angle, this_sensor.posedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700438 average_last_encoder);
439 } else {
440 *edge_angle = angles.lower_angle;
441 LOG(INFO, "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
Brian Silvermand3efb182015-05-13 23:04:29 -0400442 name_, hall_effect_name, *edge_angle, this_sensor.posedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700443 average_last_encoder);
444 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400445 *edge_encoder = this_sensor.posedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700446 found_edge = true;
447 }
448 }
449
450 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
451 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
452 LOG(WARNING, "%s: Uncertain which side, rejecting negedge\n", name_);
453 } else {
454 const double average_last_encoder =
455 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400456 if (this_sensor.negedge_value() > average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700457 *edge_angle = angles.upper_angle;
458 LOG(INFO, "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
Brian Silvermand3efb182015-05-13 23:04:29 -0400459 name_, hall_effect_name, *edge_angle, this_sensor.negedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700460 average_last_encoder);
461 } else {
462 *edge_angle = angles.lower_decreasing_angle;
463 LOG(INFO, "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
Brian Silvermand3efb182015-05-13 23:04:29 -0400464 name_, hall_effect_name, *edge_angle, this_sensor.negedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700465 average_last_encoder);
466 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400467 *edge_encoder = this_sensor.negedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700468 found_edge = true;
469 }
470 }
471
472 return found_edge;
473}
474
475bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
476 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
477 double *edge_angle) {
478 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
479 calibration_, back_, "front")) {
480 return true;
481 }
482 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
483 calibration_, front_, back_, "calibration")) {
484 return true;
485 }
486 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
487 calibration_, front_, "back")) {
488 return true;
489 }
490 return false;
491}
492
493void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
494 double edge_angle) {
495 double old_offset = offset_;
496 offset_ = edge_angle - edge_encoder;
497 const double doffset = offset_ - old_offset;
498 motor_->ChangeTopOffset(doffset);
499}
500
501double TopZeroedStateFeedbackLoop::ComputeCalibrationChange(double edge_encoder,
502 double edge_angle) {
503 const double offset = edge_angle - edge_encoder;
504 const double doffset = offset - offset_;
505 return doffset;
506}
507
508void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
509 double edge_angle) {
510 double old_offset = offset_;
511 offset_ = edge_angle - edge_encoder;
512 const double doffset = offset_ - old_offset;
513 motor_->ChangeBottomOffset(doffset);
514}
515
516double BottomZeroedStateFeedbackLoop::ComputeCalibrationChange(
517 double edge_encoder, double edge_angle) {
518 const double offset = edge_angle - edge_encoder;
519 const double doffset = offset - offset_;
520 return doffset;
521}
522
523void ClawMotor::ChangeTopOffset(double doffset) {
524 claw_.ChangeTopOffset(doffset);
525 if (has_top_claw_goal_) {
526 top_claw_goal_ += doffset;
527 }
528}
529
530void ClawMotor::ChangeBottomOffset(double doffset) {
531 claw_.ChangeBottomOffset(doffset);
532 if (has_bottom_claw_goal_) {
533 bottom_claw_goal_ += doffset;
534 }
535}
536
537void ClawLimitedLoop::ChangeTopOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700538 mutable_X_hat()(1, 0) += doffset;
539 LOG(INFO, "Changing top offset by %f\n", doffset);
540}
541void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700542 mutable_X_hat()(0, 0) += doffset;
543 mutable_X_hat()(1, 0) -= doffset;
544 LOG(INFO, "Changing bottom offset by %f\n", doffset);
545}
546
547void LimitClawGoal(double *bottom_goal, double *top_goal,
548 const frc971::constants::Values &values) {
549 // first update position based on angle limit
550 const double separation = *top_goal - *bottom_goal;
551 if (separation > values.claw.soft_max_separation) {
552 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
553 const double dsep = (separation - values.claw.soft_max_separation) / 2.0;
554 *bottom_goal += dsep;
555 *top_goal -= dsep;
556 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
557 }
558 if (separation < values.claw.soft_min_separation) {
559 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
560 const double dsep = (separation - values.claw.soft_min_separation) / 2.0;
561 *bottom_goal += dsep;
562 *top_goal -= dsep;
563 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
564 }
565
566 // now move both goals in unison
567 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
568 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
569 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
570 *bottom_goal = values.claw.lower_claw.lower_limit;
571 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
572 }
573 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
574 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
575 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
576 *bottom_goal = values.claw.lower_claw.upper_limit;
577 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
578 }
579
580 if (*top_goal < values.claw.upper_claw.lower_limit) {
581 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
582 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
583 *top_goal = values.claw.upper_claw.lower_limit;
584 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
585 }
586 if (*top_goal > values.claw.upper_claw.upper_limit) {
587 LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
588 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
589 *top_goal = values.claw.upper_claw.upper_limit;
590 LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
591 }
592}
593
594bool ClawMotor::is_ready() const {
595 return (
596 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
597 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
598 (((::aos::joystick_state.get() == NULL)
599 ? true
600 : ::aos::joystick_state->autonomous) &&
601 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
602 top_claw_.zeroing_state() ==
603 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
604 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
605 bottom_claw_.zeroing_state() ==
606 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
607}
608
609bool ClawMotor::is_zeroing() const { return !is_ready(); }
610
611// Positive angle is up, and positive power is up.
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400612void ClawMotor::RunIteration(const control_loops::ClawQueue::Goal *goal,
613 const control_loops::ClawQueue::Position *position,
614 control_loops::ClawQueue::Output *output,
615 control_loops::ClawQueue::Status *status) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700616 constexpr double dt = 0.01;
617
618 // Disable the motors now so that all early returns will return with the
619 // motors disabled.
620 if (output) {
621 output->top_claw_voltage = 0;
622 output->bottom_claw_voltage = 0;
623 output->intake_voltage = 0;
624 output->tusk_voltage = 0;
625 }
626
627 if (goal) {
628 if (::std::isnan(goal->bottom_angle) ||
629 ::std::isnan(goal->separation_angle) || ::std::isnan(goal->intake) ||
630 ::std::isnan(goal->centering)) {
631 return;
632 }
633 }
634
635 if (WasReset()) {
636 top_claw_.Reset(position->top);
637 bottom_claw_.Reset(position->bottom);
638 }
639
640 const frc971::constants::Values &values = constants::GetValues();
641
642 if (position) {
643 Eigen::Matrix<double, 2, 1> Y;
644 Y << position->bottom.position + bottom_claw_.offset(),
645 position->top.position + top_claw_.offset();
646 claw_.Correct(Y);
647
648 top_claw_.SetPositionValues(position->top);
649 bottom_claw_.SetPositionValues(position->bottom);
650
651 if (!has_top_claw_goal_) {
652 has_top_claw_goal_ = true;
653 top_claw_goal_ = top_claw_.absolute_position();
654 initial_separation_ =
655 top_claw_.absolute_position() - bottom_claw_.absolute_position();
656 }
657 if (!has_bottom_claw_goal_) {
658 has_bottom_claw_goal_ = true;
659 bottom_claw_goal_ = bottom_claw_.absolute_position();
660 initial_separation_ =
661 top_claw_.absolute_position() - bottom_claw_.absolute_position();
662 }
663 LOG_STRUCT(DEBUG, "absolute position",
664 ClawPositionToLog(top_claw_.absolute_position(),
665 bottom_claw_.absolute_position()));
666 }
667
668 bool autonomous, enabled;
669 if (::aos::joystick_state.get() == nullptr) {
670 autonomous = true;
671 enabled = false;
672 } else {
673 autonomous = ::aos::joystick_state->autonomous;
674 enabled = ::aos::joystick_state->enabled;
675 }
676
677 double bottom_claw_velocity_ = 0.0;
678 double top_claw_velocity_ = 0.0;
679
680 if (goal != NULL &&
681 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
682 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
683 (autonomous &&
684 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
685 top_claw_.zeroing_state() ==
686 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
687 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
688 bottom_claw_.zeroing_state() ==
689 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))))) {
690 // Ready to use the claw.
691 // Limit the goals here.
692 bottom_claw_goal_ = goal->bottom_angle;
693 top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
694 has_bottom_claw_goal_ = true;
695 has_top_claw_goal_ = true;
696 doing_calibration_fine_tune_ = false;
697 mode_ = READY;
698
699 bottom_claw_.HandleCalibrationError(values.claw.lower_claw);
700 top_claw_.HandleCalibrationError(values.claw.upper_claw);
701 } else if (top_claw_.zeroing_state() !=
702 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
703 bottom_claw_.zeroing_state() !=
704 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
705 // Time to fine tune the zero.
706 // Limit the goals here.
707 if (!enabled) {
708 // If we are disabled, start the fine tune process over again.
709 doing_calibration_fine_tune_ = false;
710 }
711 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
712 // always get the bottom claw to calibrated first
713 LOG(DEBUG, "Calibrating the bottom of the claw\n");
714 if (!doing_calibration_fine_tune_) {
715 if (::std::abs(bottom_absolute_position() -
716 values.claw.start_fine_tune_pos) <
717 values.claw.claw_unimportant_epsilon) {
718 doing_calibration_fine_tune_ = true;
719 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
720 top_claw_velocity_ = bottom_claw_velocity_ =
721 values.claw.claw_zeroing_speed;
722 LOG(DEBUG, "Ready to fine tune the bottom\n");
723 mode_ = FINE_TUNE_BOTTOM;
724 } else {
725 // send bottom to zeroing start
726 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
727 LOG(DEBUG, "Going to the start position for the bottom\n");
728 mode_ = PREP_FINE_TUNE_BOTTOM;
729 }
730 } else {
731 mode_ = FINE_TUNE_BOTTOM;
732 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
733 top_claw_velocity_ = bottom_claw_velocity_ =
734 values.claw.claw_zeroing_speed;
735 if (top_claw_.front_or_back_triggered() ||
736 bottom_claw_.front_or_back_triggered()) {
737 // We shouldn't hit a limit, but if we do, go back to the zeroing
738 // point and try again.
739 doing_calibration_fine_tune_ = false;
740 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
741 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
742 LOG(DEBUG, "Found a limit, starting over.\n");
743 mode_ = PREP_FINE_TUNE_BOTTOM;
744 }
745
746 if (position && bottom_claw_.SawFilteredPosedge(
747 bottom_claw_.calibration(), bottom_claw_.front(),
748 bottom_claw_.back())) {
749 // do calibration
750 bottom_claw_.SetCalibration(
Brian Silvermand3efb182015-05-13 23:04:29 -0400751 position->bottom.calibration.posedge_value,
Brian Silverman17f503e2015-08-02 18:17:18 -0700752 values.claw.lower_claw.calibration.lower_angle);
753 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
754 // calibrated so we are done fine tuning bottom
755 doing_calibration_fine_tune_ = false;
756 LOG(DEBUG, "Calibrated the bottom correctly!\n");
757 } else if (bottom_claw_.calibration().last_value()) {
758 LOG(DEBUG, "Aborting bottom fine tune because sensor triggered\n");
759 doing_calibration_fine_tune_ = false;
760 bottom_claw_.set_zeroing_state(
761 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
762 } else {
763 LOG(DEBUG, "Fine tuning\n");
764 }
765 }
766 // now set the top claw to track
767
768 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
769 } else {
770 // bottom claw must be calibrated, start on the top
771 if (!doing_calibration_fine_tune_) {
772 if (::std::abs(top_absolute_position() -
773 values.claw.start_fine_tune_pos) <
774 values.claw.claw_unimportant_epsilon) {
775 doing_calibration_fine_tune_ = true;
776 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
777 top_claw_velocity_ = bottom_claw_velocity_ =
778 values.claw.claw_zeroing_speed;
779 LOG(DEBUG, "Ready to fine tune the top\n");
780 mode_ = FINE_TUNE_TOP;
781 } else {
782 // send top to zeroing start
783 top_claw_goal_ = values.claw.start_fine_tune_pos;
784 LOG(DEBUG, "Going to the start position for the top\n");
785 mode_ = PREP_FINE_TUNE_TOP;
786 }
787 } else {
788 mode_ = FINE_TUNE_TOP;
789 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
790 top_claw_velocity_ = bottom_claw_velocity_ =
791 values.claw.claw_zeroing_speed;
792 if (top_claw_.front_or_back_triggered() ||
793 bottom_claw_.front_or_back_triggered()) {
794 // this should not happen, but now we know it won't
795 doing_calibration_fine_tune_ = false;
796 top_claw_goal_ = values.claw.start_fine_tune_pos;
797 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
798 LOG(DEBUG, "Found a limit, starting over.\n");
799 mode_ = PREP_FINE_TUNE_TOP;
800 }
801
802 if (position &&
803 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
804 top_claw_.front(), top_claw_.back())) {
805 // do calibration
806 top_claw_.SetCalibration(
Brian Silvermand3efb182015-05-13 23:04:29 -0400807 position->top.calibration.posedge_value,
Brian Silverman17f503e2015-08-02 18:17:18 -0700808 values.claw.upper_claw.calibration.lower_angle);
809 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
810 // calibrated so we are done fine tuning top
811 doing_calibration_fine_tune_ = false;
812 LOG(DEBUG, "Calibrated the top correctly!\n");
813 } else if (top_claw_.calibration().last_value()) {
814 LOG(DEBUG, "Aborting top fine tune because sensor triggered\n");
815 doing_calibration_fine_tune_ = false;
816 top_claw_.set_zeroing_state(
817 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
818 }
819 }
820 // now set the bottom claw to track
821 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
822 }
823 } else {
824 doing_calibration_fine_tune_ = false;
825 if (!was_enabled_ && enabled) {
826 if (position) {
827 top_claw_goal_ = position->top.position + top_claw_.offset();
828 bottom_claw_goal_ = position->bottom.position + bottom_claw_.offset();
829 initial_separation_ =
830 position->top.position - position->bottom.position;
831 } else {
832 has_top_claw_goal_ = false;
833 has_bottom_claw_goal_ = false;
834 }
835 }
836
837 if ((bottom_claw_.zeroing_state() !=
838 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
839 bottom_claw_.front().value() || top_claw_.front().value()) &&
840 !top_claw_.back().value() && !bottom_claw_.back().value()) {
841 if (enabled) {
842 // Time to slowly move back up to find any position to narrow down the
843 // zero.
844 top_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
845 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
846 top_claw_velocity_ = bottom_claw_velocity_ =
847 values.claw.claw_zeroing_off_speed;
848 LOG(DEBUG, "Bottom is known.\n");
849 }
850 } else {
851 // We don't know where either claw is. Slowly start moving down to find
852 // any hall effect.
853 if (enabled) {
854 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
855 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
856 top_claw_velocity_ = bottom_claw_velocity_ =
857 -values.claw.claw_zeroing_off_speed;
858 LOG(DEBUG, "Both are unknown.\n");
859 }
860 }
861
862 if (position) {
863 if (enabled) {
864 top_claw_.SetCalibrationOnEdge(
865 values.claw.upper_claw,
866 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
867 bottom_claw_.SetCalibrationOnEdge(
868 values.claw.lower_claw,
869 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
870 } else {
871 // TODO(austin): Only calibrate on the predetermined edge.
872 // We might be able to just ignore this since the backlash is soooo
873 // low.
874 // :)
875 top_claw_.SetCalibrationOnEdge(
876 values.claw.upper_claw,
877 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
878 bottom_claw_.SetCalibrationOnEdge(
879 values.claw.lower_claw,
880 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
881 }
882 }
883 mode_ = UNKNOWN_LOCATION;
884 }
885
886 // Limit the goals if both claws have been (mostly) found.
887 if (mode_ != UNKNOWN_LOCATION) {
888 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
889 }
890
891 claw_.set_positions_known(
892 top_claw_.zeroing_state() != ZeroedStateFeedbackLoop::UNKNOWN_POSITION,
893 bottom_claw_.zeroing_state() !=
894 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
895 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
896 claw_.mutable_R() << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
897 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
898 LOG_MATRIX(DEBUG, "actual goal", claw_.R());
899
900 // Only cap power when one of the halves of the claw is moving slowly and
901 // could wind up.
902 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
903 mode_ == FINE_TUNE_BOTTOM);
904 claw_.Update(output == nullptr);
905 } else {
906 claw_.Update(true);
907 }
908
909 capped_goal_ = false;
910 switch (mode_) {
911 case READY:
912 case PREP_FINE_TUNE_TOP:
913 case PREP_FINE_TUNE_BOTTOM:
914 break;
915 case FINE_TUNE_BOTTOM:
916 case FINE_TUNE_TOP:
917 case UNKNOWN_LOCATION: {
918 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
919 double dx_bot = (claw_.U_uncapped(0, 0) -
920 values.claw.max_zeroing_voltage) /
921 claw_.K(0, 0);
922 double dx_top = (claw_.U_uncapped(1, 0) -
923 values.claw.max_zeroing_voltage) /
924 claw_.K(0, 0);
925 double dx = ::std::max(dx_top, dx_bot);
926 bottom_claw_goal_ -= dx;
927 top_claw_goal_ -= dx;
928 Eigen::Matrix<double, 4, 1> R;
929 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0),
930 claw_.R(3, 0);
931 claw_.mutable_U() = claw_.K() * (R - claw_.X_hat());
932 capped_goal_ = true;
933 LOG(DEBUG, "Moving the goal by %f to prevent windup."
934 " Uncapped is %f, max is %f, difference is %f\n",
935 dx,
936 claw_.uncapped_average_voltage(), values.claw.max_zeroing_voltage,
937 (claw_.uncapped_average_voltage() -
938 values.claw.max_zeroing_voltage));
939 } else if (claw_.uncapped_average_voltage() <
940 -values.claw.max_zeroing_voltage) {
941 double dx_bot = (claw_.U_uncapped(0, 0) +
942 values.claw.max_zeroing_voltage) /
943 claw_.K(0, 0);
944 double dx_top = (claw_.U_uncapped(1, 0) +
945 values.claw.max_zeroing_voltage) /
946 claw_.K(0, 0);
947 double dx = ::std::min(dx_top, dx_bot);
948 bottom_claw_goal_ -= dx;
949 top_claw_goal_ -= dx;
950 Eigen::Matrix<double, 4, 1> R;
951 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0),
952 claw_.R(3, 0);
953 claw_.mutable_U() = claw_.K() * (R - claw_.X_hat());
954 capped_goal_ = true;
955 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
956 }
957 } break;
958 }
959
960 if (output) {
961 if (goal) {
962 //setup the intake
963 output->intake_voltage =
964 (goal->intake > 12.0) ? 12 : (goal->intake < -12.0) ? -12.0
965 : goal->intake;
966 output->tusk_voltage = goal->centering;
967 output->tusk_voltage =
968 (goal->centering > 12.0) ? 12 : (goal->centering < -12.0)
969 ? -12.0
970 : goal->centering;
971 }
972 output->top_claw_voltage = claw_.U(1, 0);
973 output->bottom_claw_voltage = claw_.U(0, 0);
974
975 if (output->top_claw_voltage > kMaxVoltage) {
976 output->top_claw_voltage = kMaxVoltage;
977 } else if (output->top_claw_voltage < -kMaxVoltage) {
978 output->top_claw_voltage = -kMaxVoltage;
979 }
980
981 if (output->bottom_claw_voltage > kMaxVoltage) {
982 output->bottom_claw_voltage = kMaxVoltage;
983 } else if (output->bottom_claw_voltage < -kMaxVoltage) {
984 output->bottom_claw_voltage = -kMaxVoltage;
985 }
986 }
987
988 status->bottom = bottom_absolute_position();
989 status->separation = top_absolute_position() - bottom_absolute_position();
990 status->bottom_velocity = claw_.X_hat(2, 0);
991 status->separation_velocity = claw_.X_hat(3, 0);
992
993 if (goal) {
994 bool bottom_done =
995 ::std::abs(bottom_absolute_position() - goal->bottom_angle) < 0.020;
996 bool bottom_velocity_done = ::std::abs(status->bottom_velocity) < 0.2;
997 bool separation_done =
998 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
999 goal->separation_angle) < 0.020;
1000 bool separation_done_with_ball =
1001 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
1002 goal->separation_angle) < 0.06;
1003 status->done = is_ready() && separation_done && bottom_done && bottom_velocity_done;
1004 status->done_with_ball =
1005 is_ready() && separation_done_with_ball && bottom_done && bottom_velocity_done;
1006 } else {
1007 status->done = status->done_with_ball = false;
1008 }
1009
1010 status->zeroed = is_ready();
1011 status->zeroed_for_auto =
1012 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
1013 top_claw_.zeroing_state() ==
1014 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
1015 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
1016 bottom_claw_.zeroing_state() ==
1017 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
1018
1019 was_enabled_ = enabled;
1020}
1021
1022} // namespace control_loops
1023} // namespace frc971
1024