blob: 59b830fa4ef1e2f445442b8b89960df79894cb92 [file] [log] [blame]
Austin Schuh3bb9a442014-02-02 16:01:45 -08001#include "frc971/control_loops/claw/claw.h"
2
Austin Schuh3bb9a442014-02-02 16:01:45 -08003#include <algorithm>
4
Briana6553ed2014-04-02 21:26:46 -07005#include "aos/common/controls/control_loops.q.h"
Austin Schuh3bb9a442014-02-02 16:01:45 -08006#include "aos/common/logging/logging.h"
Brian Silvermanf48fab32014-03-09 14:32:24 -07007#include "aos/common/logging/queue_logging.h"
Brian Silverman6dd2c532014-03-29 23:34:39 -07008#include "aos/common/logging/matrix_logging.h"
Austin Schuh3bb9a442014-02-02 16:01:45 -08009
10#include "frc971/constants.h"
Austin Schuhcda86af2014-02-16 16:16:39 -080011#include "frc971/control_loops/claw/claw_motor_plant.h"
12
Austin Schuh3bb9a442014-02-02 16:01:45 -080013// Zeroing plan.
14// There are 2 types of zeros. Enabled and disabled ones.
15// Disabled ones are only valid during auto mode, and can be used to speed up
16// the enabled zero process. We need to re-zero during teleop in case the auto
17// zero was poor and causes us to miss all our shots.
18//
19// We need to be able to zero manually while disabled by moving the joint over
20// the zeros.
21// Zero on the down edge when disabled (gravity in the direction of motion)
22//
23// When enabled, zero on the up edge (gravity opposing the direction of motion)
24// The enabled sequence needs to work as follows. We can crash the claw if we
25// bring them too close to each other or too far from each other. The only safe
26// thing to do is to move them in unison.
27//
28// Start by moving them both towards the front of the bot to either find either
29// the middle hall effect on either jaw, or the front hall effect on the bottom
30// jaw. Any edge that isn't the desired edge will provide an approximate edge
31// location that can be used for the fine tuning step.
32// Once an edge is found on the front claw, move back the other way with both
33// claws until an edge is found for the other claw.
34// Now that we have an approximate zero, we can robustify the limits to keep
35// both claws safe. Then, we can move both claws to a position that is the
36// correct side of the zero and go zero.
37
38// Valid region plan.
Ben Fredrickson81ba2d52014-03-02 08:21:46 +000039// Difference between the arms has a range, and the values of each arm has a
40// range.
Austin Schuh3bb9a442014-02-02 16:01:45 -080041// If a claw runs up against a static limit, don't let the goal change outside
42// the limit.
43// If a claw runs up against a movable limit, move both claws outwards to get
44// out of the condition.
45
46namespace frc971 {
47namespace control_loops {
Brian Silverman6dd2c532014-03-29 23:34:39 -070048namespace {
49
50template <typename T> int sign(T val) {
51 if (val > T(0)) {
52 return 1;
53 } else {
54 return -1;
55 }
56}
57
58} // namespace
Austin Schuh3bb9a442014-02-02 16:01:45 -080059
Austin Schuh01c652b2014-02-21 23:13:42 -080060static const double kZeroingVoltage = 4.0;
61static const double kMaxVoltage = 12.0;
Brian Silverman084372e2014-04-10 10:55:53 -070062const double kRezeroThreshold = 0.03;
Austin Schuhf84a1302014-02-19 00:23:30 -080063
Austin Schuh27b8fb12014-02-22 15:10:05 -080064ClawLimitedLoop::ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> loop)
65 : StateFeedbackLoop<4, 2, 2>(loop),
66 uncapped_average_voltage_(0.0),
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070067 is_zeroing_(true),
68 U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0,
69 -1, 0,
70 0, 1,
71 0, -1).finished(),
72 (Eigen::Matrix<double, 4, 1>() << kMaxVoltage, kMaxVoltage,
Brian Silverman6dd2c532014-03-29 23:34:39 -070073 kMaxVoltage, kMaxVoltage).finished()),
74 U_Poly_zeroing_((Eigen::Matrix<double, 4, 2>() << 1, 0,
75 -1, 0,
76 0, 1,
77 0, -1).finished(),
78 (Eigen::Matrix<double, 4, 1>() <<
79 kZeroingVoltage, kZeroingVoltage,
80 kZeroingVoltage, kZeroingVoltage).finished()) {
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070081 ::aos::controls::HPolytope<0>::Init();
82}
Austin Schuh27b8fb12014-02-22 15:10:05 -080083
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070084// Caps the voltage prioritizing reducing velocity error over reducing
85// positional error.
86// Uses the polytope libararies which we used to just use for the drivetrain.
87// Uses a region representing the maximum voltage and then transforms it such
88// that the points represent different amounts of positional error and
89// constrains the region such that, if at all possible, it will maintain its
90// current efforts to reduce velocity error.
Austin Schuhcda86af2014-02-16 16:16:39 -080091void ClawLimitedLoop::CapU() {
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070092 Eigen::Matrix<double, 4, 1> error = R - X_hat;
Austin Schuh4cb047f2014-02-16 21:10:19 -080093
James Kuszmauld536a402014-02-18 22:32:12 -080094 double u_top = U(1, 0);
95 double u_bottom = U(0, 0);
Austin Schuhcda86af2014-02-16 16:16:39 -080096
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070097 uncapped_average_voltage_ = (u_top + u_bottom) / 2;
98
99 double max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
100
101 if (::std::abs(u_bottom) > max_voltage or ::std::abs(u_top) > max_voltage) {
Brian Silverman6dd2c532014-03-29 23:34:39 -0700102 LOG_MATRIX(DEBUG, "U at start", U);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700103 // H * U <= k
104 // U = UPos + UVel
105 // H * (UPos + UVel) <= k
106 // H * UPos <= k - H * UVel
107
108 // Now, we can do a coordinate transformation and say the following.
109
110 // UPos = position_K * position_error
111 // (H * position_K) * position_error <= k - H * UVel
112
113 Eigen::Matrix<double, 2, 2> position_K;
114 position_K << K(0, 0), K(0, 1),
115 K(1, 0), K(1, 1);
116 Eigen::Matrix<double, 2, 2> velocity_K;
117 velocity_K << K(0, 2), K(0, 3),
118 K(1, 2), K(1, 3);
119
120 Eigen::Matrix<double, 2, 1> position_error;
121 position_error << error(0, 0), error(1, 0);
122 Eigen::Matrix<double, 2, 1> velocity_error;
123 velocity_error << error(2, 0), error(3, 0);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700124 LOG_MATRIX(DEBUG, "error", error);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700125
Brian Silverman6dd2c532014-03-29 23:34:39 -0700126 const auto &poly = is_zeroing_ ? U_Poly_zeroing_ : U_Poly_;
127 const Eigen::Matrix<double, 4, 2> pos_poly_H = poly.H() * position_K;
128 const Eigen::Matrix<double, 4, 1> pos_poly_k =
129 poly.k() - poly.H() * velocity_K * velocity_error;
130 const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700131
Brian Silverman6dd2c532014-03-29 23:34:39 -0700132 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
133 {
134 const auto &P = position_error;
Brian Silvermanb087c0a2014-03-30 12:59:52 -0700135
136 // This line was at 45 degrees but is now at some angle steeper than the
137 // straight one between the points.
138 Eigen::Matrix<double, 1, 2> angle_45;
139 // If the top claw is above its soft upper limit, make the line actually
140 // 45 degrees to avoid smashing it into the limit in an attempt to fix the
141 // separation error faster than the bottom position one.
142 if (X_hat(0, 0) + X_hat(1, 0) >
143 constants::GetValues().claw.upper_claw.upper_limit) {
144 angle_45 << 1, 1;
145 } else {
146 // Fixing separation error half as fast as positional error works well
147 // because it means they both close evenly.
148 angle_45 << ::std::sqrt(3), 1;
149 }
150 Eigen::Matrix<double, 1, 2> L45_quadrant;
151 L45_quadrant << sign(P(1, 0)), -sign(P(0, 0));
152 const auto L45 = L45_quadrant.cwiseProduct(angle_45);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700153 const double w45 = 0;
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700154
Brian Silverman6dd2c532014-03-29 23:34:39 -0700155 Eigen::Matrix<double, 1, 2> LH;
156 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
157 LH << 0, 1;
158 } else {
159 LH << 1, 0;
160 }
161 const double wh = LH.dot(P);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700162
Brian Silverman6dd2c532014-03-29 23:34:39 -0700163 Eigen::Matrix<double, 2, 2> standard;
164 standard << L45, LH;
165 Eigen::Matrix<double, 2, 1> W;
166 W << w45, wh;
167 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
168
169 bool is_inside_h;
170 const auto adjusted_pos_error_h =
171 DoCoerceGoal(pos_poly, LH, wh, position_error, &is_inside_h);
172 const auto adjusted_pos_error_45 =
173 DoCoerceGoal(pos_poly, L45, w45, intersection, nullptr);
174 if (pos_poly.IsInside(intersection)) {
175 adjusted_pos_error = adjusted_pos_error_h;
176 } else {
177 if (is_inside_h) {
178 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
179 adjusted_pos_error = adjusted_pos_error_h;
180 } else {
181 adjusted_pos_error = adjusted_pos_error_45;
182 }
183 } else {
184 adjusted_pos_error = adjusted_pos_error_45;
185 }
186 }
187 }
188
189 LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700190 U = velocity_K * velocity_error + position_K * adjusted_pos_error;
Brian Silverman6dd2c532014-03-29 23:34:39 -0700191 LOG_MATRIX(DEBUG, "U is now", U);
Brian Silverman7b7c9072014-03-30 13:33:30 -0700192
193 {
194 const auto values = constants::GetValues().claw;
Brian Silvermaned9df2f2014-04-05 07:07:15 -0700195 if (top_known_) {
196 if (X_hat(0, 0) + X_hat(1, 0) > values.upper_claw.upper_limit &&
197 U(1, 0) > 0) {
198 LOG(WARNING, "upper claw too high and moving up\n");
199 U(1, 0) = 0;
200 } else if (X_hat(0, 0) + X_hat(1, 0) < values.upper_claw.lower_limit &&
201 U(1, 0) < 0) {
202 LOG(WARNING, "upper claw too low and moving down\n");
203 U(1, 0) = 0;
204 }
Brian Silverman7b7c9072014-03-30 13:33:30 -0700205 }
Brian Silvermaned9df2f2014-04-05 07:07:15 -0700206 if (bottom_known_) {
207 if (X_hat(0, 0) > values.lower_claw.upper_limit && U(0, 0) > 0) {
208 LOG(WARNING, "lower claw too high and moving up\n");
209 U(0, 0) = 0;
210 } else if (X_hat(0, 0) < values.lower_claw.lower_limit && U(0, 0) < 0) {
211 LOG(WARNING, "lower claw too low and moving down\n");
212 U(0, 0) = 0;
213 }
Brian Silverman7b7c9072014-03-30 13:33:30 -0700214 }
215 }
James Kuszmauld536a402014-02-18 22:32:12 -0800216 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800217}
218
Austin Schuh27b8fb12014-02-22 15:10:05 -0800219ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
220 ClawMotor *motor)
221 : offset_(0.0),
222 name_(name),
223 motor_(motor),
224 zeroing_state_(UNKNOWN_POSITION),
225 posedge_value_(0.0),
226 negedge_value_(0.0),
227 encoder_(0.0),
228 last_encoder_(0.0) {}
229
230void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition &claw) {
231 front_.Update(claw.front);
232 calibration_.Update(claw.calibration);
233 back_.Update(claw.back);
234
235 bool any_sensor_triggered = any_triggered();
236 if (any_sensor_triggered && any_triggered_last_) {
237 // We are still on the hall effect and nothing has changed.
238 min_hall_effect_on_angle_ =
239 ::std::min(min_hall_effect_on_angle_, claw.position);
240 max_hall_effect_on_angle_ =
241 ::std::max(max_hall_effect_on_angle_, claw.position);
242 } else if (!any_sensor_triggered && !any_triggered_last_) {
243 // We are still off the hall effect and nothing has changed.
244 min_hall_effect_off_angle_ =
245 ::std::min(min_hall_effect_off_angle_, claw.position);
246 max_hall_effect_off_angle_ =
247 ::std::max(max_hall_effect_off_angle_, claw.position);
248 } else if (any_sensor_triggered && !any_triggered_last_) {
249 // Saw a posedge on the hall effect. Reset the limits.
250 min_hall_effect_on_angle_ = ::std::min(claw.posedge_value, claw.position);
251 max_hall_effect_on_angle_ = ::std::max(claw.posedge_value, claw.position);
252 } else if (!any_sensor_triggered && any_triggered_last_) {
253 // Saw a negedge on the hall effect. Reset the limits.
254 min_hall_effect_off_angle_ = ::std::min(claw.negedge_value, claw.position);
255 max_hall_effect_off_angle_ = ::std::max(claw.negedge_value, claw.position);
256 }
257
258 posedge_value_ = claw.posedge_value;
259 negedge_value_ = claw.negedge_value;
260 last_encoder_ = encoder_;
261 if (front().value() || calibration().value() || back().value()) {
262 last_on_encoder_ = encoder_;
263 } else {
264 last_off_encoder_ = encoder_;
265 }
266 encoder_ = claw.position;
267 any_triggered_last_ = any_sensor_triggered;
268}
269
270void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition &claw) {
271 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
272
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000273 front_.Reset(claw.front);
274 calibration_.Reset(claw.calibration);
275 back_.Reset(claw.back);
Austin Schuh27b8fb12014-02-22 15:10:05 -0800276 // close up the min and max edge positions as they are no longer valid and
277 // will be expanded in future iterations
278 min_hall_effect_on_angle_ = claw.position;
279 max_hall_effect_on_angle_ = claw.position;
280 min_hall_effect_off_angle_ = claw.position;
281 max_hall_effect_off_angle_ = claw.position;
282 any_triggered_last_ = any_triggered();
283}
284
285bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
286 const constants::Values::Claws::Claw &claw_values,
287 JointZeroingState zeroing_state) {
288 double edge_encoder;
289 double edge_angle;
290 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
291 LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
292 SetCalibration(edge_encoder, edge_angle);
293 set_zeroing_state(zeroing_state);
294 return true;
295 }
296 return false;
297}
298
Brian Silverman084372e2014-04-10 10:55:53 -0700299void TopZeroedStateFeedbackLoop::HandleCalibrationError(
300 const constants::Values::Claws::Claw &claw_values) {
301 double edge_encoder;
302 double edge_angle;
303 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
304 const double calibration_error =
305 ComputeCalibrationChange(edge_encoder, edge_angle);
306 LOG(INFO, "Top calibration error is %f\n", calibration_error);
307 if (::std::abs(calibration_error) > kRezeroThreshold) {
Brian Silvermane4c701d2014-04-10 19:29:25 -0700308 LOG(WARNING, "rezeroing top\n");
Brian Silverman084372e2014-04-10 10:55:53 -0700309 SetCalibration(edge_encoder, edge_angle);
310 set_zeroing_state(ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
311 }
312 }
313}
314
315
316void BottomZeroedStateFeedbackLoop::HandleCalibrationError(
317 const constants::Values::Claws::Claw &claw_values) {
318 double edge_encoder;
319 double edge_angle;
320 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
321 const double calibration_error =
322 ComputeCalibrationChange(edge_encoder, edge_angle);
323 LOG(INFO, "Bottom calibration error is %f\n", calibration_error);
324 if (::std::abs(calibration_error) > kRezeroThreshold) {
Brian Silvermane4c701d2014-04-10 19:29:25 -0700325 LOG(WARNING, "rezeroing bottom\n");
Brian Silverman084372e2014-04-10 10:55:53 -0700326 SetCalibration(edge_encoder, edge_angle);
327 set_zeroing_state(ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
328 }
329 }
330}
331
Austin Schuh27b8fb12014-02-22 15:10:05 -0800332bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
333 const constants::Values::Claws::Claw &claw_values,
334 JointZeroingState zeroing_state) {
335 double edge_encoder;
336 double edge_angle;
337 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
338 LOG(INFO, "Calibration edge.\n");
339 SetCalibration(edge_encoder, edge_angle);
340 set_zeroing_state(zeroing_state);
341 return true;
342 }
343 return false;
344}
345
Austin Schuhcc0bf312014-02-09 00:39:29 -0800346ClawMotor::ClawMotor(control_loops::ClawGroup *my_claw)
Brian Silverman38111502014-04-10 12:36:26 -0700347 : aos::controls::ControlLoop<control_loops::ClawGroup, true, true,
Brian Silverman71fbee02014-03-13 17:24:54 -0700348 false>(my_claw),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800349 has_top_claw_goal_(false),
350 top_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800351 top_claw_(this),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800352 has_bottom_claw_goal_(false),
353 bottom_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800354 bottom_claw_(this),
355 claw_(MakeClawLoop()),
Ben Fredrickson9b388422014-02-13 06:15:31 +0000356 was_enabled_(false),
Austin Schuh4cb047f2014-02-16 21:10:19 -0800357 doing_calibration_fine_tune_(false),
Austin Schuhe7f90d12014-02-17 00:48:25 -0800358 capped_goal_(false),
359 mode_(UNKNOWN_LOCATION) {}
Austin Schuh3bb9a442014-02-02 16:01:45 -0800360
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800361const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800362
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000363bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
364 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
365 const HallEffectTracker &sensorB) {
366 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
367 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
368 this_sensor.value() && !this_sensor.last_value()) {
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000369 posedge_filter_ = &this_sensor;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000370 } else if (posedge_filter_ == &this_sensor &&
371 !this_sensor.posedge_count_changed() &&
372 !sensorA.posedge_count_changed() &&
373 !sensorB.posedge_count_changed() && this_sensor.value()) {
374 posedge_filter_ = nullptr;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000375 return true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000376 } else if (posedge_filter_ == &this_sensor) {
377 posedge_filter_ = nullptr;
378 }
379 return false;
380}
381
382bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
383 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
384 const HallEffectTracker &sensorB) {
385 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
386 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
387 !this_sensor.value() && this_sensor.last_value()) {
388 negedge_filter_ = &this_sensor;
389 } else if (negedge_filter_ == &this_sensor &&
390 !this_sensor.negedge_count_changed() &&
391 !sensorA.negedge_count_changed() &&
392 !sensorB.negedge_count_changed() && !this_sensor.value()) {
393 negedge_filter_ = nullptr;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000394 return true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000395 } else if (negedge_filter_ == &this_sensor) {
396 negedge_filter_ = nullptr;
397 }
398 return false;
399}
400
Brian Silvermane0a95462014-02-17 00:41:09 -0800401bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
402 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000403 double *edge_angle, const HallEffectTracker &this_sensor,
404 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
Brian Silvermane0a95462014-02-17 00:41:09 -0800405 const char *hall_effect_name) {
Austin Schuhf84a1302014-02-19 00:23:30 -0800406 bool found_edge = false;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800407
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000408 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800409 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700410 LOG(WARNING, "%s: Uncertain which side, rejecting posedge\n", name_);
Brian Silvermane0a95462014-02-17 00:41:09 -0800411 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800412 const double average_last_encoder =
413 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
414 if (posedge_value_ < average_last_encoder) {
415 *edge_angle = angles.upper_decreasing_angle;
416 LOG(INFO, "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
417 name_, hall_effect_name, *edge_angle, posedge_value_,
418 average_last_encoder);
419 } else {
420 *edge_angle = angles.lower_angle;
421 LOG(INFO, "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
422 name_, hall_effect_name, *edge_angle, posedge_value_,
423 average_last_encoder);
424 }
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000425 *edge_encoder = posedge_value_;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800426 found_edge = true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000427 }
428 }
429
430 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
431 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700432 LOG(WARNING, "%s: Uncertain which side, rejecting negedge\n", name_);
Brian Silvermane0a95462014-02-17 00:41:09 -0800433 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800434 const double average_last_encoder =
435 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
436 if (negedge_value_ > average_last_encoder) {
437 *edge_angle = angles.upper_angle;
438 LOG(INFO, "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
439 name_, hall_effect_name, *edge_angle, negedge_value_,
440 average_last_encoder);
441 } else {
442 *edge_angle = angles.lower_decreasing_angle;
443 LOG(INFO, "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
444 name_, hall_effect_name, *edge_angle, negedge_value_,
445 average_last_encoder);
446 }
447 *edge_encoder = negedge_value_;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000448 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800449 }
Austin Schuh27b8fb12014-02-22 15:10:05 -0800450 }
451
Austin Schuhf84a1302014-02-19 00:23:30 -0800452 return found_edge;
Brian Silvermane0a95462014-02-17 00:41:09 -0800453}
454
Austin Schuhf9286cd2014-02-11 00:51:09 -0800455bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800456 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
Austin Schuhf9286cd2014-02-11 00:51:09 -0800457 double *edge_angle) {
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000458 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
459 calibration_, back_, "front")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800460 return true;
461 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800462 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000463 calibration_, front_, back_, "calibration")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800464 return true;
465 }
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000466 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
467 calibration_, front_, "back")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800468 return true;
469 }
470 return false;
471}
472
Austin Schuhcda86af2014-02-16 16:16:39 -0800473void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
474 double edge_angle) {
475 double old_offset = offset_;
476 offset_ = edge_angle - edge_encoder;
477 const double doffset = offset_ - old_offset;
478 motor_->ChangeTopOffset(doffset);
479}
480
Brian Silverman084372e2014-04-10 10:55:53 -0700481double TopZeroedStateFeedbackLoop::ComputeCalibrationChange(double edge_encoder,
482 double edge_angle) {
483 const double offset = edge_angle - edge_encoder;
484 const double doffset = offset - offset_;
485 return doffset;
486}
487
Austin Schuhcda86af2014-02-16 16:16:39 -0800488void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
489 double edge_angle) {
490 double old_offset = offset_;
491 offset_ = edge_angle - edge_encoder;
492 const double doffset = offset_ - old_offset;
493 motor_->ChangeBottomOffset(doffset);
494}
495
Brian Silverman084372e2014-04-10 10:55:53 -0700496double BottomZeroedStateFeedbackLoop::ComputeCalibrationChange(
497 double edge_encoder, double edge_angle) {
498 const double offset = edge_angle - edge_encoder;
499 const double doffset = offset - offset_;
500 return doffset;
501}
502
Austin Schuhcda86af2014-02-16 16:16:39 -0800503void ClawMotor::ChangeTopOffset(double doffset) {
504 claw_.ChangeTopOffset(doffset);
505 if (has_top_claw_goal_) {
506 top_claw_goal_ += doffset;
507 }
508}
509
510void ClawMotor::ChangeBottomOffset(double doffset) {
511 claw_.ChangeBottomOffset(doffset);
512 if (has_bottom_claw_goal_) {
513 bottom_claw_goal_ += doffset;
514 }
515}
516
517void ClawLimitedLoop::ChangeTopOffset(double doffset) {
518 Y_(1, 0) += doffset;
519 X_hat(1, 0) += doffset;
520 LOG(INFO, "Changing top offset by %f\n", doffset);
521}
522void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
523 Y_(0, 0) += doffset;
524 X_hat(0, 0) += doffset;
525 X_hat(1, 0) -= doffset;
526 LOG(INFO, "Changing bottom offset by %f\n", doffset);
527}
joe7376ff52014-02-16 18:28:42 -0800528
Austin Schuh069143b2014-02-17 02:46:26 -0800529void LimitClawGoal(double *bottom_goal, double *top_goal,
530 const frc971::constants::Values &values) {
531 // first update position based on angle limit
532
533 const double separation = *top_goal - *bottom_goal;
534 if (separation > values.claw.claw_max_separation) {
Austin Schuh069143b2014-02-17 02:46:26 -0800535 const double dsep = (separation - values.claw.claw_max_separation) / 2.0;
536 *bottom_goal += dsep;
537 *top_goal -= dsep;
538 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
539 }
540 if (separation < values.claw.claw_min_separation) {
Austin Schuh069143b2014-02-17 02:46:26 -0800541 const double dsep = (separation - values.claw.claw_min_separation) / 2.0;
542 *bottom_goal += dsep;
543 *top_goal -= dsep;
544 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
545 }
546
547 // now move both goals in unison
548 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
549 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
550 *bottom_goal = values.claw.lower_claw.lower_limit;
551 }
552 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
553 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
554 *bottom_goal = values.claw.lower_claw.upper_limit;
555 }
556
557 if (*top_goal < values.claw.upper_claw.lower_limit) {
558 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
559 *top_goal = values.claw.upper_claw.lower_limit;
560 }
561 if (*top_goal > values.claw.upper_claw.upper_limit) {
562 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
563 *top_goal = values.claw.upper_claw.upper_limit;
564 }
565}
Austin Schuhcda86af2014-02-16 16:16:39 -0800566
Austin Schuhe7f90d12014-02-17 00:48:25 -0800567bool ClawMotor::is_ready() const {
568 return (
569 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
570 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
Brian Silverman71fbee02014-03-13 17:24:54 -0700571 (((::aos::robot_state.get() == NULL) ? true
572 : ::aos::robot_state->autonomous) &&
Austin Schuhe7f90d12014-02-17 00:48:25 -0800573 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
574 top_claw_.zeroing_state() ==
575 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
576 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
577 bottom_claw_.zeroing_state() ==
578 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
579}
580
581bool ClawMotor::is_zeroing() const { return !is_ready(); }
582
Austin Schuh3bb9a442014-02-02 16:01:45 -0800583// Positive angle is up, and positive power is up.
Austin Schuhcc0bf312014-02-09 00:39:29 -0800584void ClawMotor::RunIteration(const control_loops::ClawGroup::Goal *goal,
585 const control_loops::ClawGroup::Position *position,
586 control_loops::ClawGroup::Output *output,
James Kuszmaul9ead1de2014-02-28 21:24:39 -0800587 control_loops::ClawGroup::Status *status) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800588 constexpr double dt = 0.01;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800589
590 // Disable the motors now so that all early returns will return with the
591 // motors disabled.
592 if (output) {
593 output->top_claw_voltage = 0;
594 output->bottom_claw_voltage = 0;
595 output->intake_voltage = 0;
Ben Fredrickson61893d52014-03-02 09:43:23 +0000596 output->tusk_voltage = 0;
597 }
598
Brian Silverman71fbee02014-03-13 17:24:54 -0700599 if (goal) {
600 if (::std::isnan(goal->bottom_angle) ||
601 ::std::isnan(goal->separation_angle) || ::std::isnan(goal->intake) ||
602 ::std::isnan(goal->centering)) {
603 return;
604 }
Austin Schuh3bb9a442014-02-02 16:01:45 -0800605 }
606
Austin Schuh1a499942014-02-17 01:51:58 -0800607 if (reset()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800608 top_claw_.Reset(position->top);
609 bottom_claw_.Reset(position->bottom);
Austin Schuh1a499942014-02-17 01:51:58 -0800610 }
611
Austin Schuhf9286cd2014-02-11 00:51:09 -0800612 const frc971::constants::Values &values = constants::GetValues();
613
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800614 if (position) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800615 Eigen::Matrix<double, 2, 1> Y;
616 Y << position->bottom.position + bottom_claw_.offset(),
617 position->top.position + top_claw_.offset();
618 claw_.Correct(Y);
619
Austin Schuhf9286cd2014-02-11 00:51:09 -0800620 top_claw_.SetPositionValues(position->top);
621 bottom_claw_.SetPositionValues(position->bottom);
622
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800623 if (!has_top_claw_goal_) {
624 has_top_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800625 top_claw_goal_ = top_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800626 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800627 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800628 }
629 if (!has_bottom_claw_goal_) {
630 has_bottom_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800631 bottom_claw_goal_ = bottom_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800632 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800633 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800634 }
Brian Silvermanf48fab32014-03-09 14:32:24 -0700635 LOG_STRUCT(DEBUG, "absolute position",
636 ClawPositionToLog(top_claw_.absolute_position(),
637 bottom_claw_.absolute_position()));
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800638 }
639
Brian Silverman71fbee02014-03-13 17:24:54 -0700640 bool autonomous, enabled;
641 if (::aos::robot_state.get() == nullptr) {
642 autonomous = true;
643 enabled = false;
644 } else {
645 autonomous = ::aos::robot_state->autonomous;
646 enabled = ::aos::robot_state->enabled;
647 }
Austin Schuh069143b2014-02-17 02:46:26 -0800648
649 double bottom_claw_velocity_ = 0.0;
650 double top_claw_velocity_ = 0.0;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800651
Brian Silverman71fbee02014-03-13 17:24:54 -0700652 if (goal != NULL &&
653 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
654 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
655 (autonomous &&
656 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
657 top_claw_.zeroing_state() ==
658 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
659 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
660 bottom_claw_.zeroing_state() ==
661 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))))) {
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800662 // Ready to use the claw.
663 // Limit the goals here.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800664 bottom_claw_goal_ = goal->bottom_angle;
Brian Silverman7c021c42014-02-17 15:15:56 -0800665 top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
Austin Schuhcda86af2014-02-16 16:16:39 -0800666 has_bottom_claw_goal_ = true;
667 has_top_claw_goal_ = true;
668 doing_calibration_fine_tune_ = false;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800669 mode_ = READY;
Brian Silverman084372e2014-04-10 10:55:53 -0700670
671 bottom_claw_.HandleCalibrationError(values.claw.lower_claw);
672 top_claw_.HandleCalibrationError(values.claw.upper_claw);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800673 } else if (top_claw_.zeroing_state() !=
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000674 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800675 bottom_claw_.zeroing_state() !=
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000676 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800677 // Time to fine tune the zero.
678 // Limit the goals here.
Austin Schuh0c733422014-02-17 01:17:12 -0800679 if (!enabled) {
680 // If we are disabled, start the fine tune process over again.
681 doing_calibration_fine_tune_ = false;
682 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800683 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800684 // always get the bottom claw to calibrated first
685 LOG(DEBUG, "Calibrating the bottom of the claw\n");
686 if (!doing_calibration_fine_tune_) {
687 if (::std::abs(bottom_absolute_position() -
Austin Schuhd27931c2014-02-16 19:18:20 -0800688 values.claw.start_fine_tune_pos) <
689 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800690 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800691 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800692 top_claw_velocity_ = bottom_claw_velocity_ =
693 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800694 LOG(DEBUG, "Ready to fine tune the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800695 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800696 } else {
697 // send bottom to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800698 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800699 LOG(DEBUG, "Going to the start position for the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800700 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800701 }
702 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800703 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhd27931c2014-02-16 19:18:20 -0800704 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800705 top_claw_velocity_ = bottom_claw_velocity_ =
706 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800707 if (top_claw_.front_or_back_triggered() ||
708 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800709 // We shouldn't hit a limit, but if we do, go back to the zeroing
710 // point and try again.
711 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800712 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800713 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800714 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800715 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800716 }
Austin Schuh288c8c32014-02-16 17:20:17 -0800717
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000718 if (position && bottom_claw_.SawFilteredPosedge(
719 bottom_claw_.calibration(), bottom_claw_.front(),
720 bottom_claw_.back())) {
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000721 // do calibration
722 bottom_claw_.SetCalibration(
723 position->bottom.posedge_value,
724 values.claw.lower_claw.calibration.lower_angle);
725 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
726 // calibrated so we are done fine tuning bottom
727 doing_calibration_fine_tune_ = false;
728 LOG(DEBUG, "Calibrated the bottom correctly!\n");
729 } else if (bottom_claw_.calibration().last_value()) {
Brian Silvermane4c701d2014-04-10 19:29:25 -0700730 LOG(DEBUG, "Aborting bottom fine tune because sensor triggered\n");
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000731 doing_calibration_fine_tune_ = false;
Brian Silvermancc3844a2014-04-10 21:15:07 -0700732 bottom_claw_.set_zeroing_state(
733 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
Austin Schuhcda86af2014-02-16 16:16:39 -0800734 } else {
735 LOG(DEBUG, "Fine tuning\n");
736 }
737 }
738 // now set the top claw to track
739
Austin Schuhd27931c2014-02-16 19:18:20 -0800740 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800741 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800742 // bottom claw must be calibrated, start on the top
743 if (!doing_calibration_fine_tune_) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800744 if (::std::abs(top_absolute_position() -
745 values.claw.start_fine_tune_pos) <
746 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800747 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800748 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800749 top_claw_velocity_ = bottom_claw_velocity_ =
750 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800751 LOG(DEBUG, "Ready to fine tune the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800752 mode_ = FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800753 } else {
754 // send top to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800755 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800756 LOG(DEBUG, "Going to the start position for the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800757 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800758 }
759 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800760 mode_ = FINE_TUNE_TOP;
Austin Schuhd27931c2014-02-16 19:18:20 -0800761 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800762 top_claw_velocity_ = bottom_claw_velocity_ =
763 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800764 if (top_claw_.front_or_back_triggered() ||
765 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800766 // this should not happen, but now we know it won't
767 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800768 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800769 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800770 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800771 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800772 }
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000773
774 if (position &&
775 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
776 top_claw_.front(), top_claw_.back())) {
777 // do calibration
778 top_claw_.SetCalibration(
779 position->top.posedge_value,
780 values.claw.upper_claw.calibration.lower_angle);
781 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
782 // calibrated so we are done fine tuning top
783 doing_calibration_fine_tune_ = false;
784 LOG(DEBUG, "Calibrated the top correctly!\n");
785 } else if (top_claw_.calibration().last_value()) {
Brian Silvermane4c701d2014-04-10 19:29:25 -0700786 LOG(DEBUG, "Aborting top fine tune because sensor triggered\n");
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000787 doing_calibration_fine_tune_ = false;
Brian Silvermancc3844a2014-04-10 21:15:07 -0700788 top_claw_.set_zeroing_state(
789 ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
Austin Schuhcda86af2014-02-16 16:16:39 -0800790 }
791 }
792 // now set the bottom claw to track
Austin Schuhd27931c2014-02-16 19:18:20 -0800793 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800794 }
795 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800796 doing_calibration_fine_tune_ = false;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800797 if (!was_enabled_ && enabled) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800798 if (position) {
Brian Silverman72035692014-03-14 10:18:27 -0700799 top_claw_goal_ = position->top.position + top_claw_.offset();
800 bottom_claw_goal_ = position->bottom.position + bottom_claw_.offset();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800801 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800802 position->top.position - position->bottom.position;
Austin Schuhf9286cd2014-02-11 00:51:09 -0800803 } else {
804 has_top_claw_goal_ = false;
805 has_bottom_claw_goal_ = false;
806 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800807 }
Austin Schuhf9286cd2014-02-11 00:51:09 -0800808
Austin Schuh4cb047f2014-02-16 21:10:19 -0800809 if ((bottom_claw_.zeroing_state() !=
810 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
Brian Silvermane0a95462014-02-17 00:41:09 -0800811 bottom_claw_.front().value() || top_claw_.front().value()) &&
812 !top_claw_.back().value() && !bottom_claw_.back().value()) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800813 if (enabled) {
814 // Time to slowly move back up to find any position to narrow down the
815 // zero.
Austin Schuhd27931c2014-02-16 19:18:20 -0800816 top_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
817 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800818 top_claw_velocity_ = bottom_claw_velocity_ =
819 values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800820 LOG(DEBUG, "Bottom is known.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800821 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800822 } else {
823 // We don't know where either claw is. Slowly start moving down to find
824 // any hall effect.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800825 if (enabled) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800826 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
827 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800828 top_claw_velocity_ = bottom_claw_velocity_ =
829 -values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800830 LOG(DEBUG, "Both are unknown.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800831 }
832 }
833
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000834 if (position) {
835 if (enabled) {
836 top_claw_.SetCalibrationOnEdge(
837 values.claw.upper_claw,
838 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
839 bottom_claw_.SetCalibrationOnEdge(
840 values.claw.lower_claw,
841 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
842 } else {
843 // TODO(austin): Only calibrate on the predetermined edge.
844 // We might be able to just ignore this since the backlash is soooo
845 // low.
846 // :)
847 top_claw_.SetCalibrationOnEdge(
848 values.claw.upper_claw,
849 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
850 bottom_claw_.SetCalibrationOnEdge(
851 values.claw.lower_claw,
852 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
853 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800854 }
Austin Schuhe7f90d12014-02-17 00:48:25 -0800855 mode_ = UNKNOWN_LOCATION;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800856 }
857
Austin Schuh069143b2014-02-17 02:46:26 -0800858 // Limit the goals if both claws have been (mostly) found.
859 if (mode_ != UNKNOWN_LOCATION) {
860 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
861 }
862
Brian Silvermaned9df2f2014-04-05 07:07:15 -0700863 claw_.set_positions_known(top_claw_.zeroing_state() != ZeroedStateFeedbackLoop::UNKNOWN_POSITION, bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800864 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
Austin Schuh069143b2014-02-17 02:46:26 -0800865 claw_.R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
866 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Austin Schuhcda86af2014-02-16 16:16:39 -0800867 double separation = -971;
868 if (position != nullptr) {
869 separation = position->top.position - position->bottom.position;
870 }
Brian Silvermanf48fab32014-03-09 14:32:24 -0700871 LOG_STRUCT(DEBUG, "actual goal",
872 ClawGoalToLog(claw_.R(0, 0), claw_.R(1, 0), separation));
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800873
Austin Schuh01c652b2014-02-21 23:13:42 -0800874 // Only cap power when one of the halves of the claw is moving slowly and
875 // could wind up.
Austin Schuhe7f90d12014-02-17 00:48:25 -0800876 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
877 mode_ == FINE_TUNE_BOTTOM);
Austin Schuhcda86af2014-02-16 16:16:39 -0800878 claw_.Update(output == nullptr);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800879 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800880 claw_.Update(true);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800881 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800882
Austin Schuh4cb047f2014-02-16 21:10:19 -0800883 capped_goal_ = false;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800884 switch (mode_) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800885 case READY:
Austin Schuhe7f90d12014-02-17 00:48:25 -0800886 case PREP_FINE_TUNE_TOP:
887 case PREP_FINE_TUNE_BOTTOM:
Austin Schuhcda86af2014-02-16 16:16:39 -0800888 break;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800889 case FINE_TUNE_BOTTOM:
890 case FINE_TUNE_TOP:
Austin Schuh4cb047f2014-02-16 21:10:19 -0800891 case UNKNOWN_LOCATION: {
Brian Silverman7b7c9072014-03-30 13:33:30 -0700892 LOG_MATRIX(DEBUG, "U_uncapped", claw_.U_uncapped);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800893 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
Brian Silverman6dd2c532014-03-29 23:34:39 -0700894 double dx_bot = (claw_.U_uncapped(0, 0) -
Austin Schuh4cb047f2014-02-16 21:10:19 -0800895 values.claw.max_zeroing_voltage) /
896 claw_.K(0, 0);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700897 double dx_top = (claw_.U_uncapped(1, 0) -
James Kuszmaul0e866512014-02-21 13:12:52 -0800898 values.claw.max_zeroing_voltage) /
899 claw_.K(0, 0);
900 double dx = ::std::max(dx_top, dx_bot);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800901 bottom_claw_goal_ -= dx;
902 top_claw_goal_ -= dx;
James Kuszmaul0e866512014-02-21 13:12:52 -0800903 Eigen::Matrix<double, 4, 1> R;
904 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0), claw_.R(3, 0);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700905 claw_.U = claw_.K() * (R - claw_.X_hat);
Austin Schuhcda86af2014-02-16 16:16:39 -0800906 capped_goal_ = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700907 LOG(DEBUG, "Moving the goal by %f to prevent windup."
908 " Uncapped is %f, max is %f, difference is %f\n",
909 dx,
Austin Schuhe7f90d12014-02-17 00:48:25 -0800910 claw_.uncapped_average_voltage(), values.claw.max_zeroing_voltage,
911 (claw_.uncapped_average_voltage() -
912 values.claw.max_zeroing_voltage));
Austin Schuh4cb047f2014-02-16 21:10:19 -0800913 } else if (claw_.uncapped_average_voltage() <
914 -values.claw.max_zeroing_voltage) {
Brian Silverman6dd2c532014-03-29 23:34:39 -0700915 double dx_bot = (claw_.U_uncapped(0, 0) +
Austin Schuh4cb047f2014-02-16 21:10:19 -0800916 values.claw.max_zeroing_voltage) /
917 claw_.K(0, 0);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700918 double dx_top = (claw_.U_uncapped(1, 0) +
James Kuszmaul0e866512014-02-21 13:12:52 -0800919 values.claw.max_zeroing_voltage) /
920 claw_.K(0, 0);
921 double dx = ::std::min(dx_top, dx_bot);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800922 bottom_claw_goal_ -= dx;
923 top_claw_goal_ -= dx;
James Kuszmaul0e866512014-02-21 13:12:52 -0800924 Eigen::Matrix<double, 4, 1> R;
925 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0), claw_.R(3, 0);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700926 claw_.U = claw_.K() * (R - claw_.X_hat);
Austin Schuhcda86af2014-02-16 16:16:39 -0800927 capped_goal_ = true;
Austin Schuh4cb047f2014-02-16 21:10:19 -0800928 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Austin Schuhcda86af2014-02-16 16:16:39 -0800929 }
Austin Schuh4cb047f2014-02-16 21:10:19 -0800930 } break;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800931 }
932
933 if (output) {
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000934 if (goal) {
935 //setup the intake
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000936 output->intake_voltage =
937 (goal->intake > 12.0) ? 12 : (goal->intake < -12.0) ? -12.0
938 : goal->intake;
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000939 output->tusk_voltage = goal->centering;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000940 output->tusk_voltage =
941 (goal->centering > 12.0) ? 12 : (goal->centering < -12.0)
942 ? -12.0
943 : goal->centering;
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000944 }
James Kuszmauld536a402014-02-18 22:32:12 -0800945 output->top_claw_voltage = claw_.U(1, 0);
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000946 output->bottom_claw_voltage = claw_.U(0, 0);
Austin Schuhf84a1302014-02-19 00:23:30 -0800947
948 if (output->top_claw_voltage > kMaxVoltage) {
949 output->top_claw_voltage = kMaxVoltage;
950 } else if (output->top_claw_voltage < -kMaxVoltage) {
951 output->top_claw_voltage = -kMaxVoltage;
952 }
953
954 if (output->bottom_claw_voltage > kMaxVoltage) {
955 output->bottom_claw_voltage = kMaxVoltage;
956 } else if (output->bottom_claw_voltage < -kMaxVoltage) {
957 output->bottom_claw_voltage = -kMaxVoltage;
958 }
Austin Schuh3bb9a442014-02-02 16:01:45 -0800959 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800960
James Kuszmaul4abaf482014-02-26 21:16:35 -0800961 status->bottom = bottom_absolute_position();
962 status->separation = top_absolute_position() - bottom_absolute_position();
963 status->bottom_velocity = claw_.X_hat(2, 0);
964 status->separation_velocity = claw_.X_hat(3, 0);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800965
Brian Silverman71fbee02014-03-13 17:24:54 -0700966 if (goal) {
967 bool bottom_done =
968 ::std::abs(bottom_absolute_position() - goal->bottom_angle) < 0.020;
969 bool bottom_velocity_done = ::std::abs(status->bottom_velocity) < 0.2;
970 bool separation_done =
971 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
972 goal->separation_angle) < 0.020;
973 bool separation_done_with_ball =
974 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
975 goal->separation_angle) < 0.06;
976 status->done = is_ready() && separation_done && bottom_done && bottom_velocity_done;
977 status->done_with_ball =
978 is_ready() && separation_done_with_ball && bottom_done && bottom_velocity_done;
979 } else {
980 status->done = status->done_with_ball = false;
981 }
Austin Schuha556b012014-03-02 11:55:52 -0800982
Austin Schuh4f8633f2014-03-02 13:59:46 -0800983 status->zeroed = is_ready();
Brian Silverman80fc94c2014-03-09 16:56:01 -0700984 status->zeroed_for_auto =
985 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
986 top_claw_.zeroing_state() ==
987 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
988 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
989 bottom_claw_.zeroing_state() ==
990 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
Austin Schuh4f8633f2014-03-02 13:59:46 -0800991
Brian Silverman71fbee02014-03-13 17:24:54 -0700992 was_enabled_ = enabled;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800993}
994
995} // namespace control_loops
996} // namespace frc971
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000997