blob: 94526373a14ccde6fdc31a396698a8079e203df0 [file] [log] [blame]
Ben Fredrickson6b5ba792015-01-25 17:14:40 -08001#include "frc971/control_loops/fridge/fridge.h"
2
Austin Schuh703b8d42015-02-01 14:56:34 -08003#include <cmath>
4
Ben Fredrickson6b5ba792015-01-25 17:14:40 -08005#include "aos/common/controls/control_loops.q.h"
6#include "aos/common/logging/logging.h"
7
8#include "frc971/control_loops/fridge/elevator_motor_plant.h"
9#include "frc971/control_loops/fridge/arm_motor_plant.h"
Austin Schuhb966c432015-02-16 15:47:18 -080010#include "frc971/control_loops/voltage_cap/voltage_cap.h"
Austin Schuh703b8d42015-02-01 14:56:34 -080011#include "frc971/zeroing/zeroing.h"
12
13#include "frc971/constants.h"
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080014
15namespace frc971 {
16namespace control_loops {
17
Austin Schuh703b8d42015-02-01 14:56:34 -080018namespace {
Austin Schuhb966c432015-02-16 15:47:18 -080019constexpr double kZeroingVoltage = 4.0;
20constexpr double kElevatorZeroingVelocity = 0.10;
Daniel Pettie1bb13e2015-02-17 13:59:15 -080021// What speed we move to our safe height at.
22constexpr double kElevatorSafeHeightVelocity = 0.2;
Austin Schuhb966c432015-02-16 15:47:18 -080023constexpr double kArmZeroingVelocity = 0.20;
Austin Schuh703b8d42015-02-01 14:56:34 -080024} // namespace
25
26
27void CappedStateFeedbackLoop::CapU() {
Austin Schuhb966c432015-02-16 15:47:18 -080028 VoltageCap(max_voltage_, U(0, 0), U(1, 0), &mutable_U(0, 0),
29 &mutable_U(1, 0));
Austin Schuh703b8d42015-02-01 14:56:34 -080030}
31
32Eigen::Matrix<double, 2, 1>
33CappedStateFeedbackLoop::UnsaturateOutputGoalChange() {
34 // Compute the K matrix used to compensate for position errors.
35 Eigen::Matrix<double, 2, 2> Kp;
36 Kp.setZero();
37 Kp.col(0) = K().col(0);
38 Kp.col(1) = K().col(2);
39
40 Eigen::Matrix<double, 2, 2> Kp_inv = Kp.inverse();
41
42 // Compute how much we need to change R in order to achieve the change in U
43 // that was observed.
44 Eigen::Matrix<double, 2, 1> deltaR = -Kp_inv * (U_uncapped() - U());
45 return deltaR;
46}
47
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080048Fridge::Fridge(control_loops::FridgeQueue *fridge)
Brian Silverman089f5812015-02-15 01:58:19 -050049 : aos::controls::ControlLoop<control_loops::FridgeQueue>(fridge),
Austin Schuh703b8d42015-02-01 14:56:34 -080050 arm_loop_(new CappedStateFeedbackLoop(
51 StateFeedbackLoop<4, 2, 2>(MakeArmLoop()))),
52 elevator_loop_(new CappedStateFeedbackLoop(
53 StateFeedbackLoop<4, 2, 2>(MakeElevatorLoop()))),
Daniel Pettia7827412015-02-13 20:55:57 -080054 left_arm_estimator_(constants::GetValues().fridge.left_arm_zeroing),
55 right_arm_estimator_(constants::GetValues().fridge.right_arm_zeroing),
Austin Schuh482a4e12015-02-14 22:43:43 -080056 left_elevator_estimator_(constants::GetValues().fridge.left_elev_zeroing),
Austin Schuh703b8d42015-02-01 14:56:34 -080057 right_elevator_estimator_(
Daniel Pettia7827412015-02-13 20:55:57 -080058 constants::GetValues().fridge.right_elev_zeroing) {}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080059
Austin Schuh703b8d42015-02-01 14:56:34 -080060void Fridge::UpdateZeroingState() {
Austin Schuh47bf6902015-02-14 22:46:22 -080061 if (left_elevator_estimator_.offset_ratio_ready() < 1.0 ||
62 right_elevator_estimator_.offset_ratio_ready() < 1.0 ||
63 left_arm_estimator_.offset_ratio_ready() < 1.0 ||
64 right_arm_estimator_.offset_ratio_ready() < 1.0) {
Austin Schuh703b8d42015-02-01 14:56:34 -080065 state_ = INITIALIZING;
66 } else if (!left_elevator_estimator_.zeroed() ||
67 !right_elevator_estimator_.zeroed()) {
68 state_ = ZEROING_ELEVATOR;
69 } else if (!left_arm_estimator_.zeroed() ||
70 !right_arm_estimator_.zeroed()) {
71 state_ = ZEROING_ARM;
72 } else {
73 state_ = RUNNING;
74 }
75}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080076
Austin Schuh703b8d42015-02-01 14:56:34 -080077void Fridge::Correct() {
78 {
79 Eigen::Matrix<double, 2, 1> Y;
80 Y << left_elevator(), right_elevator();
81 elevator_loop_->Correct(Y);
82 }
83
84 {
85 Eigen::Matrix<double, 2, 1> Y;
86 Y << left_arm(), right_arm();
87 arm_loop_->Correct(Y);
88 }
89}
90
91void Fridge::SetElevatorOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -080092 LOG(INFO, "Changing Elevator offset from %f, %f to %f, %f\n",
93 left_elevator_offset_, right_elevator_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -080094 double left_doffset = left_offset - left_elevator_offset_;
95 double right_doffset = right_offset - right_elevator_offset_;
96
97 // Adjust the average height and height difference between the two sides.
98 // The derivatives of both should not need to be updated since the speeds
99 // haven't changed.
100 // The height difference is calculated as left - right, not right - left.
101 elevator_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
102 elevator_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
103
104 // Modify the zeroing goal.
105 elevator_goal_ += (left_doffset + right_doffset) / 2;
106
107 // Update the cached offset values to the actual values.
108 left_elevator_offset_ = left_offset;
109 right_elevator_offset_ = right_offset;
110}
111
112void Fridge::SetArmOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800113 LOG(INFO, "Changing Arm offset from %f, %f to %f, %f\n", left_arm_offset_,
114 right_arm_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -0800115 double left_doffset = left_offset - left_arm_offset_;
116 double right_doffset = right_offset - right_arm_offset_;
117
118 // Adjust the average angle and angle difference between the two sides.
119 // The derivatives of both should not need to be updated since the speeds
120 // haven't changed.
121 arm_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
122 arm_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
123
124 // Modify the zeroing goal.
125 arm_goal_ += (left_doffset + right_doffset) / 2;
126
127 // Update the cached offset values to the actual values.
128 left_arm_offset_ = left_offset;
129 right_arm_offset_ = right_offset;
130}
131
132double Fridge::estimated_left_elevator() {
133 return current_position_.elevator.left.encoder +
134 left_elevator_estimator_.offset();
135}
136double Fridge::estimated_right_elevator() {
137 return current_position_.elevator.right.encoder +
138 right_elevator_estimator_.offset();
139}
140
141double Fridge::estimated_elevator() {
142 return (estimated_left_elevator() + estimated_right_elevator()) / 2.0;
143}
144
145double Fridge::estimated_left_arm() {
146 return current_position_.elevator.left.encoder + left_arm_estimator_.offset();
147}
148double Fridge::estimated_right_arm() {
149 return current_position_.elevator.right.encoder +
150 right_arm_estimator_.offset();
151}
152double Fridge::estimated_arm() {
153 return (estimated_left_arm() + estimated_right_arm()) / 2.0;
154}
155
156double Fridge::left_elevator() {
157 return current_position_.elevator.left.encoder + left_elevator_offset_;
158}
159double Fridge::right_elevator() {
160 return current_position_.elevator.right.encoder + right_elevator_offset_;
161}
162
163double Fridge::elevator() { return (left_elevator() + right_elevator()) / 2.0; }
164
165double Fridge::left_arm() {
166 return current_position_.arm.left.encoder + left_arm_offset_;
167}
168double Fridge::right_arm() {
169 return current_position_.arm.right.encoder + right_arm_offset_;
170}
171double Fridge::arm() { return (left_arm() + right_arm()) / 2.0; }
172
173double Fridge::elevator_zeroing_velocity() {
174 double average_elevator =
175 (constants::GetValues().fridge.elevator.lower_limit +
176 constants::GetValues().fridge.elevator.upper_limit) /
177 2.0;
178
179 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800180 constants::GetValues().fridge.left_elev_zeroing.index_difference,
181 constants::GetValues().fridge.right_elev_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800182
183 if (elevator_zeroing_velocity_ == 0) {
184 if (estimated_elevator() > average_elevator) {
185 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
186 } else {
187 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
188 }
189 } else if (elevator_zeroing_velocity_ > 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800190 estimated_elevator() > average_elevator + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800191 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
192 } else if (elevator_zeroing_velocity_ < 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800193 estimated_elevator() < average_elevator - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800194 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
195 }
196 return elevator_zeroing_velocity_;
197}
198
199double Fridge::arm_zeroing_velocity() {
200 const double average_arm = (constants::GetValues().fridge.arm.lower_limit +
201 constants::GetValues().fridge.arm.upper_limit) /
202 2.0;
203 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800204 constants::GetValues().fridge.right_arm_zeroing.index_difference,
205 constants::GetValues().fridge.left_arm_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800206
207 if (arm_zeroing_velocity_ == 0) {
208 if (estimated_arm() > average_arm) {
209 arm_zeroing_velocity_ = -kArmZeroingVelocity;
210 } else {
211 arm_zeroing_velocity_ = kArmZeroingVelocity;
212 }
213 } else if (arm_zeroing_velocity_ > 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800214 estimated_arm() > average_arm + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800215 arm_zeroing_velocity_ = -kArmZeroingVelocity;
216 } else if (arm_zeroing_velocity_ < 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800217 estimated_arm() < average_arm - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800218 arm_zeroing_velocity_ = kArmZeroingVelocity;
219 }
220 return arm_zeroing_velocity_;
221}
222
Austin Schuh482a4e12015-02-14 22:43:43 -0800223void Fridge::RunIteration(const control_loops::FridgeQueue::Goal *unsafe_goal,
Austin Schuh703b8d42015-02-01 14:56:34 -0800224 const control_loops::FridgeQueue::Position *position,
225 control_loops::FridgeQueue::Output *output,
226 control_loops::FridgeQueue::Status *status) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800227 if (WasReset()) {
228 LOG(ERROR, "WPILib reset, restarting\n");
229 left_elevator_estimator_.Reset();
230 right_elevator_estimator_.Reset();
231 left_arm_estimator_.Reset();
232 right_arm_estimator_.Reset();
233 state_ = UNINITIALIZED;
234 }
235
Austin Schuh703b8d42015-02-01 14:56:34 -0800236 // Get a reference to the constants struct since we use it so often in this
237 // code.
Philipp Schrader82c65072015-02-16 00:47:09 +0000238 const auto &values = constants::GetValues();
Austin Schuh703b8d42015-02-01 14:56:34 -0800239
240 // Bool to track if we should turn the motors on or not.
241 bool disable = output == nullptr;
242 double elevator_goal_velocity = 0.0;
243 double arm_goal_velocity = 0.0;
244
245 // Save the current position so it can be used easily in the class.
246 current_position_ = *position;
247
248 left_elevator_estimator_.UpdateEstimate(position->elevator.left);
249 right_elevator_estimator_.UpdateEstimate(position->elevator.right);
250 left_arm_estimator_.UpdateEstimate(position->arm.left);
251 right_arm_estimator_.UpdateEstimate(position->arm.right);
252
253 if (state_ != UNINITIALIZED) {
254 Correct();
255 }
256
257 // Zeroing will work as follows:
258 // At startup, record the offset of the two halves of the two subsystems.
259 // Then, start moving the elevator towards the center until both halves are
260 // zeroed.
261 // Then, start moving the claw towards the center until both halves are
262 // zeroed.
263 // Then, done!
264
265 // We'll then need code to do sanity checking on values.
266
267 // Now, we need to figure out which way to go.
268
269 switch (state_) {
270 case UNINITIALIZED:
271 LOG(DEBUG, "Uninitialized\n");
272 // Startup. Assume that we are at the origin everywhere.
273 // This records the encoder offset between the two sides of the elevator.
274 left_elevator_offset_ = -position->elevator.left.encoder;
275 right_elevator_offset_ = -position->elevator.right.encoder;
276 left_arm_offset_ = -position->arm.left.encoder;
277 right_arm_offset_ = -position->arm.right.encoder;
Austin Schuhaab01572015-02-15 00:12:35 -0800278 elevator_loop_->mutable_X_hat().setZero();
279 arm_loop_->mutable_X_hat().setZero();
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800280 LOG(INFO, "Initializing arm offsets to %f, %f\n", left_arm_offset_,
281 right_arm_offset_);
282 LOG(INFO, "Initializing elevator offsets to %f, %f\n",
283 left_elevator_offset_, right_elevator_offset_);
Austin Schuh703b8d42015-02-01 14:56:34 -0800284 Correct();
285 state_ = INITIALIZING;
286 disable = true;
287 break;
288
289 case INITIALIZING:
290 LOG(DEBUG, "Waiting for accurate initial position.\n");
291 disable = true;
292 // Update state_ to accurately represent the state of the zeroing
293 // estimators.
294 UpdateZeroingState();
295 if (state_ != INITIALIZING) {
296 // Set the goals to where we are now.
297 elevator_goal_ = elevator();
298 arm_goal_ = arm();
299 }
300 break;
301
302 case ZEROING_ELEVATOR:
303 LOG(DEBUG, "Zeroing elevator\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800304
305 // Update state_ to accurately represent the state of the zeroing
306 // estimators.
307 UpdateZeroingState();
308 if (left_elevator_estimator_.zeroed() &&
309 right_elevator_estimator_.zeroed()) {
310 SetElevatorOffset(left_elevator_estimator_.offset(),
311 right_elevator_estimator_.offset());
312 LOG(DEBUG, "Zeroed the elevator!\n");
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800313
Austin Schuh5e872c82015-02-17 22:59:08 -0800314 if (elevator() < values.fridge.arm_zeroing_height &&
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800315 state_ != INITIALIZING) {
316 // Move the elevator to a safe height before we start zeroing the arm,
317 // so that we don't crash anything.
318 LOG(DEBUG, "Moving elevator to safe height.\n");
319 elevator_goal_ += kElevatorSafeHeightVelocity *
320 ::aos::controls::kLoopFrequency.ToSeconds();
321 elevator_goal_velocity = kElevatorSafeHeightVelocity;
322
323 state_ = ZEROING_ELEVATOR;
324 break;
325 }
326
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800327 } else if (!disable) {
328 elevator_goal_velocity = elevator_zeroing_velocity();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800329 elevator_goal_ += elevator_goal_velocity *
330 ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800331 }
332 break;
333
334 case ZEROING_ARM:
335 LOG(DEBUG, "Zeroing the arm\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800336
337 // Update state_ to accurately represent the state of the zeroing
338 // estimators.
339 UpdateZeroingState();
340 if (left_arm_estimator_.zeroed() && right_arm_estimator_.zeroed()) {
341 SetArmOffset(left_arm_estimator_.offset(),
342 right_arm_estimator_.offset());
343 LOG(DEBUG, "Zeroed the arm!\n");
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800344 } else if (!disable) {
345 arm_goal_velocity = arm_zeroing_velocity();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800346 arm_goal_ += arm_goal_velocity *
347 ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800348 }
349 break;
350
351 case RUNNING:
352 LOG(DEBUG, "Running!\n");
Austin Schuh482a4e12015-02-14 22:43:43 -0800353 if (unsafe_goal) {
354 arm_goal_velocity = unsafe_goal->angular_velocity;
355 elevator_goal_velocity = unsafe_goal->velocity;
356 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800357
358 // Update state_ to accurately represent the state of the zeroing
359 // estimators.
360 UpdateZeroingState();
Austin Schuh482a4e12015-02-14 22:43:43 -0800361 if (unsafe_goal) {
362 arm_goal_ = unsafe_goal->angle;
363 elevator_goal_ = unsafe_goal->height;
364 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800365
366 if (state_ != RUNNING && state_ != ESTOP) {
367 state_ = UNINITIALIZED;
368 }
369 break;
370
371 case ESTOP:
372 LOG(ERROR, "Estop\n");
373 disable = true;
374 break;
375 }
376
377 // Commence death if either left/right tracking error gets too big. This
378 // should run immediately after the SetArmOffset and SetElevatorOffset
379 // functions to double-check that the hardware is in a sane state.
380 if (::std::abs(left_arm() - right_arm()) >=
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800381 values.max_allowed_left_right_arm_difference) {
382 LOG(ERROR, "The arms are too far apart. |%f - %f| > %f\n", left_arm(),
383 right_arm(), values.max_allowed_left_right_arm_difference);
384
385 // Indicate an ESTOP condition and stop the motors.
386 state_ = ESTOP;
387 disable = true;
388 }
389
390 if (::std::abs(left_elevator() - right_elevator()) >=
391 values.max_allowed_left_right_elevator_difference) {
392 LOG(ERROR, "The elevators are too far apart. |%f - %f| > %f\n",
393 left_elevator(), right_elevator(),
394 values.max_allowed_left_right_elevator_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800395
396 // Indicate an ESTOP condition and stop the motors.
397 state_ = ESTOP;
398 disable = true;
399 }
400
401 // Limit the goals so we can't exceed the hardware limits if we are RUNNING.
402 if (state_ == RUNNING) {
403 // Limit the arm goal to min/max allowable angles.
404 if (arm_goal_ >= values.fridge.arm.upper_limit) {
405 LOG(WARNING, "Arm goal above limit, %f > %f\n", arm_goal_,
406 values.fridge.arm.upper_limit);
407 arm_goal_ = values.fridge.arm.upper_limit;
408 }
409 if (arm_goal_ <= values.fridge.arm.lower_limit) {
410 LOG(WARNING, "Arm goal below limit, %f < %f\n", arm_goal_,
411 values.fridge.arm.lower_limit);
412 arm_goal_ = values.fridge.arm.lower_limit;
413 }
414
415 // Limit the elevator goal to min/max allowable heights.
416 if (elevator_goal_ >= values.fridge.elevator.upper_limit) {
417 LOG(WARNING, "Elevator goal above limit, %f > %f\n", elevator_goal_,
418 values.fridge.elevator.upper_limit);
419 elevator_goal_ = values.fridge.elevator.upper_limit;
420 }
421 if (elevator_goal_ <= values.fridge.elevator.lower_limit) {
422 LOG(WARNING, "Elevator goal below limit, %f < %f\n", elevator_goal_,
423 values.fridge.elevator.lower_limit);
424 elevator_goal_ = values.fridge.elevator.lower_limit;
425 }
426 }
427
428 // Check the lower level hardware limit as well.
429 if (state_ == RUNNING) {
430 if (left_arm() >= values.fridge.arm.upper_hard_limit ||
431 left_arm() <= values.fridge.arm.lower_hard_limit) {
432 LOG(ERROR, "Left arm at %f out of bounds [%f, %f], ESTOPing\n",
433 left_arm(), values.fridge.arm.lower_hard_limit,
434 values.fridge.arm.upper_hard_limit);
435 state_ = ESTOP;
436 }
437
438 if (right_arm() >= values.fridge.arm.upper_hard_limit ||
439 right_arm() <= values.fridge.arm.lower_hard_limit) {
440 LOG(ERROR, "Right arm at %f out of bounds [%f, %f], ESTOPing\n",
441 right_arm(), values.fridge.arm.lower_hard_limit,
442 values.fridge.arm.upper_hard_limit);
443 state_ = ESTOP;
444 }
445
446 if (left_elevator() >= values.fridge.elevator.upper_hard_limit ||
447 left_elevator() <= values.fridge.elevator.lower_hard_limit) {
448 LOG(ERROR, "Left elevator at %f out of bounds [%f, %f], ESTOPing\n",
449 left_elevator(), values.fridge.elevator.lower_hard_limit,
450 values.fridge.elevator.upper_hard_limit);
451 state_ = ESTOP;
452 }
453
454 if (right_elevator() >= values.fridge.elevator.upper_hard_limit ||
455 right_elevator() <= values.fridge.elevator.lower_hard_limit) {
456 LOG(ERROR, "Right elevator at %f out of bounds [%f, %f], ESTOPing\n",
457 right_elevator(), values.fridge.elevator.lower_hard_limit,
458 values.fridge.elevator.upper_hard_limit);
459 state_ = ESTOP;
460 }
461 }
462
463 // Set the goals.
464 arm_loop_->mutable_R() << arm_goal_, arm_goal_velocity, 0.0, 0.0;
465 elevator_loop_->mutable_R() << elevator_goal_, elevator_goal_velocity, 0.0,
466 0.0;
467
468 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
469 arm_loop_->set_max_voltage(max_voltage);
470 elevator_loop_->set_max_voltage(max_voltage);
471
472 if (state_ == ESTOP) {
473 disable = true;
474 }
475 arm_loop_->Update(disable);
476 elevator_loop_->Update(disable);
477
478 if (state_ == INITIALIZING || state_ == ZEROING_ELEVATOR ||
479 state_ == ZEROING_ARM) {
480 if (arm_loop_->U() != arm_loop_->U_uncapped()) {
481 Eigen::Matrix<double, 2, 1> deltaR =
482 arm_loop_->UnsaturateOutputGoalChange();
483
484 // Move the average arm goal by the amount observed.
485 LOG(WARNING, "Moving arm goal by %f to handle saturation\n",
486 deltaR(0, 0));
487 arm_goal_ += deltaR(0, 0);
488 }
489
490 if (elevator_loop_->U() != elevator_loop_->U_uncapped()) {
491 Eigen::Matrix<double, 2, 1> deltaR =
492 elevator_loop_->UnsaturateOutputGoalChange();
493
494 // Move the average elevator goal by the amount observed.
495 LOG(WARNING, "Moving elevator goal by %f to handle saturation\n",
496 deltaR(0, 0));
497 elevator_goal_ += deltaR(0, 0);
498 }
499 }
500
501 if (output) {
502 output->left_arm = arm_loop_->U(0, 0);
503 output->right_arm = arm_loop_->U(1, 0);
504 output->left_elevator = elevator_loop_->U(0, 0);
505 output->right_elevator = elevator_loop_->U(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800506 if (unsafe_goal) {
507 output->grabbers = unsafe_goal->grabbers;
508 } else {
509 output->grabbers.top_front = false;
510 output->grabbers.top_back = false;
511 output->grabbers.bottom_front = false;
512 output->grabbers.bottom_back = false;
513 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800514 }
515
516 // TODO(austin): Populate these fully.
Austin Schuh5ae4efd2015-02-15 23:34:22 -0800517 status->zeroed = state_ == RUNNING;
Austin Schuh703b8d42015-02-01 14:56:34 -0800518 status->angle = arm_loop_->X_hat(0, 0);
519 status->height = elevator_loop_->X_hat(0, 0);
Austin Schuh5e872c82015-02-17 22:59:08 -0800520 status->goal_angle = arm_goal_;
521 status->goal_height = elevator_goal_;
Austin Schuh482a4e12015-02-14 22:43:43 -0800522 if (unsafe_goal) {
523 status->grabbers = unsafe_goal->grabbers;
524 } else {
525 status->grabbers.top_front = false;
526 status->grabbers.top_back = false;
527 status->grabbers.bottom_front = false;
528 status->grabbers.bottom_back = false;
529 }
Daniel Pettiab274232015-02-16 19:15:34 -0800530 zeroing::PopulateEstimatorState(left_arm_estimator_,
531 &status->left_arm_state);
532 zeroing::PopulateEstimatorState(right_arm_estimator_,
533 &status->right_arm_state);
534 zeroing::PopulateEstimatorState(left_elevator_estimator_,
Austin Schuh9e37c322015-02-16 15:47:49 -0800535 &status->left_elevator_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800536 zeroing::PopulateEstimatorState(right_elevator_estimator_,
Austin Schuh9e37c322015-02-16 15:47:49 -0800537 &status->right_elevator_state);
Austin Schuh703b8d42015-02-01 14:56:34 -0800538 status->estopped = (state_ == ESTOP);
Austin Schuh482a4e12015-02-14 22:43:43 -0800539 status->state = state_;
Austin Schuh703b8d42015-02-01 14:56:34 -0800540 last_state_ = state_;
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800541}
542
543} // namespace control_loops
544} // namespace frc971