blob: 14dbf11c9014a78ff296b4127a0cc0a29230b5a7 [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#include "y2014/control_loops/claw/claw.h"
2
3#include <algorithm>
4
John Park33858a32018-09-28 23:05:48 -07005#include "aos/controls/control_loops.q.h"
6#include "aos/logging/logging.h"
7#include "aos/logging/queue_logging.h"
8#include "aos/logging/matrix_logging.h"
9#include "aos/commonmath.h"
Brian Silverman17f503e2015-08-02 18:17:18 -070010
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;
Brian Silvermanb601d892015-12-20 18:24:38 -050053using ::y2014::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) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070097 AOS_LOG_MATRIX(DEBUG, "U at start", U());
Brian Silverman17f503e2015-08-02 18:17:18 -070098 // 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;
Austin Schuh32501832017-02-25 18:32:56 -0800109 position_K << controller().K(0, 0), controller().K(0, 1),
110 controller().K(1, 0), controller().K(1, 1);
Brian Silverman17f503e2015-08-02 18:17:18 -0700111 Eigen::Matrix<double, 2, 2> velocity_K;
Austin Schuh32501832017-02-25 18:32:56 -0800112 velocity_K << controller().K(0, 2), controller().K(0, 3),
113 controller().K(1, 2), controller().K(1, 3);
Brian Silverman17f503e2015-08-02 18:17:18 -0700114
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);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700119 AOS_LOG_MATRIX(DEBUG, "error", error);
Brian Silverman17f503e2015-08-02 18:17:18 -0700120
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);
Austin Schuhce8c6cd2016-11-26 15:13:21 -0800126 const ::aos::controls::HVPolytope<2, 4, 4> hv_pos_poly(
127 pos_poly_H, pos_poly_k, pos_poly.Vertices());
Brian Silverman17f503e2015-08-02 18:17:18 -0700128
129 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
130 {
131 const auto &P = position_error;
132
133 // This line was at 45 degrees but is now at some angle steeper than the
134 // straight one between the points.
135 Eigen::Matrix<double, 1, 2> angle_45;
136 // If the top claw is above its soft upper limit, make the line actually
137 // 45 degrees to avoid smashing it into the limit in an attempt to fix the
138 // separation error faster than the bottom position one.
139 if (X_hat(0, 0) + X_hat(1, 0) >
140 constants::GetValues().claw.upper_claw.upper_limit) {
141 angle_45 << 1, 1;
142 } else {
143 // Fixing separation error half as fast as positional error works well
144 // because it means they both close evenly.
145 angle_45 << ::std::sqrt(3), 1;
146 }
147 Eigen::Matrix<double, 1, 2> L45_quadrant;
148 L45_quadrant << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
149 const auto L45 = L45_quadrant.cwiseProduct(angle_45);
150 const double w45 = 0;
151
152 Eigen::Matrix<double, 1, 2> LH;
153 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
154 LH << 0, 1;
155 } else {
156 LH << 1, 0;
157 }
158 const double wh = LH.dot(P);
159
160 Eigen::Matrix<double, 2, 2> standard;
161 standard << L45, LH;
162 Eigen::Matrix<double, 2, 1> W;
163 W << w45, wh;
164 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
165
166 bool is_inside_h;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700167 const auto adjusted_pos_error_h = DoCoerceGoal<double>(
168 hv_pos_poly, LH, wh, position_error, &is_inside_h);
Brian Silverman17f503e2015-08-02 18:17:18 -0700169 const auto adjusted_pos_error_45 =
Austin Schuhbcce26a2018-03-26 23:41:24 -0700170 DoCoerceGoal<double>(hv_pos_poly, L45, w45, intersection, nullptr);
Brian Silverman17f503e2015-08-02 18:17:18 -0700171 if (pos_poly.IsInside(intersection)) {
172 adjusted_pos_error = adjusted_pos_error_h;
173 } else {
174 if (is_inside_h) {
175 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
176 adjusted_pos_error = adjusted_pos_error_h;
177 } else {
178 adjusted_pos_error = adjusted_pos_error_45;
179 }
180 } else {
181 adjusted_pos_error = adjusted_pos_error_45;
182 }
183 }
184 }
185
Austin Schuhf257f3c2019-10-27 21:00:43 -0700186 AOS_LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
Brian Silverman17f503e2015-08-02 18:17:18 -0700187 mutable_U() = velocity_K * velocity_error + position_K * adjusted_pos_error;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700188 AOS_LOG_MATRIX(DEBUG, "U is now", U());
Brian Silverman17f503e2015-08-02 18:17:18 -0700189
190 {
191 const auto values = constants::GetValues().claw;
192 if (top_known_) {
193 if (X_hat(0, 0) + X_hat(1, 0) > values.upper_claw.upper_limit && U(1, 0) > 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700194 AOS_LOG(WARNING, "upper claw too high and moving up\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700195 mutable_U(1, 0) = 0;
196 } else if (X_hat(0, 0) + X_hat(1, 0) < values.upper_claw.lower_limit &&
197 U(1, 0) < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700198 AOS_LOG(WARNING, "upper claw too low and moving down\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700199 mutable_U(1, 0) = 0;
200 }
201 }
202 if (bottom_known_) {
203 if (X_hat(0, 0) > values.lower_claw.upper_limit && U(0, 0) > 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700204 AOS_LOG(WARNING, "lower claw too high and moving up\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700205 mutable_U(0, 0) = 0;
206 } else if (X_hat(0, 0) < values.lower_claw.lower_limit && U(0, 0) < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700207 AOS_LOG(WARNING, "lower claw too low and moving down\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700208 mutable_U(0, 0) = 0;
209 }
210 }
211 }
212 }
213}
214
215ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
216 ClawMotor *motor)
217 : offset_(0.0),
218 name_(name),
219 motor_(motor),
220 zeroing_state_(UNKNOWN_POSITION),
Brian Silverman17f503e2015-08-02 18:17:18 -0700221 encoder_(0.0),
222 last_encoder_(0.0) {}
223
Austin Schuh24957102015-11-28 16:04:40 -0800224void ZeroedStateFeedbackLoop::SetPositionValues(
Brian Silvermanb601d892015-12-20 18:24:38 -0500225 const ::y2014::control_loops::HalfClawPosition &claw) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700226 front_.Update(claw.front);
227 calibration_.Update(claw.calibration);
228 back_.Update(claw.back);
229
230 bool any_sensor_triggered = any_triggered();
231 if (any_sensor_triggered && any_triggered_last_) {
232 // We are still on the hall effect and nothing has changed.
233 min_hall_effect_on_angle_ =
234 ::std::min(min_hall_effect_on_angle_, claw.position);
235 max_hall_effect_on_angle_ =
236 ::std::max(max_hall_effect_on_angle_, claw.position);
237 } else if (!any_sensor_triggered && !any_triggered_last_) {
238 // We are still off the hall effect and nothing has changed.
239 min_hall_effect_off_angle_ =
240 ::std::min(min_hall_effect_off_angle_, claw.position);
241 max_hall_effect_off_angle_ =
242 ::std::max(max_hall_effect_off_angle_, claw.position);
Brian Silverman17f503e2015-08-02 18:17:18 -0700243 }
244
Brian Silvermand3efb182015-05-13 23:04:29 -0400245 if (front_.is_posedge()) {
246 // Saw a posedge on the hall effect. Reset the limits.
247 min_hall_effect_on_angle_ =
248 ::std::min(claw.front.posedge_value, claw.position);
249 max_hall_effect_on_angle_ =
250 ::std::max(claw.front.posedge_value, claw.position);
251 }
252 if (calibration_.is_posedge()) {
253 // Saw a posedge on the hall effect. Reset the limits.
254 min_hall_effect_on_angle_ =
255 ::std::min(claw.calibration.posedge_value, claw.position);
256 max_hall_effect_on_angle_ =
257 ::std::max(claw.calibration.posedge_value, claw.position);
258 }
259 if (back_.is_posedge()) {
260 // Saw a posedge on the hall effect. Reset the limits.
261 min_hall_effect_on_angle_ =
262 ::std::min(claw.back.posedge_value, claw.position);
263 max_hall_effect_on_angle_ =
264 ::std::max(claw.back.posedge_value, claw.position);
265 }
266
267 if (front_.is_negedge()) {
268 // Saw a negedge on the hall effect. Reset the limits.
269 min_hall_effect_off_angle_ =
270 ::std::min(claw.front.negedge_value, claw.position);
271 max_hall_effect_off_angle_ =
272 ::std::max(claw.front.negedge_value, claw.position);
273 }
274 if (calibration_.is_negedge()) {
275 // Saw a negedge on the hall effect. Reset the limits.
276 min_hall_effect_off_angle_ =
277 ::std::min(claw.calibration.negedge_value, claw.position);
278 max_hall_effect_off_angle_ =
279 ::std::max(claw.calibration.negedge_value, claw.position);
280 }
281 if (back_.is_negedge()) {
282 // Saw a negedge on the hall effect. Reset the limits.
283 min_hall_effect_off_angle_ =
284 ::std::min(claw.back.negedge_value, claw.position);
285 max_hall_effect_off_angle_ =
286 ::std::max(claw.back.negedge_value, claw.position);
287 }
288
Brian Silverman17f503e2015-08-02 18:17:18 -0700289 last_encoder_ = encoder_;
290 if (front().value() || calibration().value() || back().value()) {
291 last_on_encoder_ = encoder_;
292 } else {
293 last_off_encoder_ = encoder_;
294 }
295 encoder_ = claw.position;
296 any_triggered_last_ = any_sensor_triggered;
297}
298
Austin Schuh24957102015-11-28 16:04:40 -0800299void ZeroedStateFeedbackLoop::Reset(
Brian Silvermanb601d892015-12-20 18:24:38 -0500300 const ::y2014::control_loops::HalfClawPosition &claw) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700301 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
302
303 front_.Reset(claw.front);
304 calibration_.Reset(claw.calibration);
305 back_.Reset(claw.back);
306 // close up the min and max edge positions as they are no longer valid and
307 // will be expanded in future iterations
308 min_hall_effect_on_angle_ = claw.position;
309 max_hall_effect_on_angle_ = claw.position;
310 min_hall_effect_off_angle_ = claw.position;
311 max_hall_effect_off_angle_ = claw.position;
312 any_triggered_last_ = any_triggered();
313}
314
315bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
316 const constants::Values::Claws::Claw &claw_values,
317 JointZeroingState zeroing_state) {
318 double edge_encoder;
319 double edge_angle;
320 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700321 AOS_LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
Brian Silverman17f503e2015-08-02 18:17:18 -0700322 SetCalibration(edge_encoder, edge_angle);
323 set_zeroing_state(zeroing_state);
324 return true;
325 }
326 return false;
327}
328
329void TopZeroedStateFeedbackLoop::HandleCalibrationError(
330 const constants::Values::Claws::Claw &claw_values) {
331 double edge_encoder;
332 double edge_angle;
333 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
334 const double calibration_error =
335 ComputeCalibrationChange(edge_encoder, edge_angle);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700336 AOS_LOG(INFO, "Top calibration error is %f\n", calibration_error);
Brian Silverman17f503e2015-08-02 18:17:18 -0700337 if (::std::abs(calibration_error) > kRezeroThreshold) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700338 AOS_LOG(WARNING, "rezeroing top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700339 SetCalibration(edge_encoder, edge_angle);
340 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
341 }
342 }
343}
344
345
346void BottomZeroedStateFeedbackLoop::HandleCalibrationError(
347 const constants::Values::Claws::Claw &claw_values) {
348 double edge_encoder;
349 double edge_angle;
350 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
351 const double calibration_error =
352 ComputeCalibrationChange(edge_encoder, edge_angle);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700353 AOS_LOG(INFO, "Bottom calibration error is %f\n", calibration_error);
Brian Silverman17f503e2015-08-02 18:17:18 -0700354 if (::std::abs(calibration_error) > kRezeroThreshold) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700355 AOS_LOG(WARNING, "rezeroing bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700356 SetCalibration(edge_encoder, edge_angle);
357 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
358 }
359 }
360}
361
362bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
363 const constants::Values::Claws::Claw &claw_values,
364 JointZeroingState zeroing_state) {
365 double edge_encoder;
366 double edge_angle;
367 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700368 AOS_LOG(INFO, "Calibration edge.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700369 SetCalibration(edge_encoder, edge_angle);
370 set_zeroing_state(zeroing_state);
371 return true;
372 }
373 return false;
374}
375
Austin Schuh55a13dc2019-01-27 22:39:03 -0800376ClawMotor::ClawMotor(::aos::EventLoop *event_loop, const ::std::string &name)
377 : aos::controls::ControlLoop<::y2014::control_loops::ClawQueue>(event_loop,
378 name),
Brian Silverman17f503e2015-08-02 18:17:18 -0700379 has_top_claw_goal_(false),
380 top_claw_goal_(0.0),
381 top_claw_(this),
382 has_bottom_claw_goal_(false),
383 bottom_claw_goal_(0.0),
384 bottom_claw_(this),
Austin Schuhedc317c2015-11-08 14:07:42 -0800385 claw_(::y2014::control_loops::claw::MakeClawLoop()),
Brian Silverman17f503e2015-08-02 18:17:18 -0700386 was_enabled_(false),
387 doing_calibration_fine_tune_(false),
388 capped_goal_(false),
389 mode_(UNKNOWN_LOCATION) {}
390
391const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
392
393bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
394 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
395 const HallEffectTracker &sensorB) {
396 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
397 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
398 this_sensor.value() && !this_sensor.last_value()) {
399 posedge_filter_ = &this_sensor;
400 } else if (posedge_filter_ == &this_sensor &&
401 !this_sensor.posedge_count_changed() &&
402 !sensorA.posedge_count_changed() &&
403 !sensorB.posedge_count_changed() && this_sensor.value()) {
404 posedge_filter_ = nullptr;
405 return true;
406 } else if (posedge_filter_ == &this_sensor) {
407 posedge_filter_ = nullptr;
408 }
409 return false;
410}
411
412bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
413 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
414 const HallEffectTracker &sensorB) {
415 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
416 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
417 !this_sensor.value() && this_sensor.last_value()) {
418 negedge_filter_ = &this_sensor;
419 } else if (negedge_filter_ == &this_sensor &&
420 !this_sensor.negedge_count_changed() &&
421 !sensorA.negedge_count_changed() &&
422 !sensorB.negedge_count_changed() && !this_sensor.value()) {
423 negedge_filter_ = nullptr;
424 return true;
425 } else if (negedge_filter_ == &this_sensor) {
426 negedge_filter_ = nullptr;
427 }
428 return false;
429}
430
431bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
432 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
433 double *edge_angle, const HallEffectTracker &this_sensor,
434 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
435 const char *hall_effect_name) {
436 bool found_edge = false;
437
438 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
439 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700440 AOS_LOG(WARNING, "%s: Uncertain which side, rejecting posedge\n", name_);
Brian Silverman17f503e2015-08-02 18:17:18 -0700441 } else {
442 const double average_last_encoder =
443 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400444 if (this_sensor.posedge_value() < average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700445 *edge_angle = angles.upper_decreasing_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700446 AOS_LOG(INFO,
447 "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
448 name_, hall_effect_name, *edge_angle,
449 this_sensor.posedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700450 } else {
451 *edge_angle = angles.lower_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700452 AOS_LOG(INFO,
453 "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
454 name_, hall_effect_name, *edge_angle,
455 this_sensor.posedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700456 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400457 *edge_encoder = this_sensor.posedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700458 found_edge = true;
459 }
460 }
461
462 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
463 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700464 AOS_LOG(WARNING, "%s: Uncertain which side, rejecting negedge\n", name_);
Brian Silverman17f503e2015-08-02 18:17:18 -0700465 } else {
466 const double average_last_encoder =
467 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400468 if (this_sensor.negedge_value() > average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700469 *edge_angle = angles.upper_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700470 AOS_LOG(INFO,
471 "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
472 name_, hall_effect_name, *edge_angle,
473 this_sensor.negedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700474 } else {
475 *edge_angle = angles.lower_decreasing_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700476 AOS_LOG(INFO,
477 "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
478 name_, hall_effect_name, *edge_angle,
479 this_sensor.negedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700480 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400481 *edge_encoder = this_sensor.negedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700482 found_edge = true;
483 }
484 }
485
486 return found_edge;
487}
488
489bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
490 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
491 double *edge_angle) {
492 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
493 calibration_, back_, "front")) {
494 return true;
495 }
496 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
497 calibration_, front_, back_, "calibration")) {
498 return true;
499 }
500 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
501 calibration_, front_, "back")) {
502 return true;
503 }
504 return false;
505}
506
507void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
508 double edge_angle) {
509 double old_offset = offset_;
510 offset_ = edge_angle - edge_encoder;
511 const double doffset = offset_ - old_offset;
512 motor_->ChangeTopOffset(doffset);
513}
514
515double TopZeroedStateFeedbackLoop::ComputeCalibrationChange(double edge_encoder,
516 double edge_angle) {
517 const double offset = edge_angle - edge_encoder;
518 const double doffset = offset - offset_;
519 return doffset;
520}
521
522void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
523 double edge_angle) {
524 double old_offset = offset_;
525 offset_ = edge_angle - edge_encoder;
526 const double doffset = offset_ - old_offset;
527 motor_->ChangeBottomOffset(doffset);
528}
529
530double BottomZeroedStateFeedbackLoop::ComputeCalibrationChange(
531 double edge_encoder, double edge_angle) {
532 const double offset = edge_angle - edge_encoder;
533 const double doffset = offset - offset_;
534 return doffset;
535}
536
537void ClawMotor::ChangeTopOffset(double doffset) {
538 claw_.ChangeTopOffset(doffset);
539 if (has_top_claw_goal_) {
540 top_claw_goal_ += doffset;
541 }
542}
543
544void ClawMotor::ChangeBottomOffset(double doffset) {
545 claw_.ChangeBottomOffset(doffset);
546 if (has_bottom_claw_goal_) {
547 bottom_claw_goal_ += doffset;
548 }
549}
550
551void ClawLimitedLoop::ChangeTopOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700552 mutable_X_hat()(1, 0) += doffset;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700553 AOS_LOG(INFO, "Changing top offset by %f\n", doffset);
Brian Silverman17f503e2015-08-02 18:17:18 -0700554}
555void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700556 mutable_X_hat()(0, 0) += doffset;
557 mutable_X_hat()(1, 0) -= doffset;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700558 AOS_LOG(INFO, "Changing bottom offset by %f\n", doffset);
Brian Silverman17f503e2015-08-02 18:17:18 -0700559}
560
561void LimitClawGoal(double *bottom_goal, double *top_goal,
Austin Schuh24957102015-11-28 16:04:40 -0800562 const constants::Values &values) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700563 // first update position based on angle limit
564 const double separation = *top_goal - *bottom_goal;
565 if (separation > values.claw.soft_max_separation) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700566 AOS_LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700567 const double dsep = (separation - values.claw.soft_max_separation) / 2.0;
568 *bottom_goal += dsep;
569 *top_goal -= dsep;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700570 AOS_LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700571 }
572 if (separation < values.claw.soft_min_separation) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700573 AOS_LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700574 const double dsep = (separation - values.claw.soft_min_separation) / 2.0;
575 *bottom_goal += dsep;
576 *top_goal -= dsep;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700577 AOS_LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700578 }
579
580 // now move both goals in unison
581 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700582 AOS_LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700583 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
584 *bottom_goal = values.claw.lower_claw.lower_limit;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700585 AOS_LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700586 }
587 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700588 AOS_LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700589 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
590 *bottom_goal = values.claw.lower_claw.upper_limit;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700591 AOS_LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700592 }
593
594 if (*top_goal < values.claw.upper_claw.lower_limit) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700595 AOS_LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700596 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
597 *top_goal = values.claw.upper_claw.lower_limit;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700598 AOS_LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700599 }
600 if (*top_goal > values.claw.upper_claw.upper_limit) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700601 AOS_LOG_STRUCT(DEBUG, "before", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700602 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
603 *top_goal = values.claw.upper_claw.upper_limit;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700604 AOS_LOG_STRUCT(DEBUG, "after", ClawPositionToLog(*top_goal, *bottom_goal));
Brian Silverman17f503e2015-08-02 18:17:18 -0700605 }
606}
607
608bool ClawMotor::is_ready() const {
609 return (
610 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
611 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
Austin Schuheeec74a2019-01-27 20:58:59 -0800612 ((has_joystick_state() ? joystick_state().autonomous : true) &&
Brian Silverman17f503e2015-08-02 18:17:18 -0700613 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
614 top_claw_.zeroing_state() ==
615 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
616 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
617 bottom_claw_.zeroing_state() ==
618 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
619}
620
621bool ClawMotor::is_zeroing() const { return !is_ready(); }
622
623// Positive angle is up, and positive power is up.
Austin Schuh24957102015-11-28 16:04:40 -0800624void ClawMotor::RunIteration(
Brian Silvermanb601d892015-12-20 18:24:38 -0500625 const ::y2014::control_loops::ClawQueue::Goal *goal,
626 const ::y2014::control_loops::ClawQueue::Position *position,
627 ::y2014::control_loops::ClawQueue::Output *output,
628 ::y2014::control_loops::ClawQueue::Status *status) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700629 // Disable the motors now so that all early returns will return with the
630 // motors disabled.
631 if (output) {
632 output->top_claw_voltage = 0;
633 output->bottom_claw_voltage = 0;
634 output->intake_voltage = 0;
635 output->tusk_voltage = 0;
636 }
637
638 if (goal) {
639 if (::std::isnan(goal->bottom_angle) ||
640 ::std::isnan(goal->separation_angle) || ::std::isnan(goal->intake) ||
641 ::std::isnan(goal->centering)) {
642 return;
643 }
644 }
645
646 if (WasReset()) {
647 top_claw_.Reset(position->top);
648 bottom_claw_.Reset(position->bottom);
649 }
650
Austin Schuh24957102015-11-28 16:04:40 -0800651 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700652
653 if (position) {
654 Eigen::Matrix<double, 2, 1> Y;
655 Y << position->bottom.position + bottom_claw_.offset(),
656 position->top.position + top_claw_.offset();
657 claw_.Correct(Y);
658
659 top_claw_.SetPositionValues(position->top);
660 bottom_claw_.SetPositionValues(position->bottom);
661
662 if (!has_top_claw_goal_) {
663 has_top_claw_goal_ = true;
664 top_claw_goal_ = top_claw_.absolute_position();
665 initial_separation_ =
666 top_claw_.absolute_position() - bottom_claw_.absolute_position();
667 }
668 if (!has_bottom_claw_goal_) {
669 has_bottom_claw_goal_ = true;
670 bottom_claw_goal_ = bottom_claw_.absolute_position();
671 initial_separation_ =
672 top_claw_.absolute_position() - bottom_claw_.absolute_position();
673 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700674 AOS_LOG_STRUCT(DEBUG, "absolute position",
675 ClawPositionToLog(top_claw_.absolute_position(),
676 bottom_claw_.absolute_position()));
Brian Silverman17f503e2015-08-02 18:17:18 -0700677 }
678
679 bool autonomous, enabled;
Austin Schuheeec74a2019-01-27 20:58:59 -0800680 if (has_joystick_state()) {
681 autonomous = joystick_state().autonomous;
682 enabled = joystick_state().enabled;
683 } else {
Brian Silverman17f503e2015-08-02 18:17:18 -0700684 autonomous = true;
685 enabled = false;
Brian Silverman17f503e2015-08-02 18:17:18 -0700686 }
687
688 double bottom_claw_velocity_ = 0.0;
689 double top_claw_velocity_ = 0.0;
690
691 if (goal != NULL &&
692 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
693 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
694 (autonomous &&
695 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
696 top_claw_.zeroing_state() ==
697 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
698 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
699 bottom_claw_.zeroing_state() ==
700 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))))) {
701 // Ready to use the claw.
702 // Limit the goals here.
703 bottom_claw_goal_ = goal->bottom_angle;
704 top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
705 has_bottom_claw_goal_ = true;
706 has_top_claw_goal_ = true;
707 doing_calibration_fine_tune_ = false;
708 mode_ = READY;
709
710 bottom_claw_.HandleCalibrationError(values.claw.lower_claw);
711 top_claw_.HandleCalibrationError(values.claw.upper_claw);
712 } else if (top_claw_.zeroing_state() !=
713 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
714 bottom_claw_.zeroing_state() !=
715 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
716 // Time to fine tune the zero.
717 // Limit the goals here.
718 if (!enabled) {
719 // If we are disabled, start the fine tune process over again.
720 doing_calibration_fine_tune_ = false;
721 }
722 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
723 // always get the bottom claw to calibrated first
Austin Schuhf257f3c2019-10-27 21:00:43 -0700724 AOS_LOG(DEBUG, "Calibrating the bottom of the claw\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700725 if (!doing_calibration_fine_tune_) {
726 if (::std::abs(bottom_absolute_position() -
727 values.claw.start_fine_tune_pos) <
728 values.claw.claw_unimportant_epsilon) {
729 doing_calibration_fine_tune_ = true;
Austin Schuh0e997732015-11-08 15:14:53 -0800730 bottom_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700731 top_claw_velocity_ = bottom_claw_velocity_ =
732 values.claw.claw_zeroing_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700733 AOS_LOG(DEBUG, "Ready to fine tune the bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700734 mode_ = FINE_TUNE_BOTTOM;
735 } else {
736 // send bottom to zeroing start
737 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700738 AOS_LOG(DEBUG, "Going to the start position for the bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700739 mode_ = PREP_FINE_TUNE_BOTTOM;
740 }
741 } else {
742 mode_ = FINE_TUNE_BOTTOM;
Austin Schuh0e997732015-11-08 15:14:53 -0800743 bottom_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700744 top_claw_velocity_ = bottom_claw_velocity_ =
745 values.claw.claw_zeroing_speed;
746 if (top_claw_.front_or_back_triggered() ||
747 bottom_claw_.front_or_back_triggered()) {
748 // We shouldn't hit a limit, but if we do, go back to the zeroing
749 // point and try again.
750 doing_calibration_fine_tune_ = false;
751 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
752 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700753 AOS_LOG(DEBUG, "Found a limit, starting over.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700754 mode_ = PREP_FINE_TUNE_BOTTOM;
755 }
756
757 if (position && bottom_claw_.SawFilteredPosedge(
758 bottom_claw_.calibration(), bottom_claw_.front(),
759 bottom_claw_.back())) {
760 // do calibration
761 bottom_claw_.SetCalibration(
Brian Silvermand3efb182015-05-13 23:04:29 -0400762 position->bottom.calibration.posedge_value,
Brian Silverman17f503e2015-08-02 18:17:18 -0700763 values.claw.lower_claw.calibration.lower_angle);
764 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
765 // calibrated so we are done fine tuning bottom
766 doing_calibration_fine_tune_ = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700767 AOS_LOG(DEBUG, "Calibrated the bottom correctly!\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700768 } else if (bottom_claw_.calibration().last_value()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700769 AOS_LOG(DEBUG,
770 "Aborting bottom fine tune because sensor triggered\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700771 doing_calibration_fine_tune_ = false;
772 bottom_claw_.set_zeroing_state(
773 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
774 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700775 AOS_LOG(DEBUG, "Fine tuning\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700776 }
777 }
778 // now set the top claw to track
779
780 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
781 } else {
782 // bottom claw must be calibrated, start on the top
783 if (!doing_calibration_fine_tune_) {
784 if (::std::abs(top_absolute_position() -
785 values.claw.start_fine_tune_pos) <
786 values.claw.claw_unimportant_epsilon) {
787 doing_calibration_fine_tune_ = true;
Austin Schuh0e997732015-11-08 15:14:53 -0800788 top_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700789 top_claw_velocity_ = bottom_claw_velocity_ =
790 values.claw.claw_zeroing_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700791 AOS_LOG(DEBUG, "Ready to fine tune the top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700792 mode_ = FINE_TUNE_TOP;
793 } else {
794 // send top to zeroing start
795 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700796 AOS_LOG(DEBUG, "Going to the start position for the top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700797 mode_ = PREP_FINE_TUNE_TOP;
798 }
799 } else {
800 mode_ = FINE_TUNE_TOP;
Austin Schuh0e997732015-11-08 15:14:53 -0800801 top_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700802 top_claw_velocity_ = bottom_claw_velocity_ =
803 values.claw.claw_zeroing_speed;
804 if (top_claw_.front_or_back_triggered() ||
805 bottom_claw_.front_or_back_triggered()) {
806 // this should not happen, but now we know it won't
807 doing_calibration_fine_tune_ = false;
808 top_claw_goal_ = values.claw.start_fine_tune_pos;
809 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700810 AOS_LOG(DEBUG, "Found a limit, starting over.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700811 mode_ = PREP_FINE_TUNE_TOP;
812 }
813
814 if (position &&
815 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
816 top_claw_.front(), top_claw_.back())) {
817 // do calibration
818 top_claw_.SetCalibration(
Brian Silvermand3efb182015-05-13 23:04:29 -0400819 position->top.calibration.posedge_value,
Brian Silverman17f503e2015-08-02 18:17:18 -0700820 values.claw.upper_claw.calibration.lower_angle);
821 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
822 // calibrated so we are done fine tuning top
823 doing_calibration_fine_tune_ = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700824 AOS_LOG(DEBUG, "Calibrated the top correctly!\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700825 } else if (top_claw_.calibration().last_value()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700826 AOS_LOG(DEBUG, "Aborting top fine tune because sensor triggered\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700827 doing_calibration_fine_tune_ = false;
828 top_claw_.set_zeroing_state(
829 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
830 }
831 }
832 // now set the bottom claw to track
833 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
834 }
835 } else {
836 doing_calibration_fine_tune_ = false;
837 if (!was_enabled_ && enabled) {
838 if (position) {
839 top_claw_goal_ = position->top.position + top_claw_.offset();
840 bottom_claw_goal_ = position->bottom.position + bottom_claw_.offset();
841 initial_separation_ =
842 position->top.position - position->bottom.position;
843 } else {
844 has_top_claw_goal_ = false;
845 has_bottom_claw_goal_ = false;
846 }
847 }
848
849 if ((bottom_claw_.zeroing_state() !=
850 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
851 bottom_claw_.front().value() || top_claw_.front().value()) &&
852 !top_claw_.back().value() && !bottom_claw_.back().value()) {
853 if (enabled) {
854 // Time to slowly move back up to find any position to narrow down the
855 // zero.
Austin Schuh0e997732015-11-08 15:14:53 -0800856 top_claw_goal_ += values.claw.claw_zeroing_off_speed * kDt;
857 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700858 top_claw_velocity_ = bottom_claw_velocity_ =
859 values.claw.claw_zeroing_off_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700860 AOS_LOG(DEBUG, "Bottom is known.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700861 }
862 } else {
863 // We don't know where either claw is. Slowly start moving down to find
864 // any hall effect.
865 if (enabled) {
Austin Schuh0e997732015-11-08 15:14:53 -0800866 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * kDt;
867 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700868 top_claw_velocity_ = bottom_claw_velocity_ =
869 -values.claw.claw_zeroing_off_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700870 AOS_LOG(DEBUG, "Both are unknown.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700871 }
872 }
873
874 if (position) {
875 if (enabled) {
876 top_claw_.SetCalibrationOnEdge(
877 values.claw.upper_claw,
878 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
879 bottom_claw_.SetCalibrationOnEdge(
880 values.claw.lower_claw,
881 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
882 } else {
883 // TODO(austin): Only calibrate on the predetermined edge.
884 // We might be able to just ignore this since the backlash is soooo
885 // low.
886 // :)
887 top_claw_.SetCalibrationOnEdge(
888 values.claw.upper_claw,
889 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
890 bottom_claw_.SetCalibrationOnEdge(
891 values.claw.lower_claw,
892 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
893 }
894 }
895 mode_ = UNKNOWN_LOCATION;
896 }
897
898 // Limit the goals if both claws have been (mostly) found.
899 if (mode_ != UNKNOWN_LOCATION) {
900 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
901 }
902
903 claw_.set_positions_known(
904 top_claw_.zeroing_state() != ZeroedStateFeedbackLoop::UNKNOWN_POSITION,
905 bottom_claw_.zeroing_state() !=
906 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
907 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
908 claw_.mutable_R() << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
909 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700910 AOS_LOG_MATRIX(DEBUG, "actual goal", claw_.R());
Brian Silverman17f503e2015-08-02 18:17:18 -0700911
912 // Only cap power when one of the halves of the claw is moving slowly and
913 // could wind up.
914 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
915 mode_ == FINE_TUNE_BOTTOM);
916 claw_.Update(output == nullptr);
917 } else {
918 claw_.Update(true);
919 }
920
921 capped_goal_ = false;
922 switch (mode_) {
923 case READY:
924 case PREP_FINE_TUNE_TOP:
925 case PREP_FINE_TUNE_BOTTOM:
926 break;
927 case FINE_TUNE_BOTTOM:
928 case FINE_TUNE_TOP:
929 case UNKNOWN_LOCATION: {
930 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
Austin Schuh32501832017-02-25 18:32:56 -0800931 double dx_bot =
932 (claw_.U_uncapped(0, 0) - values.claw.max_zeroing_voltage) /
933 claw_.controller().K(0, 0);
934 double dx_top =
935 (claw_.U_uncapped(1, 0) - values.claw.max_zeroing_voltage) /
936 claw_.controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700937 double dx = ::std::max(dx_top, dx_bot);
938 bottom_claw_goal_ -= dx;
939 top_claw_goal_ -= dx;
940 Eigen::Matrix<double, 4, 1> R;
941 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0),
942 claw_.R(3, 0);
Austin Schuh32501832017-02-25 18:32:56 -0800943 claw_.mutable_U() = claw_.controller().K() * (R - claw_.X_hat());
Brian Silverman17f503e2015-08-02 18:17:18 -0700944 capped_goal_ = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700945 AOS_LOG(DEBUG,
946 "Moving the goal by %f to prevent windup."
947 " Uncapped is %f, max is %f, difference is %f\n",
948 dx, claw_.uncapped_average_voltage(),
949 values.claw.max_zeroing_voltage,
950 (claw_.uncapped_average_voltage() -
951 values.claw.max_zeroing_voltage));
Brian Silverman17f503e2015-08-02 18:17:18 -0700952 } else if (claw_.uncapped_average_voltage() <
953 -values.claw.max_zeroing_voltage) {
Austin Schuh32501832017-02-25 18:32:56 -0800954 double dx_bot =
955 (claw_.U_uncapped(0, 0) + values.claw.max_zeroing_voltage) /
956 claw_.controller().K(0, 0);
957 double dx_top =
958 (claw_.U_uncapped(1, 0) + values.claw.max_zeroing_voltage) /
959 claw_.controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700960 double dx = ::std::min(dx_top, dx_bot);
961 bottom_claw_goal_ -= dx;
962 top_claw_goal_ -= dx;
963 Eigen::Matrix<double, 4, 1> R;
964 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0),
965 claw_.R(3, 0);
Austin Schuh32501832017-02-25 18:32:56 -0800966 claw_.mutable_U() = claw_.controller().K() * (R - claw_.X_hat());
Brian Silverman17f503e2015-08-02 18:17:18 -0700967 capped_goal_ = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700968 AOS_LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Brian Silverman17f503e2015-08-02 18:17:18 -0700969 }
970 } break;
971 }
972
973 if (output) {
974 if (goal) {
975 //setup the intake
976 output->intake_voltage =
977 (goal->intake > 12.0) ? 12 : (goal->intake < -12.0) ? -12.0
978 : goal->intake;
979 output->tusk_voltage = goal->centering;
980 output->tusk_voltage =
981 (goal->centering > 12.0) ? 12 : (goal->centering < -12.0)
982 ? -12.0
983 : goal->centering;
984 }
985 output->top_claw_voltage = claw_.U(1, 0);
986 output->bottom_claw_voltage = claw_.U(0, 0);
987
988 if (output->top_claw_voltage > kMaxVoltage) {
989 output->top_claw_voltage = kMaxVoltage;
990 } else if (output->top_claw_voltage < -kMaxVoltage) {
991 output->top_claw_voltage = -kMaxVoltage;
992 }
993
994 if (output->bottom_claw_voltage > kMaxVoltage) {
995 output->bottom_claw_voltage = kMaxVoltage;
996 } else if (output->bottom_claw_voltage < -kMaxVoltage) {
997 output->bottom_claw_voltage = -kMaxVoltage;
998 }
999 }
1000
1001 status->bottom = bottom_absolute_position();
1002 status->separation = top_absolute_position() - bottom_absolute_position();
1003 status->bottom_velocity = claw_.X_hat(2, 0);
1004 status->separation_velocity = claw_.X_hat(3, 0);
1005
1006 if (goal) {
1007 bool bottom_done =
1008 ::std::abs(bottom_absolute_position() - goal->bottom_angle) < 0.020;
1009 bool bottom_velocity_done = ::std::abs(status->bottom_velocity) < 0.2;
1010 bool separation_done =
1011 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
1012 goal->separation_angle) < 0.020;
1013 bool separation_done_with_ball =
1014 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
1015 goal->separation_angle) < 0.06;
1016 status->done = is_ready() && separation_done && bottom_done && bottom_velocity_done;
1017 status->done_with_ball =
1018 is_ready() && separation_done_with_ball && bottom_done && bottom_velocity_done;
1019 } else {
1020 status->done = status->done_with_ball = false;
1021 }
1022
1023 status->zeroed = is_ready();
1024 status->zeroed_for_auto =
1025 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
1026 top_claw_.zeroing_state() ==
1027 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
1028 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
1029 bottom_claw_.zeroing_state() ==
1030 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
1031
1032 was_enabled_ = enabled;
1033}
1034
1035} // namespace control_loops
Austin Schuh24957102015-11-28 16:04:40 -08001036} // namespace y2014