blob: 46d4f28ca2f194a69465f54842590b0cc0b8661e [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"
Austin Schuh8de10c72015-02-27 23:33:40 -08009#include "frc971/control_loops/fridge/integral_arm_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
Austin Schuh8de10c72015-02-27 23:33:40 -080027template <int S>
28void CappedStateFeedbackLoop<S>::CapU() {
29 VoltageCap(max_voltage_, this->U(0, 0), this->U(1, 0), &this->mutable_U(0, 0),
30 &this->mutable_U(1, 0));
Austin Schuh703b8d42015-02-01 14:56:34 -080031}
32
Austin Schuh8de10c72015-02-27 23:33:40 -080033template <int S>
Austin Schuh703b8d42015-02-01 14:56:34 -080034Eigen::Matrix<double, 2, 1>
Austin Schuh8de10c72015-02-27 23:33:40 -080035CappedStateFeedbackLoop<S>::UnsaturateOutputGoalChange() {
Austin Schuh703b8d42015-02-01 14:56:34 -080036 // Compute the K matrix used to compensate for position errors.
37 Eigen::Matrix<double, 2, 2> Kp;
38 Kp.setZero();
Austin Schuh8de10c72015-02-27 23:33:40 -080039 Kp.col(0) = this->K().col(0);
40 Kp.col(1) = this->K().col(2);
Austin Schuh703b8d42015-02-01 14:56:34 -080041
42 Eigen::Matrix<double, 2, 2> Kp_inv = Kp.inverse();
43
44 // Compute how much we need to change R in order to achieve the change in U
45 // that was observed.
Austin Schuh8de10c72015-02-27 23:33:40 -080046 Eigen::Matrix<double, 2, 1> deltaR =
47 -Kp_inv * (this->U_uncapped() - this->U());
Austin Schuh703b8d42015-02-01 14:56:34 -080048 return deltaR;
49}
50
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080051Fridge::Fridge(control_loops::FridgeQueue *fridge)
Brian Silverman089f5812015-02-15 01:58:19 -050052 : aos::controls::ControlLoop<control_loops::FridgeQueue>(fridge),
Austin Schuh8de10c72015-02-27 23:33:40 -080053 arm_loop_(new CappedStateFeedbackLoop<5>(
54 StateFeedbackLoop<5, 2, 2>(MakeIntegralArmLoop()))),
55 elevator_loop_(new CappedStateFeedbackLoop<4>(
Austin Schuh703b8d42015-02-01 14:56:34 -080056 StateFeedbackLoop<4, 2, 2>(MakeElevatorLoop()))),
Daniel Pettia7827412015-02-13 20:55:57 -080057 left_arm_estimator_(constants::GetValues().fridge.left_arm_zeroing),
58 right_arm_estimator_(constants::GetValues().fridge.right_arm_zeroing),
Austin Schuh482a4e12015-02-14 22:43:43 -080059 left_elevator_estimator_(constants::GetValues().fridge.left_elev_zeroing),
Austin Schuh703b8d42015-02-01 14:56:34 -080060 right_elevator_estimator_(
Daniel Pettia7827412015-02-13 20:55:57 -080061 constants::GetValues().fridge.right_elev_zeroing) {}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080062
Austin Schuh703b8d42015-02-01 14:56:34 -080063void Fridge::UpdateZeroingState() {
Austin Schuh47bf6902015-02-14 22:46:22 -080064 if (left_elevator_estimator_.offset_ratio_ready() < 1.0 ||
65 right_elevator_estimator_.offset_ratio_ready() < 1.0 ||
66 left_arm_estimator_.offset_ratio_ready() < 1.0 ||
67 right_arm_estimator_.offset_ratio_ready() < 1.0) {
Austin Schuh703b8d42015-02-01 14:56:34 -080068 state_ = INITIALIZING;
69 } else if (!left_elevator_estimator_.zeroed() ||
70 !right_elevator_estimator_.zeroed()) {
71 state_ = ZEROING_ELEVATOR;
72 } else if (!left_arm_estimator_.zeroed() ||
73 !right_arm_estimator_.zeroed()) {
74 state_ = ZEROING_ARM;
75 } else {
76 state_ = RUNNING;
77 }
78}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080079
Austin Schuh703b8d42015-02-01 14:56:34 -080080void Fridge::Correct() {
81 {
82 Eigen::Matrix<double, 2, 1> Y;
83 Y << left_elevator(), right_elevator();
84 elevator_loop_->Correct(Y);
85 }
86
87 {
88 Eigen::Matrix<double, 2, 1> Y;
89 Y << left_arm(), right_arm();
90 arm_loop_->Correct(Y);
91 }
92}
93
94void Fridge::SetElevatorOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -080095 LOG(INFO, "Changing Elevator offset from %f, %f to %f, %f\n",
96 left_elevator_offset_, right_elevator_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -080097 double left_doffset = left_offset - left_elevator_offset_;
98 double right_doffset = right_offset - right_elevator_offset_;
99
100 // Adjust the average height and height difference between the two sides.
101 // The derivatives of both should not need to be updated since the speeds
102 // haven't changed.
103 // The height difference is calculated as left - right, not right - left.
104 elevator_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
105 elevator_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
106
107 // Modify the zeroing goal.
108 elevator_goal_ += (left_doffset + right_doffset) / 2;
109
110 // Update the cached offset values to the actual values.
111 left_elevator_offset_ = left_offset;
112 right_elevator_offset_ = right_offset;
113}
114
115void Fridge::SetArmOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800116 LOG(INFO, "Changing Arm offset from %f, %f to %f, %f\n", left_arm_offset_,
117 right_arm_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -0800118 double left_doffset = left_offset - left_arm_offset_;
119 double right_doffset = right_offset - right_arm_offset_;
120
121 // Adjust the average angle and angle difference between the two sides.
122 // The derivatives of both should not need to be updated since the speeds
123 // haven't changed.
124 arm_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
125 arm_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
126
127 // Modify the zeroing goal.
128 arm_goal_ += (left_doffset + right_doffset) / 2;
129
130 // Update the cached offset values to the actual values.
131 left_arm_offset_ = left_offset;
132 right_arm_offset_ = right_offset;
133}
134
135double Fridge::estimated_left_elevator() {
136 return current_position_.elevator.left.encoder +
137 left_elevator_estimator_.offset();
138}
139double Fridge::estimated_right_elevator() {
140 return current_position_.elevator.right.encoder +
141 right_elevator_estimator_.offset();
142}
143
144double Fridge::estimated_elevator() {
145 return (estimated_left_elevator() + estimated_right_elevator()) / 2.0;
146}
147
148double Fridge::estimated_left_arm() {
149 return current_position_.elevator.left.encoder + left_arm_estimator_.offset();
150}
151double Fridge::estimated_right_arm() {
152 return current_position_.elevator.right.encoder +
153 right_arm_estimator_.offset();
154}
155double Fridge::estimated_arm() {
156 return (estimated_left_arm() + estimated_right_arm()) / 2.0;
157}
158
159double Fridge::left_elevator() {
160 return current_position_.elevator.left.encoder + left_elevator_offset_;
161}
162double Fridge::right_elevator() {
163 return current_position_.elevator.right.encoder + right_elevator_offset_;
164}
165
166double Fridge::elevator() { return (left_elevator() + right_elevator()) / 2.0; }
167
168double Fridge::left_arm() {
169 return current_position_.arm.left.encoder + left_arm_offset_;
170}
171double Fridge::right_arm() {
172 return current_position_.arm.right.encoder + right_arm_offset_;
173}
174double Fridge::arm() { return (left_arm() + right_arm()) / 2.0; }
175
176double Fridge::elevator_zeroing_velocity() {
177 double average_elevator =
178 (constants::GetValues().fridge.elevator.lower_limit +
179 constants::GetValues().fridge.elevator.upper_limit) /
180 2.0;
181
182 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800183 constants::GetValues().fridge.left_elev_zeroing.index_difference,
184 constants::GetValues().fridge.right_elev_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800185
186 if (elevator_zeroing_velocity_ == 0) {
187 if (estimated_elevator() > average_elevator) {
188 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
189 } else {
190 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
191 }
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 } else if (elevator_zeroing_velocity_ < 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800196 estimated_elevator() < average_elevator - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800197 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
198 }
199 return elevator_zeroing_velocity_;
200}
201
202double Fridge::arm_zeroing_velocity() {
203 const double average_arm = (constants::GetValues().fridge.arm.lower_limit +
204 constants::GetValues().fridge.arm.upper_limit) /
205 2.0;
206 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800207 constants::GetValues().fridge.right_arm_zeroing.index_difference,
208 constants::GetValues().fridge.left_arm_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800209
210 if (arm_zeroing_velocity_ == 0) {
211 if (estimated_arm() > average_arm) {
212 arm_zeroing_velocity_ = -kArmZeroingVelocity;
213 } else {
214 arm_zeroing_velocity_ = kArmZeroingVelocity;
215 }
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 } else if (arm_zeroing_velocity_ < 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800220 estimated_arm() < average_arm - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800221 arm_zeroing_velocity_ = kArmZeroingVelocity;
222 }
223 return arm_zeroing_velocity_;
224}
225
Austin Schuh482a4e12015-02-14 22:43:43 -0800226void Fridge::RunIteration(const control_loops::FridgeQueue::Goal *unsafe_goal,
Austin Schuh703b8d42015-02-01 14:56:34 -0800227 const control_loops::FridgeQueue::Position *position,
228 control_loops::FridgeQueue::Output *output,
229 control_loops::FridgeQueue::Status *status) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800230 if (WasReset()) {
231 LOG(ERROR, "WPILib reset, restarting\n");
232 left_elevator_estimator_.Reset();
233 right_elevator_estimator_.Reset();
234 left_arm_estimator_.Reset();
235 right_arm_estimator_.Reset();
236 state_ = UNINITIALIZED;
237 }
238
Austin Schuh703b8d42015-02-01 14:56:34 -0800239 // Get a reference to the constants struct since we use it so often in this
240 // code.
Philipp Schrader82c65072015-02-16 00:47:09 +0000241 const auto &values = constants::GetValues();
Austin Schuh703b8d42015-02-01 14:56:34 -0800242
243 // Bool to track if we should turn the motors on or not.
244 bool disable = output == nullptr;
245 double elevator_goal_velocity = 0.0;
246 double arm_goal_velocity = 0.0;
247
248 // Save the current position so it can be used easily in the class.
249 current_position_ = *position;
250
251 left_elevator_estimator_.UpdateEstimate(position->elevator.left);
252 right_elevator_estimator_.UpdateEstimate(position->elevator.right);
253 left_arm_estimator_.UpdateEstimate(position->arm.left);
254 right_arm_estimator_.UpdateEstimate(position->arm.right);
255
256 if (state_ != UNINITIALIZED) {
257 Correct();
258 }
259
260 // Zeroing will work as follows:
261 // At startup, record the offset of the two halves of the two subsystems.
262 // Then, start moving the elevator towards the center until both halves are
263 // zeroed.
264 // Then, start moving the claw towards the center until both halves are
265 // zeroed.
266 // Then, done!
267
268 // We'll then need code to do sanity checking on values.
269
270 // Now, we need to figure out which way to go.
271
272 switch (state_) {
273 case UNINITIALIZED:
274 LOG(DEBUG, "Uninitialized\n");
275 // Startup. Assume that we are at the origin everywhere.
276 // This records the encoder offset between the two sides of the elevator.
277 left_elevator_offset_ = -position->elevator.left.encoder;
278 right_elevator_offset_ = -position->elevator.right.encoder;
279 left_arm_offset_ = -position->arm.left.encoder;
280 right_arm_offset_ = -position->arm.right.encoder;
Austin Schuhaab01572015-02-15 00:12:35 -0800281 elevator_loop_->mutable_X_hat().setZero();
282 arm_loop_->mutable_X_hat().setZero();
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800283 LOG(INFO, "Initializing arm offsets to %f, %f\n", left_arm_offset_,
284 right_arm_offset_);
285 LOG(INFO, "Initializing elevator offsets to %f, %f\n",
286 left_elevator_offset_, right_elevator_offset_);
Austin Schuh703b8d42015-02-01 14:56:34 -0800287 Correct();
288 state_ = INITIALIZING;
289 disable = true;
290 break;
291
292 case INITIALIZING:
293 LOG(DEBUG, "Waiting for accurate initial position.\n");
294 disable = true;
295 // Update state_ to accurately represent the state of the zeroing
296 // estimators.
297 UpdateZeroingState();
298 if (state_ != INITIALIZING) {
299 // Set the goals to where we are now.
300 elevator_goal_ = elevator();
301 arm_goal_ = arm();
302 }
303 break;
304
305 case ZEROING_ELEVATOR:
306 LOG(DEBUG, "Zeroing elevator\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800307
308 // Update state_ to accurately represent the state of the zeroing
309 // estimators.
310 UpdateZeroingState();
311 if (left_elevator_estimator_.zeroed() &&
312 right_elevator_estimator_.zeroed()) {
313 SetElevatorOffset(left_elevator_estimator_.offset(),
314 right_elevator_estimator_.offset());
315 LOG(DEBUG, "Zeroed the elevator!\n");
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800316
Austin Schuh5e872c82015-02-17 22:59:08 -0800317 if (elevator() < values.fridge.arm_zeroing_height &&
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800318 state_ != INITIALIZING) {
319 // Move the elevator to a safe height before we start zeroing the arm,
320 // so that we don't crash anything.
321 LOG(DEBUG, "Moving elevator to safe height.\n");
322 elevator_goal_ += kElevatorSafeHeightVelocity *
323 ::aos::controls::kLoopFrequency.ToSeconds();
324 elevator_goal_velocity = kElevatorSafeHeightVelocity;
325
326 state_ = ZEROING_ELEVATOR;
327 break;
328 }
329
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800330 } else if (!disable) {
331 elevator_goal_velocity = elevator_zeroing_velocity();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800332 elevator_goal_ += elevator_goal_velocity *
333 ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800334 }
335 break;
336
337 case ZEROING_ARM:
338 LOG(DEBUG, "Zeroing the arm\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800339
340 // Update state_ to accurately represent the state of the zeroing
341 // estimators.
342 UpdateZeroingState();
343 if (left_arm_estimator_.zeroed() && right_arm_estimator_.zeroed()) {
344 SetArmOffset(left_arm_estimator_.offset(),
345 right_arm_estimator_.offset());
346 LOG(DEBUG, "Zeroed the arm!\n");
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800347 } else if (!disable) {
348 arm_goal_velocity = arm_zeroing_velocity();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800349 arm_goal_ += arm_goal_velocity *
350 ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800351 }
352 break;
353
354 case RUNNING:
355 LOG(DEBUG, "Running!\n");
Austin Schuh482a4e12015-02-14 22:43:43 -0800356 if (unsafe_goal) {
357 arm_goal_velocity = unsafe_goal->angular_velocity;
358 elevator_goal_velocity = unsafe_goal->velocity;
359 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800360
361 // Update state_ to accurately represent the state of the zeroing
362 // estimators.
363 UpdateZeroingState();
Austin Schuh482a4e12015-02-14 22:43:43 -0800364 if (unsafe_goal) {
365 arm_goal_ = unsafe_goal->angle;
366 elevator_goal_ = unsafe_goal->height;
367 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800368
369 if (state_ != RUNNING && state_ != ESTOP) {
370 state_ = UNINITIALIZED;
371 }
372 break;
373
374 case ESTOP:
375 LOG(ERROR, "Estop\n");
376 disable = true;
377 break;
378 }
379
380 // Commence death if either left/right tracking error gets too big. This
381 // should run immediately after the SetArmOffset and SetElevatorOffset
382 // functions to double-check that the hardware is in a sane state.
383 if (::std::abs(left_arm() - right_arm()) >=
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800384 values.max_allowed_left_right_arm_difference) {
385 LOG(ERROR, "The arms are too far apart. |%f - %f| > %f\n", left_arm(),
386 right_arm(), values.max_allowed_left_right_arm_difference);
387
388 // Indicate an ESTOP condition and stop the motors.
389 state_ = ESTOP;
390 disable = true;
391 }
392
393 if (::std::abs(left_elevator() - right_elevator()) >=
394 values.max_allowed_left_right_elevator_difference) {
395 LOG(ERROR, "The elevators are too far apart. |%f - %f| > %f\n",
396 left_elevator(), right_elevator(),
397 values.max_allowed_left_right_elevator_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800398
399 // Indicate an ESTOP condition and stop the motors.
400 state_ = ESTOP;
401 disable = true;
402 }
403
404 // Limit the goals so we can't exceed the hardware limits if we are RUNNING.
405 if (state_ == RUNNING) {
406 // Limit the arm goal to min/max allowable angles.
407 if (arm_goal_ >= values.fridge.arm.upper_limit) {
408 LOG(WARNING, "Arm goal above limit, %f > %f\n", arm_goal_,
409 values.fridge.arm.upper_limit);
410 arm_goal_ = values.fridge.arm.upper_limit;
411 }
412 if (arm_goal_ <= values.fridge.arm.lower_limit) {
413 LOG(WARNING, "Arm goal below limit, %f < %f\n", arm_goal_,
414 values.fridge.arm.lower_limit);
415 arm_goal_ = values.fridge.arm.lower_limit;
416 }
417
418 // Limit the elevator goal to min/max allowable heights.
419 if (elevator_goal_ >= values.fridge.elevator.upper_limit) {
420 LOG(WARNING, "Elevator goal above limit, %f > %f\n", elevator_goal_,
421 values.fridge.elevator.upper_limit);
422 elevator_goal_ = values.fridge.elevator.upper_limit;
423 }
424 if (elevator_goal_ <= values.fridge.elevator.lower_limit) {
425 LOG(WARNING, "Elevator goal below limit, %f < %f\n", elevator_goal_,
426 values.fridge.elevator.lower_limit);
427 elevator_goal_ = values.fridge.elevator.lower_limit;
428 }
429 }
430
431 // Check the lower level hardware limit as well.
432 if (state_ == RUNNING) {
433 if (left_arm() >= values.fridge.arm.upper_hard_limit ||
434 left_arm() <= values.fridge.arm.lower_hard_limit) {
435 LOG(ERROR, "Left arm at %f out of bounds [%f, %f], ESTOPing\n",
436 left_arm(), values.fridge.arm.lower_hard_limit,
437 values.fridge.arm.upper_hard_limit);
438 state_ = ESTOP;
439 }
440
441 if (right_arm() >= values.fridge.arm.upper_hard_limit ||
442 right_arm() <= values.fridge.arm.lower_hard_limit) {
443 LOG(ERROR, "Right arm at %f out of bounds [%f, %f], ESTOPing\n",
444 right_arm(), values.fridge.arm.lower_hard_limit,
445 values.fridge.arm.upper_hard_limit);
446 state_ = ESTOP;
447 }
448
449 if (left_elevator() >= values.fridge.elevator.upper_hard_limit ||
450 left_elevator() <= values.fridge.elevator.lower_hard_limit) {
451 LOG(ERROR, "Left elevator at %f out of bounds [%f, %f], ESTOPing\n",
452 left_elevator(), values.fridge.elevator.lower_hard_limit,
453 values.fridge.elevator.upper_hard_limit);
454 state_ = ESTOP;
455 }
456
457 if (right_elevator() >= values.fridge.elevator.upper_hard_limit ||
458 right_elevator() <= values.fridge.elevator.lower_hard_limit) {
459 LOG(ERROR, "Right elevator at %f out of bounds [%f, %f], ESTOPing\n",
460 right_elevator(), values.fridge.elevator.lower_hard_limit,
461 values.fridge.elevator.upper_hard_limit);
462 state_ = ESTOP;
463 }
464 }
465
466 // Set the goals.
Austin Schuh8de10c72015-02-27 23:33:40 -0800467 arm_loop_->mutable_R() << arm_goal_, arm_goal_velocity, 0.0, 0.0, 0.0;
Austin Schuh703b8d42015-02-01 14:56:34 -0800468 elevator_loop_->mutable_R() << elevator_goal_, elevator_goal_velocity, 0.0,
469 0.0;
470
471 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
472 arm_loop_->set_max_voltage(max_voltage);
473 elevator_loop_->set_max_voltage(max_voltage);
474
475 if (state_ == ESTOP) {
476 disable = true;
477 }
478 arm_loop_->Update(disable);
479 elevator_loop_->Update(disable);
480
481 if (state_ == INITIALIZING || state_ == ZEROING_ELEVATOR ||
482 state_ == ZEROING_ARM) {
483 if (arm_loop_->U() != arm_loop_->U_uncapped()) {
484 Eigen::Matrix<double, 2, 1> deltaR =
485 arm_loop_->UnsaturateOutputGoalChange();
486
487 // Move the average arm goal by the amount observed.
488 LOG(WARNING, "Moving arm goal by %f to handle saturation\n",
489 deltaR(0, 0));
490 arm_goal_ += deltaR(0, 0);
491 }
492
493 if (elevator_loop_->U() != elevator_loop_->U_uncapped()) {
494 Eigen::Matrix<double, 2, 1> deltaR =
495 elevator_loop_->UnsaturateOutputGoalChange();
496
497 // Move the average elevator goal by the amount observed.
498 LOG(WARNING, "Moving elevator goal by %f to handle saturation\n",
499 deltaR(0, 0));
500 elevator_goal_ += deltaR(0, 0);
501 }
502 }
503
504 if (output) {
505 output->left_arm = arm_loop_->U(0, 0);
506 output->right_arm = arm_loop_->U(1, 0);
507 output->left_elevator = elevator_loop_->U(0, 0);
508 output->right_elevator = elevator_loop_->U(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800509 if (unsafe_goal) {
510 output->grabbers = unsafe_goal->grabbers;
511 } else {
512 output->grabbers.top_front = false;
513 output->grabbers.top_back = false;
514 output->grabbers.bottom_front = false;
515 output->grabbers.bottom_back = false;
516 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800517 }
518
519 // TODO(austin): Populate these fully.
Austin Schuh5ae4efd2015-02-15 23:34:22 -0800520 status->zeroed = state_ == RUNNING;
Austin Schuh703b8d42015-02-01 14:56:34 -0800521 status->angle = arm_loop_->X_hat(0, 0);
522 status->height = elevator_loop_->X_hat(0, 0);
Austin Schuh5e872c82015-02-17 22:59:08 -0800523 status->goal_angle = arm_goal_;
524 status->goal_height = elevator_goal_;
Austin Schuh482a4e12015-02-14 22:43:43 -0800525 if (unsafe_goal) {
526 status->grabbers = unsafe_goal->grabbers;
527 } else {
528 status->grabbers.top_front = false;
529 status->grabbers.top_back = false;
530 status->grabbers.bottom_front = false;
531 status->grabbers.bottom_back = false;
532 }
Daniel Pettiab274232015-02-16 19:15:34 -0800533 zeroing::PopulateEstimatorState(left_arm_estimator_,
534 &status->left_arm_state);
535 zeroing::PopulateEstimatorState(right_arm_estimator_,
536 &status->right_arm_state);
537 zeroing::PopulateEstimatorState(left_elevator_estimator_,
Austin Schuh9e37c322015-02-16 15:47:49 -0800538 &status->left_elevator_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800539 zeroing::PopulateEstimatorState(right_elevator_estimator_,
Austin Schuh9e37c322015-02-16 15:47:49 -0800540 &status->right_elevator_state);
Austin Schuh703b8d42015-02-01 14:56:34 -0800541 status->estopped = (state_ == ESTOP);
Austin Schuh482a4e12015-02-14 22:43:43 -0800542 status->state = state_;
Austin Schuh703b8d42015-02-01 14:56:34 -0800543 last_state_ = state_;
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800544}
545
546} // namespace control_loops
547} // namespace frc971