blob: b8043991648cf59942ea7e4de96af718e763293a [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
5#include "aos/common/control_loop/control_loops.q.h"
6#include "aos/common/logging/logging.h"
7
8#include "frc971/constants.h"
Austin Schuhcda86af2014-02-16 16:16:39 -08009#include "frc971/control_loops/claw/claw_motor_plant.h"
10
Austin Schuh3bb9a442014-02-02 16:01:45 -080011// Zeroing plan.
12// There are 2 types of zeros. Enabled and disabled ones.
13// Disabled ones are only valid during auto mode, and can be used to speed up
14// the enabled zero process. We need to re-zero during teleop in case the auto
15// zero was poor and causes us to miss all our shots.
16//
17// We need to be able to zero manually while disabled by moving the joint over
18// the zeros.
19// Zero on the down edge when disabled (gravity in the direction of motion)
20//
21// When enabled, zero on the up edge (gravity opposing the direction of motion)
22// The enabled sequence needs to work as follows. We can crash the claw if we
23// bring them too close to each other or too far from each other. The only safe
24// thing to do is to move them in unison.
25//
26// Start by moving them both towards the front of the bot to either find either
27// the middle hall effect on either jaw, or the front hall effect on the bottom
28// jaw. Any edge that isn't the desired edge will provide an approximate edge
29// location that can be used for the fine tuning step.
30// Once an edge is found on the front claw, move back the other way with both
31// claws until an edge is found for the other claw.
32// Now that we have an approximate zero, we can robustify the limits to keep
33// both claws safe. Then, we can move both claws to a position that is the
34// correct side of the zero and go zero.
35
36// Valid region plan.
37// Difference between the arms has a range, and the values of each arm has a range.
38// If a claw runs up against a static limit, don't let the goal change outside
39// the limit.
40// If a claw runs up against a movable limit, move both claws outwards to get
41// out of the condition.
42
43namespace frc971 {
44namespace control_loops {
45
Austin Schuh01c652b2014-02-21 23:13:42 -080046static const double kZeroingVoltage = 4.0;
47static const double kMaxVoltage = 12.0;
Austin Schuhf84a1302014-02-19 00:23:30 -080048
Austin Schuh27b8fb12014-02-22 15:10:05 -080049ClawLimitedLoop::ClawLimitedLoop(StateFeedbackLoop<4, 2, 2> loop)
50 : StateFeedbackLoop<4, 2, 2>(loop),
51 uncapped_average_voltage_(0.0),
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070052 is_zeroing_(true),
53 U_Poly_((Eigen::Matrix<double, 4, 2>() << 1, 0,
54 -1, 0,
55 0, 1,
56 0, -1).finished(),
57 (Eigen::Matrix<double, 4, 1>() << kMaxVoltage, kMaxVoltage,
58 kMaxVoltage, kMaxVoltage).finished()) {
59 ::aos::controls::HPolytope<0>::Init();
60}
Austin Schuh27b8fb12014-02-22 15:10:05 -080061
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070062// Caps the voltage prioritizing reducing velocity error over reducing
63// positional error.
64// Uses the polytope libararies which we used to just use for the drivetrain.
65// Uses a region representing the maximum voltage and then transforms it such
66// that the points represent different amounts of positional error and
67// constrains the region such that, if at all possible, it will maintain its
68// current efforts to reduce velocity error.
Austin Schuhcda86af2014-02-16 16:16:39 -080069void ClawLimitedLoop::CapU() {
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070070 Eigen::Matrix<double, 4, 1> error = R - X_hat;
Austin Schuh4cb047f2014-02-16 21:10:19 -080071
James Kuszmauld536a402014-02-18 22:32:12 -080072 double u_top = U(1, 0);
73 double u_bottom = U(0, 0);
Austin Schuhcda86af2014-02-16 16:16:39 -080074
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070075 uncapped_average_voltage_ = (u_top + u_bottom) / 2;
76
77 double max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
78
79 if (::std::abs(u_bottom) > max_voltage or ::std::abs(u_top) > max_voltage) {
80 // H * U <= k
81 // U = UPos + UVel
82 // H * (UPos + UVel) <= k
83 // H * UPos <= k - H * UVel
84
85 // Now, we can do a coordinate transformation and say the following.
86
87 // UPos = position_K * position_error
88 // (H * position_K) * position_error <= k - H * UVel
89
90 Eigen::Matrix<double, 2, 2> position_K;
91 position_K << K(0, 0), K(0, 1),
92 K(1, 0), K(1, 1);
93 Eigen::Matrix<double, 2, 2> velocity_K;
94 velocity_K << K(0, 2), K(0, 3),
95 K(1, 2), K(1, 3);
96
97 Eigen::Matrix<double, 2, 1> position_error;
98 position_error << error(0, 0), error(1, 0);
99 Eigen::Matrix<double, 2, 1> velocity_error;
100 velocity_error << error(2, 0), error(3, 0);
101
102 Eigen::Matrix<double, 4, 1> pos_poly_k =
103 U_Poly_.k() - U_Poly_.H() * velocity_K * velocity_error;
104 Eigen::Matrix<double, 4, 2> pos_poly_H = U_Poly_.H() * position_K;
105 ::aos::controls::HPolytope<2> pos_poly(pos_poly_H, pos_poly_k);
106
107
108 Eigen::Matrix<double, 2, 1> adjusted_pos_error = CoerceGoal(
109 pos_poly, (Eigen::Matrix<double, 1, 2>() << position_error(1, 0),
110 -position_error(0, 0)).finished(),
111 0.0, position_error);
112
113 LOG(DEBUG, "Capping U is now %f %f\n", U(0, 0), U(1, 0));
114 U = velocity_K * velocity_error + position_K * adjusted_pos_error;
James Kuszmauld536a402014-02-18 22:32:12 -0800115 }
116
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800117}
118
Austin Schuh27b8fb12014-02-22 15:10:05 -0800119ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
120 ClawMotor *motor)
121 : offset_(0.0),
122 name_(name),
123 motor_(motor),
124 zeroing_state_(UNKNOWN_POSITION),
125 posedge_value_(0.0),
126 negedge_value_(0.0),
127 encoder_(0.0),
128 last_encoder_(0.0) {}
129
130void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition &claw) {
131 front_.Update(claw.front);
132 calibration_.Update(claw.calibration);
133 back_.Update(claw.back);
134
135 bool any_sensor_triggered = any_triggered();
136 if (any_sensor_triggered && any_triggered_last_) {
137 // We are still on the hall effect and nothing has changed.
138 min_hall_effect_on_angle_ =
139 ::std::min(min_hall_effect_on_angle_, claw.position);
140 max_hall_effect_on_angle_ =
141 ::std::max(max_hall_effect_on_angle_, claw.position);
142 } else if (!any_sensor_triggered && !any_triggered_last_) {
143 // We are still off the hall effect and nothing has changed.
144 min_hall_effect_off_angle_ =
145 ::std::min(min_hall_effect_off_angle_, claw.position);
146 max_hall_effect_off_angle_ =
147 ::std::max(max_hall_effect_off_angle_, claw.position);
148 } else if (any_sensor_triggered && !any_triggered_last_) {
149 // Saw a posedge on the hall effect. Reset the limits.
150 min_hall_effect_on_angle_ = ::std::min(claw.posedge_value, claw.position);
151 max_hall_effect_on_angle_ = ::std::max(claw.posedge_value, claw.position);
152 } else if (!any_sensor_triggered && any_triggered_last_) {
153 // Saw a negedge on the hall effect. Reset the limits.
154 min_hall_effect_off_angle_ = ::std::min(claw.negedge_value, claw.position);
155 max_hall_effect_off_angle_ = ::std::max(claw.negedge_value, claw.position);
156 }
157
158 posedge_value_ = claw.posedge_value;
159 negedge_value_ = claw.negedge_value;
160 last_encoder_ = encoder_;
161 if (front().value() || calibration().value() || back().value()) {
162 last_on_encoder_ = encoder_;
163 } else {
164 last_off_encoder_ = encoder_;
165 }
166 encoder_ = claw.position;
167 any_triggered_last_ = any_sensor_triggered;
168}
169
170void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition &claw) {
171 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
172
173 front_.Reset();
174 calibration_.Reset();
175 back_.Reset();
176 // close up the min and max edge positions as they are no longer valid and
177 // will be expanded in future iterations
178 min_hall_effect_on_angle_ = claw.position;
179 max_hall_effect_on_angle_ = claw.position;
180 min_hall_effect_off_angle_ = claw.position;
181 max_hall_effect_off_angle_ = claw.position;
182 any_triggered_last_ = any_triggered();
183}
184
185bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
186 const constants::Values::Claws::Claw &claw_values,
187 JointZeroingState zeroing_state) {
188 double edge_encoder;
189 double edge_angle;
190 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
191 LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
192 SetCalibration(edge_encoder, edge_angle);
193 set_zeroing_state(zeroing_state);
194 return true;
195 }
196 return false;
197}
198
199bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
200 const constants::Values::Claws::Claw &claw_values,
201 JointZeroingState zeroing_state) {
202 double edge_encoder;
203 double edge_angle;
204 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
205 LOG(INFO, "Calibration edge.\n");
206 SetCalibration(edge_encoder, edge_angle);
207 set_zeroing_state(zeroing_state);
208 return true;
209 }
210 return false;
211}
212
Austin Schuhcc0bf312014-02-09 00:39:29 -0800213ClawMotor::ClawMotor(control_loops::ClawGroup *my_claw)
214 : aos::control_loops::ControlLoop<control_loops::ClawGroup>(my_claw),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800215 has_top_claw_goal_(false),
216 top_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800217 top_claw_(this),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800218 has_bottom_claw_goal_(false),
219 bottom_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800220 bottom_claw_(this),
221 claw_(MakeClawLoop()),
Ben Fredrickson9b388422014-02-13 06:15:31 +0000222 was_enabled_(false),
Austin Schuh4cb047f2014-02-16 21:10:19 -0800223 doing_calibration_fine_tune_(false),
Austin Schuhe7f90d12014-02-17 00:48:25 -0800224 capped_goal_(false),
225 mode_(UNKNOWN_LOCATION) {}
Austin Schuh3bb9a442014-02-02 16:01:45 -0800226
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800227const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800228
Brian Silvermane0a95462014-02-17 00:41:09 -0800229bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
230 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
231 double *edge_angle, const HallEffectTracker &sensor,
232 const char *hall_effect_name) {
Austin Schuhf84a1302014-02-19 00:23:30 -0800233 bool found_edge = false;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800234
Brian Silvermane0a95462014-02-17 00:41:09 -0800235 if (sensor.posedge_count_changed()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800236 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Ben Fredricksonade3eab2014-02-22 07:30:53 +0000237 // we oddly got two of the same edge.
238 *edge_angle = last_edge_value_;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800239 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800240 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800241 const double average_last_encoder =
242 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
243 if (posedge_value_ < average_last_encoder) {
244 *edge_angle = angles.upper_decreasing_angle;
245 LOG(INFO, "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
246 name_, hall_effect_name, *edge_angle, posedge_value_,
247 average_last_encoder);
248 } else {
249 *edge_angle = angles.lower_angle;
250 LOG(INFO, "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
251 name_, hall_effect_name, *edge_angle, posedge_value_,
252 average_last_encoder);
253 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800254 }
255 *edge_encoder = posedge_value_;
Austin Schuhf84a1302014-02-19 00:23:30 -0800256 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800257 }
258 if (sensor.negedge_count_changed()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800259 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
Ben Fredricksonade3eab2014-02-22 07:30:53 +0000260 *edge_angle = last_edge_value_;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800261 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800262 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800263 const double average_last_encoder =
264 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
265 if (negedge_value_ > average_last_encoder) {
266 *edge_angle = angles.upper_angle;
267 LOG(INFO, "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
268 name_, hall_effect_name, *edge_angle, negedge_value_,
269 average_last_encoder);
270 } else {
271 *edge_angle = angles.lower_decreasing_angle;
272 LOG(INFO, "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
273 name_, hall_effect_name, *edge_angle, negedge_value_,
274 average_last_encoder);
275 }
276 *edge_encoder = negedge_value_;
Brian Silvermane0a95462014-02-17 00:41:09 -0800277 }
Austin Schuhf84a1302014-02-19 00:23:30 -0800278 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800279 }
280
Austin Schuh27b8fb12014-02-22 15:10:05 -0800281 if (found_edge) {
282 last_edge_value_ = *edge_angle;
283 }
284
Austin Schuhf84a1302014-02-19 00:23:30 -0800285 return found_edge;
Brian Silvermane0a95462014-02-17 00:41:09 -0800286}
287
Austin Schuhf9286cd2014-02-11 00:51:09 -0800288bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800289 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
Austin Schuhf9286cd2014-02-11 00:51:09 -0800290 double *edge_angle) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800291 // TODO(austin): Validate that the hall effect edge makes sense.
292 // We must now be on the side of the edge that we expect to be, and the
293 // encoder must have been on either side of the edge before and after.
294
Austin Schuhcda86af2014-02-16 16:16:39 -0800295 // TODO(austin): Compute the last off range min and max and compare the edge
296 // value to the middle of the range. This will be quite a bit more reliable.
297
Brian Silvermane0a95462014-02-17 00:41:09 -0800298 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle,
299 front_, "front")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800300 return true;
301 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800302 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
303 calibration_, "calibration")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800304 return true;
305 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800306 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle,
307 back_, "back")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800308 return true;
309 }
310 return false;
311}
312
Austin Schuhcda86af2014-02-16 16:16:39 -0800313void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
314 double edge_angle) {
315 double old_offset = offset_;
316 offset_ = edge_angle - edge_encoder;
317 const double doffset = offset_ - old_offset;
318 motor_->ChangeTopOffset(doffset);
319}
320
321void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
322 double edge_angle) {
323 double old_offset = offset_;
324 offset_ = edge_angle - edge_encoder;
325 const double doffset = offset_ - old_offset;
326 motor_->ChangeBottomOffset(doffset);
327}
328
329void ClawMotor::ChangeTopOffset(double doffset) {
330 claw_.ChangeTopOffset(doffset);
331 if (has_top_claw_goal_) {
332 top_claw_goal_ += doffset;
333 }
334}
335
336void ClawMotor::ChangeBottomOffset(double doffset) {
337 claw_.ChangeBottomOffset(doffset);
338 if (has_bottom_claw_goal_) {
339 bottom_claw_goal_ += doffset;
340 }
341}
342
343void ClawLimitedLoop::ChangeTopOffset(double doffset) {
344 Y_(1, 0) += doffset;
345 X_hat(1, 0) += doffset;
346 LOG(INFO, "Changing top offset by %f\n", doffset);
347}
348void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
349 Y_(0, 0) += doffset;
350 X_hat(0, 0) += doffset;
351 X_hat(1, 0) -= doffset;
352 LOG(INFO, "Changing bottom offset by %f\n", doffset);
353}
joe7376ff52014-02-16 18:28:42 -0800354
Austin Schuh069143b2014-02-17 02:46:26 -0800355void LimitClawGoal(double *bottom_goal, double *top_goal,
356 const frc971::constants::Values &values) {
357 // first update position based on angle limit
358
359 const double separation = *top_goal - *bottom_goal;
360 if (separation > values.claw.claw_max_separation) {
361 LOG(DEBUG, "Greater than\n");
362 const double dsep = (separation - values.claw.claw_max_separation) / 2.0;
363 *bottom_goal += dsep;
364 *top_goal -= dsep;
365 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
366 }
367 if (separation < values.claw.claw_min_separation) {
368 LOG(DEBUG, "Less than\n");
369 const double dsep = (separation - values.claw.claw_min_separation) / 2.0;
370 *bottom_goal += dsep;
371 *top_goal -= dsep;
372 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
373 }
374
375 // now move both goals in unison
376 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
377 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
378 *bottom_goal = values.claw.lower_claw.lower_limit;
379 }
380 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
381 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
382 *bottom_goal = values.claw.lower_claw.upper_limit;
383 }
384
385 if (*top_goal < values.claw.upper_claw.lower_limit) {
386 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
387 *top_goal = values.claw.upper_claw.lower_limit;
388 }
389 if (*top_goal > values.claw.upper_claw.upper_limit) {
390 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
391 *top_goal = values.claw.upper_claw.upper_limit;
392 }
393}
Austin Schuhcda86af2014-02-16 16:16:39 -0800394
Austin Schuhe7f90d12014-02-17 00:48:25 -0800395bool ClawMotor::is_ready() const {
396 return (
397 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
398 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
399 (::aos::robot_state->autonomous &&
400 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
401 top_claw_.zeroing_state() ==
402 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
403 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
404 bottom_claw_.zeroing_state() ==
405 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
406}
407
408bool ClawMotor::is_zeroing() const { return !is_ready(); }
409
Austin Schuh3bb9a442014-02-02 16:01:45 -0800410// Positive angle is up, and positive power is up.
Austin Schuhcc0bf312014-02-09 00:39:29 -0800411void ClawMotor::RunIteration(const control_loops::ClawGroup::Goal *goal,
412 const control_loops::ClawGroup::Position *position,
413 control_loops::ClawGroup::Output *output,
Austin Schuh3bb9a442014-02-02 16:01:45 -0800414 ::aos::control_loops::Status *status) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800415 constexpr double dt = 0.01;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800416
417 // Disable the motors now so that all early returns will return with the
418 // motors disabled.
419 if (output) {
420 output->top_claw_voltage = 0;
421 output->bottom_claw_voltage = 0;
422 output->intake_voltage = 0;
423 }
424
Austin Schuh1a499942014-02-17 01:51:58 -0800425 if (reset()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800426 top_claw_.Reset(position->top);
427 bottom_claw_.Reset(position->bottom);
Austin Schuh1a499942014-02-17 01:51:58 -0800428 }
429
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800430 if (::aos::robot_state.get() == nullptr) {
431 return;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800432 }
433
Austin Schuhf9286cd2014-02-11 00:51:09 -0800434 const frc971::constants::Values &values = constants::GetValues();
435
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800436 if (position) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800437 Eigen::Matrix<double, 2, 1> Y;
438 Y << position->bottom.position + bottom_claw_.offset(),
439 position->top.position + top_claw_.offset();
440 claw_.Correct(Y);
441
Austin Schuhf9286cd2014-02-11 00:51:09 -0800442 top_claw_.SetPositionValues(position->top);
443 bottom_claw_.SetPositionValues(position->bottom);
444
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800445 if (!has_top_claw_goal_) {
446 has_top_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800447 top_claw_goal_ = top_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800448 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800449 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800450 }
451 if (!has_bottom_claw_goal_) {
452 has_bottom_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800453 bottom_claw_goal_ = bottom_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800454 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800455 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800456 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800457 LOG(DEBUG, "Claw position is (top: %f bottom: %f\n",
458 top_claw_.absolute_position(), bottom_claw_.absolute_position());
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800459 }
460
Austin Schuh069143b2014-02-17 02:46:26 -0800461 const bool autonomous = ::aos::robot_state->autonomous;
462 const bool enabled = ::aos::robot_state->enabled;
463
464 double bottom_claw_velocity_ = 0.0;
465 double top_claw_velocity_ = 0.0;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800466
467 if ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
468 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
469 (autonomous &&
470 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
471 top_claw_.zeroing_state() ==
472 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
473 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
474 bottom_claw_.zeroing_state() ==
475 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION)))) {
476 // Ready to use the claw.
477 // Limit the goals here.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800478 bottom_claw_goal_ = goal->bottom_angle;
Brian Silverman7c021c42014-02-17 15:15:56 -0800479 top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
Austin Schuhcda86af2014-02-16 16:16:39 -0800480 has_bottom_claw_goal_ = true;
481 has_top_claw_goal_ = true;
482 doing_calibration_fine_tune_ = false;
483
Austin Schuhe7f90d12014-02-17 00:48:25 -0800484 mode_ = READY;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800485 } else if (top_claw_.zeroing_state() !=
486 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
487 bottom_claw_.zeroing_state() !=
488 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
489 // Time to fine tune the zero.
490 // Limit the goals here.
Austin Schuh0c733422014-02-17 01:17:12 -0800491 if (!enabled) {
492 // If we are disabled, start the fine tune process over again.
493 doing_calibration_fine_tune_ = false;
494 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800495 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800496 // always get the bottom claw to calibrated first
497 LOG(DEBUG, "Calibrating the bottom of the claw\n");
498 if (!doing_calibration_fine_tune_) {
499 if (::std::abs(bottom_absolute_position() -
Austin Schuhd27931c2014-02-16 19:18:20 -0800500 values.claw.start_fine_tune_pos) <
501 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800502 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800503 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800504 top_claw_velocity_ = bottom_claw_velocity_ =
505 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800506 LOG(DEBUG, "Ready to fine tune the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800507 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800508 } else {
509 // send bottom to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800510 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800511 LOG(DEBUG, "Going to the start position for the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800512 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800513 }
514 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800515 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhd27931c2014-02-16 19:18:20 -0800516 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800517 top_claw_velocity_ = bottom_claw_velocity_ =
518 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800519 if (top_claw_.front_or_back_triggered() ||
520 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800521 // We shouldn't hit a limit, but if we do, go back to the zeroing
522 // point and try again.
523 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800524 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800525 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800526 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800527 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800528 }
Austin Schuh288c8c32014-02-16 17:20:17 -0800529
Brian Silvermane0a95462014-02-17 00:41:09 -0800530 if (bottom_claw_.calibration().value()) {
531 if (bottom_claw_.calibration().posedge_count_changed() &&
Austin Schuh288c8c32014-02-16 17:20:17 -0800532 position) {
533 // do calibration
534 bottom_claw_.SetCalibration(
535 position->bottom.posedge_value,
Austin Schuhd27931c2014-02-16 19:18:20 -0800536 values.claw.lower_claw.calibration.lower_angle);
Austin Schuh288c8c32014-02-16 17:20:17 -0800537 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
538 // calibrated so we are done fine tuning bottom
539 doing_calibration_fine_tune_ = false;
540 LOG(DEBUG, "Calibrated the bottom correctly!\n");
541 } else {
542 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800543 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800544 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800545 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuh288c8c32014-02-16 17:20:17 -0800546 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800547 } else {
548 LOG(DEBUG, "Fine tuning\n");
549 }
550 }
551 // now set the top claw to track
552
Austin Schuhd27931c2014-02-16 19:18:20 -0800553 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800554 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800555 // bottom claw must be calibrated, start on the top
556 if (!doing_calibration_fine_tune_) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800557 if (::std::abs(top_absolute_position() -
558 values.claw.start_fine_tune_pos) <
559 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800560 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800561 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800562 top_claw_velocity_ = bottom_claw_velocity_ =
563 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800564 LOG(DEBUG, "Ready to fine tune the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800565 mode_ = FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800566 } else {
567 // send top to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800568 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800569 LOG(DEBUG, "Going to the start position for the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800570 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800571 }
572 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800573 mode_ = FINE_TUNE_TOP;
Austin Schuhd27931c2014-02-16 19:18:20 -0800574 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800575 top_claw_velocity_ = bottom_claw_velocity_ =
576 values.claw.claw_zeroing_speed;
Brian Silvermane0a95462014-02-17 00:41:09 -0800577 if (top_claw_.front_or_back_triggered() ||
578 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800579 // this should not happen, but now we know it won't
580 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800581 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800582 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800583 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800584 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800585 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800586 if (top_claw_.calibration().value()) {
587 if (top_claw_.calibration().posedge_count_changed() &&
Austin Schuh288c8c32014-02-16 17:20:17 -0800588 position) {
589 // do calibration
Austin Schuhd27931c2014-02-16 19:18:20 -0800590 top_claw_.SetCalibration(
591 position->top.posedge_value,
592 values.claw.upper_claw.calibration.lower_angle);
Austin Schuh288c8c32014-02-16 17:20:17 -0800593 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
Austin Schuhe7f90d12014-02-17 00:48:25 -0800594 // calibrated so we are done fine tuning top
Austin Schuh288c8c32014-02-16 17:20:17 -0800595 doing_calibration_fine_tune_ = false;
596 LOG(DEBUG, "Calibrated the top correctly!\n");
597 } else {
598 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800599 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800600 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800601 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuh288c8c32014-02-16 17:20:17 -0800602 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800603 }
604 }
605 // now set the bottom claw to track
Austin Schuhd27931c2014-02-16 19:18:20 -0800606 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800607 }
608 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800609 doing_calibration_fine_tune_ = false;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800610 if (!was_enabled_ && enabled) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800611 if (position) {
612 top_claw_goal_ = position->top.position;
613 bottom_claw_goal_ = position->bottom.position;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800614 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800615 position->top.position - position->bottom.position;
Austin Schuhf9286cd2014-02-11 00:51:09 -0800616 } else {
617 has_top_claw_goal_ = false;
618 has_bottom_claw_goal_ = false;
619 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800620 }
Austin Schuhf9286cd2014-02-11 00:51:09 -0800621
Austin Schuh4cb047f2014-02-16 21:10:19 -0800622 if ((bottom_claw_.zeroing_state() !=
623 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
Brian Silvermane0a95462014-02-17 00:41:09 -0800624 bottom_claw_.front().value() || top_claw_.front().value()) &&
625 !top_claw_.back().value() && !bottom_claw_.back().value()) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800626 if (enabled) {
627 // Time to slowly move back up to find any position to narrow down the
628 // zero.
Austin Schuhd27931c2014-02-16 19:18:20 -0800629 top_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
630 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800631 top_claw_velocity_ = bottom_claw_velocity_ =
632 values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800633 LOG(DEBUG, "Bottom is known.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800634 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800635 } else {
636 // We don't know where either claw is. Slowly start moving down to find
637 // any hall effect.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800638 if (enabled) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800639 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
640 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800641 top_claw_velocity_ = bottom_claw_velocity_ =
642 -values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800643 LOG(DEBUG, "Both are unknown.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800644 }
645 }
646
647 if (enabled) {
648 top_claw_.SetCalibrationOnEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800649 values.claw.upper_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800650 bottom_claw_.SetCalibrationOnEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800651 values.claw.lower_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800652 } else {
Austin Schuh069143b2014-02-17 02:46:26 -0800653 // TODO(austin): Only calibrate on the predetermined edge.
654 // We might be able to just ignore this since the backlash is soooo low. :)
Austin Schuhf9286cd2014-02-11 00:51:09 -0800655 top_claw_.SetCalibrationOnEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800656 values.claw.upper_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800657 bottom_claw_.SetCalibrationOnEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800658 values.claw.lower_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800659 }
Austin Schuhe7f90d12014-02-17 00:48:25 -0800660 mode_ = UNKNOWN_LOCATION;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800661 }
662
Austin Schuh069143b2014-02-17 02:46:26 -0800663 // Limit the goals if both claws have been (mostly) found.
664 if (mode_ != UNKNOWN_LOCATION) {
665 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
666 }
667
Austin Schuhf9286cd2014-02-11 00:51:09 -0800668 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
Austin Schuh069143b2014-02-17 02:46:26 -0800669 claw_.R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
670 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Austin Schuhcda86af2014-02-16 16:16:39 -0800671 double separation = -971;
672 if (position != nullptr) {
673 separation = position->top.position - position->bottom.position;
674 }
675 LOG(DEBUG, "Goal is %f (bottom) %f, separation is %f\n", claw_.R(0, 0),
676 claw_.R(1, 0), separation);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800677
Austin Schuh01c652b2014-02-21 23:13:42 -0800678 // Only cap power when one of the halves of the claw is moving slowly and
679 // could wind up.
Austin Schuhe7f90d12014-02-17 00:48:25 -0800680 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
681 mode_ == FINE_TUNE_BOTTOM);
Austin Schuhcda86af2014-02-16 16:16:39 -0800682 claw_.Update(output == nullptr);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800683 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800684 claw_.Update(true);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800685 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800686
Austin Schuh4cb047f2014-02-16 21:10:19 -0800687 capped_goal_ = false;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800688 switch (mode_) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800689 case READY:
Austin Schuhe7f90d12014-02-17 00:48:25 -0800690 case PREP_FINE_TUNE_TOP:
691 case PREP_FINE_TUNE_BOTTOM:
Austin Schuhcda86af2014-02-16 16:16:39 -0800692 break;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800693 case FINE_TUNE_BOTTOM:
694 case FINE_TUNE_TOP:
Austin Schuh4cb047f2014-02-16 21:10:19 -0800695 case UNKNOWN_LOCATION: {
James Kuszmaul0e866512014-02-21 13:12:52 -0800696 Eigen::Matrix<double, 2, 1> U = claw_.K() * (claw_.R - claw_.X_hat);
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700697 LOG(DEBUG, "Uncapped voltages: Top: %f, Bottom: %f\n", U(1, 0), U(0, 0));
Austin Schuh4cb047f2014-02-16 21:10:19 -0800698 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
James Kuszmaul0e866512014-02-21 13:12:52 -0800699 double dx_bot = (U(0, 0) -
Austin Schuh4cb047f2014-02-16 21:10:19 -0800700 values.claw.max_zeroing_voltage) /
701 claw_.K(0, 0);
James Kuszmaul0e866512014-02-21 13:12:52 -0800702 double dx_top = (U(1, 0) -
703 values.claw.max_zeroing_voltage) /
704 claw_.K(0, 0);
705 double dx = ::std::max(dx_top, dx_bot);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800706 bottom_claw_goal_ -= dx;
707 top_claw_goal_ -= dx;
James Kuszmaul0e866512014-02-21 13:12:52 -0800708 Eigen::Matrix<double, 4, 1> R;
709 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0), claw_.R(3, 0);
710 U = claw_.K() * (R - claw_.X_hat);
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 Schuhe7f90d12014-02-17 00:48:25 -0800713 LOG(DEBUG, "Uncapped is %f, max is %f, difference is %f\n",
714 claw_.uncapped_average_voltage(), values.claw.max_zeroing_voltage,
715 (claw_.uncapped_average_voltage() -
716 values.claw.max_zeroing_voltage));
Austin Schuh4cb047f2014-02-16 21:10:19 -0800717 } else if (claw_.uncapped_average_voltage() <
718 -values.claw.max_zeroing_voltage) {
James Kuszmaul0e866512014-02-21 13:12:52 -0800719 double dx_bot = (U(0, 0) +
Austin Schuh4cb047f2014-02-16 21:10:19 -0800720 values.claw.max_zeroing_voltage) /
721 claw_.K(0, 0);
James Kuszmaul0e866512014-02-21 13:12:52 -0800722 double dx_top = (U(1, 0) +
723 values.claw.max_zeroing_voltage) /
724 claw_.K(0, 0);
725 double dx = ::std::min(dx_top, dx_bot);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800726 bottom_claw_goal_ -= dx;
727 top_claw_goal_ -= dx;
James Kuszmaul0e866512014-02-21 13:12:52 -0800728 Eigen::Matrix<double, 4, 1> R;
729 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0), claw_.R(3, 0);
730 U = claw_.K() * (R - claw_.X_hat);
Austin Schuhcda86af2014-02-16 16:16:39 -0800731 capped_goal_ = true;
Austin Schuh4cb047f2014-02-16 21:10:19 -0800732 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Austin Schuhcda86af2014-02-16 16:16:39 -0800733 }
Austin Schuh4cb047f2014-02-16 21:10:19 -0800734 } break;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800735 }
736
737 if (output) {
James Kuszmauld536a402014-02-18 22:32:12 -0800738 output->top_claw_voltage = claw_.U(1, 0);
Austin Schuhcda86af2014-02-16 16:16:39 -0800739 output->bottom_claw_voltage = claw_.U(0, 0);
Austin Schuhf84a1302014-02-19 00:23:30 -0800740
741 if (output->top_claw_voltage > kMaxVoltage) {
742 output->top_claw_voltage = kMaxVoltage;
743 } else if (output->top_claw_voltage < -kMaxVoltage) {
744 output->top_claw_voltage = -kMaxVoltage;
745 }
746
747 if (output->bottom_claw_voltage > kMaxVoltage) {
748 output->bottom_claw_voltage = kMaxVoltage;
749 } else if (output->bottom_claw_voltage < -kMaxVoltage) {
750 output->bottom_claw_voltage = -kMaxVoltage;
751 }
Austin Schuh3bb9a442014-02-02 16:01:45 -0800752 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800753 status->done = false;
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