blob: 7596a756a436f31003c44449ac710ae62a34c5a0 [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;
21constexpr double kArmZeroingVelocity = 0.20;
Austin Schuh703b8d42015-02-01 14:56:34 -080022} // namespace
23
24
25void CappedStateFeedbackLoop::CapU() {
Austin Schuhb966c432015-02-16 15:47:18 -080026 VoltageCap(max_voltage_, U(0, 0), U(1, 0), &mutable_U(0, 0),
27 &mutable_U(1, 0));
Austin Schuh703b8d42015-02-01 14:56:34 -080028}
29
30Eigen::Matrix<double, 2, 1>
31CappedStateFeedbackLoop::UnsaturateOutputGoalChange() {
32 // Compute the K matrix used to compensate for position errors.
33 Eigen::Matrix<double, 2, 2> Kp;
34 Kp.setZero();
35 Kp.col(0) = K().col(0);
36 Kp.col(1) = K().col(2);
37
38 Eigen::Matrix<double, 2, 2> Kp_inv = Kp.inverse();
39
40 // Compute how much we need to change R in order to achieve the change in U
41 // that was observed.
42 Eigen::Matrix<double, 2, 1> deltaR = -Kp_inv * (U_uncapped() - U());
43 return deltaR;
44}
45
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080046Fridge::Fridge(control_loops::FridgeQueue *fridge)
Brian Silverman089f5812015-02-15 01:58:19 -050047 : aos::controls::ControlLoop<control_loops::FridgeQueue>(fridge),
Austin Schuh703b8d42015-02-01 14:56:34 -080048 arm_loop_(new CappedStateFeedbackLoop(
49 StateFeedbackLoop<4, 2, 2>(MakeArmLoop()))),
50 elevator_loop_(new CappedStateFeedbackLoop(
51 StateFeedbackLoop<4, 2, 2>(MakeElevatorLoop()))),
Daniel Pettia7827412015-02-13 20:55:57 -080052 left_arm_estimator_(constants::GetValues().fridge.left_arm_zeroing),
53 right_arm_estimator_(constants::GetValues().fridge.right_arm_zeroing),
Austin Schuh482a4e12015-02-14 22:43:43 -080054 left_elevator_estimator_(constants::GetValues().fridge.left_elev_zeroing),
Austin Schuh703b8d42015-02-01 14:56:34 -080055 right_elevator_estimator_(
Daniel Pettia7827412015-02-13 20:55:57 -080056 constants::GetValues().fridge.right_elev_zeroing) {}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080057
Austin Schuh703b8d42015-02-01 14:56:34 -080058void Fridge::UpdateZeroingState() {
Austin Schuh47bf6902015-02-14 22:46:22 -080059 if (left_elevator_estimator_.offset_ratio_ready() < 1.0 ||
60 right_elevator_estimator_.offset_ratio_ready() < 1.0 ||
61 left_arm_estimator_.offset_ratio_ready() < 1.0 ||
62 right_arm_estimator_.offset_ratio_ready() < 1.0) {
Austin Schuh703b8d42015-02-01 14:56:34 -080063 state_ = INITIALIZING;
64 } else if (!left_elevator_estimator_.zeroed() ||
65 !right_elevator_estimator_.zeroed()) {
66 state_ = ZEROING_ELEVATOR;
67 } else if (!left_arm_estimator_.zeroed() ||
68 !right_arm_estimator_.zeroed()) {
69 state_ = ZEROING_ARM;
70 } else {
71 state_ = RUNNING;
72 }
73}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080074
Austin Schuh703b8d42015-02-01 14:56:34 -080075void Fridge::Correct() {
76 {
77 Eigen::Matrix<double, 2, 1> Y;
78 Y << left_elevator(), right_elevator();
79 elevator_loop_->Correct(Y);
80 }
81
82 {
83 Eigen::Matrix<double, 2, 1> Y;
84 Y << left_arm(), right_arm();
85 arm_loop_->Correct(Y);
86 }
87}
88
89void Fridge::SetElevatorOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -080090 LOG(INFO, "Changing Elevator offset from %f, %f to %f, %f\n",
91 left_elevator_offset_, right_elevator_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -080092 double left_doffset = left_offset - left_elevator_offset_;
93 double right_doffset = right_offset - right_elevator_offset_;
94
95 // Adjust the average height and height difference between the two sides.
96 // The derivatives of both should not need to be updated since the speeds
97 // haven't changed.
98 // The height difference is calculated as left - right, not right - left.
99 elevator_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
100 elevator_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
101
102 // Modify the zeroing goal.
103 elevator_goal_ += (left_doffset + right_doffset) / 2;
104
105 // Update the cached offset values to the actual values.
106 left_elevator_offset_ = left_offset;
107 right_elevator_offset_ = right_offset;
108}
109
110void Fridge::SetArmOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800111 LOG(INFO, "Changing Arm offset from %f, %f to %f, %f\n", left_arm_offset_,
112 right_arm_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -0800113 double left_doffset = left_offset - left_arm_offset_;
114 double right_doffset = right_offset - right_arm_offset_;
115
116 // Adjust the average angle and angle difference between the two sides.
117 // The derivatives of both should not need to be updated since the speeds
118 // haven't changed.
119 arm_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
120 arm_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
121
122 // Modify the zeroing goal.
123 arm_goal_ += (left_doffset + right_doffset) / 2;
124
125 // Update the cached offset values to the actual values.
126 left_arm_offset_ = left_offset;
127 right_arm_offset_ = right_offset;
128}
129
130double Fridge::estimated_left_elevator() {
131 return current_position_.elevator.left.encoder +
132 left_elevator_estimator_.offset();
133}
134double Fridge::estimated_right_elevator() {
135 return current_position_.elevator.right.encoder +
136 right_elevator_estimator_.offset();
137}
138
139double Fridge::estimated_elevator() {
140 return (estimated_left_elevator() + estimated_right_elevator()) / 2.0;
141}
142
143double Fridge::estimated_left_arm() {
144 return current_position_.elevator.left.encoder + left_arm_estimator_.offset();
145}
146double Fridge::estimated_right_arm() {
147 return current_position_.elevator.right.encoder +
148 right_arm_estimator_.offset();
149}
150double Fridge::estimated_arm() {
151 return (estimated_left_arm() + estimated_right_arm()) / 2.0;
152}
153
154double Fridge::left_elevator() {
155 return current_position_.elevator.left.encoder + left_elevator_offset_;
156}
157double Fridge::right_elevator() {
158 return current_position_.elevator.right.encoder + right_elevator_offset_;
159}
160
161double Fridge::elevator() { return (left_elevator() + right_elevator()) / 2.0; }
162
163double Fridge::left_arm() {
164 return current_position_.arm.left.encoder + left_arm_offset_;
165}
166double Fridge::right_arm() {
167 return current_position_.arm.right.encoder + right_arm_offset_;
168}
169double Fridge::arm() { return (left_arm() + right_arm()) / 2.0; }
170
171double Fridge::elevator_zeroing_velocity() {
172 double average_elevator =
173 (constants::GetValues().fridge.elevator.lower_limit +
174 constants::GetValues().fridge.elevator.upper_limit) /
175 2.0;
176
177 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800178 constants::GetValues().fridge.left_elev_zeroing.index_difference,
179 constants::GetValues().fridge.right_elev_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800180
181 if (elevator_zeroing_velocity_ == 0) {
182 if (estimated_elevator() > average_elevator) {
183 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
184 } else {
185 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
186 }
187 } else if (elevator_zeroing_velocity_ > 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800188 estimated_elevator() > average_elevator + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800189 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
190 } else if (elevator_zeroing_velocity_ < 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800191 estimated_elevator() < average_elevator - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800192 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
193 }
194 return elevator_zeroing_velocity_;
195}
196
197double Fridge::arm_zeroing_velocity() {
198 const double average_arm = (constants::GetValues().fridge.arm.lower_limit +
199 constants::GetValues().fridge.arm.upper_limit) /
200 2.0;
201 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800202 constants::GetValues().fridge.right_arm_zeroing.index_difference,
203 constants::GetValues().fridge.left_arm_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800204
205 if (arm_zeroing_velocity_ == 0) {
206 if (estimated_arm() > average_arm) {
207 arm_zeroing_velocity_ = -kArmZeroingVelocity;
208 } else {
209 arm_zeroing_velocity_ = kArmZeroingVelocity;
210 }
211 } else if (arm_zeroing_velocity_ > 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800212 estimated_arm() > average_arm + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800213 arm_zeroing_velocity_ = -kArmZeroingVelocity;
214 } else if (arm_zeroing_velocity_ < 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800215 estimated_arm() < average_arm - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800216 arm_zeroing_velocity_ = kArmZeroingVelocity;
217 }
218 return arm_zeroing_velocity_;
219}
220
Austin Schuh482a4e12015-02-14 22:43:43 -0800221void Fridge::RunIteration(const control_loops::FridgeQueue::Goal *unsafe_goal,
Austin Schuh703b8d42015-02-01 14:56:34 -0800222 const control_loops::FridgeQueue::Position *position,
223 control_loops::FridgeQueue::Output *output,
224 control_loops::FridgeQueue::Status *status) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800225 if (WasReset()) {
226 LOG(ERROR, "WPILib reset, restarting\n");
227 left_elevator_estimator_.Reset();
228 right_elevator_estimator_.Reset();
229 left_arm_estimator_.Reset();
230 right_arm_estimator_.Reset();
231 state_ = UNINITIALIZED;
232 }
233
Austin Schuh703b8d42015-02-01 14:56:34 -0800234 // Get a reference to the constants struct since we use it so often in this
235 // code.
Philipp Schrader82c65072015-02-16 00:47:09 +0000236 const auto &values = constants::GetValues();
Austin Schuh703b8d42015-02-01 14:56:34 -0800237
238 // Bool to track if we should turn the motors on or not.
239 bool disable = output == nullptr;
240 double elevator_goal_velocity = 0.0;
241 double arm_goal_velocity = 0.0;
242
243 // Save the current position so it can be used easily in the class.
244 current_position_ = *position;
245
246 left_elevator_estimator_.UpdateEstimate(position->elevator.left);
247 right_elevator_estimator_.UpdateEstimate(position->elevator.right);
248 left_arm_estimator_.UpdateEstimate(position->arm.left);
249 right_arm_estimator_.UpdateEstimate(position->arm.right);
250
251 if (state_ != UNINITIALIZED) {
252 Correct();
253 }
254
255 // Zeroing will work as follows:
256 // At startup, record the offset of the two halves of the two subsystems.
257 // Then, start moving the elevator towards the center until both halves are
258 // zeroed.
259 // Then, start moving the claw towards the center until both halves are
260 // zeroed.
261 // Then, done!
262
263 // We'll then need code to do sanity checking on values.
264
265 // Now, we need to figure out which way to go.
266
267 switch (state_) {
268 case UNINITIALIZED:
269 LOG(DEBUG, "Uninitialized\n");
270 // Startup. Assume that we are at the origin everywhere.
271 // This records the encoder offset between the two sides of the elevator.
272 left_elevator_offset_ = -position->elevator.left.encoder;
273 right_elevator_offset_ = -position->elevator.right.encoder;
274 left_arm_offset_ = -position->arm.left.encoder;
275 right_arm_offset_ = -position->arm.right.encoder;
Austin Schuhaab01572015-02-15 00:12:35 -0800276 elevator_loop_->mutable_X_hat().setZero();
277 arm_loop_->mutable_X_hat().setZero();
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800278 LOG(INFO, "Initializing arm offsets to %f, %f\n", left_arm_offset_,
279 right_arm_offset_);
280 LOG(INFO, "Initializing elevator offsets to %f, %f\n",
281 left_elevator_offset_, right_elevator_offset_);
Austin Schuh703b8d42015-02-01 14:56:34 -0800282 Correct();
283 state_ = INITIALIZING;
284 disable = true;
285 break;
286
287 case INITIALIZING:
288 LOG(DEBUG, "Waiting for accurate initial position.\n");
289 disable = true;
290 // Update state_ to accurately represent the state of the zeroing
291 // estimators.
292 UpdateZeroingState();
293 if (state_ != INITIALIZING) {
294 // Set the goals to where we are now.
295 elevator_goal_ = elevator();
296 arm_goal_ = arm();
297 }
298 break;
299
300 case ZEROING_ELEVATOR:
301 LOG(DEBUG, "Zeroing elevator\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800302
303 // Update state_ to accurately represent the state of the zeroing
304 // estimators.
305 UpdateZeroingState();
306 if (left_elevator_estimator_.zeroed() &&
307 right_elevator_estimator_.zeroed()) {
308 SetElevatorOffset(left_elevator_estimator_.offset(),
309 right_elevator_estimator_.offset());
310 LOG(DEBUG, "Zeroed the elevator!\n");
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800311 } else if (!disable) {
312 elevator_goal_velocity = elevator_zeroing_velocity();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800313 elevator_goal_ += elevator_goal_velocity *
314 ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800315 }
316 break;
317
318 case ZEROING_ARM:
319 LOG(DEBUG, "Zeroing the arm\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800320
321 // Update state_ to accurately represent the state of the zeroing
322 // estimators.
323 UpdateZeroingState();
324 if (left_arm_estimator_.zeroed() && right_arm_estimator_.zeroed()) {
325 SetArmOffset(left_arm_estimator_.offset(),
326 right_arm_estimator_.offset());
327 LOG(DEBUG, "Zeroed the arm!\n");
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800328 } else if (!disable) {
329 arm_goal_velocity = arm_zeroing_velocity();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800330 arm_goal_ += arm_goal_velocity *
331 ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800332 }
333 break;
334
335 case RUNNING:
336 LOG(DEBUG, "Running!\n");
Austin Schuh482a4e12015-02-14 22:43:43 -0800337 if (unsafe_goal) {
338 arm_goal_velocity = unsafe_goal->angular_velocity;
339 elevator_goal_velocity = unsafe_goal->velocity;
340 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800341
342 // Update state_ to accurately represent the state of the zeroing
343 // estimators.
344 UpdateZeroingState();
Austin Schuh482a4e12015-02-14 22:43:43 -0800345 if (unsafe_goal) {
346 arm_goal_ = unsafe_goal->angle;
347 elevator_goal_ = unsafe_goal->height;
348 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800349
350 if (state_ != RUNNING && state_ != ESTOP) {
351 state_ = UNINITIALIZED;
352 }
353 break;
354
355 case ESTOP:
356 LOG(ERROR, "Estop\n");
357 disable = true;
358 break;
359 }
360
361 // Commence death if either left/right tracking error gets too big. This
362 // should run immediately after the SetArmOffset and SetElevatorOffset
363 // functions to double-check that the hardware is in a sane state.
364 if (::std::abs(left_arm() - right_arm()) >=
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800365 values.max_allowed_left_right_arm_difference) {
366 LOG(ERROR, "The arms are too far apart. |%f - %f| > %f\n", left_arm(),
367 right_arm(), values.max_allowed_left_right_arm_difference);
368
369 // Indicate an ESTOP condition and stop the motors.
370 state_ = ESTOP;
371 disable = true;
372 }
373
374 if (::std::abs(left_elevator() - right_elevator()) >=
375 values.max_allowed_left_right_elevator_difference) {
376 LOG(ERROR, "The elevators are too far apart. |%f - %f| > %f\n",
377 left_elevator(), right_elevator(),
378 values.max_allowed_left_right_elevator_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800379
380 // Indicate an ESTOP condition and stop the motors.
381 state_ = ESTOP;
382 disable = true;
383 }
384
385 // Limit the goals so we can't exceed the hardware limits if we are RUNNING.
386 if (state_ == RUNNING) {
387 // Limit the arm goal to min/max allowable angles.
388 if (arm_goal_ >= values.fridge.arm.upper_limit) {
389 LOG(WARNING, "Arm goal above limit, %f > %f\n", arm_goal_,
390 values.fridge.arm.upper_limit);
391 arm_goal_ = values.fridge.arm.upper_limit;
392 }
393 if (arm_goal_ <= values.fridge.arm.lower_limit) {
394 LOG(WARNING, "Arm goal below limit, %f < %f\n", arm_goal_,
395 values.fridge.arm.lower_limit);
396 arm_goal_ = values.fridge.arm.lower_limit;
397 }
398
399 // Limit the elevator goal to min/max allowable heights.
400 if (elevator_goal_ >= values.fridge.elevator.upper_limit) {
401 LOG(WARNING, "Elevator goal above limit, %f > %f\n", elevator_goal_,
402 values.fridge.elevator.upper_limit);
403 elevator_goal_ = values.fridge.elevator.upper_limit;
404 }
405 if (elevator_goal_ <= values.fridge.elevator.lower_limit) {
406 LOG(WARNING, "Elevator goal below limit, %f < %f\n", elevator_goal_,
407 values.fridge.elevator.lower_limit);
408 elevator_goal_ = values.fridge.elevator.lower_limit;
409 }
410 }
411
412 // Check the lower level hardware limit as well.
413 if (state_ == RUNNING) {
414 if (left_arm() >= values.fridge.arm.upper_hard_limit ||
415 left_arm() <= values.fridge.arm.lower_hard_limit) {
416 LOG(ERROR, "Left arm at %f out of bounds [%f, %f], ESTOPing\n",
417 left_arm(), values.fridge.arm.lower_hard_limit,
418 values.fridge.arm.upper_hard_limit);
419 state_ = ESTOP;
420 }
421
422 if (right_arm() >= values.fridge.arm.upper_hard_limit ||
423 right_arm() <= values.fridge.arm.lower_hard_limit) {
424 LOG(ERROR, "Right arm at %f out of bounds [%f, %f], ESTOPing\n",
425 right_arm(), values.fridge.arm.lower_hard_limit,
426 values.fridge.arm.upper_hard_limit);
427 state_ = ESTOP;
428 }
429
430 if (left_elevator() >= values.fridge.elevator.upper_hard_limit ||
431 left_elevator() <= values.fridge.elevator.lower_hard_limit) {
432 LOG(ERROR, "Left elevator at %f out of bounds [%f, %f], ESTOPing\n",
433 left_elevator(), values.fridge.elevator.lower_hard_limit,
434 values.fridge.elevator.upper_hard_limit);
435 state_ = ESTOP;
436 }
437
438 if (right_elevator() >= values.fridge.elevator.upper_hard_limit ||
439 right_elevator() <= values.fridge.elevator.lower_hard_limit) {
440 LOG(ERROR, "Right elevator at %f out of bounds [%f, %f], ESTOPing\n",
441 right_elevator(), values.fridge.elevator.lower_hard_limit,
442 values.fridge.elevator.upper_hard_limit);
443 state_ = ESTOP;
444 }
445 }
446
447 // Set the goals.
448 arm_loop_->mutable_R() << arm_goal_, arm_goal_velocity, 0.0, 0.0;
449 elevator_loop_->mutable_R() << elevator_goal_, elevator_goal_velocity, 0.0,
450 0.0;
451
452 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
453 arm_loop_->set_max_voltage(max_voltage);
454 elevator_loop_->set_max_voltage(max_voltage);
455
456 if (state_ == ESTOP) {
457 disable = true;
458 }
459 arm_loop_->Update(disable);
460 elevator_loop_->Update(disable);
461
462 if (state_ == INITIALIZING || state_ == ZEROING_ELEVATOR ||
463 state_ == ZEROING_ARM) {
464 if (arm_loop_->U() != arm_loop_->U_uncapped()) {
465 Eigen::Matrix<double, 2, 1> deltaR =
466 arm_loop_->UnsaturateOutputGoalChange();
467
468 // Move the average arm goal by the amount observed.
469 LOG(WARNING, "Moving arm goal by %f to handle saturation\n",
470 deltaR(0, 0));
471 arm_goal_ += deltaR(0, 0);
472 }
473
474 if (elevator_loop_->U() != elevator_loop_->U_uncapped()) {
475 Eigen::Matrix<double, 2, 1> deltaR =
476 elevator_loop_->UnsaturateOutputGoalChange();
477
478 // Move the average elevator goal by the amount observed.
479 LOG(WARNING, "Moving elevator goal by %f to handle saturation\n",
480 deltaR(0, 0));
481 elevator_goal_ += deltaR(0, 0);
482 }
483 }
484
485 if (output) {
486 output->left_arm = arm_loop_->U(0, 0);
487 output->right_arm = arm_loop_->U(1, 0);
488 output->left_elevator = elevator_loop_->U(0, 0);
489 output->right_elevator = elevator_loop_->U(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800490 if (unsafe_goal) {
491 output->grabbers = unsafe_goal->grabbers;
492 } else {
493 output->grabbers.top_front = false;
494 output->grabbers.top_back = false;
495 output->grabbers.bottom_front = false;
496 output->grabbers.bottom_back = false;
497 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800498 }
499
500 // TODO(austin): Populate these fully.
Austin Schuh5ae4efd2015-02-15 23:34:22 -0800501 status->zeroed = state_ == RUNNING;
Austin Schuh703b8d42015-02-01 14:56:34 -0800502 status->angle = arm_loop_->X_hat(0, 0);
503 status->height = elevator_loop_->X_hat(0, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800504 if (unsafe_goal) {
505 status->grabbers = unsafe_goal->grabbers;
506 } else {
507 status->grabbers.top_front = false;
508 status->grabbers.top_back = false;
509 status->grabbers.bottom_front = false;
510 status->grabbers.bottom_back = false;
511 }
Daniel Pettiab274232015-02-16 19:15:34 -0800512 zeroing::PopulateEstimatorState(left_arm_estimator_,
513 &status->left_arm_state);
514 zeroing::PopulateEstimatorState(right_arm_estimator_,
515 &status->right_arm_state);
516 zeroing::PopulateEstimatorState(left_elevator_estimator_,
Austin Schuh9e37c322015-02-16 15:47:49 -0800517 &status->left_elevator_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800518 zeroing::PopulateEstimatorState(right_elevator_estimator_,
Austin Schuh9e37c322015-02-16 15:47:49 -0800519 &status->right_elevator_state);
Austin Schuh703b8d42015-02-01 14:56:34 -0800520 status->estopped = (state_ == ESTOP);
Austin Schuh482a4e12015-02-14 22:43:43 -0800521 status->state = state_;
Austin Schuh703b8d42015-02-01 14:56:34 -0800522 last_state_ = state_;
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800523}
524
525} // namespace control_loops
526} // namespace frc971