blob: a4aa2e72f2fa094862e6639dc4767ac7ac0d6458 [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
Austin Schuh8de10c72015-02-27 23:33:40 -080026template <int S>
27void CappedStateFeedbackLoop<S>::CapU() {
28 VoltageCap(max_voltage_, this->U(0, 0), this->U(1, 0), &this->mutable_U(0, 0),
29 &this->mutable_U(1, 0));
Austin Schuh703b8d42015-02-01 14:56:34 -080030}
31
Austin Schuh8de10c72015-02-27 23:33:40 -080032template <int S>
Austin Schuh703b8d42015-02-01 14:56:34 -080033Eigen::Matrix<double, 2, 1>
Austin Schuh8de10c72015-02-27 23:33:40 -080034CappedStateFeedbackLoop<S>::UnsaturateOutputGoalChange() {
Austin Schuh703b8d42015-02-01 14:56:34 -080035 // Compute the K matrix used to compensate for position errors.
36 Eigen::Matrix<double, 2, 2> Kp;
37 Kp.setZero();
Austin Schuh8de10c72015-02-27 23:33:40 -080038 Kp.col(0) = this->K().col(0);
39 Kp.col(1) = this->K().col(2);
Austin Schuh703b8d42015-02-01 14:56:34 -080040
41 Eigen::Matrix<double, 2, 2> Kp_inv = Kp.inverse();
42
43 // Compute how much we need to change R in order to achieve the change in U
44 // that was observed.
Austin Schuh8de10c72015-02-27 23:33:40 -080045 Eigen::Matrix<double, 2, 1> deltaR =
46 -Kp_inv * (this->U_uncapped() - this->U());
Austin Schuh703b8d42015-02-01 14:56:34 -080047 return deltaR;
48}
49
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080050Fridge::Fridge(control_loops::FridgeQueue *fridge)
Brian Silverman089f5812015-02-15 01:58:19 -050051 : aos::controls::ControlLoop<control_loops::FridgeQueue>(fridge),
Austin Schuh8de10c72015-02-27 23:33:40 -080052 arm_loop_(new CappedStateFeedbackLoop<5>(
53 StateFeedbackLoop<5, 2, 2>(MakeIntegralArmLoop()))),
54 elevator_loop_(new CappedStateFeedbackLoop<4>(
Austin Schuh703b8d42015-02-01 14:56:34 -080055 StateFeedbackLoop<4, 2, 2>(MakeElevatorLoop()))),
Daniel Pettia7827412015-02-13 20:55:57 -080056 left_arm_estimator_(constants::GetValues().fridge.left_arm_zeroing),
57 right_arm_estimator_(constants::GetValues().fridge.right_arm_zeroing),
Austin Schuh482a4e12015-02-14 22:43:43 -080058 left_elevator_estimator_(constants::GetValues().fridge.left_elev_zeroing),
Austin Schuh703b8d42015-02-01 14:56:34 -080059 right_elevator_estimator_(
Daniel Pettia7827412015-02-13 20:55:57 -080060 constants::GetValues().fridge.right_elev_zeroing) {}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080061
Austin Schuh703b8d42015-02-01 14:56:34 -080062void Fridge::UpdateZeroingState() {
Austin Schuh47bf6902015-02-14 22:46:22 -080063 if (left_elevator_estimator_.offset_ratio_ready() < 1.0 ||
64 right_elevator_estimator_.offset_ratio_ready() < 1.0 ||
65 left_arm_estimator_.offset_ratio_ready() < 1.0 ||
66 right_arm_estimator_.offset_ratio_ready() < 1.0) {
Austin Schuh703b8d42015-02-01 14:56:34 -080067 state_ = INITIALIZING;
68 } else if (!left_elevator_estimator_.zeroed() ||
Adam Snaider3cd11c52015-02-16 02:16:09 +000069 !right_elevator_estimator_.zeroed()) {
Austin Schuh703b8d42015-02-01 14:56:34 -080070 state_ = ZEROING_ELEVATOR;
Adam Snaider3cd11c52015-02-16 02:16:09 +000071 } else if (!left_arm_estimator_.zeroed() || !right_arm_estimator_.zeroed()) {
Austin Schuh703b8d42015-02-01 14:56:34 -080072 state_ = ZEROING_ARM;
73 } else {
74 state_ = RUNNING;
75 }
76}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080077
Austin Schuh703b8d42015-02-01 14:56:34 -080078void Fridge::Correct() {
79 {
80 Eigen::Matrix<double, 2, 1> Y;
81 Y << left_elevator(), right_elevator();
82 elevator_loop_->Correct(Y);
83 }
84
85 {
86 Eigen::Matrix<double, 2, 1> Y;
87 Y << left_arm(), right_arm();
88 arm_loop_->Correct(Y);
89 }
90}
91
92void Fridge::SetElevatorOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -080093 LOG(INFO, "Changing Elevator offset from %f, %f to %f, %f\n",
94 left_elevator_offset_, right_elevator_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -080095 double left_doffset = left_offset - left_elevator_offset_;
96 double right_doffset = right_offset - right_elevator_offset_;
97
98 // Adjust the average height and height difference between the two sides.
99 // The derivatives of both should not need to be updated since the speeds
100 // haven't changed.
101 // The height difference is calculated as left - right, not right - left.
102 elevator_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
103 elevator_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
104
105 // Modify the zeroing goal.
106 elevator_goal_ += (left_doffset + right_doffset) / 2;
107
108 // Update the cached offset values to the actual values.
109 left_elevator_offset_ = left_offset;
110 right_elevator_offset_ = right_offset;
111}
112
113void Fridge::SetArmOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800114 LOG(INFO, "Changing Arm offset from %f, %f to %f, %f\n", left_arm_offset_,
115 right_arm_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -0800116 double left_doffset = left_offset - left_arm_offset_;
117 double right_doffset = right_offset - right_arm_offset_;
118
119 // Adjust the average angle and angle difference between the two sides.
120 // The derivatives of both should not need to be updated since the speeds
121 // haven't changed.
122 arm_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
123 arm_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
124
125 // Modify the zeroing goal.
126 arm_goal_ += (left_doffset + right_doffset) / 2;
127
128 // Update the cached offset values to the actual values.
129 left_arm_offset_ = left_offset;
130 right_arm_offset_ = right_offset;
131}
132
133double Fridge::estimated_left_elevator() {
134 return current_position_.elevator.left.encoder +
135 left_elevator_estimator_.offset();
136}
137double Fridge::estimated_right_elevator() {
138 return current_position_.elevator.right.encoder +
139 right_elevator_estimator_.offset();
140}
141
142double Fridge::estimated_elevator() {
143 return (estimated_left_elevator() + estimated_right_elevator()) / 2.0;
144}
145
146double Fridge::estimated_left_arm() {
147 return current_position_.elevator.left.encoder + left_arm_estimator_.offset();
148}
149double Fridge::estimated_right_arm() {
150 return current_position_.elevator.right.encoder +
151 right_arm_estimator_.offset();
152}
153double Fridge::estimated_arm() {
154 return (estimated_left_arm() + estimated_right_arm()) / 2.0;
155}
156
157double Fridge::left_elevator() {
158 return current_position_.elevator.left.encoder + left_elevator_offset_;
159}
160double Fridge::right_elevator() {
161 return current_position_.elevator.right.encoder + right_elevator_offset_;
162}
163
164double Fridge::elevator() { return (left_elevator() + right_elevator()) / 2.0; }
165
166double Fridge::left_arm() {
167 return current_position_.arm.left.encoder + left_arm_offset_;
168}
169double Fridge::right_arm() {
170 return current_position_.arm.right.encoder + right_arm_offset_;
171}
172double Fridge::arm() { return (left_arm() + right_arm()) / 2.0; }
173
174double Fridge::elevator_zeroing_velocity() {
175 double average_elevator =
176 (constants::GetValues().fridge.elevator.lower_limit +
177 constants::GetValues().fridge.elevator.upper_limit) /
178 2.0;
179
180 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800181 constants::GetValues().fridge.left_elev_zeroing.index_difference,
182 constants::GetValues().fridge.right_elev_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800183
184 if (elevator_zeroing_velocity_ == 0) {
185 if (estimated_elevator() > average_elevator) {
186 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
187 } else {
188 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
189 }
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 } else if (elevator_zeroing_velocity_ < 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800194 estimated_elevator() < average_elevator - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800195 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
196 }
197 return elevator_zeroing_velocity_;
198}
199
200double Fridge::arm_zeroing_velocity() {
201 const double average_arm = (constants::GetValues().fridge.arm.lower_limit +
202 constants::GetValues().fridge.arm.upper_limit) /
203 2.0;
204 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800205 constants::GetValues().fridge.right_arm_zeroing.index_difference,
206 constants::GetValues().fridge.left_arm_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800207
208 if (arm_zeroing_velocity_ == 0) {
209 if (estimated_arm() > average_arm) {
210 arm_zeroing_velocity_ = -kArmZeroingVelocity;
211 } else {
212 arm_zeroing_velocity_ = kArmZeroingVelocity;
213 }
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 } else if (arm_zeroing_velocity_ < 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800218 estimated_arm() < average_arm - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800219 arm_zeroing_velocity_ = kArmZeroingVelocity;
220 }
221 return arm_zeroing_velocity_;
222}
223
Austin Schuh482a4e12015-02-14 22:43:43 -0800224void Fridge::RunIteration(const control_loops::FridgeQueue::Goal *unsafe_goal,
Austin Schuh703b8d42015-02-01 14:56:34 -0800225 const control_loops::FridgeQueue::Position *position,
226 control_loops::FridgeQueue::Output *output,
227 control_loops::FridgeQueue::Status *status) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800228 if (WasReset()) {
229 LOG(ERROR, "WPILib reset, restarting\n");
230 left_elevator_estimator_.Reset();
231 right_elevator_estimator_.Reset();
232 left_arm_estimator_.Reset();
233 right_arm_estimator_.Reset();
234 state_ = UNINITIALIZED;
235 }
236
Austin Schuh703b8d42015-02-01 14:56:34 -0800237 // Get a reference to the constants struct since we use it so often in this
238 // code.
Philipp Schrader82c65072015-02-16 00:47:09 +0000239 const auto &values = constants::GetValues();
Austin Schuh703b8d42015-02-01 14:56:34 -0800240
241 // Bool to track if we should turn the motors on or not.
242 bool disable = output == nullptr;
243 double elevator_goal_velocity = 0.0;
244 double arm_goal_velocity = 0.0;
245
246 // Save the current position so it can be used easily in the class.
247 current_position_ = *position;
248
249 left_elevator_estimator_.UpdateEstimate(position->elevator.left);
250 right_elevator_estimator_.UpdateEstimate(position->elevator.right);
251 left_arm_estimator_.UpdateEstimate(position->arm.left);
252 right_arm_estimator_.UpdateEstimate(position->arm.right);
253
254 if (state_ != UNINITIALIZED) {
255 Correct();
256 }
257
258 // Zeroing will work as follows:
259 // At startup, record the offset of the two halves of the two subsystems.
260 // Then, start moving the elevator towards the center until both halves are
261 // zeroed.
262 // Then, start moving the claw towards the center until both halves are
263 // zeroed.
264 // Then, done!
265
266 // We'll then need code to do sanity checking on values.
267
268 // Now, we need to figure out which way to go.
269
270 switch (state_) {
271 case UNINITIALIZED:
272 LOG(DEBUG, "Uninitialized\n");
273 // Startup. Assume that we are at the origin everywhere.
274 // This records the encoder offset between the two sides of the elevator.
275 left_elevator_offset_ = -position->elevator.left.encoder;
276 right_elevator_offset_ = -position->elevator.right.encoder;
277 left_arm_offset_ = -position->arm.left.encoder;
278 right_arm_offset_ = -position->arm.right.encoder;
Austin Schuhaab01572015-02-15 00:12:35 -0800279 elevator_loop_->mutable_X_hat().setZero();
280 arm_loop_->mutable_X_hat().setZero();
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800281 LOG(INFO, "Initializing arm offsets to %f, %f\n", left_arm_offset_,
282 right_arm_offset_);
283 LOG(INFO, "Initializing elevator offsets to %f, %f\n",
284 left_elevator_offset_, right_elevator_offset_);
Austin Schuh703b8d42015-02-01 14:56:34 -0800285 Correct();
286 state_ = INITIALIZING;
287 disable = true;
288 break;
289
290 case INITIALIZING:
291 LOG(DEBUG, "Waiting for accurate initial position.\n");
292 disable = true;
293 // Update state_ to accurately represent the state of the zeroing
294 // estimators.
295 UpdateZeroingState();
296 if (state_ != INITIALIZING) {
297 // Set the goals to where we are now.
298 elevator_goal_ = elevator();
299 arm_goal_ = arm();
300 }
301 break;
302
303 case ZEROING_ELEVATOR:
304 LOG(DEBUG, "Zeroing elevator\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800305
306 // Update state_ to accurately represent the state of the zeroing
307 // estimators.
308 UpdateZeroingState();
309 if (left_elevator_estimator_.zeroed() &&
310 right_elevator_estimator_.zeroed()) {
311 SetElevatorOffset(left_elevator_estimator_.offset(),
312 right_elevator_estimator_.offset());
313 LOG(DEBUG, "Zeroed the elevator!\n");
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800314
Austin Schuh5e872c82015-02-17 22:59:08 -0800315 if (elevator() < values.fridge.arm_zeroing_height &&
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800316 state_ != INITIALIZING) {
317 // Move the elevator to a safe height before we start zeroing the arm,
318 // so that we don't crash anything.
319 LOG(DEBUG, "Moving elevator to safe height.\n");
320 elevator_goal_ += kElevatorSafeHeightVelocity *
321 ::aos::controls::kLoopFrequency.ToSeconds();
322 elevator_goal_velocity = kElevatorSafeHeightVelocity;
323
324 state_ = ZEROING_ELEVATOR;
325 break;
326 }
327
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800328 } else if (!disable) {
329 elevator_goal_velocity = elevator_zeroing_velocity();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800330 elevator_goal_ += elevator_goal_velocity *
Adam Snaider3cd11c52015-02-16 02:16:09 +0000331 ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800332 }
333 break;
334
335 case ZEROING_ARM:
336 LOG(DEBUG, "Zeroing the arm\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800337
338 // Update state_ to accurately represent the state of the zeroing
339 // estimators.
340 UpdateZeroingState();
341 if (left_arm_estimator_.zeroed() && right_arm_estimator_.zeroed()) {
342 SetArmOffset(left_arm_estimator_.offset(),
343 right_arm_estimator_.offset());
344 LOG(DEBUG, "Zeroed the arm!\n");
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800345 } else if (!disable) {
346 arm_goal_velocity = arm_zeroing_velocity();
Adam Snaider3cd11c52015-02-16 02:16:09 +0000347 arm_goal_ +=
348 arm_goal_velocity * ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800349 }
350 break;
351
352 case RUNNING:
353 LOG(DEBUG, "Running!\n");
Austin Schuh482a4e12015-02-14 22:43:43 -0800354 if (unsafe_goal) {
355 arm_goal_velocity = unsafe_goal->angular_velocity;
356 elevator_goal_velocity = unsafe_goal->velocity;
357 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800358
359 // Update state_ to accurately represent the state of the zeroing
360 // estimators.
361 UpdateZeroingState();
Austin Schuh482a4e12015-02-14 22:43:43 -0800362 if (unsafe_goal) {
363 arm_goal_ = unsafe_goal->angle;
364 elevator_goal_ = unsafe_goal->height;
365 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800366
367 if (state_ != RUNNING && state_ != ESTOP) {
368 state_ = UNINITIALIZED;
369 }
370 break;
371
372 case ESTOP:
373 LOG(ERROR, "Estop\n");
374 disable = true;
375 break;
376 }
377
378 // Commence death if either left/right tracking error gets too big. This
379 // should run immediately after the SetArmOffset and SetElevatorOffset
380 // functions to double-check that the hardware is in a sane state.
381 if (::std::abs(left_arm() - right_arm()) >=
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800382 values.max_allowed_left_right_arm_difference) {
383 LOG(ERROR, "The arms are too far apart. |%f - %f| > %f\n", left_arm(),
384 right_arm(), values.max_allowed_left_right_arm_difference);
385
386 // Indicate an ESTOP condition and stop the motors.
387 state_ = ESTOP;
388 disable = true;
389 }
390
391 if (::std::abs(left_elevator() - right_elevator()) >=
392 values.max_allowed_left_right_elevator_difference) {
393 LOG(ERROR, "The elevators are too far apart. |%f - %f| > %f\n",
394 left_elevator(), right_elevator(),
395 values.max_allowed_left_right_elevator_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800396
397 // Indicate an ESTOP condition and stop the motors.
398 state_ = ESTOP;
399 disable = true;
400 }
401
402 // Limit the goals so we can't exceed the hardware limits if we are RUNNING.
403 if (state_ == RUNNING) {
404 // Limit the arm goal to min/max allowable angles.
405 if (arm_goal_ >= values.fridge.arm.upper_limit) {
406 LOG(WARNING, "Arm goal above limit, %f > %f\n", arm_goal_,
407 values.fridge.arm.upper_limit);
408 arm_goal_ = values.fridge.arm.upper_limit;
409 }
410 if (arm_goal_ <= values.fridge.arm.lower_limit) {
411 LOG(WARNING, "Arm goal below limit, %f < %f\n", arm_goal_,
412 values.fridge.arm.lower_limit);
413 arm_goal_ = values.fridge.arm.lower_limit;
414 }
415
416 // Limit the elevator goal to min/max allowable heights.
417 if (elevator_goal_ >= values.fridge.elevator.upper_limit) {
418 LOG(WARNING, "Elevator goal above limit, %f > %f\n", elevator_goal_,
419 values.fridge.elevator.upper_limit);
420 elevator_goal_ = values.fridge.elevator.upper_limit;
421 }
422 if (elevator_goal_ <= values.fridge.elevator.lower_limit) {
423 LOG(WARNING, "Elevator goal below limit, %f < %f\n", elevator_goal_,
424 values.fridge.elevator.lower_limit);
425 elevator_goal_ = values.fridge.elevator.lower_limit;
426 }
427 }
428
429 // Check the lower level hardware limit as well.
430 if (state_ == RUNNING) {
431 if (left_arm() >= values.fridge.arm.upper_hard_limit ||
432 left_arm() <= values.fridge.arm.lower_hard_limit) {
433 LOG(ERROR, "Left arm at %f out of bounds [%f, %f], ESTOPing\n",
434 left_arm(), values.fridge.arm.lower_hard_limit,
435 values.fridge.arm.upper_hard_limit);
436 state_ = ESTOP;
437 }
438
439 if (right_arm() >= values.fridge.arm.upper_hard_limit ||
440 right_arm() <= values.fridge.arm.lower_hard_limit) {
441 LOG(ERROR, "Right arm at %f out of bounds [%f, %f], ESTOPing\n",
442 right_arm(), values.fridge.arm.lower_hard_limit,
443 values.fridge.arm.upper_hard_limit);
444 state_ = ESTOP;
445 }
446
447 if (left_elevator() >= values.fridge.elevator.upper_hard_limit ||
448 left_elevator() <= values.fridge.elevator.lower_hard_limit) {
449 LOG(ERROR, "Left elevator at %f out of bounds [%f, %f], ESTOPing\n",
450 left_elevator(), values.fridge.elevator.lower_hard_limit,
451 values.fridge.elevator.upper_hard_limit);
452 state_ = ESTOP;
453 }
454
455 if (right_elevator() >= values.fridge.elevator.upper_hard_limit ||
456 right_elevator() <= values.fridge.elevator.lower_hard_limit) {
457 LOG(ERROR, "Right elevator at %f out of bounds [%f, %f], ESTOPing\n",
458 right_elevator(), values.fridge.elevator.lower_hard_limit,
459 values.fridge.elevator.upper_hard_limit);
460 state_ = ESTOP;
461 }
462 }
463
464 // Set the goals.
Austin Schuh8de10c72015-02-27 23:33:40 -0800465 arm_loop_->mutable_R() << arm_goal_, arm_goal_velocity, 0.0, 0.0, 0.0;
Austin Schuh703b8d42015-02-01 14:56:34 -0800466 elevator_loop_->mutable_R() << elevator_goal_, elevator_goal_velocity, 0.0,
467 0.0;
468
469 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
470 arm_loop_->set_max_voltage(max_voltage);
471 elevator_loop_->set_max_voltage(max_voltage);
472
473 if (state_ == ESTOP) {
474 disable = true;
475 }
476 arm_loop_->Update(disable);
477 elevator_loop_->Update(disable);
478
479 if (state_ == INITIALIZING || state_ == ZEROING_ELEVATOR ||
480 state_ == ZEROING_ARM) {
481 if (arm_loop_->U() != arm_loop_->U_uncapped()) {
482 Eigen::Matrix<double, 2, 1> deltaR =
483 arm_loop_->UnsaturateOutputGoalChange();
484
485 // Move the average arm goal by the amount observed.
486 LOG(WARNING, "Moving arm goal by %f to handle saturation\n",
487 deltaR(0, 0));
488 arm_goal_ += deltaR(0, 0);
489 }
490
491 if (elevator_loop_->U() != elevator_loop_->U_uncapped()) {
492 Eigen::Matrix<double, 2, 1> deltaR =
493 elevator_loop_->UnsaturateOutputGoalChange();
494
495 // Move the average elevator goal by the amount observed.
496 LOG(WARNING, "Moving elevator goal by %f to handle saturation\n",
497 deltaR(0, 0));
498 elevator_goal_ += deltaR(0, 0);
499 }
500 }
501
502 if (output) {
503 output->left_arm = arm_loop_->U(0, 0);
504 output->right_arm = arm_loop_->U(1, 0);
505 output->left_elevator = elevator_loop_->U(0, 0);
506 output->right_elevator = elevator_loop_->U(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800507 if (unsafe_goal) {
508 output->grabbers = unsafe_goal->grabbers;
509 } else {
510 output->grabbers.top_front = false;
511 output->grabbers.top_back = false;
512 output->grabbers.bottom_front = false;
513 output->grabbers.bottom_back = false;
514 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800515 }
516
517 // TODO(austin): Populate these fully.
Austin Schuh5ae4efd2015-02-15 23:34:22 -0800518 status->zeroed = state_ == RUNNING;
Austin Schuh703b8d42015-02-01 14:56:34 -0800519 status->angle = arm_loop_->X_hat(0, 0);
520 status->height = elevator_loop_->X_hat(0, 0);
Austin Schuh5e872c82015-02-17 22:59:08 -0800521 status->goal_angle = arm_goal_;
522 status->goal_height = elevator_goal_;
Austin Schuh482a4e12015-02-14 22:43:43 -0800523 if (unsafe_goal) {
524 status->grabbers = unsafe_goal->grabbers;
525 } else {
526 status->grabbers.top_front = false;
527 status->grabbers.top_back = false;
528 status->grabbers.bottom_front = false;
529 status->grabbers.bottom_back = false;
530 }
Adam Snaider3cd11c52015-02-16 02:16:09 +0000531 zeroing::PopulateEstimatorState(left_arm_estimator_, &status->left_arm_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800532 zeroing::PopulateEstimatorState(right_arm_estimator_,
533 &status->right_arm_state);
534 zeroing::PopulateEstimatorState(left_elevator_estimator_,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000535 &status->left_elevator_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800536 zeroing::PopulateEstimatorState(right_elevator_estimator_,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000537 &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