blob: 56ca6b059bb85277338d93582e06844ed7ccbd48 [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/logging/logging.h"
John Park33858a32018-09-28 23:05:48 -07006#include "aos/commonmath.h"
Brian Silverman17f503e2015-08-02 18:17:18 -07007
8#include "y2014/constants.h"
9#include "y2014/control_loops/claw/claw_motor_plant.h"
10
11// Zeroing plan.
12// There are 2 types of zeros. Enabled and disabled ones.
13// Disabled ones are only valid during auto mode, and can be used to speed up
14// the enabled zero process. We need to re-zero during teleop in case the auto
15// zero was poor and causes us to miss all our shots.
16//
17// We need to be able to zero manually while disabled by moving the joint over
18// the zeros.
19// Zero on the down edge when disabled (gravity in the direction of motion)
20//
21// When enabled, zero on the up edge (gravity opposing the direction of motion)
22// The enabled sequence needs to work as follows. We can crash the claw if we
23// bring them too close to each other or too far from each other. The only safe
24// thing to do is to move them in unison.
25//
26// Start by moving them both towards the front of the bot to either find either
27// the middle hall effect on either jaw, or the front hall effect on the bottom
28// jaw. Any edge that isn't the desired edge will provide an approximate edge
29// location that can be used for the fine tuning step.
30// Once an edge is found on the front claw, move back the other way with both
31// claws until an edge is found for the other claw.
32// Now that we have an approximate zero, we can robustify the limits to keep
33// both claws safe. Then, we can move both claws to a position that is the
34// correct side of the zero and go zero.
35
36// Valid region plan.
37// Difference between the arms has a range, and the values of each arm has a
38// range.
39// If a claw runs up against a static limit, don't let the goal change outside
40// the limit.
41// If a claw runs up against a movable limit, move both claws outwards to get
42// out of the condition.
43
Austin Schuh24957102015-11-28 16:04:40 -080044namespace y2014 {
Brian Silverman17f503e2015-08-02 18:17:18 -070045namespace control_loops {
Alex Perrycb7da4b2019-08-28 19:35:56 -070046namespace claw {
Brian Silverman17f503e2015-08-02 18:17:18 -070047
Austin Schuh24957102015-11-28 16:04:40 -080048using ::frc971::HallEffectTracker;
Austin Schuh0e997732015-11-08 15:14:53 -080049using ::y2014::control_loops::claw::kDt;
Austin Schuh24957102015-11-28 16:04:40 -080050using ::frc971::control_loops::DoCoerceGoal;
Austin Schuh0e997732015-11-08 15:14:53 -080051
Brian Silverman17f503e2015-08-02 18:17:18 -070052static const double kZeroingVoltage = 4.0;
53static const double kMaxVoltage = 12.0;
54const double kRezeroThreshold = 0.07;
55
56ClawLimitedLoop::ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> &&loop)
57 : StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
58 uncapped_average_voltage_(0.0),
59 is_zeroing_(true),
60 U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0,
61 -1, 0,
62 0, 1,
63 0, -1).finished(),
64 (Eigen::Matrix<double, 4, 1>() << kMaxVoltage, kMaxVoltage,
65 kMaxVoltage, kMaxVoltage).finished()),
66 U_Poly_zeroing_((Eigen::Matrix<double, 4, 2>() << 1, 0,
67 -1, 0,
68 0, 1,
69 0, -1).finished(),
70 (Eigen::Matrix<double, 4, 1>() <<
71 kZeroingVoltage, kZeroingVoltage,
72 kZeroingVoltage, kZeroingVoltage).finished()) {
73 ::aos::controls::HPolytope<0>::Init();
74}
75
76// Caps the voltage prioritizing reducing velocity error over reducing
77// positional error.
78// Uses the polytope libararies which we used to just use for the drivetrain.
79// Uses a region representing the maximum voltage and then transforms it such
80// that the points represent different amounts of positional error and
81// constrains the region such that, if at all possible, it will maintain its
82// current efforts to reduce velocity error.
83void ClawLimitedLoop::CapU() {
84 const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
85
86 double u_top = U(1, 0);
87 double u_bottom = U(0, 0);
88
89 uncapped_average_voltage_ = (u_top + u_bottom) / 2;
90
91 double max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
92
93 if (::std::abs(u_bottom) > max_voltage || ::std::abs(u_top) > max_voltage) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070094 VLOG(1) << "U at start " << U();
Brian Silverman17f503e2015-08-02 18:17:18 -070095 // H * U <= k
96 // U = UPos + UVel
97 // H * (UPos + UVel) <= k
98 // H * UPos <= k - H * UVel
99
100 // Now, we can do a coordinate transformation and say the following.
101
102 // UPos = position_K * position_error
103 // (H * position_K) * position_error <= k - H * UVel
104
105 Eigen::Matrix<double, 2, 2> position_K;
Austin Schuh32501832017-02-25 18:32:56 -0800106 position_K << controller().K(0, 0), controller().K(0, 1),
107 controller().K(1, 0), controller().K(1, 1);
Brian Silverman17f503e2015-08-02 18:17:18 -0700108 Eigen::Matrix<double, 2, 2> velocity_K;
Austin Schuh32501832017-02-25 18:32:56 -0800109 velocity_K << controller().K(0, 2), controller().K(0, 3),
110 controller().K(1, 2), controller().K(1, 3);
Brian Silverman17f503e2015-08-02 18:17:18 -0700111
112 Eigen::Matrix<double, 2, 1> position_error;
113 position_error << error(0, 0), error(1, 0);
114 Eigen::Matrix<double, 2, 1> velocity_error;
115 velocity_error << error(2, 0), error(3, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700116 VLOG(1) << "error " << error;
Brian Silverman17f503e2015-08-02 18:17:18 -0700117
118 const auto &poly = is_zeroing_ ? U_Poly_zeroing_ : U_Poly_;
119 const Eigen::Matrix<double, 4, 2> pos_poly_H = poly.H() * position_K;
120 const Eigen::Matrix<double, 4, 1> pos_poly_k =
121 poly.k() - poly.H() * velocity_K * velocity_error;
122 const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
Austin Schuhce8c6cd2016-11-26 15:13:21 -0800123 const ::aos::controls::HVPolytope<2, 4, 4> hv_pos_poly(
124 pos_poly_H, pos_poly_k, pos_poly.Vertices());
Brian Silverman17f503e2015-08-02 18:17:18 -0700125
126 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
127 {
128 const auto &P = position_error;
129
130 // This line was at 45 degrees but is now at some angle steeper than the
131 // straight one between the points.
132 Eigen::Matrix<double, 1, 2> angle_45;
133 // If the top claw is above its soft upper limit, make the line actually
134 // 45 degrees to avoid smashing it into the limit in an attempt to fix the
135 // separation error faster than the bottom position one.
136 if (X_hat(0, 0) + X_hat(1, 0) >
137 constants::GetValues().claw.upper_claw.upper_limit) {
138 angle_45 << 1, 1;
139 } else {
140 // Fixing separation error half as fast as positional error works well
141 // because it means they both close evenly.
142 angle_45 << ::std::sqrt(3), 1;
143 }
144 Eigen::Matrix<double, 1, 2> L45_quadrant;
145 L45_quadrant << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
146 const auto L45 = L45_quadrant.cwiseProduct(angle_45);
147 const double w45 = 0;
148
149 Eigen::Matrix<double, 1, 2> LH;
150 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
151 LH << 0, 1;
152 } else {
153 LH << 1, 0;
154 }
155 const double wh = LH.dot(P);
156
157 Eigen::Matrix<double, 2, 2> standard;
158 standard << L45, LH;
159 Eigen::Matrix<double, 2, 1> W;
160 W << w45, wh;
161 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
162
163 bool is_inside_h;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700164 const auto adjusted_pos_error_h = DoCoerceGoal<double>(
165 hv_pos_poly, LH, wh, position_error, &is_inside_h);
Brian Silverman17f503e2015-08-02 18:17:18 -0700166 const auto adjusted_pos_error_45 =
Austin Schuhbcce26a2018-03-26 23:41:24 -0700167 DoCoerceGoal<double>(hv_pos_poly, L45, w45, intersection, nullptr);
Brian Silverman17f503e2015-08-02 18:17:18 -0700168 if (pos_poly.IsInside(intersection)) {
169 adjusted_pos_error = adjusted_pos_error_h;
170 } else {
171 if (is_inside_h) {
172 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
173 adjusted_pos_error = adjusted_pos_error_h;
174 } else {
175 adjusted_pos_error = adjusted_pos_error_45;
176 }
177 } else {
178 adjusted_pos_error = adjusted_pos_error_45;
179 }
180 }
181 }
182
Alex Perrycb7da4b2019-08-28 19:35:56 -0700183 VLOG(1) << "adjusted_pos_error " << adjusted_pos_error;
Brian Silverman17f503e2015-08-02 18:17:18 -0700184 mutable_U() = velocity_K * velocity_error + position_K * adjusted_pos_error;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700185 VLOG(1) << "U is now" << U();
Brian Silverman17f503e2015-08-02 18:17:18 -0700186
187 {
188 const auto values = constants::GetValues().claw;
189 if (top_known_) {
190 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 -0700191 AOS_LOG(WARNING, "upper claw too high and moving up\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700192 mutable_U(1, 0) = 0;
193 } else if (X_hat(0, 0) + X_hat(1, 0) < values.upper_claw.lower_limit &&
194 U(1, 0) < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700195 AOS_LOG(WARNING, "upper claw too low and moving down\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700196 mutable_U(1, 0) = 0;
197 }
198 }
199 if (bottom_known_) {
200 if (X_hat(0, 0) > values.lower_claw.upper_limit && U(0, 0) > 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700201 AOS_LOG(WARNING, "lower claw too high and moving up\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700202 mutable_U(0, 0) = 0;
203 } else if (X_hat(0, 0) < values.lower_claw.lower_limit && U(0, 0) < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700204 AOS_LOG(WARNING, "lower claw too low and moving down\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700205 mutable_U(0, 0) = 0;
206 }
207 }
208 }
209 }
210}
211
212ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
213 ClawMotor *motor)
214 : offset_(0.0),
215 name_(name),
216 motor_(motor),
217 zeroing_state_(UNKNOWN_POSITION),
Brian Silverman17f503e2015-08-02 18:17:18 -0700218 encoder_(0.0),
219 last_encoder_(0.0) {}
220
Alex Perrycb7da4b2019-08-28 19:35:56 -0700221void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition *claw) {
222 front_.Update(claw->front());
223 calibration_.Update(claw->calibration());
224 back_.Update(claw->back());
Brian Silverman17f503e2015-08-02 18:17:18 -0700225
226 bool any_sensor_triggered = any_triggered();
227 if (any_sensor_triggered && any_triggered_last_) {
228 // We are still on the hall effect and nothing has changed.
229 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700230 ::std::min(min_hall_effect_on_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700231 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700232 ::std::max(max_hall_effect_on_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700233 } else if (!any_sensor_triggered && !any_triggered_last_) {
234 // We are still off the hall effect and nothing has changed.
235 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700236 ::std::min(min_hall_effect_off_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700237 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700238 ::std::max(max_hall_effect_off_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700239 }
240
Brian Silvermand3efb182015-05-13 23:04:29 -0400241 if (front_.is_posedge()) {
242 // Saw a posedge on the hall effect. Reset the limits.
243 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700244 ::std::min(claw->front()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400245 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700246 ::std::max(claw->front()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400247 }
248 if (calibration_.is_posedge()) {
249 // Saw a posedge on the hall effect. Reset the limits.
250 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700251 ::std::min(claw->calibration()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400252 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700253 ::std::max(claw->calibration()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400254 }
255 if (back_.is_posedge()) {
256 // Saw a posedge on the hall effect. Reset the limits.
257 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700258 ::std::min(claw->back()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400259 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700260 ::std::max(claw->back()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400261 }
262
263 if (front_.is_negedge()) {
264 // Saw a negedge on the hall effect. Reset the limits.
265 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700266 ::std::min(claw->front()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400267 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700268 ::std::max(claw->front()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400269 }
270 if (calibration_.is_negedge()) {
271 // Saw a negedge on the hall effect. Reset the limits.
272 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700273 ::std::min(claw->calibration()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400274 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700275 ::std::max(claw->calibration()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400276 }
277 if (back_.is_negedge()) {
278 // Saw a negedge on the hall effect. Reset the limits.
279 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700280 ::std::min(claw->back()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400281 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700282 ::std::max(claw->back()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400283 }
284
Brian Silverman17f503e2015-08-02 18:17:18 -0700285 last_encoder_ = encoder_;
286 if (front().value() || calibration().value() || back().value()) {
287 last_on_encoder_ = encoder_;
288 } else {
289 last_off_encoder_ = encoder_;
290 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700291 encoder_ = claw->position();
Brian Silverman17f503e2015-08-02 18:17:18 -0700292 any_triggered_last_ = any_sensor_triggered;
293}
294
Alex Perrycb7da4b2019-08-28 19:35:56 -0700295void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition *claw) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700296 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
297
Alex Perrycb7da4b2019-08-28 19:35:56 -0700298 front_.Reset(claw->front());
299 calibration_.Reset(claw->calibration());
300 back_.Reset(claw->back());
Brian Silverman17f503e2015-08-02 18:17:18 -0700301 // close up the min and max edge positions as they are no longer valid and
302 // will be expanded in future iterations
Alex Perrycb7da4b2019-08-28 19:35:56 -0700303 min_hall_effect_on_angle_ = claw->position();
304 max_hall_effect_on_angle_ = claw->position();
305 min_hall_effect_off_angle_ = claw->position();
306 max_hall_effect_off_angle_ = claw->position();
Brian Silverman17f503e2015-08-02 18:17:18 -0700307 any_triggered_last_ = any_triggered();
308}
309
310bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
311 const constants::Values::Claws::Claw &claw_values,
312 JointZeroingState zeroing_state) {
313 double edge_encoder;
314 double edge_angle;
315 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700316 AOS_LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
Brian Silverman17f503e2015-08-02 18:17:18 -0700317 SetCalibration(edge_encoder, edge_angle);
318 set_zeroing_state(zeroing_state);
319 return true;
320 }
321 return false;
322}
323
324void TopZeroedStateFeedbackLoop::HandleCalibrationError(
325 const constants::Values::Claws::Claw &claw_values) {
326 double edge_encoder;
327 double edge_angle;
328 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
329 const double calibration_error =
330 ComputeCalibrationChange(edge_encoder, edge_angle);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700331 AOS_LOG(INFO, "Top calibration error is %f\n", calibration_error);
Brian Silverman17f503e2015-08-02 18:17:18 -0700332 if (::std::abs(calibration_error) > kRezeroThreshold) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700333 AOS_LOG(WARNING, "rezeroing top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700334 SetCalibration(edge_encoder, edge_angle);
335 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
336 }
337 }
338}
339
340
341void BottomZeroedStateFeedbackLoop::HandleCalibrationError(
342 const constants::Values::Claws::Claw &claw_values) {
343 double edge_encoder;
344 double edge_angle;
345 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
346 const double calibration_error =
347 ComputeCalibrationChange(edge_encoder, edge_angle);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700348 AOS_LOG(INFO, "Bottom calibration error is %f\n", calibration_error);
Brian Silverman17f503e2015-08-02 18:17:18 -0700349 if (::std::abs(calibration_error) > kRezeroThreshold) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700350 AOS_LOG(WARNING, "rezeroing bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700351 SetCalibration(edge_encoder, edge_angle);
352 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
353 }
354 }
355}
356
357bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
358 const constants::Values::Claws::Claw &claw_values,
359 JointZeroingState zeroing_state) {
360 double edge_encoder;
361 double edge_angle;
362 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700363 AOS_LOG(INFO, "Calibration edge.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700364 SetCalibration(edge_encoder, edge_angle);
365 set_zeroing_state(zeroing_state);
366 return true;
367 }
368 return false;
369}
370
Austin Schuh55a13dc2019-01-27 22:39:03 -0800371ClawMotor::ClawMotor(::aos::EventLoop *event_loop, const ::std::string &name)
Alex Perrycb7da4b2019-08-28 19:35:56 -0700372 : aos::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
373 name),
Brian Silverman17f503e2015-08-02 18:17:18 -0700374 has_top_claw_goal_(false),
375 top_claw_goal_(0.0),
376 top_claw_(this),
377 has_bottom_claw_goal_(false),
378 bottom_claw_goal_(0.0),
379 bottom_claw_(this),
Austin Schuhedc317c2015-11-08 14:07:42 -0800380 claw_(::y2014::control_loops::claw::MakeClawLoop()),
Brian Silverman17f503e2015-08-02 18:17:18 -0700381 was_enabled_(false),
382 doing_calibration_fine_tune_(false),
383 capped_goal_(false),
384 mode_(UNKNOWN_LOCATION) {}
385
386const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
387
388bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
389 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
390 const HallEffectTracker &sensorB) {
391 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
392 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
393 this_sensor.value() && !this_sensor.last_value()) {
394 posedge_filter_ = &this_sensor;
395 } else if (posedge_filter_ == &this_sensor &&
396 !this_sensor.posedge_count_changed() &&
397 !sensorA.posedge_count_changed() &&
398 !sensorB.posedge_count_changed() && this_sensor.value()) {
399 posedge_filter_ = nullptr;
400 return true;
401 } else if (posedge_filter_ == &this_sensor) {
402 posedge_filter_ = nullptr;
403 }
404 return false;
405}
406
407bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
408 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
409 const HallEffectTracker &sensorB) {
410 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
411 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
412 !this_sensor.value() && this_sensor.last_value()) {
413 negedge_filter_ = &this_sensor;
414 } else if (negedge_filter_ == &this_sensor &&
415 !this_sensor.negedge_count_changed() &&
416 !sensorA.negedge_count_changed() &&
417 !sensorB.negedge_count_changed() && !this_sensor.value()) {
418 negedge_filter_ = nullptr;
419 return true;
420 } else if (negedge_filter_ == &this_sensor) {
421 negedge_filter_ = nullptr;
422 }
423 return false;
424}
425
426bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
427 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
428 double *edge_angle, const HallEffectTracker &this_sensor,
429 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
430 const char *hall_effect_name) {
431 bool found_edge = false;
432
433 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
434 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700435 AOS_LOG(WARNING, "%s: Uncertain which side, rejecting posedge\n", name_);
Brian Silverman17f503e2015-08-02 18:17:18 -0700436 } else {
437 const double average_last_encoder =
438 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400439 if (this_sensor.posedge_value() < average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700440 *edge_angle = angles.upper_decreasing_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700441 AOS_LOG(INFO,
442 "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
443 name_, hall_effect_name, *edge_angle,
444 this_sensor.posedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700445 } else {
446 *edge_angle = angles.lower_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700447 AOS_LOG(INFO,
448 "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
449 name_, hall_effect_name, *edge_angle,
450 this_sensor.posedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700451 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400452 *edge_encoder = this_sensor.posedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700453 found_edge = true;
454 }
455 }
456
457 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
458 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700459 AOS_LOG(WARNING, "%s: Uncertain which side, rejecting negedge\n", name_);
Brian Silverman17f503e2015-08-02 18:17:18 -0700460 } else {
461 const double average_last_encoder =
462 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400463 if (this_sensor.negedge_value() > average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700464 *edge_angle = angles.upper_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700465 AOS_LOG(INFO,
466 "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
467 name_, hall_effect_name, *edge_angle,
468 this_sensor.negedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700469 } else {
470 *edge_angle = angles.lower_decreasing_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700471 AOS_LOG(INFO,
472 "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
473 name_, hall_effect_name, *edge_angle,
474 this_sensor.negedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700475 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400476 *edge_encoder = this_sensor.negedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700477 found_edge = true;
478 }
479 }
480
481 return found_edge;
482}
483
484bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
485 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
486 double *edge_angle) {
487 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
488 calibration_, back_, "front")) {
489 return true;
490 }
491 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
492 calibration_, front_, back_, "calibration")) {
493 return true;
494 }
495 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
496 calibration_, front_, "back")) {
497 return true;
498 }
499 return false;
500}
501
502void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
503 double edge_angle) {
504 double old_offset = offset_;
505 offset_ = edge_angle - edge_encoder;
506 const double doffset = offset_ - old_offset;
507 motor_->ChangeTopOffset(doffset);
508}
509
510double TopZeroedStateFeedbackLoop::ComputeCalibrationChange(double edge_encoder,
511 double edge_angle) {
512 const double offset = edge_angle - edge_encoder;
513 const double doffset = offset - offset_;
514 return doffset;
515}
516
517void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
518 double edge_angle) {
519 double old_offset = offset_;
520 offset_ = edge_angle - edge_encoder;
521 const double doffset = offset_ - old_offset;
522 motor_->ChangeBottomOffset(doffset);
523}
524
525double BottomZeroedStateFeedbackLoop::ComputeCalibrationChange(
526 double edge_encoder, double edge_angle) {
527 const double offset = edge_angle - edge_encoder;
528 const double doffset = offset - offset_;
529 return doffset;
530}
531
532void ClawMotor::ChangeTopOffset(double doffset) {
533 claw_.ChangeTopOffset(doffset);
534 if (has_top_claw_goal_) {
535 top_claw_goal_ += doffset;
536 }
537}
538
539void ClawMotor::ChangeBottomOffset(double doffset) {
540 claw_.ChangeBottomOffset(doffset);
541 if (has_bottom_claw_goal_) {
542 bottom_claw_goal_ += doffset;
543 }
544}
545
546void ClawLimitedLoop::ChangeTopOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700547 mutable_X_hat()(1, 0) += doffset;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700548 AOS_LOG(INFO, "Changing top offset by %f\n", doffset);
Brian Silverman17f503e2015-08-02 18:17:18 -0700549}
550void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700551 mutable_X_hat()(0, 0) += doffset;
552 mutable_X_hat()(1, 0) -= doffset;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700553 AOS_LOG(INFO, "Changing bottom offset by %f\n", doffset);
Brian Silverman17f503e2015-08-02 18:17:18 -0700554}
555
556void LimitClawGoal(double *bottom_goal, double *top_goal,
Austin Schuh24957102015-11-28 16:04:40 -0800557 const constants::Values &values) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700558 // first update position based on angle limit
559 const double separation = *top_goal - *bottom_goal;
560 if (separation > values.claw.soft_max_separation) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700561 const double dsep = (separation - values.claw.soft_max_separation) / 2.0;
562 *bottom_goal += dsep;
563 *top_goal -= dsep;
Brian Silverman17f503e2015-08-02 18:17:18 -0700564 }
565 if (separation < values.claw.soft_min_separation) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700566 const double dsep = (separation - values.claw.soft_min_separation) / 2.0;
567 *bottom_goal += dsep;
568 *top_goal -= dsep;
Brian Silverman17f503e2015-08-02 18:17:18 -0700569 }
570
571 // now move both goals in unison
572 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700573 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
574 *bottom_goal = values.claw.lower_claw.lower_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700575 }
576 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700577 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
578 *bottom_goal = values.claw.lower_claw.upper_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700579 }
580
581 if (*top_goal < values.claw.upper_claw.lower_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700582 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
583 *top_goal = values.claw.upper_claw.lower_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700584 }
585 if (*top_goal > values.claw.upper_claw.upper_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700586 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
587 *top_goal = values.claw.upper_claw.upper_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700588 }
589}
590
591bool ClawMotor::is_ready() const {
592 return (
593 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
594 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
Alex Perrycb7da4b2019-08-28 19:35:56 -0700595 ((has_joystick_state() ? joystick_state().autonomous() : true) &&
Brian Silverman17f503e2015-08-02 18:17:18 -0700596 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
597 top_claw_.zeroing_state() ==
598 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
599 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
600 bottom_claw_.zeroing_state() ==
601 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
602}
603
604bool ClawMotor::is_zeroing() const { return !is_ready(); }
605
606// Positive angle is up, and positive power is up.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700607void ClawMotor::RunIteration(const Goal *goal, const Position *position,
608 aos::Sender<Output>::Builder *output,
609 aos::Sender<Status>::Builder *status) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700610 // Disable the motors now so that all early returns will return with the
611 // motors disabled.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700612 OutputT output_struct;
Brian Silverman17f503e2015-08-02 18:17:18 -0700613 if (output) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700614 output_struct.top_claw_voltage = 0;
615 output_struct.bottom_claw_voltage = 0;
616 output_struct.intake_voltage = 0;
617 output_struct.tusk_voltage = 0;
Brian Silverman17f503e2015-08-02 18:17:18 -0700618 }
619
Alex Perrycb7da4b2019-08-28 19:35:56 -0700620 StatusT status_struct;
Brian Silverman17f503e2015-08-02 18:17:18 -0700621 if (goal) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700622 if (::std::isnan(goal->bottom_angle()) ||
623 ::std::isnan(goal->separation_angle()) ||
624 ::std::isnan(goal->intake()) || ::std::isnan(goal->centering())) {
625 status->Send(Status::Pack(*status->fbb(), &status_struct));
626 output->Send(Output::Pack(*output->fbb(), &output_struct));
Brian Silverman17f503e2015-08-02 18:17:18 -0700627 return;
628 }
629 }
630
631 if (WasReset()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700632 top_claw_.Reset(position->top());
633 bottom_claw_.Reset(position->bottom());
Brian Silverman17f503e2015-08-02 18:17:18 -0700634 }
635
Austin Schuh24957102015-11-28 16:04:40 -0800636 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700637
638 if (position) {
639 Eigen::Matrix<double, 2, 1> Y;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700640 Y << position->bottom()->position() + bottom_claw_.offset(),
641 position->top()->position() + top_claw_.offset();
Brian Silverman17f503e2015-08-02 18:17:18 -0700642 claw_.Correct(Y);
643
Alex Perrycb7da4b2019-08-28 19:35:56 -0700644 top_claw_.SetPositionValues(position->top());
645 bottom_claw_.SetPositionValues(position->bottom());
Brian Silverman17f503e2015-08-02 18:17:18 -0700646
647 if (!has_top_claw_goal_) {
648 has_top_claw_goal_ = true;
649 top_claw_goal_ = top_claw_.absolute_position();
650 initial_separation_ =
651 top_claw_.absolute_position() - bottom_claw_.absolute_position();
652 }
653 if (!has_bottom_claw_goal_) {
654 has_bottom_claw_goal_ = true;
655 bottom_claw_goal_ = bottom_claw_.absolute_position();
656 initial_separation_ =
657 top_claw_.absolute_position() - bottom_claw_.absolute_position();
658 }
Brian Silverman17f503e2015-08-02 18:17:18 -0700659 }
660
661 bool autonomous, enabled;
Austin Schuheeec74a2019-01-27 20:58:59 -0800662 if (has_joystick_state()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700663 autonomous = joystick_state().autonomous();
664 enabled = joystick_state().enabled();
Austin Schuheeec74a2019-01-27 20:58:59 -0800665 } else {
Brian Silverman17f503e2015-08-02 18:17:18 -0700666 autonomous = true;
667 enabled = false;
Brian Silverman17f503e2015-08-02 18:17:18 -0700668 }
669
670 double bottom_claw_velocity_ = 0.0;
671 double top_claw_velocity_ = 0.0;
672
673 if (goal != NULL &&
674 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
675 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
676 (autonomous &&
677 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
678 top_claw_.zeroing_state() ==
679 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
680 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
681 bottom_claw_.zeroing_state() ==
682 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))))) {
683 // Ready to use the claw.
684 // Limit the goals here.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700685 bottom_claw_goal_ = goal->bottom_angle();
686 top_claw_goal_ = goal->bottom_angle() + goal->separation_angle();
Brian Silverman17f503e2015-08-02 18:17:18 -0700687 has_bottom_claw_goal_ = true;
688 has_top_claw_goal_ = true;
689 doing_calibration_fine_tune_ = false;
690 mode_ = READY;
691
692 bottom_claw_.HandleCalibrationError(values.claw.lower_claw);
693 top_claw_.HandleCalibrationError(values.claw.upper_claw);
694 } else if (top_claw_.zeroing_state() !=
695 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
696 bottom_claw_.zeroing_state() !=
697 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
698 // Time to fine tune the zero.
699 // Limit the goals here.
700 if (!enabled) {
701 // If we are disabled, start the fine tune process over again.
702 doing_calibration_fine_tune_ = false;
703 }
704 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
705 // always get the bottom claw to calibrated first
Austin Schuhf257f3c2019-10-27 21:00:43 -0700706 AOS_LOG(DEBUG, "Calibrating the bottom of the claw\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700707 if (!doing_calibration_fine_tune_) {
708 if (::std::abs(bottom_absolute_position() -
709 values.claw.start_fine_tune_pos) <
710 values.claw.claw_unimportant_epsilon) {
711 doing_calibration_fine_tune_ = true;
Austin Schuh0e997732015-11-08 15:14:53 -0800712 bottom_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700713 top_claw_velocity_ = bottom_claw_velocity_ =
714 values.claw.claw_zeroing_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700715 AOS_LOG(DEBUG, "Ready to fine tune the bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700716 mode_ = FINE_TUNE_BOTTOM;
717 } else {
718 // send bottom to zeroing start
719 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700720 AOS_LOG(DEBUG, "Going to the start position for the bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700721 mode_ = PREP_FINE_TUNE_BOTTOM;
722 }
723 } else {
724 mode_ = FINE_TUNE_BOTTOM;
Austin Schuh0e997732015-11-08 15:14:53 -0800725 bottom_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700726 top_claw_velocity_ = bottom_claw_velocity_ =
727 values.claw.claw_zeroing_speed;
728 if (top_claw_.front_or_back_triggered() ||
729 bottom_claw_.front_or_back_triggered()) {
730 // We shouldn't hit a limit, but if we do, go back to the zeroing
731 // point and try again.
732 doing_calibration_fine_tune_ = false;
733 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
734 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700735 AOS_LOG(DEBUG, "Found a limit, starting over.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700736 mode_ = PREP_FINE_TUNE_BOTTOM;
737 }
738
739 if (position && bottom_claw_.SawFilteredPosedge(
740 bottom_claw_.calibration(), bottom_claw_.front(),
741 bottom_claw_.back())) {
742 // do calibration
743 bottom_claw_.SetCalibration(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700744 position->bottom()->calibration()->posedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700745 values.claw.lower_claw.calibration.lower_angle);
746 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
747 // calibrated so we are done fine tuning bottom
748 doing_calibration_fine_tune_ = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700749 AOS_LOG(DEBUG, "Calibrated the bottom correctly!\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700750 } else if (bottom_claw_.calibration().last_value()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700751 AOS_LOG(DEBUG,
752 "Aborting bottom fine tune because sensor triggered\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700753 doing_calibration_fine_tune_ = false;
754 bottom_claw_.set_zeroing_state(
755 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
756 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700757 AOS_LOG(DEBUG, "Fine tuning\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700758 }
759 }
760 // now set the top claw to track
761
762 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
763 } else {
764 // bottom claw must be calibrated, start on the top
765 if (!doing_calibration_fine_tune_) {
766 if (::std::abs(top_absolute_position() -
767 values.claw.start_fine_tune_pos) <
768 values.claw.claw_unimportant_epsilon) {
769 doing_calibration_fine_tune_ = true;
Austin Schuh0e997732015-11-08 15:14:53 -0800770 top_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700771 top_claw_velocity_ = bottom_claw_velocity_ =
772 values.claw.claw_zeroing_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700773 AOS_LOG(DEBUG, "Ready to fine tune the top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700774 mode_ = FINE_TUNE_TOP;
775 } else {
776 // send top to zeroing start
777 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700778 AOS_LOG(DEBUG, "Going to the start position for the top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700779 mode_ = PREP_FINE_TUNE_TOP;
780 }
781 } else {
782 mode_ = FINE_TUNE_TOP;
Austin Schuh0e997732015-11-08 15:14:53 -0800783 top_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700784 top_claw_velocity_ = bottom_claw_velocity_ =
785 values.claw.claw_zeroing_speed;
786 if (top_claw_.front_or_back_triggered() ||
787 bottom_claw_.front_or_back_triggered()) {
788 // this should not happen, but now we know it won't
789 doing_calibration_fine_tune_ = false;
790 top_claw_goal_ = values.claw.start_fine_tune_pos;
791 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700792 AOS_LOG(DEBUG, "Found a limit, starting over.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700793 mode_ = PREP_FINE_TUNE_TOP;
794 }
795
796 if (position &&
797 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
798 top_claw_.front(), top_claw_.back())) {
799 // do calibration
800 top_claw_.SetCalibration(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700801 position->top()->calibration()->posedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700802 values.claw.upper_claw.calibration.lower_angle);
803 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
804 // calibrated so we are done fine tuning top
805 doing_calibration_fine_tune_ = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700806 AOS_LOG(DEBUG, "Calibrated the top correctly!\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700807 } else if (top_claw_.calibration().last_value()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700808 AOS_LOG(DEBUG, "Aborting top fine tune because sensor triggered\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700809 doing_calibration_fine_tune_ = false;
810 top_claw_.set_zeroing_state(
811 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
812 }
813 }
814 // now set the bottom claw to track
815 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
816 }
817 } else {
818 doing_calibration_fine_tune_ = false;
819 if (!was_enabled_ && enabled) {
820 if (position) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700821 top_claw_goal_ = position->top()->position() + top_claw_.offset();
822 bottom_claw_goal_ =
823 position->bottom()->position() + bottom_claw_.offset();
Brian Silverman17f503e2015-08-02 18:17:18 -0700824 initial_separation_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700825 position->top()->position() - position->bottom()->position();
Brian Silverman17f503e2015-08-02 18:17:18 -0700826 } else {
827 has_top_claw_goal_ = false;
828 has_bottom_claw_goal_ = false;
829 }
830 }
831
832 if ((bottom_claw_.zeroing_state() !=
833 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
834 bottom_claw_.front().value() || top_claw_.front().value()) &&
835 !top_claw_.back().value() && !bottom_claw_.back().value()) {
836 if (enabled) {
837 // Time to slowly move back up to find any position to narrow down the
838 // zero.
Austin Schuh0e997732015-11-08 15:14:53 -0800839 top_claw_goal_ += values.claw.claw_zeroing_off_speed * kDt;
840 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700841 top_claw_velocity_ = bottom_claw_velocity_ =
842 values.claw.claw_zeroing_off_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700843 AOS_LOG(DEBUG, "Bottom is known.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700844 }
845 } else {
846 // We don't know where either claw is. Slowly start moving down to find
847 // any hall effect.
848 if (enabled) {
Austin Schuh0e997732015-11-08 15:14:53 -0800849 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * kDt;
850 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700851 top_claw_velocity_ = bottom_claw_velocity_ =
852 -values.claw.claw_zeroing_off_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700853 AOS_LOG(DEBUG, "Both are unknown.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700854 }
855 }
856
857 if (position) {
858 if (enabled) {
859 top_claw_.SetCalibrationOnEdge(
860 values.claw.upper_claw,
861 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
862 bottom_claw_.SetCalibrationOnEdge(
863 values.claw.lower_claw,
864 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
865 } else {
866 // TODO(austin): Only calibrate on the predetermined edge.
867 // We might be able to just ignore this since the backlash is soooo
868 // low.
869 // :)
870 top_claw_.SetCalibrationOnEdge(
871 values.claw.upper_claw,
872 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
873 bottom_claw_.SetCalibrationOnEdge(
874 values.claw.lower_claw,
875 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
876 }
877 }
878 mode_ = UNKNOWN_LOCATION;
879 }
880
881 // Limit the goals if both claws have been (mostly) found.
882 if (mode_ != UNKNOWN_LOCATION) {
883 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
884 }
885
886 claw_.set_positions_known(
887 top_claw_.zeroing_state() != ZeroedStateFeedbackLoop::UNKNOWN_POSITION,
888 bottom_claw_.zeroing_state() !=
889 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
890 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
891 claw_.mutable_R() << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
892 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Brian Silverman17f503e2015-08-02 18:17:18 -0700893
894 // Only cap power when one of the halves of the claw is moving slowly and
895 // could wind up.
896 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
897 mode_ == FINE_TUNE_BOTTOM);
898 claw_.Update(output == nullptr);
899 } else {
900 claw_.Update(true);
901 }
902
903 capped_goal_ = false;
904 switch (mode_) {
905 case READY:
906 case PREP_FINE_TUNE_TOP:
907 case PREP_FINE_TUNE_BOTTOM:
908 break;
909 case FINE_TUNE_BOTTOM:
910 case FINE_TUNE_TOP:
911 case UNKNOWN_LOCATION: {
912 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
Austin Schuh32501832017-02-25 18:32:56 -0800913 double dx_bot =
914 (claw_.U_uncapped(0, 0) - values.claw.max_zeroing_voltage) /
915 claw_.controller().K(0, 0);
916 double dx_top =
917 (claw_.U_uncapped(1, 0) - values.claw.max_zeroing_voltage) /
918 claw_.controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700919 double dx = ::std::max(dx_top, dx_bot);
920 bottom_claw_goal_ -= dx;
921 top_claw_goal_ -= dx;
922 Eigen::Matrix<double, 4, 1> R;
923 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0),
924 claw_.R(3, 0);
Austin Schuh32501832017-02-25 18:32:56 -0800925 claw_.mutable_U() = claw_.controller().K() * (R - claw_.X_hat());
Brian Silverman17f503e2015-08-02 18:17:18 -0700926 capped_goal_ = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700927 AOS_LOG(DEBUG,
928 "Moving the goal by %f to prevent windup."
929 " Uncapped is %f, max is %f, difference is %f\n",
930 dx, claw_.uncapped_average_voltage(),
931 values.claw.max_zeroing_voltage,
932 (claw_.uncapped_average_voltage() -
933 values.claw.max_zeroing_voltage));
Brian Silverman17f503e2015-08-02 18:17:18 -0700934 } else if (claw_.uncapped_average_voltage() <
935 -values.claw.max_zeroing_voltage) {
Austin Schuh32501832017-02-25 18:32:56 -0800936 double dx_bot =
937 (claw_.U_uncapped(0, 0) + values.claw.max_zeroing_voltage) /
938 claw_.controller().K(0, 0);
939 double dx_top =
940 (claw_.U_uncapped(1, 0) + values.claw.max_zeroing_voltage) /
941 claw_.controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700942 double dx = ::std::min(dx_top, dx_bot);
943 bottom_claw_goal_ -= dx;
944 top_claw_goal_ -= dx;
945 Eigen::Matrix<double, 4, 1> R;
946 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0),
947 claw_.R(3, 0);
Austin Schuh32501832017-02-25 18:32:56 -0800948 claw_.mutable_U() = claw_.controller().K() * (R - claw_.X_hat());
Brian Silverman17f503e2015-08-02 18:17:18 -0700949 capped_goal_ = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700950 AOS_LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Brian Silverman17f503e2015-08-02 18:17:18 -0700951 }
952 } break;
953 }
954
955 if (output) {
956 if (goal) {
957 //setup the intake
Alex Perrycb7da4b2019-08-28 19:35:56 -0700958 output_struct.intake_voltage =
959 (goal->intake() > 12.0)
960 ? 12
961 : (goal->intake() < -12.0) ? -12.0 : goal->intake();
962 output_struct.tusk_voltage = goal->centering();
963 output_struct.tusk_voltage =
964 (goal->centering() > 12.0) ? 12 : (goal->centering() < -12.0)
Brian Silverman17f503e2015-08-02 18:17:18 -0700965 ? -12.0
Alex Perrycb7da4b2019-08-28 19:35:56 -0700966 : goal->centering();
Brian Silverman17f503e2015-08-02 18:17:18 -0700967 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700968 output_struct.top_claw_voltage = claw_.U(1, 0);
969 output_struct.bottom_claw_voltage = claw_.U(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700970
Alex Perrycb7da4b2019-08-28 19:35:56 -0700971 if (output_struct.top_claw_voltage > kMaxVoltage) {
972 output_struct.top_claw_voltage = kMaxVoltage;
973 } else if (output_struct.top_claw_voltage < -kMaxVoltage) {
974 output_struct.top_claw_voltage = -kMaxVoltage;
Brian Silverman17f503e2015-08-02 18:17:18 -0700975 }
976
Alex Perrycb7da4b2019-08-28 19:35:56 -0700977 if (output_struct.bottom_claw_voltage > kMaxVoltage) {
978 output_struct.bottom_claw_voltage = kMaxVoltage;
979 } else if (output_struct.bottom_claw_voltage < -kMaxVoltage) {
980 output_struct.bottom_claw_voltage = -kMaxVoltage;
Brian Silverman17f503e2015-08-02 18:17:18 -0700981 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700982
983 output->Send(Output::Pack(*output->fbb(), &output_struct));
Brian Silverman17f503e2015-08-02 18:17:18 -0700984 }
985
Alex Perrycb7da4b2019-08-28 19:35:56 -0700986 status_struct.bottom = bottom_absolute_position();
987 status_struct.separation =
988 top_absolute_position() - bottom_absolute_position();
989 status_struct.bottom_velocity = claw_.X_hat(2, 0);
990 status_struct.separation_velocity = claw_.X_hat(3, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700991
992 if (goal) {
993 bool bottom_done =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700994 ::std::abs(bottom_absolute_position() - goal->bottom_angle()) < 0.020;
995 bool bottom_velocity_done = ::std::abs(status_struct.bottom_velocity) < 0.2;
Brian Silverman17f503e2015-08-02 18:17:18 -0700996 bool separation_done =
997 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
Alex Perrycb7da4b2019-08-28 19:35:56 -0700998 goal->separation_angle()) < 0.020;
Brian Silverman17f503e2015-08-02 18:17:18 -0700999 bool separation_done_with_ball =
1000 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
Alex Perrycb7da4b2019-08-28 19:35:56 -07001001 goal->separation_angle()) < 0.06;
1002 status_struct.done =
1003 is_ready() && separation_done && bottom_done && bottom_velocity_done;
1004 status_struct.done_with_ball = is_ready() && separation_done_with_ball &&
1005 bottom_done && bottom_velocity_done;
Brian Silverman17f503e2015-08-02 18:17:18 -07001006 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -07001007 status_struct.done = status_struct.done_with_ball = false;
Brian Silverman17f503e2015-08-02 18:17:18 -07001008 }
1009
Alex Perrycb7da4b2019-08-28 19:35:56 -07001010 status_struct.zeroed = is_ready();
1011 status_struct.zeroed_for_auto =
Brian Silverman17f503e2015-08-02 18:17:18 -07001012 (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
Alex Perrycb7da4b2019-08-28 19:35:56 -07001019 status->Send(Status::Pack(*status->fbb(), &status_struct));
1020
Brian Silverman17f503e2015-08-02 18:17:18 -07001021 was_enabled_ = enabled;
1022}
1023
Alex Perrycb7da4b2019-08-28 19:35:56 -07001024} // namespace claw
Brian Silverman17f503e2015-08-02 18:17:18 -07001025} // namespace control_loops
Austin Schuh24957102015-11-28 16:04:40 -08001026} // namespace y2014