blob: 4b76fd2e669082ce181d12e5c096c4491d4cc23e [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
Austin Schuh24957102015-11-28 16:04:40 -080043namespace y2014 {
Brian Silverman17f503e2015-08-02 18:17:18 -070044namespace control_loops {
Alex Perrycb7da4b2019-08-28 19:35:56 -070045namespace claw {
Brian Silverman17f503e2015-08-02 18:17:18 -070046
Austin Schuh24957102015-11-28 16:04:40 -080047using ::frc971::HallEffectTracker;
Austin Schuh24957102015-11-28 16:04:40 -080048using ::frc971::control_loops::DoCoerceGoal;
James Kuszmaul61750662021-06-21 21:32:33 -070049using ::y2014::control_loops::claw::kDt;
Austin Schuh0e997732015-11-08 15:14:53 -080050
Brian Silverman17f503e2015-08-02 18:17:18 -070051static const double kZeroingVoltage = 4.0;
52static const double kMaxVoltage = 12.0;
53const double kRezeroThreshold = 0.07;
54
55ClawLimitedLoop::ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> &&loop)
56 : StateFeedbackLoop<4, 2, 2>(::std::move(loop)),
57 uncapped_average_voltage_(0.0),
58 is_zeroing_(true),
James Kuszmaul61750662021-06-21 21:32:33 -070059 U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0, -1, 0, 0, 1, 0, -1)
60 .finished(),
Brian Silverman17f503e2015-08-02 18:17:18 -070061 (Eigen::Matrix<double, 4, 1>() << kMaxVoltage, kMaxVoltage,
James Kuszmaul61750662021-06-21 21:32:33 -070062 kMaxVoltage, kMaxVoltage)
63 .finished()),
64 U_Poly_zeroing_(
65 (Eigen::Matrix<double, 4, 2>() << 1, 0, -1, 0, 0, 1, 0, -1)
66 .finished(),
67 (Eigen::Matrix<double, 4, 1>() << kZeroingVoltage, kZeroingVoltage,
68 kZeroingVoltage, kZeroingVoltage)
69 .finished()) {
70 ::frc971::controls::HPolytope<0>::Init();
Brian Silverman17f503e2015-08-02 18:17:18 -070071}
72
73// Caps the voltage prioritizing reducing velocity error over reducing
74// positional error.
75// Uses the polytope libararies which we used to just use for the drivetrain.
76// Uses a region representing the maximum voltage and then transforms it such
77// that the points represent different amounts of positional error and
78// constrains the region such that, if at all possible, it will maintain its
79// current efforts to reduce velocity error.
80void ClawLimitedLoop::CapU() {
81 const Eigen::Matrix<double, 4, 1> error = R() - X_hat();
82
83 double u_top = U(1, 0);
84 double u_bottom = U(0, 0);
85
86 uncapped_average_voltage_ = (u_top + u_bottom) / 2;
87
88 double max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
89
90 if (::std::abs(u_bottom) > max_voltage || ::std::abs(u_top) > max_voltage) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070091 VLOG(1) << "U at start " << U();
Brian Silverman17f503e2015-08-02 18:17:18 -070092 // H * U <= k
93 // U = UPos + UVel
94 // H * (UPos + UVel) <= k
95 // H * UPos <= k - H * UVel
96
97 // Now, we can do a coordinate transformation and say the following.
98
99 // UPos = position_K * position_error
100 // (H * position_K) * position_error <= k - H * UVel
101
102 Eigen::Matrix<double, 2, 2> position_K;
Austin Schuh32501832017-02-25 18:32:56 -0800103 position_K << controller().K(0, 0), controller().K(0, 1),
104 controller().K(1, 0), controller().K(1, 1);
Brian Silverman17f503e2015-08-02 18:17:18 -0700105 Eigen::Matrix<double, 2, 2> velocity_K;
Austin Schuh32501832017-02-25 18:32:56 -0800106 velocity_K << controller().K(0, 2), controller().K(0, 3),
107 controller().K(1, 2), controller().K(1, 3);
Brian Silverman17f503e2015-08-02 18:17:18 -0700108
109 Eigen::Matrix<double, 2, 1> position_error;
110 position_error << error(0, 0), error(1, 0);
111 Eigen::Matrix<double, 2, 1> velocity_error;
112 velocity_error << error(2, 0), error(3, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700113 VLOG(1) << "error " << error;
Brian Silverman17f503e2015-08-02 18:17:18 -0700114
115 const auto &poly = is_zeroing_ ? U_Poly_zeroing_ : U_Poly_;
116 const Eigen::Matrix<double, 4, 2> pos_poly_H = poly.H() * position_K;
117 const Eigen::Matrix<double, 4, 1> pos_poly_k =
118 poly.k() - poly.H() * velocity_K * velocity_error;
James Kuszmaul61750662021-06-21 21:32:33 -0700119 const ::frc971::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
120 const ::frc971::controls::HVPolytope<2, 4, 4> hv_pos_poly(
Austin Schuhce8c6cd2016-11-26 15:13:21 -0800121 pos_poly_H, pos_poly_k, pos_poly.Vertices());
Brian Silverman17f503e2015-08-02 18:17:18 -0700122
123 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
124 {
125 const auto &P = position_error;
126
127 // This line was at 45 degrees but is now at some angle steeper than the
128 // straight one between the points.
129 Eigen::Matrix<double, 1, 2> angle_45;
130 // If the top claw is above its soft upper limit, make the line actually
131 // 45 degrees to avoid smashing it into the limit in an attempt to fix the
132 // separation error faster than the bottom position one.
133 if (X_hat(0, 0) + X_hat(1, 0) >
134 constants::GetValues().claw.upper_claw.upper_limit) {
135 angle_45 << 1, 1;
136 } else {
137 // Fixing separation error half as fast as positional error works well
138 // because it means they both close evenly.
139 angle_45 << ::std::sqrt(3), 1;
140 }
141 Eigen::Matrix<double, 1, 2> L45_quadrant;
142 L45_quadrant << ::aos::sign(P(1, 0)), -::aos::sign(P(0, 0));
143 const auto L45 = L45_quadrant.cwiseProduct(angle_45);
144 const double w45 = 0;
145
146 Eigen::Matrix<double, 1, 2> LH;
147 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
148 LH << 0, 1;
149 } else {
150 LH << 1, 0;
151 }
152 const double wh = LH.dot(P);
153
154 Eigen::Matrix<double, 2, 2> standard;
155 standard << L45, LH;
156 Eigen::Matrix<double, 2, 1> W;
157 W << w45, wh;
158 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
159
160 bool is_inside_h;
Austin Schuhbcce26a2018-03-26 23:41:24 -0700161 const auto adjusted_pos_error_h = DoCoerceGoal<double>(
162 hv_pos_poly, LH, wh, position_error, &is_inside_h);
Brian Silverman17f503e2015-08-02 18:17:18 -0700163 const auto adjusted_pos_error_45 =
Austin Schuhbcce26a2018-03-26 23:41:24 -0700164 DoCoerceGoal<double>(hv_pos_poly, L45, w45, intersection, nullptr);
Brian Silverman17f503e2015-08-02 18:17:18 -0700165 if (pos_poly.IsInside(intersection)) {
166 adjusted_pos_error = adjusted_pos_error_h;
167 } else {
168 if (is_inside_h) {
169 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
170 adjusted_pos_error = adjusted_pos_error_h;
171 } else {
172 adjusted_pos_error = adjusted_pos_error_45;
173 }
174 } else {
175 adjusted_pos_error = adjusted_pos_error_45;
176 }
177 }
178 }
179
Alex Perrycb7da4b2019-08-28 19:35:56 -0700180 VLOG(1) << "adjusted_pos_error " << adjusted_pos_error;
Brian Silverman17f503e2015-08-02 18:17:18 -0700181 mutable_U() = velocity_K * velocity_error + position_K * adjusted_pos_error;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700182 VLOG(1) << "U is now" << U();
Brian Silverman17f503e2015-08-02 18:17:18 -0700183
184 {
185 const auto values = constants::GetValues().claw;
186 if (top_known_) {
James Kuszmaul61750662021-06-21 21:32:33 -0700187 if (X_hat(0, 0) + X_hat(1, 0) > values.upper_claw.upper_limit &&
188 U(1, 0) > 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700189 AOS_LOG(WARNING, "upper claw too high and moving up\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700190 mutable_U(1, 0) = 0;
191 } else if (X_hat(0, 0) + X_hat(1, 0) < values.upper_claw.lower_limit &&
192 U(1, 0) < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700193 AOS_LOG(WARNING, "upper claw too low and moving down\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700194 mutable_U(1, 0) = 0;
195 }
196 }
197 if (bottom_known_) {
198 if (X_hat(0, 0) > values.lower_claw.upper_limit && U(0, 0) > 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700199 AOS_LOG(WARNING, "lower claw too high and moving up\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700200 mutable_U(0, 0) = 0;
201 } else if (X_hat(0, 0) < values.lower_claw.lower_limit && U(0, 0) < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700202 AOS_LOG(WARNING, "lower claw too low and moving down\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700203 mutable_U(0, 0) = 0;
204 }
205 }
206 }
207 }
208}
209
210ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
211 ClawMotor *motor)
212 : offset_(0.0),
213 name_(name),
214 motor_(motor),
215 zeroing_state_(UNKNOWN_POSITION),
Brian Silverman17f503e2015-08-02 18:17:18 -0700216 encoder_(0.0),
217 last_encoder_(0.0) {}
218
Alex Perrycb7da4b2019-08-28 19:35:56 -0700219void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition *claw) {
220 front_.Update(claw->front());
221 calibration_.Update(claw->calibration());
222 back_.Update(claw->back());
Brian Silverman17f503e2015-08-02 18:17:18 -0700223
224 bool any_sensor_triggered = any_triggered();
225 if (any_sensor_triggered && any_triggered_last_) {
226 // We are still on the hall effect and nothing has changed.
227 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700228 ::std::min(min_hall_effect_on_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700229 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700230 ::std::max(max_hall_effect_on_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700231 } else if (!any_sensor_triggered && !any_triggered_last_) {
232 // We are still off the hall effect and nothing has changed.
233 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700234 ::std::min(min_hall_effect_off_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700235 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700236 ::std::max(max_hall_effect_off_angle_, claw->position());
Brian Silverman17f503e2015-08-02 18:17:18 -0700237 }
238
Brian Silvermand3efb182015-05-13 23:04:29 -0400239 if (front_.is_posedge()) {
240 // Saw a posedge on the hall effect. Reset the limits.
241 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700242 ::std::min(claw->front()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400243 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700244 ::std::max(claw->front()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400245 }
246 if (calibration_.is_posedge()) {
247 // Saw a posedge on the hall effect. Reset the limits.
248 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700249 ::std::min(claw->calibration()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400250 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700251 ::std::max(claw->calibration()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400252 }
253 if (back_.is_posedge()) {
254 // Saw a posedge on the hall effect. Reset the limits.
255 min_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700256 ::std::min(claw->back()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400257 max_hall_effect_on_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700258 ::std::max(claw->back()->posedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400259 }
260
261 if (front_.is_negedge()) {
262 // Saw a negedge on the hall effect. Reset the limits.
263 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700264 ::std::min(claw->front()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400265 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700266 ::std::max(claw->front()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400267 }
268 if (calibration_.is_negedge()) {
269 // Saw a negedge on the hall effect. Reset the limits.
270 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700271 ::std::min(claw->calibration()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400272 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700273 ::std::max(claw->calibration()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400274 }
275 if (back_.is_negedge()) {
276 // Saw a negedge on the hall effect. Reset the limits.
277 min_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700278 ::std::min(claw->back()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400279 max_hall_effect_off_angle_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700280 ::std::max(claw->back()->negedge_value(), claw->position());
Brian Silvermand3efb182015-05-13 23:04:29 -0400281 }
282
Brian Silverman17f503e2015-08-02 18:17:18 -0700283 last_encoder_ = encoder_;
284 if (front().value() || calibration().value() || back().value()) {
285 last_on_encoder_ = encoder_;
286 } else {
287 last_off_encoder_ = encoder_;
288 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700289 encoder_ = claw->position();
Brian Silverman17f503e2015-08-02 18:17:18 -0700290 any_triggered_last_ = any_sensor_triggered;
291}
292
Alex Perrycb7da4b2019-08-28 19:35:56 -0700293void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition *claw) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700294 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
295
Alex Perrycb7da4b2019-08-28 19:35:56 -0700296 front_.Reset(claw->front());
297 calibration_.Reset(claw->calibration());
298 back_.Reset(claw->back());
Brian Silverman17f503e2015-08-02 18:17:18 -0700299 // close up the min and max edge positions as they are no longer valid and
300 // will be expanded in future iterations
Alex Perrycb7da4b2019-08-28 19:35:56 -0700301 min_hall_effect_on_angle_ = claw->position();
302 max_hall_effect_on_angle_ = claw->position();
303 min_hall_effect_off_angle_ = claw->position();
304 max_hall_effect_off_angle_ = claw->position();
Brian Silverman17f503e2015-08-02 18:17:18 -0700305 any_triggered_last_ = any_triggered();
306}
307
308bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
309 const constants::Values::Claws::Claw &claw_values,
310 JointZeroingState zeroing_state) {
311 double edge_encoder;
312 double edge_angle;
313 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700314 AOS_LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
Brian Silverman17f503e2015-08-02 18:17:18 -0700315 SetCalibration(edge_encoder, edge_angle);
316 set_zeroing_state(zeroing_state);
317 return true;
318 }
319 return false;
320}
321
322void TopZeroedStateFeedbackLoop::HandleCalibrationError(
323 const constants::Values::Claws::Claw &claw_values) {
324 double edge_encoder;
325 double edge_angle;
326 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
327 const double calibration_error =
328 ComputeCalibrationChange(edge_encoder, edge_angle);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700329 AOS_LOG(INFO, "Top calibration error is %f\n", calibration_error);
Brian Silverman17f503e2015-08-02 18:17:18 -0700330 if (::std::abs(calibration_error) > kRezeroThreshold) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700331 AOS_LOG(WARNING, "rezeroing top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700332 SetCalibration(edge_encoder, edge_angle);
333 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
334 }
335 }
336}
337
Brian Silverman17f503e2015-08-02 18:17:18 -0700338void BottomZeroedStateFeedbackLoop::HandleCalibrationError(
339 const constants::Values::Claws::Claw &claw_values) {
340 double edge_encoder;
341 double edge_angle;
342 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
343 const double calibration_error =
344 ComputeCalibrationChange(edge_encoder, edge_angle);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700345 AOS_LOG(INFO, "Bottom calibration error is %f\n", calibration_error);
Brian Silverman17f503e2015-08-02 18:17:18 -0700346 if (::std::abs(calibration_error) > kRezeroThreshold) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700347 AOS_LOG(WARNING, "rezeroing bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700348 SetCalibration(edge_encoder, edge_angle);
349 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
350 }
351 }
352}
353
354bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
355 const constants::Values::Claws::Claw &claw_values,
356 JointZeroingState zeroing_state) {
357 double edge_encoder;
358 double edge_angle;
359 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700360 AOS_LOG(INFO, "Calibration edge.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700361 SetCalibration(edge_encoder, edge_angle);
362 set_zeroing_state(zeroing_state);
363 return true;
364 }
365 return false;
366}
367
Austin Schuh55a13dc2019-01-27 22:39:03 -0800368ClawMotor::ClawMotor(::aos::EventLoop *event_loop, const ::std::string &name)
James Kuszmaul61750662021-06-21 21:32:33 -0700369 : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
370 name),
Brian Silverman17f503e2015-08-02 18:17:18 -0700371 has_top_claw_goal_(false),
372 top_claw_goal_(0.0),
373 top_claw_(this),
374 has_bottom_claw_goal_(false),
375 bottom_claw_goal_(0.0),
376 bottom_claw_(this),
Austin Schuhedc317c2015-11-08 14:07:42 -0800377 claw_(::y2014::control_loops::claw::MakeClawLoop()),
Brian Silverman17f503e2015-08-02 18:17:18 -0700378 was_enabled_(false),
379 doing_calibration_fine_tune_(false),
380 capped_goal_(false),
381 mode_(UNKNOWN_LOCATION) {}
382
383const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
384
385bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
386 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
387 const HallEffectTracker &sensorB) {
388 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
389 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
390 this_sensor.value() && !this_sensor.last_value()) {
391 posedge_filter_ = &this_sensor;
392 } else if (posedge_filter_ == &this_sensor &&
393 !this_sensor.posedge_count_changed() &&
394 !sensorA.posedge_count_changed() &&
395 !sensorB.posedge_count_changed() && this_sensor.value()) {
396 posedge_filter_ = nullptr;
397 return true;
398 } else if (posedge_filter_ == &this_sensor) {
399 posedge_filter_ = nullptr;
400 }
401 return false;
402}
403
404bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
405 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
406 const HallEffectTracker &sensorB) {
407 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
408 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
409 !this_sensor.value() && this_sensor.last_value()) {
410 negedge_filter_ = &this_sensor;
411 } else if (negedge_filter_ == &this_sensor &&
412 !this_sensor.negedge_count_changed() &&
413 !sensorA.negedge_count_changed() &&
414 !sensorB.negedge_count_changed() && !this_sensor.value()) {
415 negedge_filter_ = nullptr;
416 return true;
417 } else if (negedge_filter_ == &this_sensor) {
418 negedge_filter_ = nullptr;
419 }
420 return false;
421}
422
423bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
424 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
425 double *edge_angle, const HallEffectTracker &this_sensor,
426 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
427 const char *hall_effect_name) {
428 bool found_edge = false;
429
430 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
431 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700432 AOS_LOG(WARNING, "%s: Uncertain which side, rejecting posedge\n", name_);
Brian Silverman17f503e2015-08-02 18:17:18 -0700433 } else {
434 const double average_last_encoder =
435 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400436 if (this_sensor.posedge_value() < average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700437 *edge_angle = angles.upper_decreasing_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700438 AOS_LOG(INFO,
439 "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
440 name_, hall_effect_name, *edge_angle,
441 this_sensor.posedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700442 } else {
443 *edge_angle = angles.lower_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700444 AOS_LOG(INFO,
445 "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
446 name_, hall_effect_name, *edge_angle,
447 this_sensor.posedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700448 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400449 *edge_encoder = this_sensor.posedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700450 found_edge = true;
451 }
452 }
453
454 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
455 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700456 AOS_LOG(WARNING, "%s: Uncertain which side, rejecting negedge\n", name_);
Brian Silverman17f503e2015-08-02 18:17:18 -0700457 } else {
458 const double average_last_encoder =
459 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
Brian Silvermand3efb182015-05-13 23:04:29 -0400460 if (this_sensor.negedge_value() > average_last_encoder) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700461 *edge_angle = angles.upper_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700462 AOS_LOG(INFO,
463 "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
464 name_, hall_effect_name, *edge_angle,
465 this_sensor.negedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700466 } else {
467 *edge_angle = angles.lower_decreasing_angle;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700468 AOS_LOG(INFO,
469 "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
470 name_, hall_effect_name, *edge_angle,
471 this_sensor.negedge_value(), average_last_encoder);
Brian Silverman17f503e2015-08-02 18:17:18 -0700472 }
Brian Silvermand3efb182015-05-13 23:04:29 -0400473 *edge_encoder = this_sensor.negedge_value();
Brian Silverman17f503e2015-08-02 18:17:18 -0700474 found_edge = true;
475 }
476 }
477
478 return found_edge;
479}
480
481bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
482 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
483 double *edge_angle) {
484 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
485 calibration_, back_, "front")) {
486 return true;
487 }
488 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
489 calibration_, front_, back_, "calibration")) {
490 return true;
491 }
492 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
493 calibration_, front_, "back")) {
494 return true;
495 }
496 return false;
497}
498
499void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
500 double edge_angle) {
501 double old_offset = offset_;
502 offset_ = edge_angle - edge_encoder;
503 const double doffset = offset_ - old_offset;
504 motor_->ChangeTopOffset(doffset);
505}
506
507double TopZeroedStateFeedbackLoop::ComputeCalibrationChange(double edge_encoder,
508 double edge_angle) {
509 const double offset = edge_angle - edge_encoder;
510 const double doffset = offset - offset_;
511 return doffset;
512}
513
514void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
515 double edge_angle) {
516 double old_offset = offset_;
517 offset_ = edge_angle - edge_encoder;
518 const double doffset = offset_ - old_offset;
519 motor_->ChangeBottomOffset(doffset);
520}
521
522double BottomZeroedStateFeedbackLoop::ComputeCalibrationChange(
523 double edge_encoder, double edge_angle) {
524 const double offset = edge_angle - edge_encoder;
525 const double doffset = offset - offset_;
526 return doffset;
527}
528
529void ClawMotor::ChangeTopOffset(double doffset) {
530 claw_.ChangeTopOffset(doffset);
531 if (has_top_claw_goal_) {
532 top_claw_goal_ += doffset;
533 }
534}
535
536void ClawMotor::ChangeBottomOffset(double doffset) {
537 claw_.ChangeBottomOffset(doffset);
538 if (has_bottom_claw_goal_) {
539 bottom_claw_goal_ += doffset;
540 }
541}
542
543void ClawLimitedLoop::ChangeTopOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700544 mutable_X_hat()(1, 0) += doffset;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700545 AOS_LOG(INFO, "Changing top offset by %f\n", doffset);
Brian Silverman17f503e2015-08-02 18:17:18 -0700546}
547void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700548 mutable_X_hat()(0, 0) += doffset;
549 mutable_X_hat()(1, 0) -= doffset;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700550 AOS_LOG(INFO, "Changing bottom offset by %f\n", doffset);
Brian Silverman17f503e2015-08-02 18:17:18 -0700551}
552
553void LimitClawGoal(double *bottom_goal, double *top_goal,
Austin Schuh24957102015-11-28 16:04:40 -0800554 const constants::Values &values) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700555 // first update position based on angle limit
556 const double separation = *top_goal - *bottom_goal;
557 if (separation > values.claw.soft_max_separation) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700558 const double dsep = (separation - values.claw.soft_max_separation) / 2.0;
559 *bottom_goal += dsep;
560 *top_goal -= dsep;
Brian Silverman17f503e2015-08-02 18:17:18 -0700561 }
562 if (separation < values.claw.soft_min_separation) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700563 const double dsep = (separation - values.claw.soft_min_separation) / 2.0;
564 *bottom_goal += dsep;
565 *top_goal -= dsep;
Brian Silverman17f503e2015-08-02 18:17:18 -0700566 }
567
568 // now move both goals in unison
569 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700570 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
571 *bottom_goal = values.claw.lower_claw.lower_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700572 }
573 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700574 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
575 *bottom_goal = values.claw.lower_claw.upper_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700576 }
577
578 if (*top_goal < values.claw.upper_claw.lower_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700579 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
580 *top_goal = values.claw.upper_claw.lower_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700581 }
582 if (*top_goal > values.claw.upper_claw.upper_limit) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700583 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
584 *top_goal = values.claw.upper_claw.upper_limit;
Brian Silverman17f503e2015-08-02 18:17:18 -0700585 }
586}
587
588bool ClawMotor::is_ready() const {
589 return (
590 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
591 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
Alex Perrycb7da4b2019-08-28 19:35:56 -0700592 ((has_joystick_state() ? joystick_state().autonomous() : true) &&
Brian Silverman17f503e2015-08-02 18:17:18 -0700593 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
594 top_claw_.zeroing_state() ==
595 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
596 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
597 bottom_claw_.zeroing_state() ==
598 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
599}
600
601bool ClawMotor::is_zeroing() const { return !is_ready(); }
602
603// Positive angle is up, and positive power is up.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700604void ClawMotor::RunIteration(const Goal *goal, const Position *position,
605 aos::Sender<Output>::Builder *output,
606 aos::Sender<Status>::Builder *status) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700607 // Disable the motors now so that all early returns will return with the
608 // motors disabled.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700609 OutputT output_struct;
Brian Silverman17f503e2015-08-02 18:17:18 -0700610 if (output) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700611 output_struct.top_claw_voltage = 0;
612 output_struct.bottom_claw_voltage = 0;
613 output_struct.intake_voltage = 0;
614 output_struct.tusk_voltage = 0;
Brian Silverman17f503e2015-08-02 18:17:18 -0700615 }
616
Alex Perrycb7da4b2019-08-28 19:35:56 -0700617 StatusT status_struct;
Brian Silverman17f503e2015-08-02 18:17:18 -0700618 if (goal) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700619 if (::std::isnan(goal->bottom_angle()) ||
620 ::std::isnan(goal->separation_angle()) ||
621 ::std::isnan(goal->intake()) || ::std::isnan(goal->centering())) {
milind1f1dca32021-07-03 13:50:07 -0700622 (void)status->Send(Status::Pack(*status->fbb(), &status_struct));
623 output->CheckOk(
624 output->Send(Output::Pack(*output->fbb(), &output_struct)));
Brian Silverman17f503e2015-08-02 18:17:18 -0700625 return;
626 }
627 }
628
629 if (WasReset()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700630 top_claw_.Reset(position->top());
631 bottom_claw_.Reset(position->bottom());
Brian Silverman17f503e2015-08-02 18:17:18 -0700632 }
633
Austin Schuh24957102015-11-28 16:04:40 -0800634 const constants::Values &values = constants::GetValues();
Brian Silverman17f503e2015-08-02 18:17:18 -0700635
636 if (position) {
637 Eigen::Matrix<double, 2, 1> Y;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700638 Y << position->bottom()->position() + bottom_claw_.offset(),
639 position->top()->position() + top_claw_.offset();
Brian Silverman17f503e2015-08-02 18:17:18 -0700640 claw_.Correct(Y);
641
Alex Perrycb7da4b2019-08-28 19:35:56 -0700642 top_claw_.SetPositionValues(position->top());
643 bottom_claw_.SetPositionValues(position->bottom());
Brian Silverman17f503e2015-08-02 18:17:18 -0700644
645 if (!has_top_claw_goal_) {
646 has_top_claw_goal_ = true;
647 top_claw_goal_ = top_claw_.absolute_position();
648 initial_separation_ =
649 top_claw_.absolute_position() - bottom_claw_.absolute_position();
650 }
651 if (!has_bottom_claw_goal_) {
652 has_bottom_claw_goal_ = true;
653 bottom_claw_goal_ = bottom_claw_.absolute_position();
654 initial_separation_ =
655 top_claw_.absolute_position() - bottom_claw_.absolute_position();
656 }
Brian Silverman17f503e2015-08-02 18:17:18 -0700657 }
658
659 bool autonomous, enabled;
Austin Schuheeec74a2019-01-27 20:58:59 -0800660 if (has_joystick_state()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700661 autonomous = joystick_state().autonomous();
662 enabled = joystick_state().enabled();
Austin Schuheeec74a2019-01-27 20:58:59 -0800663 } else {
Brian Silverman17f503e2015-08-02 18:17:18 -0700664 autonomous = true;
665 enabled = false;
Brian Silverman17f503e2015-08-02 18:17:18 -0700666 }
667
668 double bottom_claw_velocity_ = 0.0;
669 double top_claw_velocity_ = 0.0;
670
671 if (goal != NULL &&
672 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
673 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
674 (autonomous &&
675 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
676 top_claw_.zeroing_state() ==
677 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
678 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
679 bottom_claw_.zeroing_state() ==
680 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))))) {
681 // Ready to use the claw.
682 // Limit the goals here.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700683 bottom_claw_goal_ = goal->bottom_angle();
684 top_claw_goal_ = goal->bottom_angle() + goal->separation_angle();
Brian Silverman17f503e2015-08-02 18:17:18 -0700685 has_bottom_claw_goal_ = true;
686 has_top_claw_goal_ = true;
687 doing_calibration_fine_tune_ = false;
688 mode_ = READY;
689
690 bottom_claw_.HandleCalibrationError(values.claw.lower_claw);
691 top_claw_.HandleCalibrationError(values.claw.upper_claw);
692 } else if (top_claw_.zeroing_state() !=
James Kuszmaul61750662021-06-21 21:32:33 -0700693 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
Brian Silverman17f503e2015-08-02 18:17:18 -0700694 bottom_claw_.zeroing_state() !=
James Kuszmaul61750662021-06-21 21:32:33 -0700695 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700696 // Time to fine tune the zero.
697 // Limit the goals here.
698 if (!enabled) {
699 // If we are disabled, start the fine tune process over again.
700 doing_calibration_fine_tune_ = false;
701 }
702 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
703 // always get the bottom claw to calibrated first
Austin Schuhf257f3c2019-10-27 21:00:43 -0700704 AOS_LOG(DEBUG, "Calibrating the bottom of the claw\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700705 if (!doing_calibration_fine_tune_) {
706 if (::std::abs(bottom_absolute_position() -
707 values.claw.start_fine_tune_pos) <
708 values.claw.claw_unimportant_epsilon) {
709 doing_calibration_fine_tune_ = true;
Austin Schuh0e997732015-11-08 15:14:53 -0800710 bottom_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700711 top_claw_velocity_ = bottom_claw_velocity_ =
712 values.claw.claw_zeroing_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700713 AOS_LOG(DEBUG, "Ready to fine tune the bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700714 mode_ = FINE_TUNE_BOTTOM;
715 } else {
716 // send bottom to zeroing start
717 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700718 AOS_LOG(DEBUG, "Going to the start position for the bottom\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700719 mode_ = PREP_FINE_TUNE_BOTTOM;
720 }
721 } else {
722 mode_ = FINE_TUNE_BOTTOM;
Austin Schuh0e997732015-11-08 15:14:53 -0800723 bottom_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700724 top_claw_velocity_ = bottom_claw_velocity_ =
725 values.claw.claw_zeroing_speed;
726 if (top_claw_.front_or_back_triggered() ||
727 bottom_claw_.front_or_back_triggered()) {
728 // We shouldn't hit a limit, but if we do, go back to the zeroing
729 // point and try again.
730 doing_calibration_fine_tune_ = false;
731 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
732 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700733 AOS_LOG(DEBUG, "Found a limit, starting over.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700734 mode_ = PREP_FINE_TUNE_BOTTOM;
735 }
736
737 if (position && bottom_claw_.SawFilteredPosedge(
738 bottom_claw_.calibration(), bottom_claw_.front(),
739 bottom_claw_.back())) {
740 // do calibration
741 bottom_claw_.SetCalibration(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700742 position->bottom()->calibration()->posedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700743 values.claw.lower_claw.calibration.lower_angle);
744 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
745 // calibrated so we are done fine tuning bottom
746 doing_calibration_fine_tune_ = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700747 AOS_LOG(DEBUG, "Calibrated the bottom correctly!\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700748 } else if (bottom_claw_.calibration().last_value()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700749 AOS_LOG(DEBUG,
750 "Aborting bottom fine tune because sensor triggered\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700751 doing_calibration_fine_tune_ = false;
752 bottom_claw_.set_zeroing_state(
753 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
754 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700755 AOS_LOG(DEBUG, "Fine tuning\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700756 }
757 }
758 // now set the top claw to track
759
760 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
761 } else {
762 // bottom claw must be calibrated, start on the top
763 if (!doing_calibration_fine_tune_) {
764 if (::std::abs(top_absolute_position() -
765 values.claw.start_fine_tune_pos) <
766 values.claw.claw_unimportant_epsilon) {
767 doing_calibration_fine_tune_ = true;
Austin Schuh0e997732015-11-08 15:14:53 -0800768 top_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700769 top_claw_velocity_ = bottom_claw_velocity_ =
770 values.claw.claw_zeroing_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700771 AOS_LOG(DEBUG, "Ready to fine tune the top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700772 mode_ = FINE_TUNE_TOP;
773 } else {
774 // send top to zeroing start
775 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700776 AOS_LOG(DEBUG, "Going to the start position for the top\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700777 mode_ = PREP_FINE_TUNE_TOP;
778 }
779 } else {
780 mode_ = FINE_TUNE_TOP;
Austin Schuh0e997732015-11-08 15:14:53 -0800781 top_claw_goal_ += values.claw.claw_zeroing_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700782 top_claw_velocity_ = bottom_claw_velocity_ =
783 values.claw.claw_zeroing_speed;
784 if (top_claw_.front_or_back_triggered() ||
785 bottom_claw_.front_or_back_triggered()) {
786 // this should not happen, but now we know it won't
787 doing_calibration_fine_tune_ = false;
788 top_claw_goal_ = values.claw.start_fine_tune_pos;
789 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700790 AOS_LOG(DEBUG, "Found a limit, starting over.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700791 mode_ = PREP_FINE_TUNE_TOP;
792 }
793
794 if (position &&
795 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
796 top_claw_.front(), top_claw_.back())) {
797 // do calibration
798 top_claw_.SetCalibration(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700799 position->top()->calibration()->posedge_value(),
Brian Silverman17f503e2015-08-02 18:17:18 -0700800 values.claw.upper_claw.calibration.lower_angle);
801 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
802 // calibrated so we are done fine tuning top
803 doing_calibration_fine_tune_ = false;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700804 AOS_LOG(DEBUG, "Calibrated the top correctly!\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700805 } else if (top_claw_.calibration().last_value()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700806 AOS_LOG(DEBUG, "Aborting top fine tune because sensor triggered\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700807 doing_calibration_fine_tune_ = false;
808 top_claw_.set_zeroing_state(
809 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
810 }
811 }
812 // now set the bottom claw to track
813 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
814 }
815 } else {
816 doing_calibration_fine_tune_ = false;
817 if (!was_enabled_ && enabled) {
818 if (position) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700819 top_claw_goal_ = position->top()->position() + top_claw_.offset();
820 bottom_claw_goal_ =
821 position->bottom()->position() + bottom_claw_.offset();
Brian Silverman17f503e2015-08-02 18:17:18 -0700822 initial_separation_ =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700823 position->top()->position() - position->bottom()->position();
Brian Silverman17f503e2015-08-02 18:17:18 -0700824 } else {
825 has_top_claw_goal_ = false;
826 has_bottom_claw_goal_ = false;
827 }
828 }
829
830 if ((bottom_claw_.zeroing_state() !=
831 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
832 bottom_claw_.front().value() || top_claw_.front().value()) &&
833 !top_claw_.back().value() && !bottom_claw_.back().value()) {
834 if (enabled) {
835 // Time to slowly move back up to find any position to narrow down the
836 // zero.
Austin Schuh0e997732015-11-08 15:14:53 -0800837 top_claw_goal_ += values.claw.claw_zeroing_off_speed * kDt;
838 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700839 top_claw_velocity_ = bottom_claw_velocity_ =
840 values.claw.claw_zeroing_off_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700841 AOS_LOG(DEBUG, "Bottom is known.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700842 }
843 } else {
844 // We don't know where either claw is. Slowly start moving down to find
845 // any hall effect.
846 if (enabled) {
Austin Schuh0e997732015-11-08 15:14:53 -0800847 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * kDt;
848 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * kDt;
Brian Silverman17f503e2015-08-02 18:17:18 -0700849 top_claw_velocity_ = bottom_claw_velocity_ =
850 -values.claw.claw_zeroing_off_speed;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700851 AOS_LOG(DEBUG, "Both are unknown.\n");
Brian Silverman17f503e2015-08-02 18:17:18 -0700852 }
853 }
854
855 if (position) {
856 if (enabled) {
857 top_claw_.SetCalibrationOnEdge(
858 values.claw.upper_claw,
859 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
860 bottom_claw_.SetCalibrationOnEdge(
861 values.claw.lower_claw,
862 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
863 } else {
864 // TODO(austin): Only calibrate on the predetermined edge.
865 // We might be able to just ignore this since the backlash is soooo
866 // low.
867 // :)
868 top_claw_.SetCalibrationOnEdge(
869 values.claw.upper_claw,
870 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
871 bottom_claw_.SetCalibrationOnEdge(
872 values.claw.lower_claw,
873 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
874 }
875 }
876 mode_ = UNKNOWN_LOCATION;
877 }
878
879 // Limit the goals if both claws have been (mostly) found.
880 if (mode_ != UNKNOWN_LOCATION) {
881 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
882 }
883
884 claw_.set_positions_known(
885 top_claw_.zeroing_state() != ZeroedStateFeedbackLoop::UNKNOWN_POSITION,
886 bottom_claw_.zeroing_state() !=
887 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
888 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
889 claw_.mutable_R() << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
890 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Brian Silverman17f503e2015-08-02 18:17:18 -0700891
892 // Only cap power when one of the halves of the claw is moving slowly and
893 // could wind up.
894 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
895 mode_ == FINE_TUNE_BOTTOM);
896 claw_.Update(output == nullptr);
897 } else {
898 claw_.Update(true);
899 }
900
901 capped_goal_ = false;
902 switch (mode_) {
903 case READY:
904 case PREP_FINE_TUNE_TOP:
905 case PREP_FINE_TUNE_BOTTOM:
906 break;
907 case FINE_TUNE_BOTTOM:
908 case FINE_TUNE_TOP:
909 case UNKNOWN_LOCATION: {
910 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
Austin Schuh32501832017-02-25 18:32:56 -0800911 double dx_bot =
912 (claw_.U_uncapped(0, 0) - values.claw.max_zeroing_voltage) /
913 claw_.controller().K(0, 0);
914 double dx_top =
915 (claw_.U_uncapped(1, 0) - values.claw.max_zeroing_voltage) /
916 claw_.controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700917 double dx = ::std::max(dx_top, dx_bot);
918 bottom_claw_goal_ -= dx;
919 top_claw_goal_ -= dx;
920 Eigen::Matrix<double, 4, 1> R;
James Kuszmaul61750662021-06-21 21:32:33 -0700921 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
922 claw_.R(2, 0), claw_.R(3, 0);
Austin Schuh32501832017-02-25 18:32:56 -0800923 claw_.mutable_U() = claw_.controller().K() * (R - claw_.X_hat());
Brian Silverman17f503e2015-08-02 18:17:18 -0700924 capped_goal_ = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700925 AOS_LOG(DEBUG,
926 "Moving the goal by %f to prevent windup."
927 " Uncapped is %f, max is %f, difference is %f\n",
928 dx, claw_.uncapped_average_voltage(),
929 values.claw.max_zeroing_voltage,
930 (claw_.uncapped_average_voltage() -
931 values.claw.max_zeroing_voltage));
Brian Silverman17f503e2015-08-02 18:17:18 -0700932 } else if (claw_.uncapped_average_voltage() <
933 -values.claw.max_zeroing_voltage) {
Austin Schuh32501832017-02-25 18:32:56 -0800934 double dx_bot =
935 (claw_.U_uncapped(0, 0) + values.claw.max_zeroing_voltage) /
936 claw_.controller().K(0, 0);
937 double dx_top =
938 (claw_.U_uncapped(1, 0) + values.claw.max_zeroing_voltage) /
939 claw_.controller().K(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700940 double dx = ::std::min(dx_top, dx_bot);
941 bottom_claw_goal_ -= dx;
942 top_claw_goal_ -= dx;
943 Eigen::Matrix<double, 4, 1> R;
James Kuszmaul61750662021-06-21 21:32:33 -0700944 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
945 claw_.R(2, 0), claw_.R(3, 0);
Austin Schuh32501832017-02-25 18:32:56 -0800946 claw_.mutable_U() = claw_.controller().K() * (R - claw_.X_hat());
Brian Silverman17f503e2015-08-02 18:17:18 -0700947 capped_goal_ = true;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700948 AOS_LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Brian Silverman17f503e2015-08-02 18:17:18 -0700949 }
950 } break;
951 }
952
953 if (output) {
954 if (goal) {
James Kuszmaul61750662021-06-21 21:32:33 -0700955 // setup the intake
Alex Perrycb7da4b2019-08-28 19:35:56 -0700956 output_struct.intake_voltage =
957 (goal->intake() > 12.0)
958 ? 12
959 : (goal->intake() < -12.0) ? -12.0 : goal->intake();
960 output_struct.tusk_voltage = goal->centering();
961 output_struct.tusk_voltage =
James Kuszmaul61750662021-06-21 21:32:33 -0700962 (goal->centering() > 12.0)
963 ? 12
964 : (goal->centering() < -12.0) ? -12.0 : goal->centering();
Brian Silverman17f503e2015-08-02 18:17:18 -0700965 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700966 output_struct.top_claw_voltage = claw_.U(1, 0);
967 output_struct.bottom_claw_voltage = claw_.U(0, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700968
Alex Perrycb7da4b2019-08-28 19:35:56 -0700969 if (output_struct.top_claw_voltage > kMaxVoltage) {
970 output_struct.top_claw_voltage = kMaxVoltage;
971 } else if (output_struct.top_claw_voltage < -kMaxVoltage) {
972 output_struct.top_claw_voltage = -kMaxVoltage;
Brian Silverman17f503e2015-08-02 18:17:18 -0700973 }
974
Alex Perrycb7da4b2019-08-28 19:35:56 -0700975 if (output_struct.bottom_claw_voltage > kMaxVoltage) {
976 output_struct.bottom_claw_voltage = kMaxVoltage;
977 } else if (output_struct.bottom_claw_voltage < -kMaxVoltage) {
978 output_struct.bottom_claw_voltage = -kMaxVoltage;
Brian Silverman17f503e2015-08-02 18:17:18 -0700979 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700980
milind1f1dca32021-07-03 13:50:07 -0700981 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
Brian Silverman17f503e2015-08-02 18:17:18 -0700982 }
983
Alex Perrycb7da4b2019-08-28 19:35:56 -0700984 status_struct.bottom = bottom_absolute_position();
985 status_struct.separation =
986 top_absolute_position() - bottom_absolute_position();
987 status_struct.bottom_velocity = claw_.X_hat(2, 0);
988 status_struct.separation_velocity = claw_.X_hat(3, 0);
Brian Silverman17f503e2015-08-02 18:17:18 -0700989
990 if (goal) {
991 bool bottom_done =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700992 ::std::abs(bottom_absolute_position() - goal->bottom_angle()) < 0.020;
993 bool bottom_velocity_done = ::std::abs(status_struct.bottom_velocity) < 0.2;
Brian Silverman17f503e2015-08-02 18:17:18 -0700994 bool separation_done =
995 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
Alex Perrycb7da4b2019-08-28 19:35:56 -0700996 goal->separation_angle()) < 0.020;
Brian Silverman17f503e2015-08-02 18:17:18 -0700997 bool separation_done_with_ball =
998 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
Alex Perrycb7da4b2019-08-28 19:35:56 -0700999 goal->separation_angle()) < 0.06;
1000 status_struct.done =
1001 is_ready() && separation_done && bottom_done && bottom_velocity_done;
1002 status_struct.done_with_ball = is_ready() && separation_done_with_ball &&
1003 bottom_done && bottom_velocity_done;
Brian Silverman17f503e2015-08-02 18:17:18 -07001004 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -07001005 status_struct.done = status_struct.done_with_ball = false;
Brian Silverman17f503e2015-08-02 18:17:18 -07001006 }
1007
Alex Perrycb7da4b2019-08-28 19:35:56 -07001008 status_struct.zeroed = is_ready();
1009 status_struct.zeroed_for_auto =
Brian Silverman17f503e2015-08-02 18:17:18 -07001010 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
1011 top_claw_.zeroing_state() ==
1012 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
1013 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
1014 bottom_claw_.zeroing_state() ==
1015 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
1016
milind1f1dca32021-07-03 13:50:07 -07001017 (void)status->Send(Status::Pack(*status->fbb(), &status_struct));
Alex Perrycb7da4b2019-08-28 19:35:56 -07001018
Brian Silverman17f503e2015-08-02 18:17:18 -07001019 was_enabled_ = enabled;
1020}
1021
Alex Perrycb7da4b2019-08-28 19:35:56 -07001022} // namespace claw
Brian Silverman17f503e2015-08-02 18:17:18 -07001023} // namespace control_loops
Austin Schuh24957102015-11-28 16:04:40 -08001024} // namespace y2014