blob: 4d7442b69ede6f22829ba715b9aecaa373b3a393 [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() {
James Kuszmauld536a402014-02-18 22:32:12 -080057 uncapped_average_voltage_ = (U(0, 0) + U(1, 0)) / 2.0;
Austin Schuh4cb047f2014-02-16 21:10:19 -080058
Austin Schuh01c652b2014-02-21 23:13:42 -080059 const double k_max_voltage = is_zeroing_ ? kZeroingVoltage : kMaxVoltage;
Austin Schuh3bb9a442014-02-02 16:01:45 -080060 double max_value =
James Kuszmauld536a402014-02-18 22:32:12 -080061 ::std::max(::std::abs(U(0, 0)), ::std::abs(U(1, 0)));
James Kuszmaula2ebaf22014-02-22 13:34:42 -080062 double scalar = k_max_voltage / max_value;
63 bool bottom_big = (::std::abs(U(0, 0)) > k_max_voltage) &&
James Kuszmauld536a402014-02-18 22:32:12 -080064 (::std::abs(U(0, 0)) > ::std::abs(U(1, 0)));
James Kuszmaula2ebaf22014-02-22 13:34:42 -080065 bool top_big = (::std::abs(U(1, 0)) > k_max_voltage) && (!bottom_big);
James Kuszmaul0e866512014-02-21 13:12:52 -080066 double separation_voltage = U(1, 0) - U(0, 0) * kClawMomentOfInertiaRatio;
James Kuszmauld536a402014-02-18 22:32:12 -080067 double u_top = U(1, 0);
68 double u_bottom = U(0, 0);
Austin Schuhcda86af2014-02-16 16:16:39 -080069
James Kuszmauld536a402014-02-18 22:32:12 -080070 if (bottom_big) {
71 LOG(DEBUG, "Capping U because bottom is %f\n", max_value);
72 u_bottom *= scalar;
James Kuszmaul0e866512014-02-21 13:12:52 -080073 u_top = separation_voltage + u_bottom * kClawMomentOfInertiaRatio;
James Kuszmauld536a402014-02-18 22:32:12 -080074 // If we can't maintain the separation, just clip it.
James Kuszmaula2ebaf22014-02-22 13:34:42 -080075 if (u_top > k_max_voltage) u_top = k_max_voltage;
76 else if (u_top < -k_max_voltage) u_top = -k_max_voltage;
Austin Schuh4b7b5d02014-02-10 21:20:34 -080077 }
James Kuszmauld536a402014-02-18 22:32:12 -080078 else if (top_big) {
79 LOG(DEBUG, "Capping U because top is %f\n", max_value);
80 u_top *= scalar;
James Kuszmaul0e866512014-02-21 13:12:52 -080081 u_bottom = (u_top - separation_voltage) / kClawMomentOfInertiaRatio;
James Kuszmaula2ebaf22014-02-22 13:34:42 -080082 if (u_bottom > k_max_voltage) u_bottom = k_max_voltage;
83 else if (u_bottom < -k_max_voltage) u_bottom = -k_max_voltage;
James Kuszmauld536a402014-02-18 22:32:12 -080084 }
85
86 U(0, 0) = u_bottom;
87 U(1, 0) = u_top;
88
89 LOG(DEBUG, "Capping U is now %f %f\n", U(0, 0), U(1, 0));
90 LOG(DEBUG, "Separation Voltage was %f, is now %f\n", separation_voltage,
James Kuszmaul0e866512014-02-21 13:12:52 -080091 U(1, 0) - U(0, 0) * kClawMomentOfInertiaRatio);
Austin Schuh4b7b5d02014-02-10 21:20:34 -080092}
93
Austin Schuh27b8fb12014-02-22 15:10:05 -080094ZeroedStateFeedbackLoop::ZeroedStateFeedbackLoop(const char *name,
95 ClawMotor *motor)
96 : offset_(0.0),
97 name_(name),
98 motor_(motor),
99 zeroing_state_(UNKNOWN_POSITION),
100 posedge_value_(0.0),
101 negedge_value_(0.0),
102 encoder_(0.0),
103 last_encoder_(0.0) {}
104
105void ZeroedStateFeedbackLoop::SetPositionValues(const HalfClawPosition &claw) {
106 front_.Update(claw.front);
107 calibration_.Update(claw.calibration);
108 back_.Update(claw.back);
109
110 bool any_sensor_triggered = any_triggered();
111 if (any_sensor_triggered && any_triggered_last_) {
112 // We are still on the hall effect and nothing has changed.
113 min_hall_effect_on_angle_ =
114 ::std::min(min_hall_effect_on_angle_, claw.position);
115 max_hall_effect_on_angle_ =
116 ::std::max(max_hall_effect_on_angle_, claw.position);
117 } else if (!any_sensor_triggered && !any_triggered_last_) {
118 // We are still off the hall effect and nothing has changed.
119 min_hall_effect_off_angle_ =
120 ::std::min(min_hall_effect_off_angle_, claw.position);
121 max_hall_effect_off_angle_ =
122 ::std::max(max_hall_effect_off_angle_, claw.position);
123 } else if (any_sensor_triggered && !any_triggered_last_) {
124 // Saw a posedge on the hall effect. Reset the limits.
125 min_hall_effect_on_angle_ = ::std::min(claw.posedge_value, claw.position);
126 max_hall_effect_on_angle_ = ::std::max(claw.posedge_value, claw.position);
127 } else if (!any_sensor_triggered && any_triggered_last_) {
128 // Saw a negedge on the hall effect. Reset the limits.
129 min_hall_effect_off_angle_ = ::std::min(claw.negedge_value, claw.position);
130 max_hall_effect_off_angle_ = ::std::max(claw.negedge_value, claw.position);
131 }
132
133 posedge_value_ = claw.posedge_value;
134 negedge_value_ = claw.negedge_value;
135 last_encoder_ = encoder_;
136 if (front().value() || calibration().value() || back().value()) {
137 last_on_encoder_ = encoder_;
138 } else {
139 last_off_encoder_ = encoder_;
140 }
141 encoder_ = claw.position;
142 any_triggered_last_ = any_sensor_triggered;
143}
144
145void ZeroedStateFeedbackLoop::Reset(const HalfClawPosition &claw) {
146 set_zeroing_state(ZeroedStateFeedbackLoop::UNKNOWN_POSITION);
147
148 front_.Reset();
149 calibration_.Reset();
150 back_.Reset();
151 // close up the min and max edge positions as they are no longer valid and
152 // will be expanded in future iterations
153 min_hall_effect_on_angle_ = claw.position;
154 max_hall_effect_on_angle_ = claw.position;
155 min_hall_effect_off_angle_ = claw.position;
156 max_hall_effect_off_angle_ = claw.position;
157 any_triggered_last_ = any_triggered();
158}
159
160bool TopZeroedStateFeedbackLoop::SetCalibrationOnEdge(
161 const constants::Values::Claws::Claw &claw_values,
162 JointZeroingState zeroing_state) {
163 double edge_encoder;
164 double edge_angle;
165 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
166 LOG(INFO, "Calibration edge edge should be %f.\n", edge_angle);
167 SetCalibration(edge_encoder, edge_angle);
168 set_zeroing_state(zeroing_state);
169 return true;
170 }
171 return false;
172}
173
174bool BottomZeroedStateFeedbackLoop::SetCalibrationOnEdge(
175 const constants::Values::Claws::Claw &claw_values,
176 JointZeroingState zeroing_state) {
177 double edge_encoder;
178 double edge_angle;
179 if (GetPositionOfEdge(claw_values, &edge_encoder, &edge_angle)) {
180 LOG(INFO, "Calibration edge.\n");
181 SetCalibration(edge_encoder, edge_angle);
182 set_zeroing_state(zeroing_state);
183 return true;
184 }
185 return false;
186}
187
Austin Schuhcc0bf312014-02-09 00:39:29 -0800188ClawMotor::ClawMotor(control_loops::ClawGroup *my_claw)
189 : aos::control_loops::ControlLoop<control_loops::ClawGroup>(my_claw),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800190 has_top_claw_goal_(false),
191 top_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800192 top_claw_(this),
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800193 has_bottom_claw_goal_(false),
194 bottom_claw_goal_(0.0),
Austin Schuhcda86af2014-02-16 16:16:39 -0800195 bottom_claw_(this),
196 claw_(MakeClawLoop()),
Ben Fredrickson9b388422014-02-13 06:15:31 +0000197 was_enabled_(false),
Austin Schuh4cb047f2014-02-16 21:10:19 -0800198 doing_calibration_fine_tune_(false),
Austin Schuhe7f90d12014-02-17 00:48:25 -0800199 capped_goal_(false),
200 mode_(UNKNOWN_LOCATION) {}
Austin Schuh3bb9a442014-02-02 16:01:45 -0800201
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800202const int ZeroedStateFeedbackLoop::kZeroingMaxVoltage;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800203
Brian Silvermane0a95462014-02-17 00:41:09 -0800204bool ZeroedStateFeedbackLoop::DoGetPositionOfEdge(
205 const constants::Values::Claws::AnglePair &angles, double *edge_encoder,
206 double *edge_angle, const HallEffectTracker &sensor,
207 const char *hall_effect_name) {
Austin Schuhf84a1302014-02-19 00:23:30 -0800208 bool found_edge = false;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800209
Brian Silvermane0a95462014-02-17 00:41:09 -0800210 if (sensor.posedge_count_changed()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800211 if (min_hall_effect_off_angle_ == max_hall_effect_off_angle_) {
Ben Fredricksonade3eab2014-02-22 07:30:53 +0000212 // we oddly got two of the same edge.
213 *edge_angle = last_edge_value_;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800214 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800215 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800216 const double average_last_encoder =
217 (min_hall_effect_off_angle_ + max_hall_effect_off_angle_) / 2.0;
218 if (posedge_value_ < average_last_encoder) {
219 *edge_angle = angles.upper_decreasing_angle;
220 LOG(INFO, "%s Posedge upper of %s -> %f posedge: %f avg_encoder: %f\n",
221 name_, hall_effect_name, *edge_angle, posedge_value_,
222 average_last_encoder);
223 } else {
224 *edge_angle = angles.lower_angle;
225 LOG(INFO, "%s Posedge lower of %s -> %f posedge: %f avg_encoder: %f\n",
226 name_, hall_effect_name, *edge_angle, posedge_value_,
227 average_last_encoder);
228 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800229 }
230 *edge_encoder = posedge_value_;
Austin Schuhf84a1302014-02-19 00:23:30 -0800231 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800232 }
233 if (sensor.negedge_count_changed()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800234 if (min_hall_effect_on_angle_ == max_hall_effect_on_angle_) {
Ben Fredricksonade3eab2014-02-22 07:30:53 +0000235 *edge_angle = last_edge_value_;
Austin Schuh27b8fb12014-02-22 15:10:05 -0800236 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800237 } else {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800238 const double average_last_encoder =
239 (min_hall_effect_on_angle_ + max_hall_effect_on_angle_) / 2.0;
240 if (negedge_value_ > average_last_encoder) {
241 *edge_angle = angles.upper_angle;
242 LOG(INFO, "%s Negedge upper of %s -> %f negedge: %f avg_encoder: %f\n",
243 name_, hall_effect_name, *edge_angle, negedge_value_,
244 average_last_encoder);
245 } else {
246 *edge_angle = angles.lower_decreasing_angle;
247 LOG(INFO, "%s Negedge lower of %s -> %f negedge: %f avg_encoder: %f\n",
248 name_, hall_effect_name, *edge_angle, negedge_value_,
249 average_last_encoder);
250 }
251 *edge_encoder = negedge_value_;
Brian Silvermane0a95462014-02-17 00:41:09 -0800252 }
Austin Schuhf84a1302014-02-19 00:23:30 -0800253 found_edge = true;
Brian Silvermane0a95462014-02-17 00:41:09 -0800254 }
255
Austin Schuh27b8fb12014-02-22 15:10:05 -0800256 if (found_edge) {
257 last_edge_value_ = *edge_angle;
258 }
259
Austin Schuhf84a1302014-02-19 00:23:30 -0800260 return found_edge;
Brian Silvermane0a95462014-02-17 00:41:09 -0800261}
262
Austin Schuhf9286cd2014-02-11 00:51:09 -0800263bool ZeroedStateFeedbackLoop::GetPositionOfEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800264 const constants::Values::Claws::Claw &claw_values, double *edge_encoder,
Austin Schuhf9286cd2014-02-11 00:51:09 -0800265 double *edge_angle) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800266 // TODO(austin): Validate that the hall effect edge makes sense.
267 // We must now be on the side of the edge that we expect to be, and the
268 // encoder must have been on either side of the edge before and after.
269
Austin Schuhcda86af2014-02-16 16:16:39 -0800270 // TODO(austin): Compute the last off range min and max and compare the edge
271 // value to the middle of the range. This will be quite a bit more reliable.
272
Brian Silvermane0a95462014-02-17 00:41:09 -0800273 if (DoGetPositionOfEdge(claw_values.front, edge_encoder, edge_angle,
274 front_, "front")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800275 return true;
276 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800277 if (DoGetPositionOfEdge(claw_values.calibration, edge_encoder, edge_angle,
278 calibration_, "calibration")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800279 return true;
280 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800281 if (DoGetPositionOfEdge(claw_values.back, edge_encoder, edge_angle,
282 back_, "back")) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800283 return true;
284 }
285 return false;
286}
287
Austin Schuhcda86af2014-02-16 16:16:39 -0800288void TopZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
289 double edge_angle) {
290 double old_offset = offset_;
291 offset_ = edge_angle - edge_encoder;
292 const double doffset = offset_ - old_offset;
293 motor_->ChangeTopOffset(doffset);
294}
295
296void BottomZeroedStateFeedbackLoop::SetCalibration(double edge_encoder,
297 double edge_angle) {
298 double old_offset = offset_;
299 offset_ = edge_angle - edge_encoder;
300 const double doffset = offset_ - old_offset;
301 motor_->ChangeBottomOffset(doffset);
302}
303
304void ClawMotor::ChangeTopOffset(double doffset) {
305 claw_.ChangeTopOffset(doffset);
306 if (has_top_claw_goal_) {
307 top_claw_goal_ += doffset;
308 }
309}
310
311void ClawMotor::ChangeBottomOffset(double doffset) {
312 claw_.ChangeBottomOffset(doffset);
313 if (has_bottom_claw_goal_) {
314 bottom_claw_goal_ += doffset;
315 }
316}
317
318void ClawLimitedLoop::ChangeTopOffset(double doffset) {
319 Y_(1, 0) += doffset;
320 X_hat(1, 0) += doffset;
321 LOG(INFO, "Changing top offset by %f\n", doffset);
322}
323void ClawLimitedLoop::ChangeBottomOffset(double doffset) {
324 Y_(0, 0) += doffset;
325 X_hat(0, 0) += doffset;
326 X_hat(1, 0) -= doffset;
327 LOG(INFO, "Changing bottom offset by %f\n", doffset);
328}
joe7376ff52014-02-16 18:28:42 -0800329
Austin Schuh069143b2014-02-17 02:46:26 -0800330void LimitClawGoal(double *bottom_goal, double *top_goal,
331 const frc971::constants::Values &values) {
332 // first update position based on angle limit
333
334 const double separation = *top_goal - *bottom_goal;
335 if (separation > values.claw.claw_max_separation) {
336 LOG(DEBUG, "Greater than\n");
337 const double dsep = (separation - values.claw.claw_max_separation) / 2.0;
338 *bottom_goal += dsep;
339 *top_goal -= dsep;
340 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
341 }
342 if (separation < values.claw.claw_min_separation) {
343 LOG(DEBUG, "Less than\n");
344 const double dsep = (separation - values.claw.claw_min_separation) / 2.0;
345 *bottom_goal += dsep;
346 *top_goal -= dsep;
347 LOG(DEBUG, "Goals now bottom: %f, top: %f\n", *bottom_goal, *top_goal);
348 }
349
350 // now move both goals in unison
351 if (*bottom_goal < values.claw.lower_claw.lower_limit) {
352 *top_goal += values.claw.lower_claw.lower_limit - *bottom_goal;
353 *bottom_goal = values.claw.lower_claw.lower_limit;
354 }
355 if (*bottom_goal > values.claw.lower_claw.upper_limit) {
356 *top_goal -= *bottom_goal - values.claw.lower_claw.upper_limit;
357 *bottom_goal = values.claw.lower_claw.upper_limit;
358 }
359
360 if (*top_goal < values.claw.upper_claw.lower_limit) {
361 *bottom_goal += values.claw.upper_claw.lower_limit - *top_goal;
362 *top_goal = values.claw.upper_claw.lower_limit;
363 }
364 if (*top_goal > values.claw.upper_claw.upper_limit) {
365 *bottom_goal -= *top_goal - values.claw.upper_claw.upper_limit;
366 *top_goal = values.claw.upper_claw.upper_limit;
367 }
368}
Austin Schuhcda86af2014-02-16 16:16:39 -0800369
Austin Schuhe7f90d12014-02-17 00:48:25 -0800370bool ClawMotor::is_ready() const {
371 return (
372 (top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
373 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
374 (::aos::robot_state->autonomous &&
375 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
376 top_claw_.zeroing_state() ==
377 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
378 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
379 bottom_claw_.zeroing_state() ==
380 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION))));
381}
382
383bool ClawMotor::is_zeroing() const { return !is_ready(); }
384
Austin Schuh3bb9a442014-02-02 16:01:45 -0800385// Positive angle is up, and positive power is up.
Austin Schuhcc0bf312014-02-09 00:39:29 -0800386void ClawMotor::RunIteration(const control_loops::ClawGroup::Goal *goal,
387 const control_loops::ClawGroup::Position *position,
388 control_loops::ClawGroup::Output *output,
Austin Schuh3bb9a442014-02-02 16:01:45 -0800389 ::aos::control_loops::Status *status) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800390 constexpr double dt = 0.01;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800391
392 // Disable the motors now so that all early returns will return with the
393 // motors disabled.
394 if (output) {
395 output->top_claw_voltage = 0;
396 output->bottom_claw_voltage = 0;
397 output->intake_voltage = 0;
398 }
399
Austin Schuh1a499942014-02-17 01:51:58 -0800400 if (reset()) {
Austin Schuh27b8fb12014-02-22 15:10:05 -0800401 top_claw_.Reset(position->top);
402 bottom_claw_.Reset(position->bottom);
Austin Schuh1a499942014-02-17 01:51:58 -0800403 }
404
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800405 if (::aos::robot_state.get() == nullptr) {
406 return;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800407 }
408
Austin Schuhf9286cd2014-02-11 00:51:09 -0800409 const frc971::constants::Values &values = constants::GetValues();
410
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800411 if (position) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800412 Eigen::Matrix<double, 2, 1> Y;
413 Y << position->bottom.position + bottom_claw_.offset(),
414 position->top.position + top_claw_.offset();
415 claw_.Correct(Y);
416
Austin Schuhf9286cd2014-02-11 00:51:09 -0800417 top_claw_.SetPositionValues(position->top);
418 bottom_claw_.SetPositionValues(position->bottom);
419
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800420 if (!has_top_claw_goal_) {
421 has_top_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800422 top_claw_goal_ = top_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800423 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800424 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800425 }
426 if (!has_bottom_claw_goal_) {
427 has_bottom_claw_goal_ = true;
Austin Schuhcda86af2014-02-16 16:16:39 -0800428 bottom_claw_goal_ = bottom_claw_.absolute_position();
Austin Schuhe7f90d12014-02-17 00:48:25 -0800429 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800430 top_claw_.absolute_position() - bottom_claw_.absolute_position();
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800431 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800432 LOG(DEBUG, "Claw position is (top: %f bottom: %f\n",
433 top_claw_.absolute_position(), bottom_claw_.absolute_position());
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800434 }
435
Austin Schuh069143b2014-02-17 02:46:26 -0800436 const bool autonomous = ::aos::robot_state->autonomous;
437 const bool enabled = ::aos::robot_state->enabled;
438
439 double bottom_claw_velocity_ = 0.0;
440 double top_claw_velocity_ = 0.0;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800441
442 if ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED &&
443 bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED) ||
444 (autonomous &&
445 ((top_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
446 top_claw_.zeroing_state() ==
447 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION) &&
448 (bottom_claw_.zeroing_state() == ZeroedStateFeedbackLoop::CALIBRATED ||
449 bottom_claw_.zeroing_state() ==
450 ZeroedStateFeedbackLoop::DISABLED_CALIBRATION)))) {
451 // Ready to use the claw.
452 // Limit the goals here.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800453 bottom_claw_goal_ = goal->bottom_angle;
Brian Silverman7c021c42014-02-17 15:15:56 -0800454 top_claw_goal_ = goal->bottom_angle + goal->separation_angle;
Austin Schuhcda86af2014-02-16 16:16:39 -0800455 has_bottom_claw_goal_ = true;
456 has_top_claw_goal_ = true;
457 doing_calibration_fine_tune_ = false;
458
Austin Schuhe7f90d12014-02-17 00:48:25 -0800459 mode_ = READY;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800460 } else if (top_claw_.zeroing_state() !=
461 ZeroedStateFeedbackLoop::UNKNOWN_POSITION &&
462 bottom_claw_.zeroing_state() !=
463 ZeroedStateFeedbackLoop::UNKNOWN_POSITION) {
464 // Time to fine tune the zero.
465 // Limit the goals here.
Austin Schuh0c733422014-02-17 01:17:12 -0800466 if (!enabled) {
467 // If we are disabled, start the fine tune process over again.
468 doing_calibration_fine_tune_ = false;
469 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800470 if (bottom_claw_.zeroing_state() != ZeroedStateFeedbackLoop::CALIBRATED) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800471 // always get the bottom claw to calibrated first
472 LOG(DEBUG, "Calibrating the bottom of the claw\n");
473 if (!doing_calibration_fine_tune_) {
474 if (::std::abs(bottom_absolute_position() -
Austin Schuhd27931c2014-02-16 19:18:20 -0800475 values.claw.start_fine_tune_pos) <
476 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800477 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800478 bottom_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800479 top_claw_velocity_ = bottom_claw_velocity_ =
480 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800481 LOG(DEBUG, "Ready to fine tune the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800482 mode_ = FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800483 } else {
484 // send bottom to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800485 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800486 LOG(DEBUG, "Going to the start position for the bottom\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800487 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800488 }
489 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800490 mode_ = FINE_TUNE_BOTTOM;
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;
Brian Silvermane0a95462014-02-17 00:41:09 -0800494 if (top_claw_.front_or_back_triggered() ||
495 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800496 // We shouldn't hit a limit, but if we do, go back to the zeroing
497 // point and try again.
498 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800499 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800500 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800501 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800502 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuhcda86af2014-02-16 16:16:39 -0800503 }
Austin Schuh288c8c32014-02-16 17:20:17 -0800504
Brian Silvermane0a95462014-02-17 00:41:09 -0800505 if (bottom_claw_.calibration().value()) {
506 if (bottom_claw_.calibration().posedge_count_changed() &&
Austin Schuh288c8c32014-02-16 17:20:17 -0800507 position) {
508 // do calibration
509 bottom_claw_.SetCalibration(
510 position->bottom.posedge_value,
Austin Schuhd27931c2014-02-16 19:18:20 -0800511 values.claw.lower_claw.calibration.lower_angle);
Austin Schuh288c8c32014-02-16 17:20:17 -0800512 bottom_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
513 // calibrated so we are done fine tuning bottom
514 doing_calibration_fine_tune_ = false;
515 LOG(DEBUG, "Calibrated the bottom correctly!\n");
516 } else {
517 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800518 bottom_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800519 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800520 mode_ = PREP_FINE_TUNE_BOTTOM;
Austin Schuh288c8c32014-02-16 17:20:17 -0800521 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800522 } else {
523 LOG(DEBUG, "Fine tuning\n");
524 }
525 }
526 // now set the top claw to track
527
Austin Schuhd27931c2014-02-16 19:18:20 -0800528 top_claw_goal_ = bottom_claw_goal_ + values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800529 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800530 // bottom claw must be calibrated, start on the top
531 if (!doing_calibration_fine_tune_) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800532 if (::std::abs(top_absolute_position() -
533 values.claw.start_fine_tune_pos) <
534 values.claw.claw_unimportant_epsilon) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800535 doing_calibration_fine_tune_ = true;
Austin Schuhd27931c2014-02-16 19:18:20 -0800536 top_claw_goal_ += values.claw.claw_zeroing_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800537 top_claw_velocity_ = bottom_claw_velocity_ =
538 values.claw.claw_zeroing_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800539 LOG(DEBUG, "Ready to fine tune the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800540 mode_ = FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800541 } else {
542 // send top to zeroing start
Austin Schuhd27931c2014-02-16 19:18:20 -0800543 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuhcda86af2014-02-16 16:16:39 -0800544 LOG(DEBUG, "Going to the start position for the top\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800545 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800546 }
547 } else {
Austin Schuhe7f90d12014-02-17 00:48:25 -0800548 mode_ = FINE_TUNE_TOP;
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;
Brian Silvermane0a95462014-02-17 00:41:09 -0800552 if (top_claw_.front_or_back_triggered() ||
553 bottom_claw_.front_or_back_triggered()) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800554 // this should not happen, but now we know it won't
555 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800556 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800557 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhcda86af2014-02-16 16:16:39 -0800558 LOG(DEBUG, "Found a limit, starting over.\n");
Austin Schuhe7f90d12014-02-17 00:48:25 -0800559 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuhcda86af2014-02-16 16:16:39 -0800560 }
Brian Silvermane0a95462014-02-17 00:41:09 -0800561 if (top_claw_.calibration().value()) {
562 if (top_claw_.calibration().posedge_count_changed() &&
Austin Schuh288c8c32014-02-16 17:20:17 -0800563 position) {
564 // do calibration
Austin Schuhd27931c2014-02-16 19:18:20 -0800565 top_claw_.SetCalibration(
566 position->top.posedge_value,
567 values.claw.upper_claw.calibration.lower_angle);
Austin Schuh288c8c32014-02-16 17:20:17 -0800568 top_claw_.set_zeroing_state(ZeroedStateFeedbackLoop::CALIBRATED);
Austin Schuhe7f90d12014-02-17 00:48:25 -0800569 // calibrated so we are done fine tuning top
Austin Schuh288c8c32014-02-16 17:20:17 -0800570 doing_calibration_fine_tune_ = false;
571 LOG(DEBUG, "Calibrated the top correctly!\n");
572 } else {
573 doing_calibration_fine_tune_ = false;
Austin Schuhd27931c2014-02-16 19:18:20 -0800574 top_claw_goal_ = values.claw.start_fine_tune_pos;
Austin Schuh069143b2014-02-17 02:46:26 -0800575 top_claw_velocity_ = bottom_claw_velocity_ = 0.0;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800576 mode_ = PREP_FINE_TUNE_TOP;
Austin Schuh288c8c32014-02-16 17:20:17 -0800577 }
Austin Schuhcda86af2014-02-16 16:16:39 -0800578 }
579 }
580 // now set the bottom claw to track
Austin Schuhd27931c2014-02-16 19:18:20 -0800581 bottom_claw_goal_ = top_claw_goal_ - values.claw.claw_zeroing_separation;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800582 }
583 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800584 doing_calibration_fine_tune_ = false;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800585 if (!was_enabled_ && enabled) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800586 if (position) {
587 top_claw_goal_ = position->top.position;
588 bottom_claw_goal_ = position->bottom.position;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800589 initial_separation_ =
Austin Schuhcda86af2014-02-16 16:16:39 -0800590 position->top.position - position->bottom.position;
Austin Schuhf9286cd2014-02-11 00:51:09 -0800591 } else {
592 has_top_claw_goal_ = false;
593 has_bottom_claw_goal_ = false;
594 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800595 }
Austin Schuhf9286cd2014-02-11 00:51:09 -0800596
Austin Schuh4cb047f2014-02-16 21:10:19 -0800597 if ((bottom_claw_.zeroing_state() !=
598 ZeroedStateFeedbackLoop::UNKNOWN_POSITION ||
Brian Silvermane0a95462014-02-17 00:41:09 -0800599 bottom_claw_.front().value() || top_claw_.front().value()) &&
600 !top_claw_.back().value() && !bottom_claw_.back().value()) {
Austin Schuhf9286cd2014-02-11 00:51:09 -0800601 if (enabled) {
602 // Time to slowly move back up to find any position to narrow down the
603 // zero.
Austin Schuhd27931c2014-02-16 19:18:20 -0800604 top_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
605 bottom_claw_goal_ += values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800606 top_claw_velocity_ = bottom_claw_velocity_ =
607 values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800608 LOG(DEBUG, "Bottom is known.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800609 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800610 } else {
611 // We don't know where either claw is. Slowly start moving down to find
612 // any hall effect.
Austin Schuhf9286cd2014-02-11 00:51:09 -0800613 if (enabled) {
Austin Schuhd27931c2014-02-16 19:18:20 -0800614 top_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
615 bottom_claw_goal_ -= values.claw.claw_zeroing_off_speed * dt;
Austin Schuh069143b2014-02-17 02:46:26 -0800616 top_claw_velocity_ = bottom_claw_velocity_ =
617 -values.claw.claw_zeroing_off_speed;
Austin Schuhcda86af2014-02-16 16:16:39 -0800618 LOG(DEBUG, "Both are unknown.\n");
Austin Schuhf9286cd2014-02-11 00:51:09 -0800619 }
620 }
621
622 if (enabled) {
623 top_claw_.SetCalibrationOnEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800624 values.claw.upper_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800625 bottom_claw_.SetCalibrationOnEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800626 values.claw.lower_claw, ZeroedStateFeedbackLoop::APPROXIMATE_CALIBRATION);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800627 } else {
Austin Schuh069143b2014-02-17 02:46:26 -0800628 // TODO(austin): Only calibrate on the predetermined edge.
629 // We might be able to just ignore this since the backlash is soooo low. :)
Austin Schuhf9286cd2014-02-11 00:51:09 -0800630 top_claw_.SetCalibrationOnEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800631 values.claw.upper_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800632 bottom_claw_.SetCalibrationOnEdge(
Austin Schuhd27931c2014-02-16 19:18:20 -0800633 values.claw.lower_claw, ZeroedStateFeedbackLoop::DISABLED_CALIBRATION);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800634 }
Austin Schuhe7f90d12014-02-17 00:48:25 -0800635 mode_ = UNKNOWN_LOCATION;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800636 }
637
Austin Schuh069143b2014-02-17 02:46:26 -0800638 // Limit the goals if both claws have been (mostly) found.
639 if (mode_ != UNKNOWN_LOCATION) {
640 LimitClawGoal(&bottom_claw_goal_, &top_claw_goal_, values);
641 }
642
Austin Schuhf9286cd2014-02-11 00:51:09 -0800643 if (has_top_claw_goal_ && has_bottom_claw_goal_) {
Austin Schuh069143b2014-02-17 02:46:26 -0800644 claw_.R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_,
645 bottom_claw_velocity_, top_claw_velocity_ - bottom_claw_velocity_;
Austin Schuhcda86af2014-02-16 16:16:39 -0800646 double separation = -971;
647 if (position != nullptr) {
648 separation = position->top.position - position->bottom.position;
649 }
650 LOG(DEBUG, "Goal is %f (bottom) %f, separation is %f\n", claw_.R(0, 0),
651 claw_.R(1, 0), separation);
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800652
Austin Schuh01c652b2014-02-21 23:13:42 -0800653 // Only cap power when one of the halves of the claw is moving slowly and
654 // could wind up.
Austin Schuhe7f90d12014-02-17 00:48:25 -0800655 claw_.set_is_zeroing(mode_ == UNKNOWN_LOCATION || mode_ == FINE_TUNE_TOP ||
656 mode_ == FINE_TUNE_BOTTOM);
Austin Schuhcda86af2014-02-16 16:16:39 -0800657 claw_.Update(output == nullptr);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800658 } else {
Austin Schuhcda86af2014-02-16 16:16:39 -0800659 claw_.Update(true);
Austin Schuhf9286cd2014-02-11 00:51:09 -0800660 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800661
Austin Schuh4cb047f2014-02-16 21:10:19 -0800662 capped_goal_ = false;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800663 switch (mode_) {
Austin Schuhcda86af2014-02-16 16:16:39 -0800664 case READY:
Austin Schuhe7f90d12014-02-17 00:48:25 -0800665 case PREP_FINE_TUNE_TOP:
666 case PREP_FINE_TUNE_BOTTOM:
Austin Schuhcda86af2014-02-16 16:16:39 -0800667 break;
Austin Schuhe7f90d12014-02-17 00:48:25 -0800668 case FINE_TUNE_BOTTOM:
669 case FINE_TUNE_TOP:
Austin Schuh4cb047f2014-02-16 21:10:19 -0800670 case UNKNOWN_LOCATION: {
James Kuszmaul0e866512014-02-21 13:12:52 -0800671 Eigen::Matrix<double, 2, 1> U = claw_.K() * (claw_.R - claw_.X_hat);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800672 if (claw_.uncapped_average_voltage() > values.claw.max_zeroing_voltage) {
James Kuszmaul0e866512014-02-21 13:12:52 -0800673 double dx_bot = (U(0, 0) -
Austin Schuh4cb047f2014-02-16 21:10:19 -0800674 values.claw.max_zeroing_voltage) /
675 claw_.K(0, 0);
James Kuszmaul0e866512014-02-21 13:12:52 -0800676 double dx_top = (U(1, 0) -
677 values.claw.max_zeroing_voltage) /
678 claw_.K(0, 0);
679 double dx = ::std::max(dx_top, dx_bot);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800680 bottom_claw_goal_ -= dx;
681 top_claw_goal_ -= dx;
James Kuszmaul0e866512014-02-21 13:12:52 -0800682 Eigen::Matrix<double, 4, 1> R;
683 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0), claw_.R(3, 0);
684 U = claw_.K() * (R - claw_.X_hat);
Austin Schuhcda86af2014-02-16 16:16:39 -0800685 capped_goal_ = true;
Austin Schuh4cb047f2014-02-16 21:10:19 -0800686 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Austin Schuhe7f90d12014-02-17 00:48:25 -0800687 LOG(DEBUG, "Uncapped is %f, max is %f, difference is %f\n",
688 claw_.uncapped_average_voltage(), values.claw.max_zeroing_voltage,
689 (claw_.uncapped_average_voltage() -
690 values.claw.max_zeroing_voltage));
Austin Schuh4cb047f2014-02-16 21:10:19 -0800691 } else if (claw_.uncapped_average_voltage() <
692 -values.claw.max_zeroing_voltage) {
James Kuszmaul0e866512014-02-21 13:12:52 -0800693 double dx_bot = (U(0, 0) +
Austin Schuh4cb047f2014-02-16 21:10:19 -0800694 values.claw.max_zeroing_voltage) /
695 claw_.K(0, 0);
James Kuszmaul0e866512014-02-21 13:12:52 -0800696 double dx_top = (U(1, 0) +
697 values.claw.max_zeroing_voltage) /
698 claw_.K(0, 0);
699 double dx = ::std::min(dx_top, dx_bot);
Austin Schuh4cb047f2014-02-16 21:10:19 -0800700 bottom_claw_goal_ -= dx;
701 top_claw_goal_ -= dx;
James Kuszmaul0e866512014-02-21 13:12:52 -0800702 Eigen::Matrix<double, 4, 1> R;
703 R << bottom_claw_goal_, top_claw_goal_ - bottom_claw_goal_, claw_.R(2, 0), claw_.R(3, 0);
704 U = claw_.K() * (R - claw_.X_hat);
Austin Schuhcda86af2014-02-16 16:16:39 -0800705 capped_goal_ = true;
Austin Schuh4cb047f2014-02-16 21:10:19 -0800706 LOG(DEBUG, "Moving the goal by %f to prevent windup\n", dx);
Austin Schuhcda86af2014-02-16 16:16:39 -0800707 }
Austin Schuh4cb047f2014-02-16 21:10:19 -0800708 } break;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800709 }
710
711 if (output) {
James Kuszmauld536a402014-02-18 22:32:12 -0800712 output->top_claw_voltage = claw_.U(1, 0);
Austin Schuhcda86af2014-02-16 16:16:39 -0800713 output->bottom_claw_voltage = claw_.U(0, 0);
Austin Schuhf84a1302014-02-19 00:23:30 -0800714
715 if (output->top_claw_voltage > kMaxVoltage) {
716 output->top_claw_voltage = kMaxVoltage;
717 } else if (output->top_claw_voltage < -kMaxVoltage) {
718 output->top_claw_voltage = -kMaxVoltage;
719 }
720
721 if (output->bottom_claw_voltage > kMaxVoltage) {
722 output->bottom_claw_voltage = kMaxVoltage;
723 } else if (output->bottom_claw_voltage < -kMaxVoltage) {
724 output->bottom_claw_voltage = -kMaxVoltage;
725 }
Austin Schuh3bb9a442014-02-02 16:01:45 -0800726 }
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800727 status->done = false;
Austin Schuh4b7b5d02014-02-10 21:20:34 -0800728
729 was_enabled_ = ::aos::robot_state->enabled;
Austin Schuh3bb9a442014-02-02 16:01:45 -0800730}
731
732} // namespace control_loops
733} // namespace frc971