blob: 85751d909b82c6c85dacea4f938e76918e7671dd [file] [log] [blame]
Austin Schuh3bb9a442014-02-02 16:01:45 -08001#include "frc971/control_loops/claw/claw.h"
2
3#include <stdio.h>
4
5#include <algorithm>
6
7#include "aos/common/control_loop/control_loops.q.h"
8#include "aos/common/logging/logging.h"
9
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.
39// Difference between the arms has a range, and the values of each arm has a range.
40// If a claw runs up against a static limit, don't let the goal change outside
41// the limit.
42// If a claw runs up against a movable limit, move both claws outwards to get
43// out of the condition.
44
45namespace frc971 {
46namespace control_loops {
47
Austin Schuh01c652b2014-02-21 23:13:42 -080048static const double kZeroingVoltage = 4.0;
49static const double kMaxVoltage = 12.0;
Austin Schuhf84a1302014-02-19 00:23:30 -080050
Austin Schuh27b8fb12014-02-22 15:10:05 -080051ClawLimitedLoop::ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> loop)
52 : StateFeedbackLoop<4, 2, 2>(loop),
53 uncapped_average_voltage_(0.0),
54 is_zeroing_(true) {}
55
Austin Schuhcda86af2014-02-16 16:16:39 -080056void ClawLimitedLoop::CapU() {
Austin Schuh4cb047f2014-02-16 21:10:19 -080057 uncapped_average_voltage_ = U(0, 0) + U(1, 0) / 2.0;
58 if (is_zeroing_) {
Austin Schuhf84a1302014-02-19 00:23:30 -080059 LOG(DEBUG, "zeroing\n");
Austin Schuh4cb047f2014-02-16 21:10:19 -080060 const frc971::constants::Values &values = constants::GetValues();
61 if (uncapped_average_voltage_ > values.claw.max_zeroing_voltage) {
62 const double difference =
63 uncapped_average_voltage_ - values.claw.max_zeroing_voltage;
64 U(0, 0) -= difference;
Austin Schuh4cb047f2014-02-16 21:10:19 -080065 } else if (uncapped_average_voltage_ < -values.claw.max_zeroing_voltage) {
66 const double difference =
67 -uncapped_average_voltage_ - values.claw.max_zeroing_voltage;
68 U(0, 0) += difference;
Austin Schuh4cb047f2014-02-16 21:10:19 -080069 }
70 }
71
72 double max_value =
73 ::std::max(::std::abs(U(0, 0)), ::std::abs(U(1, 0) + U(0, 0)));
74
Austin Schuh01c652b2014-02-21 23:13:42 -080075 const double k_max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
76 if (max_value > k_max_voltage) {
Austin Schuhcda86af2014-02-16 16:16:39 -080077 LOG(DEBUG, "Capping U because max is %f\n", max_value);
Austin Schuh01c652b2014-02-21 23:13:42 -080078 U = U * k_max_voltage / max_value;
Austin Schuhcda86af2014-02-16 16:16:39 -080079 LOG(DEBUG, "Capping U is now %f %f\n", U(0, 0), U(1, 0));
Austin Schuh4b7b5d02014-02-10 21:20:34 -080080 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -080081}
82
Austin Schuh27b8fb12014-02-22 15:10:05 -080083ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
84 ClawMotor *motor)
85 : offset_(0.0),
86 name_(name),
87 motor_(motor),
88 zeroing_state_(UNKNOWN_POSITION),
89 posedge_value_(0.0),
90 negedge_value_(0.0),
91 encoder_(0.0),
92 last_encoder_(0.0) {}
93
94void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition &claw) {
95 front_.Update(claw.front);
96 calibration_.Update(claw.calibration);
97 back_.Update(claw.back);
98
99 bool any_sensor_triggered = any_triggered();
100 if (any_sensor_triggered && any_triggered_last_) {
101 // We are still on the hall effect and nothing has changed.
102 min_hall_effect_on_angle_ =
103 ::std::min(min_hall_effect_on_angle_, claw.position);
104 max_hall_effect_on_angle_ =
105 ::std::max(max_hall_effect_on_angle_, claw.position);
106 } else if (!any_sensor_triggered && !any_triggered_last_) {
107 // We are still off the hall effect and nothing has changed.
108 min_hall_effect_off_angle_ =
109 ::std::min(min_hall_effect_off_angle_, claw.position);
110 max_hall_effect_off_angle_ =
111 ::std::max(max_hall_effect_off_angle_, claw.position);
112 } else if (any_sensor_triggered && !any_triggered_last_) {
113 // Saw a posedge on the hall effect. Reset the limits.
114 min_hall_effect_on_angle_ = ::std::min(claw.posedge_value, claw.position);
115 max_hall_effect_on_angle_ = ::std::max(claw.posedge_value, claw.position);
116 } else if (!any_sensor_triggered && any_triggered_last_) {
117 // Saw a negedge on the hall effect. Reset the limits.
118 min_hall_effect_off_angle_ = ::std::min(claw.negedge_value, claw.position);
119 max_hall_effect_off_angle_ = ::std::max(claw.negedge_value, claw.position);
120 }
121
122 posedge_value_ = claw.posedge_value;
123 negedge_value_ = claw.negedge_value;
124 last_encoder_ = encoder_;
125 if (front().value() || calibration().value() || back().value()) {
126 last_on_encoder_ = encoder_;
127 } else {
128 last_off_encoder_ = encoder_;
129 }
130 encoder_ = claw.position;
131 any_triggered_last_ = any_sensor_triggered;
132}
133
134void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition &claw) {
135 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
136
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000137 front_.Reset(claw.front);
138 calibration_.Reset(claw.calibration);
139 back_.Reset(claw.back);
Austin Schuh27b8fb12014-02-22 15:10:05 -0800140 // close up the min and max edge positions as they are no longer valid and
141 // will be expanded in future iterations
142 min_hall_effect_on_angle_ = claw.position;
143 max_hall_effect_on_angle_ = claw.position;
144 min_hall_effect_off_angle_ = claw.position;
145 max_hall_effect_off_angle_ = claw.position;
146 any_triggered_last_ = any_triggered();
147}
148
149bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
150 const constants::Values::Claws::Claw &claw_values,
151 JointZeroingState zeroing_state) {
152 double edge_encoder;
153 double edge_angle;
154 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
155 LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
156 SetCalibration(edge_encoder, edge_angle);
157 set_zeroing_state(zeroing_state);
158 return true;
159 }
160 return false;
161}
162
163bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
164 const constants::Values::Claws::Claw &claw_values,
165 JointZeroingState zeroing_state) {
166 double edge_encoder;
167 double edge_angle;
168 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
169 LOG(INFO, "Calibration edge.\n");
170 SetCalibration(edge_encoder, edge_angle);
171 set_zeroing_state(zeroing_state);
172 return true;
173 }
174 return false;
175}
176
Austin Schuhcc0bf312014-02-09 00:39:29 -0800177ClawMotor::ClawMotor(control_loops::ClawGroup *my_claw)
178 : aos::control_loops::ControlLoop<control_loops::ClawGroup>(my_claw),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800179 has_top_claw_goal_(false),
180 top_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800181 top_claw_(this),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800182 has_bottom_claw_goal_(false),
183 bottom_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800184 bottom_claw_(this),
185 claw_(MakeClawLoop()),
Ben Fredrickson9b388422014-02-13 06:15:31 +0000186 was_enabled_(false),
Austin Schuh4cb047f2014-02-16 21:10:19 -0800187 doing_calibration_fine_tune_(false),
Austin Schuhe7f90d12014-02-17 00:48:25 -0800188 capped_goal_(false),
189 mode_(UNKNOWN_LOCATION) {}
Austin Schuh3bb9a442014-02-02 16:01:45 -0800190
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800191const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800192
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000193bool ZeroedStateFeedbackLoop::SawFilteredPosedge(
194 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
195 const HallEffectTracker &sensorB) {
196 if (posedge_filter_ == nullptr && this_sensor.posedge_count_changed() &&
197 !sensorA.posedge_count_changed() && !sensorB.posedge_count_changed() &&
198 this_sensor.value() && !this_sensor.last_value()) {
199 posedge_filter_ = &this_sensor;
200 } else if (posedge_filter_ == &this_sensor &&
201 !this_sensor.posedge_count_changed() &&
202 !sensorA.posedge_count_changed() &&
203 !sensorB.posedge_count_changed() && this_sensor.value()) {
204 posedge_filter_ = nullptr;
205 return true;
206 } else if (posedge_filter_ == &this_sensor) {
207 posedge_filter_ = nullptr;
208 }
209 return false;
210}
211
212bool ZeroedStateFeedbackLoop::SawFilteredNegedge(
213 const HallEffectTracker &this_sensor, const HallEffectTracker &sensorA,
214 const HallEffectTracker &sensorB) {
215 if (negedge_filter_ == nullptr && this_sensor.negedge_count_changed() &&
216 !sensorA.negedge_count_changed() && !sensorB.negedge_count_changed() &&
217 !this_sensor.value() && this_sensor.last_value()) {
218 negedge_filter_ = &this_sensor;
219 } else if (negedge_filter_ == &this_sensor &&
220 !this_sensor.negedge_count_changed() &&
221 !sensorA.negedge_count_changed() &&
222 !sensorB.negedge_count_changed() && !this_sensor.value()) {
223 negedge_filter_ = nullptr;
224 return true;
225 } else if (negedge_filter_ == &this_sensor) {
226 negedge_filter_ = nullptr;
227 }
228 return false;
229}
230
Brian Silvermane0a95462014-02-17 00:41:09 -0800231bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
232 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000233 double *edge_angle, const HallEffectTracker &this_sensor,
234 const HallEffectTracker &sensorA, const HallEffectTracker &sensorB,
Brian Silvermane0a95462014-02-17 00:41:09 -0800235 const char *hall_effect_name) {
Austin Schuhf84a1302014-02-19 00:23:30 -0800236 bool found_edge = false;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800237
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000238 if (SawFilteredPosedge(this_sensor, sensorA, sensorB)) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800239 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000240 LOG(INFO, "%s: Uncertain which side, rejecting posedge\n", name_);
Brian Silvermane0a95462014-02-17 00:41:09 -0800241 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800242 const double average_last_encoder =
243 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
244 if (posedge_value_ < average_last_encoder) {
245 *edge_angle = angles.upper_decreasing_angle;
246 LOG(INFO, "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
247 name_, hall_effect_name, *edge_angle, posedge_value_,
248 average_last_encoder);
249 } else {
250 *edge_angle = angles.lower_angle;
251 LOG(INFO, "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
252 name_, hall_effect_name, *edge_angle, posedge_value_,
253 average_last_encoder);
254 }
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000255 *edge_encoder = posedge_value_;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800256 found_edge = true;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000257 }
258 }
259
260 if (SawFilteredNegedge(this_sensor, sensorA, sensorB)) {
261 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
262 LOG(INFO, "%s: Uncertain which side, rejecting negedge\n", name_);
Brian Silvermane0a95462014-02-17 00:41:09 -0800263 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800264 const double average_last_encoder =
265 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
266 if (negedge_value_ > average_last_encoder) {
267 *edge_angle = angles.upper_angle;
268 LOG(INFO, "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
269 name_, hall_effect_name, *edge_angle, negedge_value_,
270 average_last_encoder);
271 } else {
272 *edge_angle = angles.lower_decreasing_angle;
273 LOG(INFO, "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
274 name_, hall_effect_name, *edge_angle, negedge_value_,
275 average_last_encoder);
276 }
277 *edge_encoder = negedge_value_;
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000278 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800279 }
Austin Schuh27b8fb12014-02-22 15:10:05 -0800280 }
281
Austin Schuhf84a1302014-02-19 00:23:30 -0800282 return found_edge;
Brian Silvermane0a95462014-02-17 00:41:09 -0800283}
284
Austin Schuhf9286cd2014-02-11 00:51:09 -0800285bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800286 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
Austin Schuhf9286cd2014-02-11 00:51:09 -0800287 double *edge_angle) {
Brian Silvermane0a95462014-02-17 00:41:09 -0800288 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000289 front_, calibration_, back_, "front")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800290 return true;
291 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800292 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000293 calibration_, front_, back_, "calibration")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800294 return true;
295 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800296 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle,
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000297 back_, calibration_, front_, "back")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800298 return true;
299 }
300 return false;
301}
302
Austin Schuhcda86af2014-02-16 16:16:39 -0800303void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
304 double edge_angle) {
305 double old_offset = offset_;
306 offset_ = edge_angle - edge_encoder;
307 const double doffset = offset_ - old_offset;
308 motor_->ChangeTopOffset(doffset);
309}
310
311void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
312 double edge_angle) {
313 double old_offset = offset_;
314 offset_ = edge_angle - edge_encoder;
315 const double doffset = offset_ - old_offset;
316 motor_->ChangeBottomOffset(doffset);
317}
318
319void ClawMotor::ChangeTopOffset(double doffset) {
320 claw_.ChangeTopOffset(doffset);
321 if (has_top_claw_goal_) {
322 top_claw_goal_ += doffset;
323 }
324}
325
326void ClawMotor::ChangeBottomOffset(double doffset) {
327 claw_.ChangeBottomOffset(doffset);
328 if (has_bottom_claw_goal_) {
329 bottom_claw_goal_ += doffset;
330 }
331}
332
333void ClawLimitedLoop::ChangeTopOffset(double doffset) {
334 Y_(1, 0) += doffset;
335 X_hat(1, 0) += doffset;
336 LOG(INFO, "Changing top offset by %f\n", doffset);
337}
338void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
339 Y_(0, 0) += doffset;
340 X_hat(0, 0) += doffset;
341 X_hat(1, 0) -= doffset;
342 LOG(INFO, "Changing bottom offset by %f\n", doffset);
343}
joe7376ff52014-02-16 18:28:42 -0800344
Austin Schuh069143b2014-02-17 02:46:26 -0800345void LimitClawGoal(double *bottom_goal, double *top_goal,
346 const frc971::constants::Values &values) {
347 // first update position based on angle limit
348
349 const double separation = *top_goal - *bottom_goal;
350 if (separation > values.claw.claw_max_separation) {
Austin Schuh069143b2014-02-17 02:46:26 -0800351 const double dsep = (separation - values.claw.claw_max_separation) / 2.0;
352 *bottom_goal += dsep;
353 *top_goal -= dsep;
354 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
355 }
356 if (separation < values.claw.claw_min_separation) {
Austin Schuh069143b2014-02-17 02:46:26 -0800357 const double dsep = (separation - values.claw.claw_min_separation) / 2.0;
358 *bottom_goal += dsep;
359 *top_goal -= dsep;
360 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
361 }
362
363 // now move both goals in unison
364 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
365 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
366 *bottom_goal = values.claw.lower_claw.lower_limit;
367 }
368 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
369 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
370 *bottom_goal = values.claw.lower_claw.upper_limit;
371 }
372
373 if (*top_goal < values.claw.upper_claw.lower_limit) {
374 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
375 *top_goal = values.claw.upper_claw.lower_limit;
376 }
377 if (*top_goal > values.claw.upper_claw.upper_limit) {
378 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
379 *top_goal = values.claw.upper_claw.upper_limit;
380 }
381}
Austin Schuhcda86af2014-02-16 16:16:39 -0800382
Austin Schuhe7f90d12014-02-17 00:48:25 -0800383bool ClawMotor::is_ready() const {
384 return (
385 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
386 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
387 (::aos::robot_state->autonomous &&
388 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
389 top_claw_.zeroing_state() ==
390 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
391 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
392 bottom_claw_.zeroing_state() ==
393 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
394}
395
396bool ClawMotor::is_zeroing() const { return !is_ready(); }
397
Austin Schuh3bb9a442014-02-02 16:01:45 -0800398// Positive angle is up, and positive power is up.
Austin Schuhcc0bf312014-02-09 00:39:29 -0800399void ClawMotor::RunIteration(const control_loops::ClawGroup::Goal *goal,
400 const control_loops::ClawGroup::Position *position,
401 control_loops::ClawGroup::Output *output,
James Kuszmaul9ead1de2014-02-28 21:24:39 -0800402 control_loops::ClawGroup::Status *status) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800403 constexpr double dt = 0.01;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800404
405 // Disable the motors now so that all early returns will return with the
406 // motors disabled.
407 if (output) {
408 output->top_claw_voltage = 0;
409 output->bottom_claw_voltage = 0;
410 output->intake_voltage = 0;
411 }
412
Austin Schuh1a499942014-02-17 01:51:58 -0800413 if (reset()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800414 top_claw_.Reset(position->top);
415 bottom_claw_.Reset(position->bottom);
Austin Schuh1a499942014-02-17 01:51:58 -0800416 }
417
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800418 if (::aos::robot_state.get() == nullptr) {
419 return;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800420 }
421
Austin Schuhf9286cd2014-02-11 00:51:09 -0800422 const frc971::constants::Values &values = constants::GetValues();
423
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800424 if (position) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800425 Eigen::Matrix<double, 2, 1> Y;
426 Y << position->bottom.position + bottom_claw_.offset(),
427 position->top.position + top_claw_.offset();
428 claw_.Correct(Y);
429
Austin Schuhf9286cd2014-02-11 00:51:09 -0800430 top_claw_.SetPositionValues(position->top);
431 bottom_claw_.SetPositionValues(position->bottom);
432
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800433 if (!has_top_claw_goal_) {
434 has_top_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800435 top_claw_goal_ = top_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800436 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800437 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800438 }
439 if (!has_bottom_claw_goal_) {
440 has_bottom_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800441 bottom_claw_goal_ = bottom_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800442 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800443 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800444 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800445 LOG(DEBUG, "Claw position is (top: %f bottom: %f\n",
446 top_claw_.absolute_position(), bottom_claw_.absolute_position());
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800447 }
448
Austin Schuh069143b2014-02-17 02:46:26 -0800449 const bool autonomous = ::aos::robot_state->autonomous;
450 const bool enabled = ::aos::robot_state->enabled;
451
452 double bottom_claw_velocity_ = 0.0;
453 double top_claw_velocity_ = 0.0;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800454
455 if ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
456 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
457 (autonomous &&
458 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
459 top_claw_.zeroing_state() ==
460 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
461 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
462 bottom_claw_.zeroing_state() ==
463 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION)))) {
464 // Ready to use the claw.
465 // Limit the goals here.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800466 bottom_claw_goal_ = goal->bottom_angle;
Brian Silverman7c021c42014-02-17 15:15:56 -0800467 top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
Austin Schuhcda86af2014-02-16 16:16:39 -0800468 has_bottom_claw_goal_ = true;
469 has_top_claw_goal_ = true;
470 doing_calibration_fine_tune_ = false;
471
Austin Schuhe7f90d12014-02-17 00:48:25 -0800472 mode_ = READY;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800473 } else if (top_claw_.zeroing_state() !=
474 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
475 bottom_claw_.zeroing_state() !=
476 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
477 // Time to fine tune the zero.
478 // Limit the goals here.
Austin Schuh0c733422014-02-17 01:17:12 -0800479 if (!enabled) {
480 // If we are disabled, start the fine tune process over again.
481 doing_calibration_fine_tune_ = false;
482 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800483 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800484 // always get the bottom claw to calibrated first
485 LOG(DEBUG, "Calibrating the bottom of the claw\n");
486 if (!doing_calibration_fine_tune_) {
487 if (::std::abs(bottom_absolute_position() -
Austin Schuhd27931c2014-02-16 19:18:20 -0800488 values.claw.start_fine_tune_pos) <
489 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800490 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800491 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800492 top_claw_velocity_ = bottom_claw_velocity_ =
493 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800494 LOG(DEBUG, "Ready to fine tune the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800495 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800496 } else {
497 // send bottom to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800498 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800499 LOG(DEBUG, "Going to the start position for the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800500 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800501 }
502 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800503 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhd27931c2014-02-16 19:18:20 -0800504 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800505 top_claw_velocity_ = bottom_claw_velocity_ =
506 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800507 if (top_claw_.front_or_back_triggered() ||
508 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800509 // We shouldn't hit a limit, but if we do, go back to the zeroing
510 // point and try again.
511 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800512 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800513 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800514 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800515 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800516 }
Austin Schuh288c8c32014-02-16 17:20:17 -0800517
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000518 if (position &&
519 bottom_claw_.SawFilteredPosedge(
520 bottom_claw_.calibration(),
521 bottom_claw_.front(), bottom_claw_.back())) {
522 // do calibration
523 bottom_claw_.SetCalibration(
524 position->bottom.posedge_value,
525 values.claw.lower_claw.calibration.lower_angle);
526 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
527 // calibrated so we are done fine tuning bottom
528 doing_calibration_fine_tune_ = false;
529 LOG(DEBUG, "Calibrated the bottom correctly!\n");
530 } else if (bottom_claw_.calibration().last_value()) {
531 doing_calibration_fine_tune_ = false;
532 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
533 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
534 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800535 } else {
536 LOG(DEBUG, "Fine tuning\n");
537 }
538 }
539 // now set the top claw to track
540
Austin Schuhd27931c2014-02-16 19:18:20 -0800541 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800542 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800543 // bottom claw must be calibrated, start on the top
544 if (!doing_calibration_fine_tune_) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800545 if (::std::abs(top_absolute_position() -
546 values.claw.start_fine_tune_pos) <
547 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800548 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800549 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800550 top_claw_velocity_ = bottom_claw_velocity_ =
551 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800552 LOG(DEBUG, "Ready to fine tune the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800553 mode_ = FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800554 } else {
555 // send top to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800556 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800557 LOG(DEBUG, "Going to the start position for the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800558 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800559 }
560 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800561 mode_ = FINE_TUNE_TOP;
Austin Schuhd27931c2014-02-16 19:18:20 -0800562 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800563 top_claw_velocity_ = bottom_claw_velocity_ =
564 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800565 if (top_claw_.front_or_back_triggered() ||
566 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800567 // this should not happen, but now we know it won't
568 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800569 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800570 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800571 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800572 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800573 }
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000574
575 if (position &&
576 top_claw_.SawFilteredPosedge(top_claw_.calibration(),
577 top_claw_.front(), top_claw_.back())) {
578 // do calibration
579 top_claw_.SetCalibration(
580 position->top.posedge_value,
581 values.claw.upper_claw.calibration.lower_angle);
582 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
583 // calibrated so we are done fine tuning top
584 doing_calibration_fine_tune_ = false;
585 LOG(DEBUG, "Calibrated the top correctly!\n");
586 } else if (top_claw_.calibration().last_value()) {
587 doing_calibration_fine_tune_ = false;
588 top_claw_goal_ = values.claw.start_fine_tune_pos;
589 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
590 mode_ = PREP_FINE_TUNE_TOP;
591 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800592 }
593 // now set the bottom claw to track
Austin Schuhd27931c2014-02-16 19:18:20 -0800594 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800595 }
596 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800597 doing_calibration_fine_tune_ = false;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800598 if (!was_enabled_ && enabled) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800599 if (position) {
600 top_claw_goal_ = position->top.position;
601 bottom_claw_goal_ = position->bottom.position;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800602 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800603 position->top.position - position->bottom.position;
Austin Schuhf9286cd2014-02-11 00:51:09 -0800604 } else {
605 has_top_claw_goal_ = false;
606 has_bottom_claw_goal_ = false;
607 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800608 }
Austin Schuhf9286cd2014-02-11 00:51:09 -0800609
Austin Schuh4cb047f2014-02-16 21:10:19 -0800610 if ((bottom_claw_.zeroing_state() !=
611 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
Brian Silvermane0a95462014-02-17 00:41:09 -0800612 bottom_claw_.front().value() || top_claw_.front().value()) &&
613 !top_claw_.back().value() && !bottom_claw_.back().value()) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800614 if (enabled) {
615 // Time to slowly move back up to find any position to narrow down the
616 // zero.
Austin Schuhd27931c2014-02-16 19:18:20 -0800617 top_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
618 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800619 top_claw_velocity_ = bottom_claw_velocity_ =
620 values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800621 LOG(DEBUG, "Bottom is known.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800622 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800623 } else {
624 // We don't know where either claw is. Slowly start moving down to find
625 // any hall effect.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800626 if (enabled) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800627 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
628 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800629 top_claw_velocity_ = bottom_claw_velocity_ =
630 -values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800631 LOG(DEBUG, "Both are unknown.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800632 }
633 }
634
Ben Fredricksoneaecbbb2014-02-23 12:12:03 +0000635 if (position) {
636 if (enabled) {
637 top_claw_.SetCalibrationOnEdge(
638 values.claw.upper_claw,
639 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
640 bottom_claw_.SetCalibrationOnEdge(
641 values.claw.lower_claw,
642 ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
643 } else {
644 // TODO(austin): Only calibrate on the predetermined edge.
645 // We might be able to just ignore this since the backlash is soooo
646 // low.
647 // :)
648 top_claw_.SetCalibrationOnEdge(
649 values.claw.upper_claw,
650 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
651 bottom_claw_.SetCalibrationOnEdge(
652 values.claw.lower_claw,
653 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
654 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800655 }
Austin Schuhe7f90d12014-02-17 00:48:25 -0800656 mode_ = UNKNOWN_LOCATION;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800657 }
658
Austin Schuh069143b2014-02-17 02:46:26 -0800659 // Limit the goals if both claws have been (mostly) found.
660 if (mode_ != UNKNOWN_LOCATION) {
661 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
662 }
663
Austin Schuhf9286cd2014-02-11 00:51:09 -0800664 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
Austin Schuh069143b2014-02-17 02:46:26 -0800665 claw_.R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
666 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Austin Schuhcda86af2014-02-16 16:16:39 -0800667 double separation = -971;
668 if (position != nullptr) {
669 separation = position->top.position - position->bottom.position;
670 }
671 LOG(DEBUG, "Goal is %f (bottom) %f, separation is %f\n", claw_.R(0, 0),
672 claw_.R(1, 0), separation);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800673
Austin Schuh01c652b2014-02-21 23:13:42 -0800674 // Only cap power when one of the halves of the claw is moving slowly and
675 // could wind up.
Austin Schuhe7f90d12014-02-17 00:48:25 -0800676 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
677 mode_ == FINE_TUNE_BOTTOM);
Austin Schuhcda86af2014-02-16 16:16:39 -0800678 claw_.Update(output == nullptr);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800679 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800680 claw_.Update(true);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800681 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800682
Austin Schuh4cb047f2014-02-16 21:10:19 -0800683 capped_goal_ = false;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800684 switch (mode_) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800685 case READY:
Austin Schuhe7f90d12014-02-17 00:48:25 -0800686 case PREP_FINE_TUNE_TOP:
687 case PREP_FINE_TUNE_BOTTOM:
Austin Schuhcda86af2014-02-16 16:16:39 -0800688 break;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800689 case FINE_TUNE_BOTTOM:
690 case FINE_TUNE_TOP:
Austin Schuh4cb047f2014-02-16 21:10:19 -0800691 case UNKNOWN_LOCATION: {
692 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
693 double dx = (claw_.uncapped_average_voltage() -
694 values.claw.max_zeroing_voltage) /
695 claw_.K(0, 0);
696 bottom_claw_goal_ -= dx;
697 top_claw_goal_ -= dx;
Austin Schuhcda86af2014-02-16 16:16:39 -0800698 capped_goal_ = true;
Austin Schuh4cb047f2014-02-16 21:10:19 -0800699 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Austin Schuhe7f90d12014-02-17 00:48:25 -0800700 LOG(DEBUG, "Uncapped is %f, max is %f, difference is %f\n",
701 claw_.uncapped_average_voltage(), values.claw.max_zeroing_voltage,
702 (claw_.uncapped_average_voltage() -
703 values.claw.max_zeroing_voltage));
Austin Schuh4cb047f2014-02-16 21:10:19 -0800704 } else if (claw_.uncapped_average_voltage() <
705 -values.claw.max_zeroing_voltage) {
706 double dx = (claw_.uncapped_average_voltage() +
707 values.claw.max_zeroing_voltage) /
708 claw_.K(0, 0);
709 bottom_claw_goal_ -= dx;
710 top_claw_goal_ -= dx;
Austin Schuhcda86af2014-02-16 16:16:39 -0800711 capped_goal_ = true;
Austin Schuh4cb047f2014-02-16 21:10:19 -0800712 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Austin Schuhcda86af2014-02-16 16:16:39 -0800713 }
Austin Schuh4cb047f2014-02-16 21:10:19 -0800714 } break;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800715 }
716
717 if (output) {
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000718 if (goal) {
719 //setup the intake
Ben Fredrickson6de9b492014-02-23 06:05:19 +0000720 output->intake_voltage = (goal->intake > 12.0) ? 12 :
721 (goal->intake < -12.0) ? -12.0 : goal->intake;
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000722 output->tusk_voltage = goal->centering;
Ben Fredrickson6de9b492014-02-23 06:05:19 +0000723 output->tusk_voltage = (goal->centering > 12.0) ? 12 :
724 (goal->centering < -12.0) ? -12.0 : goal->centering;
Ben Fredrickson2f76ddf2014-02-23 05:58:23 +0000725 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800726 output->top_claw_voltage = claw_.U(1, 0) + claw_.U(0, 0);
727 output->bottom_claw_voltage = claw_.U(0, 0);
Austin Schuhf84a1302014-02-19 00:23:30 -0800728
729 if (output->top_claw_voltage > kMaxVoltage) {
730 output->top_claw_voltage = kMaxVoltage;
731 } else if (output->top_claw_voltage < -kMaxVoltage) {
732 output->top_claw_voltage = -kMaxVoltage;
733 }
734
735 if (output->bottom_claw_voltage > kMaxVoltage) {
736 output->bottom_claw_voltage = kMaxVoltage;
737 } else if (output->bottom_claw_voltage < -kMaxVoltage) {
738 output->bottom_claw_voltage = -kMaxVoltage;
739 }
Austin Schuh3bb9a442014-02-02 16:01:45 -0800740 }
James Kuszmaul4abaf482014-02-26 21:16:35 -0800741
742 bool bottom_done =
743 ::std::abs(bottom_absolute_position() - goal->bottom_angle) < 0.005;
744 bool separation_done =
745 ::std::abs((top_absolute_position() - bottom_absolute_position()) -
746 goal->separation_angle) <
747 0.005;
748 status->done = is_ready() && separation_done && bottom_done;
749
750 status->bottom = bottom_absolute_position();
751 status->separation = top_absolute_position() - bottom_absolute_position();
752 status->bottom_velocity = claw_.X_hat(2, 0);
753 status->separation_velocity = claw_.X_hat(3, 0);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800754
755 was_enabled_ = ::aos::robot_state->enabled;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800756}
757
758} // namespace control_loops
759} // namespace frc971