blob: 06d95fe94e0f7bd5d5151d889d7ef6b09e3c5de9 [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;
Brian Silvermaned9df2f2014-04-05 07:07:15 -0700194 if (top_known_) {
195 if (X_hat(0, 0) + X_hat(1, 0) > values.upper_claw.upper_limit &&
196 U(1, 0) > 0) {
197 LOG(WARNING, "upper claw too high and moving up\n");
198 U(1, 0) = 0;
199 } else if (X_hat(0, 0) + X_hat(1, 0) < values.upper_claw.lower_limit &&
200 U(1, 0) < 0) {
201 LOG(WARNING, "upper claw too low and moving down\n");
202 U(1, 0) = 0;
203 }
Brian Silverman7b7c9072014-03-30 13:33:30 -0700204 }
Brian Silvermaned9df2f2014-04-05 07:07:15 -0700205 if (bottom_known_) {
206 if (X_hat(0, 0) > values.lower_claw.upper_limit && U(0, 0) > 0) {
207 LOG(WARNING, "lower claw too high and moving up\n");
208 U(0, 0) = 0;
209 } else if (X_hat(0, 0) < values.lower_claw.lower_limit && U(0, 0) < 0) {
210 LOG(WARNING, "lower claw too low and moving down\n");
211 U(0, 0) = 0;
212 }
Brian Silverman7b7c9072014-03-30 13:33:30 -0700213 }
214 }
James Kuszmauld536a402014-02-18 22:32:12 -0800215 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800216}
217
Austin Schuh27b8fb12014-02-22 15:10:05 -0800218ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
219 ClawMotor *motor)
220 : offset_(0.0),
221 name_(name),
222 motor_(motor),
223 zeroing_state_(UNKNOWN_POSITION),
224 posedge_value_(0.0),
225 negedge_value_(0.0),
226 encoder_(0.0),
227 last_encoder_(0.0) {}
228
229void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition &claw) {
230 front_.Update(claw.front);
231 calibration_.Update(claw.calibration);
232 back_.Update(claw.back);
233
234 bool any_sensor_triggered = any_triggered();
235 if (any_sensor_triggered && any_triggered_last_) {
236 // We are still on the hall effect and nothing has changed.
237 min_hall_effect_on_angle_ =
238 ::std::min(min_hall_effect_on_angle_, claw.position);
239 max_hall_effect_on_angle_ =
240 ::std::max(max_hall_effect_on_angle_, claw.position);
241 } else if (!any_sensor_triggered && !any_triggered_last_) {
242 // We are still off the hall effect and nothing has changed.
243 min_hall_effect_off_angle_ =
244 ::std::min(min_hall_effect_off_angle_, claw.position);
245 max_hall_effect_off_angle_ =
246 ::std::max(max_hall_effect_off_angle_, claw.position);
247 } else if (any_sensor_triggered && !any_triggered_last_) {
248 // Saw a posedge on the hall effect. Reset the limits.
249 min_hall_effect_on_angle_ = ::std::min(claw.posedge_value, claw.position);
250 max_hall_effect_on_angle_ = ::std::max(claw.posedge_value, claw.position);
251 } else if (!any_sensor_triggered && any_triggered_last_) {
252 // Saw a negedge on the hall effect. Reset the limits.
253 min_hall_effect_off_angle_ = ::std::min(claw.negedge_value, claw.position);
254 max_hall_effect_off_angle_ = ::std::max(claw.negedge_value, claw.position);
255 }
256
257 posedge_value_ = claw.posedge_value;
258 negedge_value_ = claw.negedge_value;
259 last_encoder_ = encoder_;
260 if (front().value() || calibration().value() || back().value()) {
261 last_on_encoder_ = encoder_;
262 } else {
263 last_off_encoder_ = encoder_;
264 }
265 encoder_ = claw.position;
266 any_triggered_last_ = any_sensor_triggered;
267}
268
269void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition &claw) {
270 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
271
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000272 front_.Reset(claw.front);
273 calibration_.Reset(claw.calibration);
274 back_.Reset(claw.back);
Austin Schuh27b8fb12014-02-22 15:10:05 -0800275 // close up the min and max edge positions as they are no longer valid and
276 // will be expanded in future iterations
277 min_hall_effect_on_angle_ = claw.position;
278 max_hall_effect_on_angle_ = claw.position;
279 min_hall_effect_off_angle_ = claw.position;
280 max_hall_effect_off_angle_ = claw.position;
281 any_triggered_last_ = any_triggered();
282}
283
284bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
285 const constants::Values::Claws::Claw &claw_values,
286 JointZeroingState zeroing_state) {
287 double edge_encoder;
288 double edge_angle;
289 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
290 LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
291 SetCalibration(edge_encoder, edge_angle);
292 set_zeroing_state(zeroing_state);
293 return true;
294 }
295 return false;
296}
297
298bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
299 const constants::Values::Claws::Claw &claw_values,
300 JointZeroingState zeroing_state) {
301 double edge_encoder;
302 double edge_angle;
303 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
304 LOG(INFO, "Calibration edge.\n");
305 SetCalibration(edge_encoder, edge_angle);
306 set_zeroing_state(zeroing_state);
307 return true;
308 }
309 return false;
310}
311
Austin Schuhcc0bf312014-02-09 00:39:29 -0800312ClawMotor::ClawMotor(control_loops::ClawGroup *my_claw)
Brian Silverman71fbee02014-03-13 17:24:54 -0700313 : aos::control_loops::ControlLoop<control_loops::ClawGroup, true, true,
314 false>(my_claw),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800315 has_top_claw_goal_(false),
316 top_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800317 top_claw_(this),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800318 has_bottom_claw_goal_(false),
319 bottom_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800320 bottom_claw_(this),
321 claw_(MakeClawLoop()),
Ben Fredrickson9b388422014-02-13 06:15:31 +0000322 was_enabled_(false),
Austin Schuh4cb047f2014-02-16 21:10:19 -0800323 doing_calibration_fine_tune_(false),
Austin Schuhe7f90d12014-02-17 00:48:25 -0800324 capped_goal_(false),
325 mode_(UNKNOWN_LOCATION) {}
Austin Schuh3bb9a442014-02-02 16:01:45 -0800326
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800327const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800328
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000329bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
330 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
331 const HallEffectTracker &sensorB) {
332 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
333 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
334 this_sensor.value() && !this_sensor.last_value()) {
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000335 posedge_filter_ = &this_sensor;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000336 } else if (posedge_filter_ == &this_sensor &&
337 !this_sensor.posedge_count_changed() &&
338 !sensorA.posedge_count_changed() &&
339 !sensorB.posedge_count_changed() && this_sensor.value()) {
340 posedge_filter_ = nullptr;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000341 return true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000342 } else if (posedge_filter_ == &this_sensor) {
343 posedge_filter_ = nullptr;
344 }
345 return false;
346}
347
348bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
349 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
350 const HallEffectTracker &sensorB) {
351 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
352 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
353 !this_sensor.value() && this_sensor.last_value()) {
354 negedge_filter_ = &this_sensor;
355 } else if (negedge_filter_ == &this_sensor &&
356 !this_sensor.negedge_count_changed() &&
357 !sensorA.negedge_count_changed() &&
358 !sensorB.negedge_count_changed() && !this_sensor.value()) {
359 negedge_filter_ = nullptr;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000360 return true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000361 } else if (negedge_filter_ == &this_sensor) {
362 negedge_filter_ = nullptr;
363 }
364 return false;
365}
366
Brian Silvermane0a95462014-02-17 00:41:09 -0800367bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
368 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000369 double *edge_angle, const HallEffectTracker &this_sensor,
370 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
Brian Silvermane0a95462014-02-17 00:41:09 -0800371 const char *hall_effect_name) {
Austin Schuhf84a1302014-02-19 00:23:30 -0800372 bool found_edge = false;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800373
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000374 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800375 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700376 LOG(WARNING, "%s: Uncertain which side, rejecting posedge\n", name_);
Brian Silvermane0a95462014-02-17 00:41:09 -0800377 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800378 const double average_last_encoder =
379 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
380 if (posedge_value_ < average_last_encoder) {
381 *edge_angle = angles.upper_decreasing_angle;
382 LOG(INFO, "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
383 name_, hall_effect_name, *edge_angle, posedge_value_,
384 average_last_encoder);
385 } else {
386 *edge_angle = angles.lower_angle;
387 LOG(INFO, "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
388 name_, hall_effect_name, *edge_angle, posedge_value_,
389 average_last_encoder);
390 }
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000391 *edge_encoder = posedge_value_;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800392 found_edge = true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000393 }
394 }
395
396 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
397 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
Brian Silvermanf48fab32014-03-09 14:32:24 -0700398 LOG(WARNING, "%s: Uncertain which side, rejecting negedge\n", name_);
Brian Silvermane0a95462014-02-17 00:41:09 -0800399 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800400 const double average_last_encoder =
401 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
402 if (negedge_value_ > average_last_encoder) {
403 *edge_angle = angles.upper_angle;
404 LOG(INFO, "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
405 name_, hall_effect_name, *edge_angle, negedge_value_,
406 average_last_encoder);
407 } else {
408 *edge_angle = angles.lower_decreasing_angle;
409 LOG(INFO, "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
410 name_, hall_effect_name, *edge_angle, negedge_value_,
411 average_last_encoder);
412 }
413 *edge_encoder = negedge_value_;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000414 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800415 }
Austin Schuh27b8fb12014-02-22 15:10:05 -0800416 }
417
Austin Schuhf84a1302014-02-19 00:23:30 -0800418 return found_edge;
Brian Silvermane0a95462014-02-17 00:41:09 -0800419}
420
Austin Schuhf9286cd2014-02-11 00:51:09 -0800421bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800422 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
Austin Schuhf9286cd2014-02-11 00:51:09 -0800423 double *edge_angle) {
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000424 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle, front_,
425 calibration_, back_, "front")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800426 return true;
427 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800428 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000429 calibration_, front_, back_, "calibration")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800430 return true;
431 }
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000432 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle, back_,
433 calibration_, front_, "back")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800434 return true;
435 }
436 return false;
437}
438
Austin Schuhcda86af2014-02-16 16:16:39 -0800439void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
440 double edge_angle) {
441 double old_offset = offset_;
442 offset_ = edge_angle - edge_encoder;
443 const double doffset = offset_ - old_offset;
444 motor_->ChangeTopOffset(doffset);
445}
446
447void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
448 double edge_angle) {
449 double old_offset = offset_;
450 offset_ = edge_angle - edge_encoder;
451 const double doffset = offset_ - old_offset;
452 motor_->ChangeBottomOffset(doffset);
453}
454
455void ClawMotor::ChangeTopOffset(double doffset) {
456 claw_.ChangeTopOffset(doffset);
457 if (has_top_claw_goal_) {
458 top_claw_goal_ += doffset;
459 }
460}
461
462void ClawMotor::ChangeBottomOffset(double doffset) {
463 claw_.ChangeBottomOffset(doffset);
464 if (has_bottom_claw_goal_) {
465 bottom_claw_goal_ += doffset;
466 }
467}
468
469void ClawLimitedLoop::ChangeTopOffset(double doffset) {
470 Y_(1, 0) += doffset;
471 X_hat(1, 0) += doffset;
472 LOG(INFO, "Changing top offset by %f\n", doffset);
473}
474void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
475 Y_(0, 0) += doffset;
476 X_hat(0, 0) += doffset;
477 X_hat(1, 0) -= doffset;
478 LOG(INFO, "Changing bottom offset by %f\n", doffset);
479}
joe7376ff52014-02-16 18:28:42 -0800480
Austin Schuh069143b2014-02-17 02:46:26 -0800481void LimitClawGoal(double *bottom_goal, double *top_goal,
482 const frc971::constants::Values &values) {
483 // first update position based on angle limit
484
485 const double separation = *top_goal - *bottom_goal;
486 if (separation > values.claw.claw_max_separation) {
Austin Schuh069143b2014-02-17 02:46:26 -0800487 const double dsep = (separation - values.claw.claw_max_separation) / 2.0;
488 *bottom_goal += dsep;
489 *top_goal -= dsep;
490 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
491 }
492 if (separation < values.claw.claw_min_separation) {
Austin Schuh069143b2014-02-17 02:46:26 -0800493 const double dsep = (separation - values.claw.claw_min_separation) / 2.0;
494 *bottom_goal += dsep;
495 *top_goal -= dsep;
496 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
497 }
498
499 // now move both goals in unison
500 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
501 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
502 *bottom_goal = values.claw.lower_claw.lower_limit;
503 }
504 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
505 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
506 *bottom_goal = values.claw.lower_claw.upper_limit;
507 }
508
509 if (*top_goal < values.claw.upper_claw.lower_limit) {
510 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
511 *top_goal = values.claw.upper_claw.lower_limit;
512 }
513 if (*top_goal > values.claw.upper_claw.upper_limit) {
514 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
515 *top_goal = values.claw.upper_claw.upper_limit;
516 }
517}
Austin Schuhcda86af2014-02-16 16:16:39 -0800518
Austin Schuhe7f90d12014-02-17 00:48:25 -0800519bool ClawMotor::is_ready() const {
520 return (
521 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
522 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
Brian Silverman71fbee02014-03-13 17:24:54 -0700523 (((::aos::robot_state.get() == NULL) ? true
524 : ::aos::robot_state->autonomous) &&
Austin Schuhe7f90d12014-02-17 00:48:25 -0800525 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
526 top_claw_.zeroing_state() ==
527 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
528 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
529 bottom_claw_.zeroing_state() ==
530 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
531}
532
533bool ClawMotor::is_zeroing() const { return !is_ready(); }
534
Austin Schuh3bb9a442014-02-02 16:01:45 -0800535// Positive angle is up, and positive power is up.
Austin Schuhcc0bf312014-02-09 00:39:29 -0800536void ClawMotor::RunIteration(const control_loops::ClawGroup::Goal *goal,
537 const control_loops::ClawGroup::Position *position,
538 control_loops::ClawGroup::Output *output,
James Kuszmaul9ead1de2014-02-28 21:24:39 -0800539 control_loops::ClawGroup::Status *status) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800540 constexpr double dt = 0.01;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800541
542 // Disable the motors now so that all early returns will return with the
543 // motors disabled.
544 if (output) {
545 output->top_claw_voltage = 0;
546 output->bottom_claw_voltage = 0;
547 output->intake_voltage = 0;
Ben Fredrickson61893d52014-03-02 09:43:23 +0000548 output->tusk_voltage = 0;
549 }
550
Brian Silverman71fbee02014-03-13 17:24:54 -0700551 if (goal) {
552 if (::std::isnan(goal->bottom_angle) ||
553 ::std::isnan(goal->separation_angle) || ::std::isnan(goal->intake) ||
554 ::std::isnan(goal->centering)) {
555 return;
556 }
Austin Schuh3bb9a442014-02-02 16:01:45 -0800557 }
558
Austin Schuh1a499942014-02-17 01:51:58 -0800559 if (reset()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800560 top_claw_.Reset(position->top);
561 bottom_claw_.Reset(position->bottom);
Austin Schuh1a499942014-02-17 01:51:58 -0800562 }
563
Austin Schuhf9286cd2014-02-11 00:51:09 -0800564 const frc971::constants::Values &values = constants::GetValues();
565
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800566 if (position) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800567 Eigen::Matrix<double, 2, 1> Y;
568 Y << position->bottom.position + bottom_claw_.offset(),
569 position->top.position + top_claw_.offset();
570 claw_.Correct(Y);
571
Austin Schuhf9286cd2014-02-11 00:51:09 -0800572 top_claw_.SetPositionValues(position->top);
573 bottom_claw_.SetPositionValues(position->bottom);
574
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800575 if (!has_top_claw_goal_) {
576 has_top_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800577 top_claw_goal_ = top_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800578 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800579 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800580 }
581 if (!has_bottom_claw_goal_) {
582 has_bottom_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800583 bottom_claw_goal_ = bottom_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800584 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800585 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800586 }
Brian Silvermanf48fab32014-03-09 14:32:24 -0700587 LOG_STRUCT(DEBUG, "absolute position",
588 ClawPositionToLog(top_claw_.absolute_position(),
589 bottom_claw_.absolute_position()));
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800590 }
591
Brian Silverman71fbee02014-03-13 17:24:54 -0700592 bool autonomous, enabled;
593 if (::aos::robot_state.get() == nullptr) {
594 autonomous = true;
595 enabled = false;
596 } else {
597 autonomous = ::aos::robot_state->autonomous;
598 enabled = ::aos::robot_state->enabled;
599 }
Austin Schuh069143b2014-02-17 02:46:26 -0800600
601 double bottom_claw_velocity_ = 0.0;
602 double top_claw_velocity_ = 0.0;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800603
Brian Silverman71fbee02014-03-13 17:24:54 -0700604 if (goal != NULL &&
605 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
606 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
607 (autonomous &&
608 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
609 top_claw_.zeroing_state() ==
610 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
611 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
612 bottom_claw_.zeroing_state() ==
613 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))))) {
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800614 // Ready to use the claw.
615 // Limit the goals here.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800616 bottom_claw_goal_ = goal->bottom_angle;
Brian Silverman7c021c42014-02-17 15:15:56 -0800617 top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
Austin Schuhcda86af2014-02-16 16:16:39 -0800618 has_bottom_claw_goal_ = true;
619 has_top_claw_goal_ = true;
620 doing_calibration_fine_tune_ = false;
621
Austin Schuhe7f90d12014-02-17 00:48:25 -0800622 mode_ = READY;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800623 } else if (top_claw_.zeroing_state() !=
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000624 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800625 bottom_claw_.zeroing_state() !=
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000626 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800627 // Time to fine tune the zero.
628 // Limit the goals here.
Austin Schuh0c733422014-02-17 01:17:12 -0800629 if (!enabled) {
630 // If we are disabled, start the fine tune process over again.
631 doing_calibration_fine_tune_ = false;
632 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800633 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800634 // always get the bottom claw to calibrated first
635 LOG(DEBUG, "Calibrating the bottom of the claw\n");
636 if (!doing_calibration_fine_tune_) {
637 if (::std::abs(bottom_absolute_position() -
Austin Schuhd27931c2014-02-16 19:18:20 -0800638 values.claw.start_fine_tune_pos) <
639 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800640 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800641 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800642 top_claw_velocity_ = bottom_claw_velocity_ =
643 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800644 LOG(DEBUG, "Ready to fine tune the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800645 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800646 } else {
647 // send bottom to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800648 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800649 LOG(DEBUG, "Going to the start position for the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800650 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800651 }
652 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800653 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhd27931c2014-02-16 19:18:20 -0800654 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800655 top_claw_velocity_ = bottom_claw_velocity_ =
656 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800657 if (top_claw_.front_or_back_triggered() ||
658 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800659 // We shouldn't hit a limit, but if we do, go back to the zeroing
660 // point and try again.
661 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800662 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800663 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800664 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800665 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800666 }
Austin Schuh288c8c32014-02-16 17:20:17 -0800667
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000668 if (position && bottom_claw_.SawFilteredPosedge(
669 bottom_claw_.calibration(), bottom_claw_.front(),
670 bottom_claw_.back())) {
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000671 // do calibration
672 bottom_claw_.SetCalibration(
673 position->bottom.posedge_value,
674 values.claw.lower_claw.calibration.lower_angle);
675 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
676 // calibrated so we are done fine tuning bottom
677 doing_calibration_fine_tune_ = false;
678 LOG(DEBUG, "Calibrated the bottom correctly!\n");
679 } else if (bottom_claw_.calibration().last_value()) {
680 doing_calibration_fine_tune_ = false;
681 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
682 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
683 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800684 } else {
685 LOG(DEBUG, "Fine tuning\n");
686 }
687 }
688 // now set the top claw to track
689
Austin Schuhd27931c2014-02-16 19:18:20 -0800690 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800691 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800692 // bottom claw must be calibrated, start on the top
693 if (!doing_calibration_fine_tune_) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800694 if (::std::abs(top_absolute_position() -
695 values.claw.start_fine_tune_pos) <
696 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800697 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800698 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800699 top_claw_velocity_ = bottom_claw_velocity_ =
700 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800701 LOG(DEBUG, "Ready to fine tune the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800702 mode_ = FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800703 } else {
704 // send top to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800705 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800706 LOG(DEBUG, "Going to the start position for the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800707 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800708 }
709 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800710 mode_ = FINE_TUNE_TOP;
Austin Schuhd27931c2014-02-16 19:18:20 -0800711 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800712 top_claw_velocity_ = bottom_claw_velocity_ =
713 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800714 if (top_claw_.front_or_back_triggered() ||
715 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800716 // this should not happen, but now we know it won't
717 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800718 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800719 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800720 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800721 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800722 }
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000723
724 if (position &&
725 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
726 top_claw_.front(), top_claw_.back())) {
727 // do calibration
728 top_claw_.SetCalibration(
729 position->top.posedge_value,
730 values.claw.upper_claw.calibration.lower_angle);
731 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
732 // calibrated so we are done fine tuning top
733 doing_calibration_fine_tune_ = false;
734 LOG(DEBUG, "Calibrated the top correctly!\n");
735 } else if (top_claw_.calibration().last_value()) {
736 doing_calibration_fine_tune_ = false;
737 top_claw_goal_ = values.claw.start_fine_tune_pos;
738 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
739 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800740 }
741 }
742 // now set the bottom claw to track
Austin Schuhd27931c2014-02-16 19:18:20 -0800743 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800744 }
745 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800746 doing_calibration_fine_tune_ = false;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800747 if (!was_enabled_ && enabled) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800748 if (position) {
Brian Silverman72035692014-03-14 10:18:27 -0700749 top_claw_goal_ = position->top.position + top_claw_.offset();
750 bottom_claw_goal_ = position->bottom.position + bottom_claw_.offset();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800751 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800752 position->top.position - position->bottom.position;
Austin Schuhf9286cd2014-02-11 00:51:09 -0800753 } else {
754 has_top_claw_goal_ = false;
755 has_bottom_claw_goal_ = false;
756 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800757 }
Austin Schuhf9286cd2014-02-11 00:51:09 -0800758
Austin Schuh4cb047f2014-02-16 21:10:19 -0800759 if ((bottom_claw_.zeroing_state() !=
760 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
Brian Silvermane0a95462014-02-17 00:41:09 -0800761 bottom_claw_.front().value() || top_claw_.front().value()) &&
762 !top_claw_.back().value() && !bottom_claw_.back().value()) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800763 if (enabled) {
764 // Time to slowly move back up to find any position to narrow down the
765 // zero.
Austin Schuhd27931c2014-02-16 19:18:20 -0800766 top_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
767 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800768 top_claw_velocity_ = bottom_claw_velocity_ =
769 values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800770 LOG(DEBUG, "Bottom is known.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800771 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800772 } else {
773 // We don't know where either claw is. Slowly start moving down to find
774 // any hall effect.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800775 if (enabled) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800776 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
777 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800778 top_claw_velocity_ = bottom_claw_velocity_ =
779 -values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800780 LOG(DEBUG, "Both are unknown.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800781 }
782 }
783
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000784 if (position) {
785 if (enabled) {
786 top_claw_.SetCalibrationOnEdge(
787 values.claw.upper_claw,
788 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
789 bottom_claw_.SetCalibrationOnEdge(
790 values.claw.lower_claw,
791 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
792 } else {
793 // TODO(austin): Only calibrate on the predetermined edge.
794 // We might be able to just ignore this since the backlash is soooo
795 // low.
796 // :)
797 top_claw_.SetCalibrationOnEdge(
798 values.claw.upper_claw,
799 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
800 bottom_claw_.SetCalibrationOnEdge(
801 values.claw.lower_claw,
802 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
803 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800804 }
Austin Schuhe7f90d12014-02-17 00:48:25 -0800805 mode_ = UNKNOWN_LOCATION;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800806 }
807
Austin Schuh069143b2014-02-17 02:46:26 -0800808 // Limit the goals if both claws have been (mostly) found.
809 if (mode_ != UNKNOWN_LOCATION) {
810 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
811 }
812
Brian Silvermaned9df2f2014-04-05 07:07:15 -0700813 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 -0800814 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
Austin Schuh069143b2014-02-17 02:46:26 -0800815 claw_.R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
816 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Austin Schuhcda86af2014-02-16 16:16:39 -0800817 double separation = -971;
818 if (position != nullptr) {
819 separation = position->top.position - position->bottom.position;
820 }
Brian Silvermanf48fab32014-03-09 14:32:24 -0700821 LOG_STRUCT(DEBUG, "actual goal",
822 ClawGoalToLog(claw_.R(0, 0), claw_.R(1, 0), separation));
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800823
Austin Schuh01c652b2014-02-21 23:13:42 -0800824 // Only cap power when one of the halves of the claw is moving slowly and
825 // could wind up.
Austin Schuhe7f90d12014-02-17 00:48:25 -0800826 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
827 mode_ == FINE_TUNE_BOTTOM);
Austin Schuhcda86af2014-02-16 16:16:39 -0800828 claw_.Update(output == nullptr);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800829 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800830 claw_.Update(true);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800831 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800832
Austin Schuh4cb047f2014-02-16 21:10:19 -0800833 capped_goal_ = false;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800834 switch (mode_) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800835 case READY:
Austin Schuhe7f90d12014-02-17 00:48:25 -0800836 case PREP_FINE_TUNE_TOP:
837 case PREP_FINE_TUNE_BOTTOM:
Austin Schuhcda86af2014-02-16 16:16:39 -0800838 break;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800839 case FINE_TUNE_BOTTOM:
840 case FINE_TUNE_TOP:
Austin Schuh4cb047f2014-02-16 21:10:19 -0800841 case UNKNOWN_LOCATION: {
Brian Silverman7b7c9072014-03-30 13:33:30 -0700842 LOG_MATRIX(DEBUG, "U_uncapped", claw_.U_uncapped);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800843 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
Brian Silverman6dd2c532014-03-29 23:34:39 -0700844 double dx_bot = (claw_.U_uncapped(0, 0) -
Austin Schuh4cb047f2014-02-16 21:10:19 -0800845 values.claw.max_zeroing_voltage) /
846 claw_.K(0, 0);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700847 double dx_top = (claw_.U_uncapped(1, 0) -
James Kuszmaul0e866512014-02-21 13:12:52 -0800848 values.claw.max_zeroing_voltage) /
849 claw_.K(0, 0);
850 double dx = ::std::max(dx_top, dx_bot);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800851 bottom_claw_goal_ -= dx;
852 top_claw_goal_ -= dx;
James Kuszmaul0e866512014-02-21 13:12:52 -0800853 Eigen::Matrix<double, 4, 1> R;
854 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 -0700855 claw_.U = claw_.K() * (R - claw_.X_hat);
Austin Schuhcda86af2014-02-16 16:16:39 -0800856 capped_goal_ = true;
Brian Silvermanf48fab32014-03-09 14:32:24 -0700857 LOG(DEBUG, "Moving the goal by %f to prevent windup."
858 " Uncapped is %f, max is %f, difference is %f\n",
859 dx,
Austin Schuhe7f90d12014-02-17 00:48:25 -0800860 claw_.uncapped_average_voltage(), values.claw.max_zeroing_voltage,
861 (claw_.uncapped_average_voltage() -
862 values.claw.max_zeroing_voltage));
Austin Schuh4cb047f2014-02-16 21:10:19 -0800863 } else if (claw_.uncapped_average_voltage() <
864 -values.claw.max_zeroing_voltage) {
Brian Silverman6dd2c532014-03-29 23:34:39 -0700865 double dx_bot = (claw_.U_uncapped(0, 0) +
Austin Schuh4cb047f2014-02-16 21:10:19 -0800866 values.claw.max_zeroing_voltage) /
867 claw_.K(0, 0);
Brian Silverman6dd2c532014-03-29 23:34:39 -0700868 double dx_top = (claw_.U_uncapped(1, 0) +
James Kuszmaul0e866512014-02-21 13:12:52 -0800869 values.claw.max_zeroing_voltage) /
870 claw_.K(0, 0);
871 double dx = ::std::min(dx_top, dx_bot);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800872 bottom_claw_goal_ -= dx;
873 top_claw_goal_ -= dx;
James Kuszmaul0e866512014-02-21 13:12:52 -0800874 Eigen::Matrix<double, 4, 1> R;
875 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 -0700876 claw_.U = claw_.K() * (R - claw_.X_hat);
Austin Schuhcda86af2014-02-16 16:16:39 -0800877 capped_goal_ = true;
Austin Schuh4cb047f2014-02-16 21:10:19 -0800878 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Austin Schuhcda86af2014-02-16 16:16:39 -0800879 }
Austin Schuh4cb047f2014-02-16 21:10:19 -0800880 } break;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800881 }
882
883 if (output) {
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000884 if (goal) {
885 //setup the intake
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000886 output->intake_voltage =
887 (goal->intake > 12.0) ? 12 : (goal->intake < -12.0) ? -12.0
888 : goal->intake;
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000889 output->tusk_voltage = goal->centering;
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000890 output->tusk_voltage =
891 (goal->centering > 12.0) ? 12 : (goal->centering < -12.0)
892 ? -12.0
893 : goal->centering;
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000894 }
James Kuszmauld536a402014-02-18 22:32:12 -0800895 output->top_claw_voltage = claw_.U(1, 0);
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000896 output->bottom_claw_voltage = claw_.U(0, 0);
Austin Schuhf84a1302014-02-19 00:23:30 -0800897
898 if (output->top_claw_voltage > kMaxVoltage) {
899 output->top_claw_voltage = kMaxVoltage;
900 } else if (output->top_claw_voltage < -kMaxVoltage) {
901 output->top_claw_voltage = -kMaxVoltage;
902 }
903
904 if (output->bottom_claw_voltage > kMaxVoltage) {
905 output->bottom_claw_voltage = kMaxVoltage;
906 } else if (output->bottom_claw_voltage < -kMaxVoltage) {
907 output->bottom_claw_voltage = -kMaxVoltage;
908 }
Austin Schuh3bb9a442014-02-02 16:01:45 -0800909 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800910
James Kuszmaul4abaf482014-02-26 21:16:35 -0800911 status->bottom = bottom_absolute_position();
912 status->separation = top_absolute_position() - bottom_absolute_position();
913 status->bottom_velocity = claw_.X_hat(2, 0);
914 status->separation_velocity = claw_.X_hat(3, 0);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800915
Brian Silverman71fbee02014-03-13 17:24:54 -0700916 if (goal) {
917 bool bottom_done =
918 ::std::abs(bottom_absolute_position() - goal->bottom_angle) < 0.020;
919 bool bottom_velocity_done = ::std::abs(status->bottom_velocity) < 0.2;
920 bool separation_done =
921 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
922 goal->separation_angle) < 0.020;
923 bool separation_done_with_ball =
924 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
925 goal->separation_angle) < 0.06;
926 status->done = is_ready() && separation_done && bottom_done && bottom_velocity_done;
927 status->done_with_ball =
928 is_ready() && separation_done_with_ball && bottom_done && bottom_velocity_done;
929 } else {
930 status->done = status->done_with_ball = false;
931 }
Austin Schuha556b012014-03-02 11:55:52 -0800932
Austin Schuh4f8633f2014-03-02 13:59:46 -0800933 status->zeroed = is_ready();
Brian Silverman80fc94c2014-03-09 16:56:01 -0700934 status->zeroed_for_auto =
935 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
936 top_claw_.zeroing_state() ==
937 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
938 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
939 bottom_claw_.zeroing_state() ==
940 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
Austin Schuh4f8633f2014-03-02 13:59:46 -0800941
Brian Silverman71fbee02014-03-13 17:24:54 -0700942 was_enabled_ = enabled;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800943}
944
945} // namespace control_loops
946} // namespace frc971
Ben Fredrickson81ba2d52014-03-02 08:21:46 +0000947