blob: dc7e11225b65fe33a46b7282bf0c3805f56e63ff [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;
Austin Schuhf84a1302014-02-19 00:23:30 -080062
Austin Schuh27b8fb12014-02-22 15:10:05 -080063ClawLimitedLoop::ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> loop)
64 : StateFeedbackLoop<4, 2, 2>(loop),
65 uncapped_average_voltage_(0.0),
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070066 is_zeroing_(true),
67 U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0,
68 -1, 0,
69 0, 1,
70 0, -1).finished(),
71 (Eigen::Matrix<double, 4, 1>() << kMaxVoltage, kMaxVoltage,
Brian Silverman6dd2c532014-03-29 23:34:39 -070072 kMaxVoltage, kMaxVoltage).finished()),
73 U_Poly_zeroing_((Eigen::Matrix<double, 4, 2>() << 1, 0,
74 -1, 0,
75 0, 1,
76 0, -1).finished(),
77 (Eigen::Matrix<double, 4, 1>() <<
78 kZeroingVoltage, kZeroingVoltage,
79 kZeroingVoltage, kZeroingVoltage).finished()) {
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070080 ::aos::controls::HPolytope<0>::Init();
81}
Austin Schuh27b8fb12014-02-22 15:10:05 -080082
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070083// Caps the voltage prioritizing reducing velocity error over reducing
84// positional error.
85// Uses the polytope libararies which we used to just use for the drivetrain.
86// Uses a region representing the maximum voltage and then transforms it such
87// that the points represent different amounts of positional error and
88// constrains the region such that, if at all possible, it will maintain its
89// current efforts to reduce velocity error.
Austin Schuhcda86af2014-02-16 16:16:39 -080090void ClawLimitedLoop::CapU() {
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070091 Eigen::Matrix<double, 4, 1> error = R - X_hat;
Austin Schuh4cb047f2014-02-16 21:10:19 -080092
James Kuszmauld536a402014-02-18 22:32:12 -080093 double u_top = U(1, 0);
94 double u_bottom = U(0, 0);
Austin Schuhcda86af2014-02-16 16:16:39 -080095
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070096 uncapped_average_voltage_ = (u_top + u_bottom) / 2;
97
98 double max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
99
100 if (::std::abs(u_bottom) > max_voltage or ::std::abs(u_top) > max_voltage) {
Brian Silverman6dd2c532014-03-29 23:34:39 -0700101 LOG_MATRIX(DEBUG, "U at start", U);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700102 // H * U <= k
103 // U = UPos + UVel
104 // H * (UPos + UVel) <= k
105 // H * UPos <= k - H * UVel
106
107 // Now, we can do a coordinate transformation and say the following.
108
109 // UPos = position_K * position_error
110 // (H * position_K) * position_error <= k - H * UVel
111
112 Eigen::Matrix<double, 2, 2> position_K;
113 position_K << K(0, 0), K(0, 1),
114 K(1, 0), K(1, 1);
115 Eigen::Matrix<double, 2, 2> velocity_K;
116 velocity_K << K(0, 2), K(0, 3),
117 K(1, 2), K(1, 3);
118
119 Eigen::Matrix<double, 2, 1> position_error;
120 position_error << error(0, 0), error(1, 0);
121 Eigen::Matrix<double, 2, 1> velocity_error;
122 velocity_error << error(2, 0), error(3, 0);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700123 LOG_MATRIX(DEBUG, "error", error);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700124
Brian Silverman6dd2c532014-03-29 23:34:39 -0700125 const auto &poly = is_zeroing_ ? U_Poly_zeroing_ : U_Poly_;
126 const Eigen::Matrix<double, 4, 2> pos_poly_H = poly.H() * position_K;
127 const Eigen::Matrix<double, 4, 1> pos_poly_k =
128 poly.k() - poly.H() * velocity_K * velocity_error;
129 const ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700130
Brian Silverman6dd2c532014-03-29 23:34:39 -0700131 Eigen::Matrix<double, 2, 1> adjusted_pos_error;
132 {
133 const auto &P = position_error;
Brian Silvermanb087c0a2014-03-30 12:59:52 -0700134
135 // This line was at 45 degrees but is now at some angle steeper than the
136 // straight one between the points.
137 Eigen::Matrix<double, 1, 2> angle_45;
138 // If the top claw is above its soft upper limit, make the line actually
139 // 45 degrees to avoid smashing it into the limit in an attempt to fix the
140 // separation error faster than the bottom position one.
141 if (X_hat(0, 0) + X_hat(1, 0) >
142 constants::GetValues().claw.upper_claw.upper_limit) {
143 angle_45 << 1, 1;
144 } else {
145 // Fixing separation error half as fast as positional error works well
146 // because it means they both close evenly.
147 angle_45 << ::std::sqrt(3), 1;
148 }
149 Eigen::Matrix<double, 1, 2> L45_quadrant;
150 L45_quadrant << sign(P(1, 0)), -sign(P(0, 0));
151 const auto L45 = L45_quadrant.cwiseProduct(angle_45);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700152 const double w45 = 0;
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700153
Brian Silverman6dd2c532014-03-29 23:34:39 -0700154 Eigen::Matrix<double, 1, 2> LH;
155 if (::std::abs(P(0, 0)) > ::std::abs(P(1, 0))) {
156 LH << 0, 1;
157 } else {
158 LH << 1, 0;
159 }
160 const double wh = LH.dot(P);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700161
Brian Silverman6dd2c532014-03-29 23:34:39 -0700162 Eigen::Matrix<double, 2, 2> standard;
163 standard << L45, LH;
164 Eigen::Matrix<double, 2, 1> W;
165 W << w45, wh;
166 const Eigen::Matrix<double, 2, 1> intersection = standard.inverse() * W;
167
168 bool is_inside_h;
169 const auto adjusted_pos_error_h =
170 DoCoerceGoal(pos_poly, LH, wh, position_error, &is_inside_h);
171 const auto adjusted_pos_error_45 =
172 DoCoerceGoal(pos_poly, L45, w45, intersection, nullptr);
173 if (pos_poly.IsInside(intersection)) {
174 adjusted_pos_error = adjusted_pos_error_h;
175 } else {
176 if (is_inside_h) {
177 if (adjusted_pos_error_h.norm() > adjusted_pos_error_45.norm()) {
178 adjusted_pos_error = adjusted_pos_error_h;
179 } else {
180 adjusted_pos_error = adjusted_pos_error_45;
181 }
182 } else {
183 adjusted_pos_error = adjusted_pos_error_45;
184 }
185 }
186 }
187
188 LOG_MATRIX(DEBUG, "adjusted_pos_error", adjusted_pos_error);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700189 U = velocity_K * velocity_error + position_K * adjusted_pos_error;
Brian Silverman6dd2c532014-03-29 23:34:39 -0700190 LOG_MATRIX(DEBUG, "U is now", U);
Brian Silverman7b7c9072014-03-30 13:33:30 -0700191
192 {
193 const auto values = constants::GetValues().claw;
194 if (X_hat(0, 0) + X_hat(1, 0) > values.upper_claw.upper_limit &&
195 U(1, 0) > 0) {
196 LOG(WARNING, "upper claw too high and moving up\n");
197 U(1, 0) = 0;
198 } else if (X_hat(0, 0) + X_hat(1, 0) < values.upper_claw.lower_limit &&
199 U(1, 0) < 0) {
200 LOG(WARNING, "upper claw too low and moving down\n");
201 U(1, 0) = 0;
202 }
203 if (X_hat(0, 0) > values.lower_claw.upper_limit && U(0, 0) > 0) {
204 LOG(WARNING, "lower claw too high and moving up\n");
205 U(0, 0) = 0;
206 } else if (X_hat(0, 0) < values.lower_claw.lower_limit && U(0, 0) < 0) {
207 LOG(WARNING, "lower claw too low and moving down\n");
208 U(0, 0) = 0;
209 }
210 }
James Kuszmauld536a402014-02-18 22:32:12 -0800211 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800212}
213
Austin Schuh27b8fb12014-02-22 15:10:05 -0800214ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
215 ClawMotor *motor)
216 : offset_(0.0),
217 name_(name),
218 motor_(motor),
219 zeroing_state_(UNKNOWN_POSITION),
220 posedge_value_(0.0),
221 negedge_value_(0.0),
222 encoder_(0.0),
223 last_encoder_(0.0) {}
224
225void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition &claw) {
226 front_.Update(claw.front);
227 calibration_.Update(claw.calibration);
228 back_.Update(claw.back);
229
230 bool any_sensor_triggered = any_triggered();
231 if (any_sensor_triggered && any_triggered_last_) {
232 // We are still on the hall effect and nothing has changed.
233 min_hall_effect_on_angle_ =
234 ::std::min(min_hall_effect_on_angle_, claw.position);
235 max_hall_effect_on_angle_ =
236 ::std::max(max_hall_effect_on_angle_, claw.position);
237 } else if (!any_sensor_triggered && !any_triggered_last_) {
238 // We are still off the hall effect and nothing has changed.
239 min_hall_effect_off_angle_ =
240 ::std::min(min_hall_effect_off_angle_, claw.position);
241 max_hall_effect_off_angle_ =
242 ::std::max(max_hall_effect_off_angle_, claw.position);
243 } else if (any_sensor_triggered && !any_triggered_last_) {
244 // Saw a posedge on the hall effect. Reset the limits.
245 min_hall_effect_on_angle_ = ::std::min(claw.posedge_value, claw.position);
246 max_hall_effect_on_angle_ = ::std::max(claw.posedge_value, claw.position);
247 } else if (!any_sensor_triggered && any_triggered_last_) {
248 // Saw a negedge on the hall effect. Reset the limits.
249 min_hall_effect_off_angle_ = ::std::min(claw.negedge_value, claw.position);
250 max_hall_effect_off_angle_ = ::std::max(claw.negedge_value, claw.position);
251 }
252
253 posedge_value_ = claw.posedge_value;
254 negedge_value_ = claw.negedge_value;
255 last_encoder_ = encoder_;
256 if (front().value() || calibration().value() || back().value()) {
257 last_on_encoder_ = encoder_;
258 } else {
259 last_off_encoder_ = encoder_;
260 }
261 encoder_ = claw.position;
262 any_triggered_last_ = any_sensor_triggered;
263}
264
265void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition &claw) {
266 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
267
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000268 front_.Reset(claw.front);
269 calibration_.Reset(claw.calibration);
270 back_.Reset(claw.back);
Austin Schuh27b8fb12014-02-22 15:10:05 -0800271 // close up the min and max edge positions as they are no longer valid and
272 // will be expanded in future iterations
273 min_hall_effect_on_angle_ = claw.position;
274 max_hall_effect_on_angle_ = claw.position;
275 min_hall_effect_off_angle_ = claw.position;
276 max_hall_effect_off_angle_ = claw.position;
277 any_triggered_last_ = any_triggered();
278}
279
280bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
281 const constants::Values::Claws::Claw &claw_values,
282 JointZeroingState zeroing_state) {
283 double edge_encoder;
284 double edge_angle;
285 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
286 LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
287 SetCalibration(edge_encoder, edge_angle);
288 set_zeroing_state(zeroing_state);
289 return true;
290 }
291 return false;
292}
293
294bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
295 const constants::Values::Claws::Claw &claw_values,
296 JointZeroingState zeroing_state) {
297 double edge_encoder;
298 double edge_angle;
299 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
300 LOG(INFO, "Calibration edge.\n");
301 SetCalibration(edge_encoder, edge_angle);
302 set_zeroing_state(zeroing_state);
303 return true;
304 }
305 return false;
306}
307
Austin Schuhcc0bf312014-02-09 00:39:29 -0800308ClawMotor::ClawMotor(control_loops::ClawGroup *my_claw)
Brian Silverman71fbee02014-03-13 17:24:54 -0700309 : aos::control_loops::ControlLoop<control_loops::ClawGroup, true, true,
310 false>(my_claw),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800311 has_top_claw_goal_(false),
312 top_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800313 top_claw_(this),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800314 has_bottom_claw_goal_(false),
315 bottom_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800316 bottom_claw_(this),
317 claw_(MakeClawLoop()),
Ben Fredrickson9b388422014-02-13 06:15:31 +0000318 was_enabled_(false),
Austin Schuh4cb047f2014-02-16 21:10:19 -0800319 doing_calibration_fine_tune_(false),
Austin Schuhe7f90d12014-02-17 00:48:25 -0800320 capped_goal_(false),
321 mode_(UNKNOWN_LOCATION) {}
Austin Schuh3bb9a442014-02-02 16:01:45 -0800322
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800323const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800324
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000325bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
326 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
327 const HallEffectTracker &sensorB) {
328 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
329 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
330 this_sensor.value() && !this_sensor.last_value()) {
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000331 posedge_filter_ = &this_sensor;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000332 } else if (posedge_filter_ == &this_sensor &&
333 !this_sensor.posedge_count_changed() &&
334 !sensorA.posedge_count_changed() &&
335 !sensorB.posedge_count_changed() && this_sensor.value()) {
336 posedge_filter_ = nullptr;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000337 return true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000338 } else if (posedge_filter_ == &this_sensor) {
339 posedge_filter_ = nullptr;
340 }
341 return false;
342}
343
344bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
345 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
346 const HallEffectTracker &sensorB) {
347 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
348 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
349 !this_sensor.value() && this_sensor.last_value()) {
350 negedge_filter_ = &this_sensor;
351 } else if (negedge_filter_ == &this_sensor &&
352 !this_sensor.negedge_count_changed() &&
353 !sensorA.negedge_count_changed() &&
354 !sensorB.negedge_count_changed() && !this_sensor.value()) {
355 negedge_filter_ = nullptr;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000356 return true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000357 } else if (negedge_filter_ == &this_sensor) {
358 negedge_filter_ = nullptr;
359 }
360 return false;
361}
362
Brian Silvermane0a95462014-02-17 00:41:09 -0800363bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
364 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000365 double *edge_angle, const HallEffectTracker &this_sensor,
366 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
Brian Silvermane0a95462014-02-17 00:41:09 -0800367 const char *hall_effect_name) {
Austin Schuhf84a1302014-02-19 00:23:30 -0800368 bool found_edge = false;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800369
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000370 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800371 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700372 LOG(WARNING, "%s: Uncertain which side, rejecting posedge\n", name_);
Brian Silvermane0a95462014-02-17 00:41:09 -0800373 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800374 const double average_last_encoder =
375 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
376 if (posedge_value_ < average_last_encoder) {
377 *edge_angle = angles.upper_decreasing_angle;
378 LOG(INFO, "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
379 name_, hall_effect_name, *edge_angle, posedge_value_,
380 average_last_encoder);
381 } else {
382 *edge_angle = angles.lower_angle;
383 LOG(INFO, "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
384 name_, hall_effect_name, *edge_angle, posedge_value_,
385 average_last_encoder);
386 }
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000387 *edge_encoder = posedge_value_;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800388 found_edge = true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000389 }
390 }
391
392 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
393 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700394 LOG(WARNING, "%s: Uncertain which side, rejecting negedge\n", name_);
Brian Silvermane0a95462014-02-17 00:41:09 -0800395 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800396 const double average_last_encoder =
397 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
398 if (negedge_value_ > average_last_encoder) {
399 *edge_angle = angles.upper_angle;
400 LOG(INFO, "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
401 name_, hall_effect_name, *edge_angle, negedge_value_,
402 average_last_encoder);
403 } else {
404 *edge_angle = angles.lower_decreasing_angle;
405 LOG(INFO, "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
406 name_, hall_effect_name, *edge_angle, negedge_value_,
407 average_last_encoder);
408 }
409 *edge_encoder = negedge_value_;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000410 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800411 }
Austin Schuh27b8fb12014-02-22 15:10:05 -0800412 }
413
Austin Schuhf84a1302014-02-19 00:23:30 -0800414 return found_edge;
Brian Silvermane0a95462014-02-17 00:41:09 -0800415}
416
Austin Schuhf9286cd2014-02-11 00:51:09 -0800417bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800418 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
Austin Schuhf9286cd2014-02-11 00:51:09 -0800419 double *edge_angle) {
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000420 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
421 calibration_, back_, "front")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800422 return true;
423 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800424 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000425 calibration_, front_, back_, "calibration")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800426 return true;
427 }
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000428 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
429 calibration_, front_, "back")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800430 return true;
431 }
432 return false;
433}
434
Austin Schuhcda86af2014-02-16 16:16:39 -0800435void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
436 double edge_angle) {
437 double old_offset = offset_;
438 offset_ = edge_angle - edge_encoder;
439 const double doffset = offset_ - old_offset;
440 motor_->ChangeTopOffset(doffset);
441}
442
443void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
444 double edge_angle) {
445 double old_offset = offset_;
446 offset_ = edge_angle - edge_encoder;
447 const double doffset = offset_ - old_offset;
448 motor_->ChangeBottomOffset(doffset);
449}
450
451void ClawMotor::ChangeTopOffset(double doffset) {
452 claw_.ChangeTopOffset(doffset);
453 if (has_top_claw_goal_) {
454 top_claw_goal_ += doffset;
455 }
456}
457
458void ClawMotor::ChangeBottomOffset(double doffset) {
459 claw_.ChangeBottomOffset(doffset);
460 if (has_bottom_claw_goal_) {
461 bottom_claw_goal_ += doffset;
462 }
463}
464
465void ClawLimitedLoop::ChangeTopOffset(double doffset) {
466 Y_(1, 0) += doffset;
467 X_hat(1, 0) += doffset;
468 LOG(INFO, "Changing top offset by %f\n", doffset);
469}
470void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
471 Y_(0, 0) += doffset;
472 X_hat(0, 0) += doffset;
473 X_hat(1, 0) -= doffset;
474 LOG(INFO, "Changing bottom offset by %f\n", doffset);
475}
joe7376ff52014-02-16 18:28:42 -0800476
Austin Schuh069143b2014-02-17 02:46:26 -0800477void LimitClawGoal(double *bottom_goal, double *top_goal,
478 const frc971::constants::Values &values) {
479 // first update position based on angle limit
480
481 const double separation = *top_goal - *bottom_goal;
482 if (separation > values.claw.claw_max_separation) {
Austin Schuh069143b2014-02-17 02:46:26 -0800483 const double dsep = (separation - values.claw.claw_max_separation) / 2.0;
484 *bottom_goal += dsep;
485 *top_goal -= dsep;
486 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
487 }
488 if (separation < values.claw.claw_min_separation) {
Austin Schuh069143b2014-02-17 02:46:26 -0800489 const double dsep = (separation - values.claw.claw_min_separation) / 2.0;
490 *bottom_goal += dsep;
491 *top_goal -= dsep;
492 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
493 }
494
495 // now move both goals in unison
496 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
497 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
498 *bottom_goal = values.claw.lower_claw.lower_limit;
499 }
500 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
501 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
502 *bottom_goal = values.claw.lower_claw.upper_limit;
503 }
504
505 if (*top_goal < values.claw.upper_claw.lower_limit) {
506 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
507 *top_goal = values.claw.upper_claw.lower_limit;
508 }
509 if (*top_goal > values.claw.upper_claw.upper_limit) {
510 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
511 *top_goal = values.claw.upper_claw.upper_limit;
512 }
513}
Austin Schuhcda86af2014-02-16 16:16:39 -0800514
Austin Schuhe7f90d12014-02-17 00:48:25 -0800515bool ClawMotor::is_ready() const {
516 return (
517 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
518 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
Brian Silverman71fbee02014-03-13 17:24:54 -0700519 (((::aos::robot_state.get() == NULL) ? true
520 : ::aos::robot_state->autonomous) &&
Austin Schuhe7f90d12014-02-17 00:48:25 -0800521 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
522 top_claw_.zeroing_state() ==
523 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
524 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
525 bottom_claw_.zeroing_state() ==
526 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
527}
528
529bool ClawMotor::is_zeroing() const { return !is_ready(); }
530
Austin Schuh3bb9a442014-02-02 16:01:45 -0800531// Positive angle is up, and positive power is up.
Austin Schuhcc0bf312014-02-09 00:39:29 -0800532void ClawMotor::RunIteration(const control_loops::ClawGroup::Goal *goal,
533 const control_loops::ClawGroup::Position *position,
534 control_loops::ClawGroup::Output *output,
James Kuszmaul9ead1de2014-02-28 21:24:39 -0800535 control_loops::ClawGroup::Status *status) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800536 constexpr double dt = 0.01;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800537
538 // Disable the motors now so that all early returns will return with the
539 // motors disabled.
540 if (output) {
541 output->top_claw_voltage = 0;
542 output->bottom_claw_voltage = 0;
543 output->intake_voltage = 0;
Ben Fredrickson61893d52014-03-02 09:43:23 +0000544 output->tusk_voltage = 0;
545 }
546
Brian Silverman71fbee02014-03-13 17:24:54 -0700547 if (goal) {
548 if (::std::isnan(goal->bottom_angle) ||
549 ::std::isnan(goal->separation_angle) || ::std::isnan(goal->intake) ||
550 ::std::isnan(goal->centering)) {
551 return;
552 }
Austin Schuh3bb9a442014-02-02 16:01:45 -0800553 }
554
Austin Schuh1a499942014-02-17 01:51:58 -0800555 if (reset()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800556 top_claw_.Reset(position->top);
557 bottom_claw_.Reset(position->bottom);
Austin Schuh1a499942014-02-17 01:51:58 -0800558 }
559
Austin Schuhf9286cd2014-02-11 00:51:09 -0800560 const frc971::constants::Values &values = constants::GetValues();
561
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800562 if (position) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800563 Eigen::Matrix<double, 2, 1> Y;
564 Y << position->bottom.position + bottom_claw_.offset(),
565 position->top.position + top_claw_.offset();
566 claw_.Correct(Y);
567
Austin Schuhf9286cd2014-02-11 00:51:09 -0800568 top_claw_.SetPositionValues(position->top);
569 bottom_claw_.SetPositionValues(position->bottom);
570
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800571 if (!has_top_claw_goal_) {
572 has_top_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800573 top_claw_goal_ = top_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800574 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800575 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800576 }
577 if (!has_bottom_claw_goal_) {
578 has_bottom_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800579 bottom_claw_goal_ = bottom_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800580 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800581 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800582 }
Brian Silvermanf48fab32014-03-09 14:32:24 -0700583 LOG_STRUCT(DEBUG, "absolute position",
584 ClawPositionToLog(top_claw_.absolute_position(),
585 bottom_claw_.absolute_position()));
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800586 }
587
Brian Silverman71fbee02014-03-13 17:24:54 -0700588 bool autonomous, enabled;
589 if (::aos::robot_state.get() == nullptr) {
590 autonomous = true;
591 enabled = false;
592 } else {
593 autonomous = ::aos::robot_state->autonomous;
594 enabled = ::aos::robot_state->enabled;
595 }
Austin Schuh069143b2014-02-17 02:46:26 -0800596
597 double bottom_claw_velocity_ = 0.0;
598 double top_claw_velocity_ = 0.0;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800599
Brian Silverman71fbee02014-03-13 17:24:54 -0700600 if (goal != NULL &&
601 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
602 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
603 (autonomous &&
604 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
605 top_claw_.zeroing_state() ==
606 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
607 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
608 bottom_claw_.zeroing_state() ==
609 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))))) {
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800610 // Ready to use the claw.
611 // Limit the goals here.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800612 bottom_claw_goal_ = goal->bottom_angle;
Brian Silverman7c021c42014-02-17 15:15:56 -0800613 top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
Austin Schuhcda86af2014-02-16 16:16:39 -0800614 has_bottom_claw_goal_ = true;
615 has_top_claw_goal_ = true;
616 doing_calibration_fine_tune_ = false;
617
Austin Schuhe7f90d12014-02-17 00:48:25 -0800618 mode_ = READY;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800619 } else if (top_claw_.zeroing_state() !=
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000620 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800621 bottom_claw_.zeroing_state() !=
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000622 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800623 // Time to fine tune the zero.
624 // Limit the goals here.
Austin Schuh0c733422014-02-17 01:17:12 -0800625 if (!enabled) {
626 // If we are disabled, start the fine tune process over again.
627 doing_calibration_fine_tune_ = false;
628 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800629 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800630 // always get the bottom claw to calibrated first
631 LOG(DEBUG, "Calibrating the bottom of the claw\n");
632 if (!doing_calibration_fine_tune_) {
633 if (::std::abs(bottom_absolute_position() -
Austin Schuhd27931c2014-02-16 19:18:20 -0800634 values.claw.start_fine_tune_pos) <
635 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800636 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800637 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800638 top_claw_velocity_ = bottom_claw_velocity_ =
639 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800640 LOG(DEBUG, "Ready to fine tune the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800641 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800642 } else {
643 // send bottom to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800644 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800645 LOG(DEBUG, "Going to the start position for the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800646 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800647 }
648 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800649 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhd27931c2014-02-16 19:18:20 -0800650 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800651 top_claw_velocity_ = bottom_claw_velocity_ =
652 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800653 if (top_claw_.front_or_back_triggered() ||
654 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800655 // We shouldn't hit a limit, but if we do, go back to the zeroing
656 // point and try again.
657 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800658 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800659 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800660 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800661 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800662 }
Austin Schuh288c8c32014-02-16 17:20:17 -0800663
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000664 if (position && bottom_claw_.SawFilteredPosedge(
665 bottom_claw_.calibration(), bottom_claw_.front(),
666 bottom_claw_.back())) {
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000667 // do calibration
668 bottom_claw_.SetCalibration(
669 position->bottom.posedge_value,
670 values.claw.lower_claw.calibration.lower_angle);
671 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
672 // calibrated so we are done fine tuning bottom
673 doing_calibration_fine_tune_ = false;
674 LOG(DEBUG, "Calibrated the bottom correctly!\n");
675 } else if (bottom_claw_.calibration().last_value()) {
676 doing_calibration_fine_tune_ = false;
677 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
678 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
679 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800680 } else {
681 LOG(DEBUG, "Fine tuning\n");
682 }
683 }
684 // now set the top claw to track
685
Austin Schuhd27931c2014-02-16 19:18:20 -0800686 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800687 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800688 // bottom claw must be calibrated, start on the top
689 if (!doing_calibration_fine_tune_) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800690 if (::std::abs(top_absolute_position() -
691 values.claw.start_fine_tune_pos) <
692 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800693 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800694 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800695 top_claw_velocity_ = bottom_claw_velocity_ =
696 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800697 LOG(DEBUG, "Ready to fine tune the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800698 mode_ = FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800699 } else {
700 // send top to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800701 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800702 LOG(DEBUG, "Going to the start position for the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800703 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800704 }
705 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800706 mode_ = FINE_TUNE_TOP;
Austin Schuhd27931c2014-02-16 19:18:20 -0800707 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800708 top_claw_velocity_ = bottom_claw_velocity_ =
709 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800710 if (top_claw_.front_or_back_triggered() ||
711 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800712 // this should not happen, but now we know it won't
713 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800714 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800715 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800716 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800717 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800718 }
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000719
720 if (position &&
721 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
722 top_claw_.front(), top_claw_.back())) {
723 // do calibration
724 top_claw_.SetCalibration(
725 position->top.posedge_value,
726 values.claw.upper_claw.calibration.lower_angle);
727 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
728 // calibrated so we are done fine tuning top
729 doing_calibration_fine_tune_ = false;
730 LOG(DEBUG, "Calibrated the top correctly!\n");
731 } else if (top_claw_.calibration().last_value()) {
732 doing_calibration_fine_tune_ = false;
733 top_claw_goal_ = values.claw.start_fine_tune_pos;
734 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
735 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800736 }
737 }
738 // now set the bottom claw to track
Austin Schuhd27931c2014-02-16 19:18:20 -0800739 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800740 }
741 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800742 doing_calibration_fine_tune_ = false;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800743 if (!was_enabled_ && enabled) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800744 if (position) {
Brian Silverman72035692014-03-14 10:18:27 -0700745 top_claw_goal_ = position->top.position + top_claw_.offset();
746 bottom_claw_goal_ = position->bottom.position + bottom_claw_.offset();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800747 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800748 position->top.position - position->bottom.position;
Austin Schuhf9286cd2014-02-11 00:51:09 -0800749 } else {
750 has_top_claw_goal_ = false;
751 has_bottom_claw_goal_ = false;
752 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800753 }
Austin Schuhf9286cd2014-02-11 00:51:09 -0800754
Austin Schuh4cb047f2014-02-16 21:10:19 -0800755 if ((bottom_claw_.zeroing_state() !=
756 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
Brian Silvermane0a95462014-02-17 00:41:09 -0800757 bottom_claw_.front().value() || top_claw_.front().value()) &&
758 !top_claw_.back().value() && !bottom_claw_.back().value()) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800759 if (enabled) {
760 // Time to slowly move back up to find any position to narrow down the
761 // zero.
Austin Schuhd27931c2014-02-16 19:18:20 -0800762 top_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
763 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800764 top_claw_velocity_ = bottom_claw_velocity_ =
765 values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800766 LOG(DEBUG, "Bottom is known.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800767 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800768 } else {
769 // We don't know where either claw is. Slowly start moving down to find
770 // any hall effect.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800771 if (enabled) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800772 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
773 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800774 top_claw_velocity_ = bottom_claw_velocity_ =
775 -values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800776 LOG(DEBUG, "Both are unknown.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800777 }
778 }
779
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000780 if (position) {
781 if (enabled) {
782 top_claw_.SetCalibrationOnEdge(
783 values.claw.upper_claw,
784 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
785 bottom_claw_.SetCalibrationOnEdge(
786 values.claw.lower_claw,
787 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
788 } else {
789 // TODO(austin): Only calibrate on the predetermined edge.
790 // We might be able to just ignore this since the backlash is soooo
791 // low.
792 // :)
793 top_claw_.SetCalibrationOnEdge(
794 values.claw.upper_claw,
795 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
796 bottom_claw_.SetCalibrationOnEdge(
797 values.claw.lower_claw,
798 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
799 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800800 }
Austin Schuhe7f90d12014-02-17 00:48:25 -0800801 mode_ = UNKNOWN_LOCATION;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800802 }
803
Austin Schuh069143b2014-02-17 02:46:26 -0800804 // Limit the goals if both claws have been (mostly) found.
805 if (mode_ != UNKNOWN_LOCATION) {
806 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
807 }
808
Austin Schuhf9286cd2014-02-11 00:51:09 -0800809 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
Austin Schuh069143b2014-02-17 02:46:26 -0800810 claw_.R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
811 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Austin Schuhcda86af2014-02-16 16:16:39 -0800812 double separation = -971;
813 if (position != nullptr) {
814 separation = position->top.position - position->bottom.position;
815 }
Brian Silvermanf48fab32014-03-09 14:32:24 -0700816 LOG_STRUCT(DEBUG, "actual goal",
817 ClawGoalToLog(claw_.R(0, 0), claw_.R(1, 0), separation));
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800818
Austin Schuh01c652b2014-02-21 23:13:42 -0800819 // Only cap power when one of the halves of the claw is moving slowly and
820 // could wind up.
Austin Schuhe7f90d12014-02-17 00:48:25 -0800821 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
822 mode_ == FINE_TUNE_BOTTOM);
Austin Schuhcda86af2014-02-16 16:16:39 -0800823 claw_.Update(output == nullptr);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800824 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800825 claw_.Update(true);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800826 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800827
Austin Schuh4cb047f2014-02-16 21:10:19 -0800828 capped_goal_ = false;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800829 switch (mode_) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800830 case READY:
Austin Schuhe7f90d12014-02-17 00:48:25 -0800831 case PREP_FINE_TUNE_TOP:
832 case PREP_FINE_TUNE_BOTTOM:
Austin Schuhcda86af2014-02-16 16:16:39 -0800833 break;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800834 case FINE_TUNE_BOTTOM:
835 case FINE_TUNE_TOP:
Austin Schuh4cb047f2014-02-16 21:10:19 -0800836 case UNKNOWN_LOCATION: {
Brian Silverman7b7c9072014-03-30 13:33:30 -0700837 LOG_MATRIX(DEBUG, "U_uncapped", claw_.U_uncapped);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800838 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
Brian Silverman6dd2c532014-03-29 23:34:39 -0700839 double dx_bot = (claw_.U_uncapped(0, 0) -
Austin Schuh4cb047f2014-02-16 21:10:19 -0800840 values.claw.max_zeroing_voltage) /
841 claw_.K(0, 0);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700842 double dx_top = (claw_.U_uncapped(1, 0) -
James Kuszmaul0e866512014-02-21 13:12:52 -0800843 values.claw.max_zeroing_voltage) /
844 claw_.K(0, 0);
845 double dx = ::std::max(dx_top, dx_bot);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800846 bottom_claw_goal_ -= dx;
847 top_claw_goal_ -= dx;
James Kuszmaul0e866512014-02-21 13:12:52 -0800848 Eigen::Matrix<double, 4, 1> R;
849 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 -0700850 claw_.U = claw_.K() * (R - claw_.X_hat);
Austin Schuhcda86af2014-02-16 16:16:39 -0800851 capped_goal_ = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700852 LOG(DEBUG, "Moving the goal by %f to prevent windup."
853 " Uncapped is %f, max is %f, difference is %f\n",
854 dx,
Austin Schuhe7f90d12014-02-17 00:48:25 -0800855 claw_.uncapped_average_voltage(), values.claw.max_zeroing_voltage,
856 (claw_.uncapped_average_voltage() -
857 values.claw.max_zeroing_voltage));
Austin Schuh4cb047f2014-02-16 21:10:19 -0800858 } else if (claw_.uncapped_average_voltage() <
859 -values.claw.max_zeroing_voltage) {
Brian Silverman6dd2c532014-03-29 23:34:39 -0700860 double dx_bot = (claw_.U_uncapped(0, 0) +
Austin Schuh4cb047f2014-02-16 21:10:19 -0800861 values.claw.max_zeroing_voltage) /
862 claw_.K(0, 0);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700863 double dx_top = (claw_.U_uncapped(1, 0) +
James Kuszmaul0e866512014-02-21 13:12:52 -0800864 values.claw.max_zeroing_voltage) /
865 claw_.K(0, 0);
866 double dx = ::std::min(dx_top, dx_bot);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800867 bottom_claw_goal_ -= dx;
868 top_claw_goal_ -= dx;
James Kuszmaul0e866512014-02-21 13:12:52 -0800869 Eigen::Matrix<double, 4, 1> R;
870 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 -0700871 claw_.U = claw_.K() * (R - claw_.X_hat);
Austin Schuhcda86af2014-02-16 16:16:39 -0800872 capped_goal_ = true;
Austin Schuh4cb047f2014-02-16 21:10:19 -0800873 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Austin Schuhcda86af2014-02-16 16:16:39 -0800874 }
Austin Schuh4cb047f2014-02-16 21:10:19 -0800875 } break;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800876 }
877
878 if (output) {
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000879 if (goal) {
880 //setup the intake
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000881 output->intake_voltage =
882 (goal->intake > 12.0) ? 12 : (goal->intake < -12.0) ? -12.0
883 : goal->intake;
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000884 output->tusk_voltage = goal->centering;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000885 output->tusk_voltage =
886 (goal->centering > 12.0) ? 12 : (goal->centering < -12.0)
887 ? -12.0
888 : goal->centering;
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000889 }
James Kuszmauld536a402014-02-18 22:32:12 -0800890 output->top_claw_voltage = claw_.U(1, 0);
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000891 output->bottom_claw_voltage = claw_.U(0, 0);
Austin Schuhf84a1302014-02-19 00:23:30 -0800892
893 if (output->top_claw_voltage > kMaxVoltage) {
894 output->top_claw_voltage = kMaxVoltage;
895 } else if (output->top_claw_voltage < -kMaxVoltage) {
896 output->top_claw_voltage = -kMaxVoltage;
897 }
898
899 if (output->bottom_claw_voltage > kMaxVoltage) {
900 output->bottom_claw_voltage = kMaxVoltage;
901 } else if (output->bottom_claw_voltage < -kMaxVoltage) {
902 output->bottom_claw_voltage = -kMaxVoltage;
903 }
Austin Schuh3bb9a442014-02-02 16:01:45 -0800904 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800905
James Kuszmaul4abaf482014-02-26 21:16:35 -0800906 status->bottom = bottom_absolute_position();
907 status->separation = top_absolute_position() - bottom_absolute_position();
908 status->bottom_velocity = claw_.X_hat(2, 0);
909 status->separation_velocity = claw_.X_hat(3, 0);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800910
Brian Silverman71fbee02014-03-13 17:24:54 -0700911 if (goal) {
912 bool bottom_done =
913 ::std::abs(bottom_absolute_position() - goal->bottom_angle) < 0.020;
914 bool bottom_velocity_done = ::std::abs(status->bottom_velocity) < 0.2;
915 bool separation_done =
916 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
917 goal->separation_angle) < 0.020;
918 bool separation_done_with_ball =
919 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
920 goal->separation_angle) < 0.06;
921 status->done = is_ready() && separation_done && bottom_done && bottom_velocity_done;
922 status->done_with_ball =
923 is_ready() && separation_done_with_ball && bottom_done && bottom_velocity_done;
924 } else {
925 status->done = status->done_with_ball = false;
926 }
Austin Schuha556b012014-03-02 11:55:52 -0800927
Austin Schuh4f8633f2014-03-02 13:59:46 -0800928 status->zeroed = is_ready();
Brian Silverman80fc94c2014-03-09 16:56:01 -0700929 status->zeroed_for_auto =
930 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
931 top_claw_.zeroing_state() ==
932 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
933 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
934 bottom_claw_.zeroing_state() ==
935 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
Austin Schuh4f8633f2014-03-02 13:59:46 -0800936
Brian Silverman71fbee02014-03-13 17:24:54 -0700937 was_enabled_ = enabled;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800938}
939
940} // namespace control_loops
941} // namespace frc971
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000942