blob: 39ee7a47234710ecf980005267b67e35b1775144 [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/commonmath.h"
James Kuszmaul61750662021-06-21 21:32:33 -07006#include "aos/logging/logging.h"
Brian Silverman17f503e2015-08-02 18:17:18 -07007#include "y2014/constants.h"
8#include "y2014/control_loops/claw/claw_motor_plant.h"
9
10// Zeroing plan.
11// There are 2 types of zeros. Enabled and disabled ones.
12// Disabled ones are only valid during auto mode, and can be used to speed up
13// the enabled zero process. We need to re-zero during teleop in case the auto
14// zero was poor and causes us to miss all our shots.
15//
16// We need to be able to zero manually while disabled by moving the joint over
17// the zeros.
18// Zero on the down edge when disabled (gravity in the direction of motion)
19//
20// When enabled, zero on the up edge (gravity opposing the direction of motion)
21// The enabled sequence needs to work as follows. We can crash the claw if we
22// bring them too close to each other or too far from each other. The only safe
23// thing to do is to move them in unison.
24//
25// Start by moving them both towards the front of the bot to either find either
26// the middle hall effect on either jaw, or the front hall effect on the bottom
27// jaw. Any edge that isn't the desired edge will provide an approximate edge
28// location that can be used for the fine tuning step.
29// Once an edge is found on the front claw, move back the other way with both
30// claws until an edge is found for the other claw.
31// Now that we have an approximate zero, we can robustify the limits to keep
32// both claws safe. Then, we can move both claws to a position that is the
33// correct side of the zero and go zero.
34
35// Valid region plan.
36// Difference between the arms has a range, and the values of each arm has a
37// range.
38// If a claw runs up against a static limit, don't let the goal change outside
39// the limit.
40// If a claw runs up against a movable limit, move both claws outwards to get
41// out of the condition.
42
Stephan Pleinesf63bde82024-01-13 15:59:33 -080043namespace y2014::control_loops::claw {
Brian Silverman17f503e2015-08-02 18:17:18 -070044
Austin Schuh24957102015-11-28 16:04:40 -080045using ::frc971::HallEffectTracker;
Austin Schuh24957102015-11-28 16:04:40 -080046using ::frc971::control_loops::DoCoerceGoal;
James Kuszmaul61750662021-06-21 21:32:33 -070047using ::y2014::control_loops::claw::kDt;
Austin Schuh0e997732015-11-08 15:14:53 -080048
Brian Silverman17f503e2015-08-02 18:17:18 -070049static const double kZeroingVoltage = 4.0;
50static const double kMaxVoltage = 12.0;
51const double kRezeroThreshold = 0.07;
52
53ClawLimitedLoop::ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> &&loop)
54 : StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
55 uncapped_average_voltage_(0.0),
56 is_zeroing_(true),
James Kuszmaul61750662021-06-21 21:32:33 -070057 U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0, -1, 0, 0, 1, 0, -1)
58 .finished(),
Brian Silverman17f503e2015-08-02 18:17:18 -070059 (Eigen::Matrix<double, 4, 1>() << kMaxVoltage, kMaxVoltage,
James Kuszmaul61750662021-06-21 21:32:33 -070060 kMaxVoltage, kMaxVoltage)
61 .finished()),
62 U_Poly_zeroing_(
63 (Eigen::Matrix<double, 4, 2>() << 1, 0, -1, 0, 0, 1, 0, -1)
64 .finished(),
65 (Eigen::Matrix<double, 4, 1>() << kZeroingVoltage, kZeroingVoltage,
66 kZeroingVoltage, kZeroingVoltage)
67 .finished()) {
68 ::frc971::controls::HPolytope<0>::Init();
Brian Silverman17f503e2015-08-02 18:17:18 -070069}
70
71// Caps the voltage prioritizing reducing velocity error over reducing
72// positional error.
73// Uses the polytope libararies which we used to just use for the drivetrain.
74// Uses a region representing the maximum voltage and then transforms it such
75// that the points represent different amounts of positional error and
76// constrains the region such that, if at all possible, it will maintain its
77// current efforts to reduce velocity error.
78void ClawLimitedLoop::CapU() {
79 const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
80
81 double u_top = U(1, 0);
82 double u_bottom = U(0, 0);
83
84 uncapped_average_voltage_ = (u_top + u_bottom) / 2;
85
86 double max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
87
88 if (::std::abs(u_bottom) > max_voltage || ::std::abs(u_top) > max_voltage) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070089 VLOG(1) << "U at start " << U();
Brian Silverman17f503e2015-08-02 18:17:18 -070090 // H * U <= k
91 // U = UPos + UVel
92 // H * (UPos + UVel) <= k
93 // H * UPos <= k - H * UVel
94
95 // Now, we can do a coordinate transformation and say the following.
96
97 // UPos = position_K * position_error
98 // (H * position_K) * position_error <= k - H * UVel
99
100 Eigen::Matrix<double, 2, 2> position_K;
Austin Schuh32501832017-02-25 18:32:56 -0800101 position_K << controller().K(0, 0), controller().K(0, 1),
102 controller().K(1, 0), controller().K(1, 1);
Brian Silverman17f503e2015-08-02 18:17:18 -0700103 Eigen::Matrix<double, 2, 2> velocity_K;
Austin Schuh32501832017-02-25 18:32:56 -0800104 velocity_K << controller().K(0, 2), controller().K(0, 3),
105 controller().K(1, 2), controller().K(1, 3);
Brian Silverman17f503e2015-08-02 18:17:18 -0700106
107 Eigen::Matrix<double, 2, 1> position_error;
108 position_error << error(0, 0), error(1, 0);
109 Eigen::Matrix<double, 2, 1> velocity_error;
110 velocity_error << error(2, 0), error(3, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700111 VLOG(1) << "error " << error;
Brian Silverman17f503e2015-08-02 18:17:18 -0700112
113 const auto &poly = is_zeroing_ ? U_Poly_zeroing_ : U_Poly_;
114 const Eigen::Matrix<double, 4, 2> pos_poly_H = poly.H() * position_K;
115 const Eigen::Matrix<double, 4, 1> pos_poly_k =
116 poly.k() - poly.H() * velocity_K * velocity_error;
James Kuszmaul61750662021-06-21 21:32:33 -0700117 const ::frc971::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
118 const ::frc971::controls::HVPolytope<2, 4, 4> hv_pos_poly(
Austin Schuhce8c6cd2016-11-26 15:13:21 -0800119 pos_poly_H, pos_poly_k, pos_poly.Vertices());
Brian Silverman17f503e2015-08-02 18:17:18 -0700120
121 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
122 {
123 const auto &P = position_error;
124
125 // This line was at 45 degrees but is now at some angle steeper than the
126 // straight one between the points.
127 Eigen::Matrix<double, 1, 2> angle_45;
128 // If the top claw is above its soft upper limit, make the line actually
129 // 45 degrees to avoid smashing it into the limit in an attempt to fix the
130 // separation error faster than the bottom position one.
131 if (X_hat(0, 0) + X_hat(1, 0) >
132 constants::GetValues().claw.upper_claw.upper_limit) {
133 angle_45 << 1, 1;
134 } else {
135 // Fixing separation error half as fast as positional error works well
136 // because it means they both close evenly.
137 angle_45 << ::std::sqrt(3), 1;
138 }
139 Eigen::Matrix<double, 1, 2> L45_quadrant;
140 L45_quadrant << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
141 const auto L45 = L45_quadrant.cwiseProduct(angle_45);
142 const double w45 = 0;
143
144 Eigen::Matrix<double, 1, 2> LH;
145 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
146 LH << 0, 1;
147 } else {
148 LH << 1, 0;
149 }
150 const double wh = LH.dot(P);
151
152 Eigen::Matrix<double, 2, 2> standard;
153 standard << L45, LH;
154 Eigen::Matrix<double, 2, 1> W;
155 W << w45, wh;
156 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
157
158 bool is_inside_h;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700159 const auto adjusted_pos_error_h = DoCoerceGoal<double>(
160 hv_pos_poly, LH, wh, position_error, &is_inside_h);
Brian Silverman17f503e2015-08-02 18:17:18 -0700161 const auto adjusted_pos_error_45 =
Austin Schuhbcce26a2018-03-26 23:41:24 -0700162 DoCoerceGoal<double>(hv_pos_poly, L45, w45, intersection, nullptr);
Brian Silverman17f503e2015-08-02 18:17:18 -0700163 if (pos_poly.IsInside(intersection)) {
164 adjusted_pos_error = adjusted_pos_error_h;
165 } else {
166 if (is_inside_h) {
167 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
168 adjusted_pos_error = adjusted_pos_error_h;
169 } else {
170 adjusted_pos_error = adjusted_pos_error_45;
171 }
172 } else {
173 adjusted_pos_error = adjusted_pos_error_45;
174 }
175 }
176 }
177
Alex Perrycb7da4b2019-08-28 19:35:56 -0700178 VLOG(1) << "adjusted_pos_error " << adjusted_pos_error;
Brian Silverman17f503e2015-08-02 18:17:18 -0700179 mutable_U() = velocity_K * velocity_error + position_K * adjusted_pos_error;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700180 VLOG(1) << "U is now" << U();
Brian Silverman17f503e2015-08-02 18:17:18 -0700181
182 {
183 const auto values = constants::GetValues().claw;
184 if (top_known_) {
James Kuszmaul61750662021-06-21 21:32:33 -0700185 if (X_hat(0, 0) + X_hat(1, 0) > values.upper_claw.upper_limit &&
186 U(1, 0) > 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700187 AOS_LOG(WARNING, "upper claw too high and moving up\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700188 mutable_U(1, 0) = 0;
189 } else if (X_hat(0, 0) + X_hat(1, 0) < values.upper_claw.lower_limit &&
190 U(1, 0) < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700191 AOS_LOG(WARNING, "upper claw too low and moving down\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700192 mutable_U(1, 0) = 0;
193 }
194 }
195 if (bottom_known_) {
196 if (X_hat(0, 0) > values.lower_claw.upper_limit && U(0, 0) > 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700197 AOS_LOG(WARNING, "lower claw too high and moving up\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700198 mutable_U(0, 0) = 0;
199 } else if (X_hat(0, 0) < values.lower_claw.lower_limit && U(0, 0) < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700200 AOS_LOG(WARNING, "lower claw too low and moving down\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700201 mutable_U(0, 0) = 0;
202 }
203 }
204 }
205 }
206}
207
208ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
209 ClawMotor *motor)
210 : offset_(0.0),
211 name_(name),
212 motor_(motor),
213 zeroing_state_(UNKNOWN_POSITION),
Brian Silverman17f503e2015-08-02 18:17:18 -0700214 encoder_(0.0),
215 last_encoder_(0.0) {}
216
Alex Perrycb7da4b2019-08-28 19:35:56 -0700217void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition *claw) {
218 front_.Update(claw->front());
219 calibration_.Update(claw->calibration());
220 back_.Update(claw->back());
Brian Silverman17f503e2015-08-02 18:17:18 -0700221
222 bool any_sensor_triggered = any_triggered();
223 if (any_sensor_triggered && any_triggered_last_) {
224 // We are still on the hall effect and nothing has changed.
225 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700226 ::std::min(min_hall_effect_on_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700227 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700228 ::std::max(max_hall_effect_on_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700229 } else if (!any_sensor_triggered && !any_triggered_last_) {
230 // We are still off the hall effect and nothing has changed.
231 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700232 ::std::min(min_hall_effect_off_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700233 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700234 ::std::max(max_hall_effect_off_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700235 }
236
Brian Silvermand3efb182015-05-13 23:04:29 -0400237 if (front_.is_posedge()) {
238 // Saw a posedge on the hall effect. Reset the limits.
239 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700240 ::std::min(claw->front()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400241 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700242 ::std::max(claw->front()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400243 }
244 if (calibration_.is_posedge()) {
245 // Saw a posedge on the hall effect. Reset the limits.
246 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700247 ::std::min(claw->calibration()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400248 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700249 ::std::max(claw->calibration()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400250 }
251 if (back_.is_posedge()) {
252 // Saw a posedge on the hall effect. Reset the limits.
253 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700254 ::std::min(claw->back()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400255 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700256 ::std::max(claw->back()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400257 }
258
259 if (front_.is_negedge()) {
260 // Saw a negedge on the hall effect. Reset the limits.
261 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700262 ::std::min(claw->front()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400263 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700264 ::std::max(claw->front()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400265 }
266 if (calibration_.is_negedge()) {
267 // Saw a negedge on the hall effect. Reset the limits.
268 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700269 ::std::min(claw->calibration()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400270 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700271 ::std::max(claw->calibration()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400272 }
273 if (back_.is_negedge()) {
274 // Saw a negedge on the hall effect. Reset the limits.
275 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700276 ::std::min(claw->back()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400277 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700278 ::std::max(claw->back()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400279 }
280
Brian Silverman17f503e2015-08-02 18:17:18 -0700281 last_encoder_ = encoder_;
282 if (front().value() || calibration().value() || back().value()) {
283 last_on_encoder_ = encoder_;
284 } else {
285 last_off_encoder_ = encoder_;
286 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700287 encoder_ = claw->position();
Brian Silverman17f503e2015-08-02 18:17:18 -0700288 any_triggered_last_ = any_sensor_triggered;
289}
290
Alex Perrycb7da4b2019-08-28 19:35:56 -0700291void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition *claw) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700292 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
293
Alex Perrycb7da4b2019-08-28 19:35:56 -0700294 front_.Reset(claw->front());
295 calibration_.Reset(claw->calibration());
296 back_.Reset(claw->back());
Brian Silverman17f503e2015-08-02 18:17:18 -0700297 // close up the min and max edge positions as they are no longer valid and
298 // will be expanded in future iterations
Alex Perrycb7da4b2019-08-28 19:35:56 -0700299 min_hall_effect_on_angle_ = claw->position();
300 max_hall_effect_on_angle_ = claw->position();
301 min_hall_effect_off_angle_ = claw->position();
302 max_hall_effect_off_angle_ = claw->position();
Brian Silverman17f503e2015-08-02 18:17:18 -0700303 any_triggered_last_ = any_triggered();
304}
305
306bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
307 const constants::Values::Claws::Claw &claw_values,
308 JointZeroingState zeroing_state) {
309 double edge_encoder;
310 double edge_angle;
311 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700312 AOS_LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
Brian Silverman17f503e2015-08-02 18:17:18 -0700313 SetCalibration(edge_encoder, edge_angle);
314 set_zeroing_state(zeroing_state);
315 return true;
316 }
317 return false;
318}
319
320void TopZeroedStateFeedbackLoop::HandleCalibrationError(
321 const constants::Values::Claws::Claw &claw_values) {
322 double edge_encoder;
323 double edge_angle;
324 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
325 const double calibration_error =
326 ComputeCalibrationChange(edge_encoder, edge_angle);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700327 AOS_LOG(INFO, "Top calibration error is %f\n", calibration_error);
Brian Silverman17f503e2015-08-02 18:17:18 -0700328 if (::std::abs(calibration_error) > kRezeroThreshold) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700329 AOS_LOG(WARNING, "rezeroing top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700330 SetCalibration(edge_encoder, edge_angle);
331 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
332 }
333 }
334}
335
Brian Silverman17f503e2015-08-02 18:17:18 -0700336void BottomZeroedStateFeedbackLoop::HandleCalibrationError(
337 const constants::Values::Claws::Claw &claw_values) {
338 double edge_encoder;
339 double edge_angle;
340 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
341 const double calibration_error =
342 ComputeCalibrationChange(edge_encoder, edge_angle);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700343 AOS_LOG(INFO, "Bottom calibration error is %f\n", calibration_error);
Brian Silverman17f503e2015-08-02 18:17:18 -0700344 if (::std::abs(calibration_error) > kRezeroThreshold) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700345 AOS_LOG(WARNING, "rezeroing bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700346 SetCalibration(edge_encoder, edge_angle);
347 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
348 }
349 }
350}
351
352bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
353 const constants::Values::Claws::Claw &claw_values,
354 JointZeroingState zeroing_state) {
355 double edge_encoder;
356 double edge_angle;
357 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700358 AOS_LOG(INFO, "Calibration edge.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700359 SetCalibration(edge_encoder, edge_angle);
360 set_zeroing_state(zeroing_state);
361 return true;
362 }
363 return false;
364}
365
Austin Schuh55a13dc2019-01-27 22:39:03 -0800366ClawMotor::ClawMotor(::aos::EventLoop *event_loop, const ::std::string &name)
James Kuszmaul61750662021-06-21 21:32:33 -0700367 : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
368 name),
Brian Silverman17f503e2015-08-02 18:17:18 -0700369 has_top_claw_goal_(false),
370 top_claw_goal_(0.0),
371 top_claw_(this),
372 has_bottom_claw_goal_(false),
373 bottom_claw_goal_(0.0),
374 bottom_claw_(this),
Austin Schuhedc317c2015-11-08 14:07:42 -0800375 claw_(::y2014::control_loops::claw::MakeClawLoop()),
Brian Silverman17f503e2015-08-02 18:17:18 -0700376 was_enabled_(false),
377 doing_calibration_fine_tune_(false),
378 capped_goal_(false),
379 mode_(UNKNOWN_LOCATION) {}
380
381const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
382
383bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
384 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
385 const HallEffectTracker &sensorB) {
386 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
387 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
388 this_sensor.value() && !this_sensor.last_value()) {
389 posedge_filter_ = &this_sensor;
390 } else if (posedge_filter_ == &this_sensor &&
391 !this_sensor.posedge_count_changed() &&
392 !sensorA.posedge_count_changed() &&
393 !sensorB.posedge_count_changed() && this_sensor.value()) {
394 posedge_filter_ = nullptr;
395 return true;
396 } else if (posedge_filter_ == &this_sensor) {
397 posedge_filter_ = nullptr;
398 }
399 return false;
400}
401
402bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
403 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
404 const HallEffectTracker &sensorB) {
405 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
406 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
407 !this_sensor.value() && this_sensor.last_value()) {
408 negedge_filter_ = &this_sensor;
409 } else if (negedge_filter_ == &this_sensor &&
410 !this_sensor.negedge_count_changed() &&
411 !sensorA.negedge_count_changed() &&
412 !sensorB.negedge_count_changed() && !this_sensor.value()) {
413 negedge_filter_ = nullptr;
414 return true;
415 } else if (negedge_filter_ == &this_sensor) {
416 negedge_filter_ = nullptr;
417 }
418 return false;
419}
420
421bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
422 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
423 double *edge_angle, const HallEffectTracker &this_sensor,
424 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
425 const char *hall_effect_name) {
426 bool found_edge = false;
427
428 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
429 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700430 AOS_LOG(WARNING, "%s: Uncertain which side, rejecting posedge\n", name_);
Brian Silverman17f503e2015-08-02 18:17:18 -0700431 } else {
432 const double average_last_encoder =
433 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400434 if (this_sensor.posedge_value() < average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700435 *edge_angle = angles.upper_decreasing_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700436 AOS_LOG(INFO,
437 "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
438 name_, hall_effect_name, *edge_angle,
439 this_sensor.posedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700440 } else {
441 *edge_angle = angles.lower_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700442 AOS_LOG(INFO,
443 "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
444 name_, hall_effect_name, *edge_angle,
445 this_sensor.posedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700446 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400447 *edge_encoder = this_sensor.posedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700448 found_edge = true;
449 }
450 }
451
452 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
453 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700454 AOS_LOG(WARNING, "%s: Uncertain which side, rejecting negedge\n", name_);
Brian Silverman17f503e2015-08-02 18:17:18 -0700455 } else {
456 const double average_last_encoder =
457 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400458 if (this_sensor.negedge_value() > average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700459 *edge_angle = angles.upper_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700460 AOS_LOG(INFO,
461 "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
462 name_, hall_effect_name, *edge_angle,
463 this_sensor.negedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700464 } else {
465 *edge_angle = angles.lower_decreasing_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700466 AOS_LOG(INFO,
467 "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
468 name_, hall_effect_name, *edge_angle,
469 this_sensor.negedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700470 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400471 *edge_encoder = this_sensor.negedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700472 found_edge = true;
473 }
474 }
475
476 return found_edge;
477}
478
479bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
480 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
481 double *edge_angle) {
482 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
483 calibration_, back_, "front")) {
484 return true;
485 }
486 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
487 calibration_, front_, back_, "calibration")) {
488 return true;
489 }
490 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
491 calibration_, front_, "back")) {
492 return true;
493 }
494 return false;
495}
496
497void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
498 double edge_angle) {
499 double old_offset = offset_;
500 offset_ = edge_angle - edge_encoder;
501 const double doffset = offset_ - old_offset;
502 motor_->ChangeTopOffset(doffset);
503}
504
505double TopZeroedStateFeedbackLoop::ComputeCalibrationChange(double edge_encoder,
506 double edge_angle) {
507 const double offset = edge_angle - edge_encoder;
508 const double doffset = offset - offset_;
509 return doffset;
510}
511
512void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
513 double edge_angle) {
514 double old_offset = offset_;
515 offset_ = edge_angle - edge_encoder;
516 const double doffset = offset_ - old_offset;
517 motor_->ChangeBottomOffset(doffset);
518}
519
520double BottomZeroedStateFeedbackLoop::ComputeCalibrationChange(
521 double edge_encoder, double edge_angle) {
522 const double offset = edge_angle - edge_encoder;
523 const double doffset = offset - offset_;
524 return doffset;
525}
526
527void ClawMotor::ChangeTopOffset(double doffset) {
528 claw_.ChangeTopOffset(doffset);
529 if (has_top_claw_goal_) {
530 top_claw_goal_ += doffset;
531 }
532}
533
534void ClawMotor::ChangeBottomOffset(double doffset) {
535 claw_.ChangeBottomOffset(doffset);
536 if (has_bottom_claw_goal_) {
537 bottom_claw_goal_ += doffset;
538 }
539}
540
541void ClawLimitedLoop::ChangeTopOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700542 mutable_X_hat()(1, 0) += doffset;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700543 AOS_LOG(INFO, "Changing top offset by %f\n", doffset);
Brian Silverman17f503e2015-08-02 18:17:18 -0700544}
545void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700546 mutable_X_hat()(0, 0) += doffset;
547 mutable_X_hat()(1, 0) -= doffset;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700548 AOS_LOG(INFO, "Changing bottom offset by %f\n", doffset);
Brian Silverman17f503e2015-08-02 18:17:18 -0700549}
550
551void LimitClawGoal(double *bottom_goal, double *top_goal,
Austin Schuh24957102015-11-28 16:04:40 -0800552 const constants::Values &values) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700553 // first update position based on angle limit
554 const double separation = *top_goal - *bottom_goal;
555 if (separation > values.claw.soft_max_separation) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700556 const double dsep = (separation - values.claw.soft_max_separation) / 2.0;
557 *bottom_goal += dsep;
558 *top_goal -= dsep;
Brian Silverman17f503e2015-08-02 18:17:18 -0700559 }
560 if (separation < values.claw.soft_min_separation) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700561 const double dsep = (separation - values.claw.soft_min_separation) / 2.0;
562 *bottom_goal += dsep;
563 *top_goal -= dsep;
Brian Silverman17f503e2015-08-02 18:17:18 -0700564 }
565
566 // now move both goals in unison
567 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700568 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
569 *bottom_goal = values.claw.lower_claw.lower_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700570 }
571 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700572 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
573 *bottom_goal = values.claw.lower_claw.upper_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700574 }
575
576 if (*top_goal < values.claw.upper_claw.lower_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700577 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
578 *top_goal = values.claw.upper_claw.lower_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700579 }
580 if (*top_goal > values.claw.upper_claw.upper_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700581 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
582 *top_goal = values.claw.upper_claw.upper_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700583 }
584}
585
586bool ClawMotor::is_ready() const {
587 return (
588 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
589 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
Alex Perrycb7da4b2019-08-28 19:35:56 -0700590 ((has_joystick_state() ? joystick_state().autonomous() : true) &&
Brian Silverman17f503e2015-08-02 18:17:18 -0700591 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
592 top_claw_.zeroing_state() ==
593 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
594 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
595 bottom_claw_.zeroing_state() ==
596 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
597}
598
599bool ClawMotor::is_zeroing() const { return !is_ready(); }
600
601// Positive angle is up, and positive power is up.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700602void ClawMotor::RunIteration(const Goal *goal, const Position *position,
603 aos::Sender<Output>::Builder *output,
604 aos::Sender<Status>::Builder *status) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700605 // Disable the motors now so that all early returns will return with the
606 // motors disabled.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700607 OutputT output_struct;
Brian Silverman17f503e2015-08-02 18:17:18 -0700608 if (output) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700609 output_struct.top_claw_voltage = 0;
610 output_struct.bottom_claw_voltage = 0;
611 output_struct.intake_voltage = 0;
612 output_struct.tusk_voltage = 0;
Brian Silverman17f503e2015-08-02 18:17:18 -0700613 }
614
Alex Perrycb7da4b2019-08-28 19:35:56 -0700615 StatusT status_struct;
Brian Silverman17f503e2015-08-02 18:17:18 -0700616 if (goal) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700617 if (::std::isnan(goal->bottom_angle()) ||
618 ::std::isnan(goal->separation_angle()) ||
619 ::std::isnan(goal->intake()) || ::std::isnan(goal->centering())) {
milind1f1dca32021-07-03 13:50:07 -0700620 (void)status->Send(Status::Pack(*status->fbb(), &status_struct));
621 output->CheckOk(
622 output->Send(Output::Pack(*output->fbb(), &output_struct)));
Brian Silverman17f503e2015-08-02 18:17:18 -0700623 return;
624 }
625 }
626
627 if (WasReset()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700628 top_claw_.Reset(position->top());
629 bottom_claw_.Reset(position->bottom());
Brian Silverman17f503e2015-08-02 18:17:18 -0700630 }
631
Austin Schuh24957102015-11-28 16:04:40 -0800632 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700633
634 if (position) {
635 Eigen::Matrix<double, 2, 1> Y;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700636 Y << position->bottom()->position() + bottom_claw_.offset(),
637 position->top()->position() + top_claw_.offset();
Brian Silverman17f503e2015-08-02 18:17:18 -0700638 claw_.Correct(Y);
639
Alex Perrycb7da4b2019-08-28 19:35:56 -0700640 top_claw_.SetPositionValues(position->top());
641 bottom_claw_.SetPositionValues(position->bottom());
Brian Silverman17f503e2015-08-02 18:17:18 -0700642
643 if (!has_top_claw_goal_) {
644 has_top_claw_goal_ = true;
645 top_claw_goal_ = top_claw_.absolute_position();
646 initial_separation_ =
647 top_claw_.absolute_position() - bottom_claw_.absolute_position();
648 }
649 if (!has_bottom_claw_goal_) {
650 has_bottom_claw_goal_ = true;
651 bottom_claw_goal_ = bottom_claw_.absolute_position();
652 initial_separation_ =
653 top_claw_.absolute_position() - bottom_claw_.absolute_position();
654 }
Brian Silverman17f503e2015-08-02 18:17:18 -0700655 }
656
657 bool autonomous, enabled;
Austin Schuheeec74a2019-01-27 20:58:59 -0800658 if (has_joystick_state()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700659 autonomous = joystick_state().autonomous();
660 enabled = joystick_state().enabled();
Austin Schuheeec74a2019-01-27 20:58:59 -0800661 } else {
Brian Silverman17f503e2015-08-02 18:17:18 -0700662 autonomous = true;
663 enabled = false;
Brian Silverman17f503e2015-08-02 18:17:18 -0700664 }
665
666 double bottom_claw_velocity_ = 0.0;
667 double top_claw_velocity_ = 0.0;
668
669 if (goal != NULL &&
670 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
671 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
672 (autonomous &&
673 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
674 top_claw_.zeroing_state() ==
675 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
676 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
677 bottom_claw_.zeroing_state() ==
678 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))))) {
679 // Ready to use the claw.
680 // Limit the goals here.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700681 bottom_claw_goal_ = goal->bottom_angle();
682 top_claw_goal_ = goal->bottom_angle() + goal->separation_angle();
Brian Silverman17f503e2015-08-02 18:17:18 -0700683 has_bottom_claw_goal_ = true;
684 has_top_claw_goal_ = true;
685 doing_calibration_fine_tune_ = false;
686 mode_ = READY;
687
688 bottom_claw_.HandleCalibrationError(values.claw.lower_claw);
689 top_claw_.HandleCalibrationError(values.claw.upper_claw);
690 } else if (top_claw_.zeroing_state() !=
James Kuszmaul61750662021-06-21 21:32:33 -0700691 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
Brian Silverman17f503e2015-08-02 18:17:18 -0700692 bottom_claw_.zeroing_state() !=
James Kuszmaul61750662021-06-21 21:32:33 -0700693 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700694 // Time to fine tune the zero.
695 // Limit the goals here.
696 if (!enabled) {
697 // If we are disabled, start the fine tune process over again.
698 doing_calibration_fine_tune_ = false;
699 }
700 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
701 // always get the bottom claw to calibrated first
Austin Schuhf257f3c2019-10-27 21:00:43 -0700702 AOS_LOG(DEBUG, "Calibrating the bottom of the claw\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700703 if (!doing_calibration_fine_tune_) {
704 if (::std::abs(bottom_absolute_position() -
705 values.claw.start_fine_tune_pos) <
706 values.claw.claw_unimportant_epsilon) {
707 doing_calibration_fine_tune_ = true;
Austin Schuh0e997732015-11-08 15:14:53 -0800708 bottom_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700709 top_claw_velocity_ = bottom_claw_velocity_ =
710 values.claw.claw_zeroing_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700711 AOS_LOG(DEBUG, "Ready to fine tune the bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700712 mode_ = FINE_TUNE_BOTTOM;
713 } else {
714 // send bottom to zeroing start
715 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700716 AOS_LOG(DEBUG, "Going to the start position for the bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700717 mode_ = PREP_FINE_TUNE_BOTTOM;
718 }
719 } else {
720 mode_ = FINE_TUNE_BOTTOM;
Austin Schuh0e997732015-11-08 15:14:53 -0800721 bottom_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700722 top_claw_velocity_ = bottom_claw_velocity_ =
723 values.claw.claw_zeroing_speed;
724 if (top_claw_.front_or_back_triggered() ||
725 bottom_claw_.front_or_back_triggered()) {
726 // We shouldn't hit a limit, but if we do, go back to the zeroing
727 // point and try again.
728 doing_calibration_fine_tune_ = false;
729 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
730 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700731 AOS_LOG(DEBUG, "Found a limit, starting over.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700732 mode_ = PREP_FINE_TUNE_BOTTOM;
733 }
734
735 if (position && bottom_claw_.SawFilteredPosedge(
736 bottom_claw_.calibration(), bottom_claw_.front(),
737 bottom_claw_.back())) {
738 // do calibration
739 bottom_claw_.SetCalibration(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700740 position->bottom()->calibration()->posedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700741 values.claw.lower_claw.calibration.lower_angle);
742 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
743 // calibrated so we are done fine tuning bottom
744 doing_calibration_fine_tune_ = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700745 AOS_LOG(DEBUG, "Calibrated the bottom correctly!\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700746 } else if (bottom_claw_.calibration().last_value()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700747 AOS_LOG(DEBUG,
748 "Aborting bottom fine tune because sensor triggered\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700749 doing_calibration_fine_tune_ = false;
750 bottom_claw_.set_zeroing_state(
751 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
752 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700753 AOS_LOG(DEBUG, "Fine tuning\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700754 }
755 }
756 // now set the top claw to track
757
758 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
759 } else {
760 // bottom claw must be calibrated, start on the top
761 if (!doing_calibration_fine_tune_) {
762 if (::std::abs(top_absolute_position() -
763 values.claw.start_fine_tune_pos) <
764 values.claw.claw_unimportant_epsilon) {
765 doing_calibration_fine_tune_ = true;
Austin Schuh0e997732015-11-08 15:14:53 -0800766 top_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700767 top_claw_velocity_ = bottom_claw_velocity_ =
768 values.claw.claw_zeroing_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700769 AOS_LOG(DEBUG, "Ready to fine tune the top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700770 mode_ = FINE_TUNE_TOP;
771 } else {
772 // send top to zeroing start
773 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700774 AOS_LOG(DEBUG, "Going to the start position for the top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700775 mode_ = PREP_FINE_TUNE_TOP;
776 }
777 } else {
778 mode_ = FINE_TUNE_TOP;
Austin Schuh0e997732015-11-08 15:14:53 -0800779 top_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700780 top_claw_velocity_ = bottom_claw_velocity_ =
781 values.claw.claw_zeroing_speed;
782 if (top_claw_.front_or_back_triggered() ||
783 bottom_claw_.front_or_back_triggered()) {
784 // this should not happen, but now we know it won't
785 doing_calibration_fine_tune_ = false;
786 top_claw_goal_ = values.claw.start_fine_tune_pos;
787 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700788 AOS_LOG(DEBUG, "Found a limit, starting over.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700789 mode_ = PREP_FINE_TUNE_TOP;
790 }
791
792 if (position &&
793 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
794 top_claw_.front(), top_claw_.back())) {
795 // do calibration
796 top_claw_.SetCalibration(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700797 position->top()->calibration()->posedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700798 values.claw.upper_claw.calibration.lower_angle);
799 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
800 // calibrated so we are done fine tuning top
801 doing_calibration_fine_tune_ = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700802 AOS_LOG(DEBUG, "Calibrated the top correctly!\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700803 } else if (top_claw_.calibration().last_value()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700804 AOS_LOG(DEBUG, "Aborting top fine tune because sensor triggered\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700805 doing_calibration_fine_tune_ = false;
806 top_claw_.set_zeroing_state(
807 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
808 }
809 }
810 // now set the bottom claw to track
811 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
812 }
813 } else {
814 doing_calibration_fine_tune_ = false;
815 if (!was_enabled_ && enabled) {
816 if (position) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700817 top_claw_goal_ = position->top()->position() + top_claw_.offset();
818 bottom_claw_goal_ =
819 position->bottom()->position() + bottom_claw_.offset();
Brian Silverman17f503e2015-08-02 18:17:18 -0700820 initial_separation_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700821 position->top()->position() - position->bottom()->position();
Brian Silverman17f503e2015-08-02 18:17:18 -0700822 } else {
823 has_top_claw_goal_ = false;
824 has_bottom_claw_goal_ = false;
825 }
826 }
827
828 if ((bottom_claw_.zeroing_state() !=
829 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
830 bottom_claw_.front().value() || top_claw_.front().value()) &&
831 !top_claw_.back().value() && !bottom_claw_.back().value()) {
832 if (enabled) {
833 // Time to slowly move back up to find any position to narrow down the
834 // zero.
Austin Schuh0e997732015-11-08 15:14:53 -0800835 top_claw_goal_ += values.claw.claw_zeroing_off_speed * kDt;
836 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700837 top_claw_velocity_ = bottom_claw_velocity_ =
838 values.claw.claw_zeroing_off_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700839 AOS_LOG(DEBUG, "Bottom is known.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700840 }
841 } else {
842 // We don't know where either claw is. Slowly start moving down to find
843 // any hall effect.
844 if (enabled) {
Austin Schuh0e997732015-11-08 15:14:53 -0800845 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * kDt;
846 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700847 top_claw_velocity_ = bottom_claw_velocity_ =
848 -values.claw.claw_zeroing_off_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700849 AOS_LOG(DEBUG, "Both are unknown.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700850 }
851 }
852
853 if (position) {
854 if (enabled) {
855 top_claw_.SetCalibrationOnEdge(
856 values.claw.upper_claw,
857 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
858 bottom_claw_.SetCalibrationOnEdge(
859 values.claw.lower_claw,
860 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
861 } else {
862 // TODO(austin): Only calibrate on the predetermined edge.
863 // We might be able to just ignore this since the backlash is soooo
864 // low.
865 // :)
866 top_claw_.SetCalibrationOnEdge(
867 values.claw.upper_claw,
868 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
869 bottom_claw_.SetCalibrationOnEdge(
870 values.claw.lower_claw,
871 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
872 }
873 }
874 mode_ = UNKNOWN_LOCATION;
875 }
876
877 // Limit the goals if both claws have been (mostly) found.
878 if (mode_ != UNKNOWN_LOCATION) {
879 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
880 }
881
882 claw_.set_positions_known(
883 top_claw_.zeroing_state() != ZeroedStateFeedbackLoop::UNKNOWN_POSITION,
884 bottom_claw_.zeroing_state() !=
885 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
886 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
887 claw_.mutable_R() << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
888 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Brian Silverman17f503e2015-08-02 18:17:18 -0700889
890 // Only cap power when one of the halves of the claw is moving slowly and
891 // could wind up.
892 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
893 mode_ == FINE_TUNE_BOTTOM);
894 claw_.Update(output == nullptr);
895 } else {
896 claw_.Update(true);
897 }
898
899 capped_goal_ = false;
900 switch (mode_) {
901 case READY:
902 case PREP_FINE_TUNE_TOP:
903 case PREP_FINE_TUNE_BOTTOM:
904 break;
905 case FINE_TUNE_BOTTOM:
906 case FINE_TUNE_TOP:
907 case UNKNOWN_LOCATION: {
908 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
Austin Schuh32501832017-02-25 18:32:56 -0800909 double dx_bot =
910 (claw_.U_uncapped(0, 0) - values.claw.max_zeroing_voltage) /
911 claw_.controller().K(0, 0);
912 double dx_top =
913 (claw_.U_uncapped(1, 0) - values.claw.max_zeroing_voltage) /
914 claw_.controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700915 double dx = ::std::max(dx_top, dx_bot);
916 bottom_claw_goal_ -= dx;
917 top_claw_goal_ -= dx;
918 Eigen::Matrix<double, 4, 1> R;
James Kuszmaul61750662021-06-21 21:32:33 -0700919 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
920 claw_.R(2, 0), claw_.R(3, 0);
Austin Schuh32501832017-02-25 18:32:56 -0800921 claw_.mutable_U() = claw_.controller().K() * (R - claw_.X_hat());
Brian Silverman17f503e2015-08-02 18:17:18 -0700922 capped_goal_ = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700923 AOS_LOG(DEBUG,
924 "Moving the goal by %f to prevent windup."
925 " Uncapped is %f, max is %f, difference is %f\n",
926 dx, claw_.uncapped_average_voltage(),
927 values.claw.max_zeroing_voltage,
928 (claw_.uncapped_average_voltage() -
929 values.claw.max_zeroing_voltage));
Brian Silverman17f503e2015-08-02 18:17:18 -0700930 } else if (claw_.uncapped_average_voltage() <
931 -values.claw.max_zeroing_voltage) {
Austin Schuh32501832017-02-25 18:32:56 -0800932 double dx_bot =
933 (claw_.U_uncapped(0, 0) + values.claw.max_zeroing_voltage) /
934 claw_.controller().K(0, 0);
935 double dx_top =
936 (claw_.U_uncapped(1, 0) + values.claw.max_zeroing_voltage) /
937 claw_.controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700938 double dx = ::std::min(dx_top, dx_bot);
939 bottom_claw_goal_ -= dx;
940 top_claw_goal_ -= dx;
941 Eigen::Matrix<double, 4, 1> R;
James Kuszmaul61750662021-06-21 21:32:33 -0700942 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
943 claw_.R(2, 0), claw_.R(3, 0);
Austin Schuh32501832017-02-25 18:32:56 -0800944 claw_.mutable_U() = claw_.controller().K() * (R - claw_.X_hat());
Brian Silverman17f503e2015-08-02 18:17:18 -0700945 capped_goal_ = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700946 AOS_LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Brian Silverman17f503e2015-08-02 18:17:18 -0700947 }
948 } break;
949 }
950
951 if (output) {
952 if (goal) {
James Kuszmaul61750662021-06-21 21:32:33 -0700953 // setup the intake
Philipp Schrader790cb542023-07-05 21:06:52 -0700954 output_struct.intake_voltage = (goal->intake() > 12.0) ? 12
955 : (goal->intake() < -12.0)
956 ? -12.0
957 : goal->intake();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700958 output_struct.tusk_voltage = goal->centering();
Philipp Schrader790cb542023-07-05 21:06:52 -0700959 output_struct.tusk_voltage = (goal->centering() > 12.0) ? 12
960 : (goal->centering() < -12.0)
961 ? -12.0
962 : goal->centering();
Brian Silverman17f503e2015-08-02 18:17:18 -0700963 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700964 output_struct.top_claw_voltage = claw_.U(1, 0);
965 output_struct.bottom_claw_voltage = claw_.U(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700966
Alex Perrycb7da4b2019-08-28 19:35:56 -0700967 if (output_struct.top_claw_voltage > kMaxVoltage) {
968 output_struct.top_claw_voltage = kMaxVoltage;
969 } else if (output_struct.top_claw_voltage < -kMaxVoltage) {
970 output_struct.top_claw_voltage = -kMaxVoltage;
Brian Silverman17f503e2015-08-02 18:17:18 -0700971 }
972
Alex Perrycb7da4b2019-08-28 19:35:56 -0700973 if (output_struct.bottom_claw_voltage > kMaxVoltage) {
974 output_struct.bottom_claw_voltage = kMaxVoltage;
975 } else if (output_struct.bottom_claw_voltage < -kMaxVoltage) {
976 output_struct.bottom_claw_voltage = -kMaxVoltage;
Brian Silverman17f503e2015-08-02 18:17:18 -0700977 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700978
milind1f1dca32021-07-03 13:50:07 -0700979 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
Brian Silverman17f503e2015-08-02 18:17:18 -0700980 }
981
Alex Perrycb7da4b2019-08-28 19:35:56 -0700982 status_struct.bottom = bottom_absolute_position();
983 status_struct.separation =
984 top_absolute_position() - bottom_absolute_position();
985 status_struct.bottom_velocity = claw_.X_hat(2, 0);
986 status_struct.separation_velocity = claw_.X_hat(3, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700987
988 if (goal) {
989 bool bottom_done =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700990 ::std::abs(bottom_absolute_position() - goal->bottom_angle()) < 0.020;
991 bool bottom_velocity_done = ::std::abs(status_struct.bottom_velocity) < 0.2;
Brian Silverman17f503e2015-08-02 18:17:18 -0700992 bool separation_done =
993 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
Alex Perrycb7da4b2019-08-28 19:35:56 -0700994 goal->separation_angle()) < 0.020;
Brian Silverman17f503e2015-08-02 18:17:18 -0700995 bool separation_done_with_ball =
996 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
Alex Perrycb7da4b2019-08-28 19:35:56 -0700997 goal->separation_angle()) < 0.06;
998 status_struct.done =
999 is_ready() && separation_done && bottom_done && bottom_velocity_done;
1000 status_struct.done_with_ball = is_ready() && separation_done_with_ball &&
1001 bottom_done && bottom_velocity_done;
Brian Silverman17f503e2015-08-02 18:17:18 -07001002 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -07001003 status_struct.done = status_struct.done_with_ball = false;
Brian Silverman17f503e2015-08-02 18:17:18 -07001004 }
1005
Alex Perrycb7da4b2019-08-28 19:35:56 -07001006 status_struct.zeroed = is_ready();
1007 status_struct.zeroed_for_auto =
Brian Silverman17f503e2015-08-02 18:17:18 -07001008 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
1009 top_claw_.zeroing_state() ==
1010 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
1011 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
1012 bottom_claw_.zeroing_state() ==
1013 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
1014
milind1f1dca32021-07-03 13:50:07 -07001015 (void)status->Send(Status::Pack(*status->fbb(), &status_struct));
Alex Perrycb7da4b2019-08-28 19:35:56 -07001016
Brian Silverman17f503e2015-08-02 18:17:18 -07001017 was_enabled_ = enabled;
1018}
1019
Stephan Pleinesf63bde82024-01-13 15:59:33 -08001020} // namespace y2014::control_loops::claw