blob: 613cf37b95fc800477527ac0cfa963c64f984e4c [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_(
Philipp Schrader3e281412015-03-01 23:48:23 +000060 constants::GetValues().fridge.right_elev_zeroing),
61 arm_profile_(::aos::controls::kLoopFrequency),
62 elevator_profile_(::aos::controls::kLoopFrequency) {}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080063
Austin Schuh703b8d42015-02-01 14:56:34 -080064void Fridge::UpdateZeroingState() {
Austin Schuh47bf6902015-02-14 22:46:22 -080065 if (left_elevator_estimator_.offset_ratio_ready() < 1.0 ||
66 right_elevator_estimator_.offset_ratio_ready() < 1.0 ||
67 left_arm_estimator_.offset_ratio_ready() < 1.0 ||
68 right_arm_estimator_.offset_ratio_ready() < 1.0) {
Austin Schuh703b8d42015-02-01 14:56:34 -080069 state_ = INITIALIZING;
70 } else if (!left_elevator_estimator_.zeroed() ||
Adam Snaider3cd11c52015-02-16 02:16:09 +000071 !right_elevator_estimator_.zeroed()) {
Austin Schuh703b8d42015-02-01 14:56:34 -080072 state_ = ZEROING_ELEVATOR;
Adam Snaider3cd11c52015-02-16 02:16:09 +000073 } else if (!left_arm_estimator_.zeroed() || !right_arm_estimator_.zeroed()) {
Austin Schuh703b8d42015-02-01 14:56:34 -080074 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
Philipp Schrader3e281412015-03-01 23:48:23 +0000202double Fridge::UseUnlessZero(double target_value, double default_value) {
203 if (target_value != 0.0) {
204 return target_value;
205 } else {
206 return default_value;
207 }
208}
209
Austin Schuh703b8d42015-02-01 14:56:34 -0800210double Fridge::arm_zeroing_velocity() {
211 const double average_arm = (constants::GetValues().fridge.arm.lower_limit +
212 constants::GetValues().fridge.arm.upper_limit) /
213 2.0;
214 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800215 constants::GetValues().fridge.right_arm_zeroing.index_difference,
216 constants::GetValues().fridge.left_arm_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800217
218 if (arm_zeroing_velocity_ == 0) {
219 if (estimated_arm() > average_arm) {
220 arm_zeroing_velocity_ = -kArmZeroingVelocity;
221 } else {
222 arm_zeroing_velocity_ = kArmZeroingVelocity;
223 }
224 } else if (arm_zeroing_velocity_ > 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800225 estimated_arm() > average_arm + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800226 arm_zeroing_velocity_ = -kArmZeroingVelocity;
227 } else if (arm_zeroing_velocity_ < 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800228 estimated_arm() < average_arm - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800229 arm_zeroing_velocity_ = kArmZeroingVelocity;
230 }
231 return arm_zeroing_velocity_;
232}
233
Austin Schuh482a4e12015-02-14 22:43:43 -0800234void Fridge::RunIteration(const control_loops::FridgeQueue::Goal *unsafe_goal,
Austin Schuh703b8d42015-02-01 14:56:34 -0800235 const control_loops::FridgeQueue::Position *position,
236 control_loops::FridgeQueue::Output *output,
237 control_loops::FridgeQueue::Status *status) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800238 if (WasReset()) {
239 LOG(ERROR, "WPILib reset, restarting\n");
240 left_elevator_estimator_.Reset();
241 right_elevator_estimator_.Reset();
242 left_arm_estimator_.Reset();
243 right_arm_estimator_.Reset();
244 state_ = UNINITIALIZED;
245 }
246
Austin Schuh703b8d42015-02-01 14:56:34 -0800247 // Get a reference to the constants struct since we use it so often in this
248 // code.
Philipp Schrader82c65072015-02-16 00:47:09 +0000249 const auto &values = constants::GetValues();
Austin Schuh703b8d42015-02-01 14:56:34 -0800250
251 // Bool to track if we should turn the motors on or not.
252 bool disable = output == nullptr;
253 double elevator_goal_velocity = 0.0;
254 double arm_goal_velocity = 0.0;
255
256 // Save the current position so it can be used easily in the class.
257 current_position_ = *position;
258
259 left_elevator_estimator_.UpdateEstimate(position->elevator.left);
260 right_elevator_estimator_.UpdateEstimate(position->elevator.right);
261 left_arm_estimator_.UpdateEstimate(position->arm.left);
262 right_arm_estimator_.UpdateEstimate(position->arm.right);
263
264 if (state_ != UNINITIALIZED) {
265 Correct();
266 }
267
268 // Zeroing will work as follows:
269 // At startup, record the offset of the two halves of the two subsystems.
270 // Then, start moving the elevator towards the center until both halves are
271 // zeroed.
272 // Then, start moving the claw towards the center until both halves are
273 // zeroed.
274 // Then, done!
275
276 // We'll then need code to do sanity checking on values.
277
278 // Now, we need to figure out which way to go.
279
280 switch (state_) {
281 case UNINITIALIZED:
282 LOG(DEBUG, "Uninitialized\n");
283 // Startup. Assume that we are at the origin everywhere.
284 // This records the encoder offset between the two sides of the elevator.
285 left_elevator_offset_ = -position->elevator.left.encoder;
286 right_elevator_offset_ = -position->elevator.right.encoder;
287 left_arm_offset_ = -position->arm.left.encoder;
288 right_arm_offset_ = -position->arm.right.encoder;
Austin Schuhaab01572015-02-15 00:12:35 -0800289 elevator_loop_->mutable_X_hat().setZero();
290 arm_loop_->mutable_X_hat().setZero();
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800291 LOG(INFO, "Initializing arm offsets to %f, %f\n", left_arm_offset_,
292 right_arm_offset_);
293 LOG(INFO, "Initializing elevator offsets to %f, %f\n",
294 left_elevator_offset_, right_elevator_offset_);
Austin Schuh703b8d42015-02-01 14:56:34 -0800295 Correct();
296 state_ = INITIALIZING;
297 disable = true;
298 break;
299
300 case INITIALIZING:
301 LOG(DEBUG, "Waiting for accurate initial position.\n");
302 disable = true;
303 // Update state_ to accurately represent the state of the zeroing
304 // estimators.
305 UpdateZeroingState();
306 if (state_ != INITIALIZING) {
307 // Set the goals to where we are now.
308 elevator_goal_ = elevator();
309 arm_goal_ = arm();
310 }
311 break;
312
313 case ZEROING_ELEVATOR:
314 LOG(DEBUG, "Zeroing elevator\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800315
316 // Update state_ to accurately represent the state of the zeroing
317 // estimators.
318 UpdateZeroingState();
319 if (left_elevator_estimator_.zeroed() &&
320 right_elevator_estimator_.zeroed()) {
321 SetElevatorOffset(left_elevator_estimator_.offset(),
322 right_elevator_estimator_.offset());
323 LOG(DEBUG, "Zeroed the elevator!\n");
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800324
Austin Schuh5e872c82015-02-17 22:59:08 -0800325 if (elevator() < values.fridge.arm_zeroing_height &&
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800326 state_ != INITIALIZING) {
327 // Move the elevator to a safe height before we start zeroing the arm,
328 // so that we don't crash anything.
329 LOG(DEBUG, "Moving elevator to safe height.\n");
330 elevator_goal_ += kElevatorSafeHeightVelocity *
331 ::aos::controls::kLoopFrequency.ToSeconds();
332 elevator_goal_velocity = kElevatorSafeHeightVelocity;
333
334 state_ = ZEROING_ELEVATOR;
335 break;
336 }
337
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800338 } else if (!disable) {
339 elevator_goal_velocity = elevator_zeroing_velocity();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800340 elevator_goal_ += elevator_goal_velocity *
Adam Snaider3cd11c52015-02-16 02:16:09 +0000341 ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800342 }
Philipp Schrader3e281412015-03-01 23:48:23 +0000343
344 // Bypass motion profiles while we are zeroing.
345 // This is also an important step right after the elevator is zeroed and
346 // we reach into the elevator's state matrix and change it based on the
347 // newly-obtained offset.
348 {
349 Eigen::Matrix<double, 2, 1> current;
350 current.setZero();
351 current << elevator_goal_, elevator_goal_velocity;
352 elevator_profile_.MoveCurrentState(current);
353 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800354 break;
355
356 case ZEROING_ARM:
357 LOG(DEBUG, "Zeroing the arm\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800358
359 // Update state_ to accurately represent the state of the zeroing
360 // estimators.
361 UpdateZeroingState();
362 if (left_arm_estimator_.zeroed() && right_arm_estimator_.zeroed()) {
363 SetArmOffset(left_arm_estimator_.offset(),
364 right_arm_estimator_.offset());
365 LOG(DEBUG, "Zeroed the arm!\n");
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800366 } else if (!disable) {
367 arm_goal_velocity = arm_zeroing_velocity();
Adam Snaider3cd11c52015-02-16 02:16:09 +0000368 arm_goal_ +=
369 arm_goal_velocity * ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800370 }
Philipp Schrader3e281412015-03-01 23:48:23 +0000371
372 // Bypass motion profiles while we are zeroing.
373 // This is also an important step right after the arm is zeroed and
374 // we reach into the arm's state matrix and change it based on the
375 // newly-obtained offset.
376 {
377 Eigen::Matrix<double, 2, 1> current;
378 current.setZero();
379 current << arm_goal_, arm_goal_velocity;
380 arm_profile_.MoveCurrentState(current);
381 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800382 break;
383
384 case RUNNING:
385 LOG(DEBUG, "Running!\n");
Austin Schuh482a4e12015-02-14 22:43:43 -0800386 if (unsafe_goal) {
Philipp Schrader3e281412015-03-01 23:48:23 +0000387 // Pick a set of sane arm defaults if none are specified.
388 arm_profile_.set_maximum_velocity(
389 UseUnlessZero(unsafe_goal->max_angular_velocity, 1.0));
390 arm_profile_.set_maximum_acceleration(
391 UseUnlessZero(unsafe_goal->max_angular_acceleration, 3.0));
392 elevator_profile_.set_maximum_velocity(
393 UseUnlessZero(unsafe_goal->max_velocity, 0.50));
394 elevator_profile_.set_maximum_acceleration(
395 UseUnlessZero(unsafe_goal->max_acceleration, 2.0));
396
397 // Use the profiles to limit the arm's movements.
398 const double unfiltered_arm_goal = ::std::max(
399 ::std::min(unsafe_goal->angle, values.fridge.arm.upper_limit),
400 values.fridge.arm.lower_limit);
401 ::Eigen::Matrix<double, 2, 1> arm_goal_state = arm_profile_.Update(
402 unfiltered_arm_goal, unsafe_goal->angular_velocity);
403 arm_goal_ = arm_goal_state(0, 0);
404 arm_goal_velocity = arm_goal_state(1, 0);
405
406 // Use the profiles to limit the elevator's movements.
407 const double unfiltered_elevator_goal = ::std::max(
408 ::std::min(unsafe_goal->height, values.fridge.elevator.upper_limit),
409 values.fridge.elevator.lower_limit);
410 ::Eigen::Matrix<double, 2, 1> elevator_goal_state =
411 elevator_profile_.Update(unfiltered_elevator_goal,
412 unsafe_goal->velocity);
413 elevator_goal_ = elevator_goal_state(0, 0);
414 elevator_goal_velocity = elevator_goal_state(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800415 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800416
417 // Update state_ to accurately represent the state of the zeroing
418 // estimators.
419 UpdateZeroingState();
Austin Schuh703b8d42015-02-01 14:56:34 -0800420
421 if (state_ != RUNNING && state_ != ESTOP) {
422 state_ = UNINITIALIZED;
423 }
424 break;
425
426 case ESTOP:
427 LOG(ERROR, "Estop\n");
428 disable = true;
429 break;
430 }
431
432 // Commence death if either left/right tracking error gets too big. This
433 // should run immediately after the SetArmOffset and SetElevatorOffset
434 // functions to double-check that the hardware is in a sane state.
435 if (::std::abs(left_arm() - right_arm()) >=
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800436 values.max_allowed_left_right_arm_difference) {
437 LOG(ERROR, "The arms are too far apart. |%f - %f| > %f\n", left_arm(),
438 right_arm(), values.max_allowed_left_right_arm_difference);
439
440 // Indicate an ESTOP condition and stop the motors.
441 state_ = ESTOP;
442 disable = true;
443 }
444
445 if (::std::abs(left_elevator() - right_elevator()) >=
446 values.max_allowed_left_right_elevator_difference) {
447 LOG(ERROR, "The elevators are too far apart. |%f - %f| > %f\n",
448 left_elevator(), right_elevator(),
449 values.max_allowed_left_right_elevator_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800450
451 // Indicate an ESTOP condition and stop the motors.
452 state_ = ESTOP;
453 disable = true;
454 }
455
456 // Limit the goals so we can't exceed the hardware limits if we are RUNNING.
457 if (state_ == RUNNING) {
458 // Limit the arm goal to min/max allowable angles.
459 if (arm_goal_ >= values.fridge.arm.upper_limit) {
460 LOG(WARNING, "Arm goal above limit, %f > %f\n", arm_goal_,
461 values.fridge.arm.upper_limit);
462 arm_goal_ = values.fridge.arm.upper_limit;
463 }
464 if (arm_goal_ <= values.fridge.arm.lower_limit) {
465 LOG(WARNING, "Arm goal below limit, %f < %f\n", arm_goal_,
466 values.fridge.arm.lower_limit);
467 arm_goal_ = values.fridge.arm.lower_limit;
468 }
469
470 // Limit the elevator goal to min/max allowable heights.
471 if (elevator_goal_ >= values.fridge.elevator.upper_limit) {
472 LOG(WARNING, "Elevator goal above limit, %f > %f\n", elevator_goal_,
473 values.fridge.elevator.upper_limit);
474 elevator_goal_ = values.fridge.elevator.upper_limit;
475 }
476 if (elevator_goal_ <= values.fridge.elevator.lower_limit) {
477 LOG(WARNING, "Elevator goal below limit, %f < %f\n", elevator_goal_,
478 values.fridge.elevator.lower_limit);
479 elevator_goal_ = values.fridge.elevator.lower_limit;
480 }
481 }
482
483 // Check the lower level hardware limit as well.
484 if (state_ == RUNNING) {
485 if (left_arm() >= values.fridge.arm.upper_hard_limit ||
486 left_arm() <= values.fridge.arm.lower_hard_limit) {
487 LOG(ERROR, "Left arm at %f out of bounds [%f, %f], ESTOPing\n",
488 left_arm(), values.fridge.arm.lower_hard_limit,
489 values.fridge.arm.upper_hard_limit);
490 state_ = ESTOP;
491 }
492
493 if (right_arm() >= values.fridge.arm.upper_hard_limit ||
494 right_arm() <= values.fridge.arm.lower_hard_limit) {
495 LOG(ERROR, "Right arm at %f out of bounds [%f, %f], ESTOPing\n",
496 right_arm(), values.fridge.arm.lower_hard_limit,
497 values.fridge.arm.upper_hard_limit);
498 state_ = ESTOP;
499 }
500
501 if (left_elevator() >= values.fridge.elevator.upper_hard_limit ||
502 left_elevator() <= values.fridge.elevator.lower_hard_limit) {
503 LOG(ERROR, "Left elevator at %f out of bounds [%f, %f], ESTOPing\n",
504 left_elevator(), values.fridge.elevator.lower_hard_limit,
505 values.fridge.elevator.upper_hard_limit);
506 state_ = ESTOP;
507 }
508
509 if (right_elevator() >= values.fridge.elevator.upper_hard_limit ||
510 right_elevator() <= values.fridge.elevator.lower_hard_limit) {
511 LOG(ERROR, "Right elevator at %f out of bounds [%f, %f], ESTOPing\n",
512 right_elevator(), values.fridge.elevator.lower_hard_limit,
513 values.fridge.elevator.upper_hard_limit);
514 state_ = ESTOP;
515 }
516 }
517
518 // Set the goals.
Austin Schuh8de10c72015-02-27 23:33:40 -0800519 arm_loop_->mutable_R() << arm_goal_, arm_goal_velocity, 0.0, 0.0, 0.0;
Austin Schuh703b8d42015-02-01 14:56:34 -0800520 elevator_loop_->mutable_R() << elevator_goal_, elevator_goal_velocity, 0.0,
521 0.0;
522
523 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
524 arm_loop_->set_max_voltage(max_voltage);
525 elevator_loop_->set_max_voltage(max_voltage);
526
527 if (state_ == ESTOP) {
528 disable = true;
529 }
530 arm_loop_->Update(disable);
531 elevator_loop_->Update(disable);
532
533 if (state_ == INITIALIZING || state_ == ZEROING_ELEVATOR ||
534 state_ == ZEROING_ARM) {
535 if (arm_loop_->U() != arm_loop_->U_uncapped()) {
536 Eigen::Matrix<double, 2, 1> deltaR =
537 arm_loop_->UnsaturateOutputGoalChange();
538
539 // Move the average arm goal by the amount observed.
540 LOG(WARNING, "Moving arm goal by %f to handle saturation\n",
541 deltaR(0, 0));
542 arm_goal_ += deltaR(0, 0);
543 }
544
545 if (elevator_loop_->U() != elevator_loop_->U_uncapped()) {
546 Eigen::Matrix<double, 2, 1> deltaR =
547 elevator_loop_->UnsaturateOutputGoalChange();
548
549 // Move the average elevator goal by the amount observed.
550 LOG(WARNING, "Moving elevator goal by %f to handle saturation\n",
551 deltaR(0, 0));
552 elevator_goal_ += deltaR(0, 0);
553 }
554 }
555
556 if (output) {
557 output->left_arm = arm_loop_->U(0, 0);
558 output->right_arm = arm_loop_->U(1, 0);
559 output->left_elevator = elevator_loop_->U(0, 0);
560 output->right_elevator = elevator_loop_->U(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800561 if (unsafe_goal) {
562 output->grabbers = unsafe_goal->grabbers;
563 } else {
564 output->grabbers.top_front = false;
565 output->grabbers.top_back = false;
566 output->grabbers.bottom_front = false;
567 output->grabbers.bottom_back = false;
568 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800569 }
570
571 // TODO(austin): Populate these fully.
Austin Schuh5ae4efd2015-02-15 23:34:22 -0800572 status->zeroed = state_ == RUNNING;
Austin Schuh703b8d42015-02-01 14:56:34 -0800573 status->angle = arm_loop_->X_hat(0, 0);
Philipp Schrader3e281412015-03-01 23:48:23 +0000574 status->angular_velocity = arm_loop_->X_hat(1, 0);
Austin Schuh703b8d42015-02-01 14:56:34 -0800575 status->height = elevator_loop_->X_hat(0, 0);
Philipp Schrader3e281412015-03-01 23:48:23 +0000576 status->velocity = elevator_loop_->X_hat(1, 0);
Austin Schuh5e872c82015-02-17 22:59:08 -0800577 status->goal_angle = arm_goal_;
Philipp Schrader3e281412015-03-01 23:48:23 +0000578 status->goal_angular_velocity = arm_goal_velocity;
Austin Schuh5e872c82015-02-17 22:59:08 -0800579 status->goal_height = elevator_goal_;
Philipp Schrader3e281412015-03-01 23:48:23 +0000580 status->goal_velocity = elevator_goal_velocity;
Austin Schuh482a4e12015-02-14 22:43:43 -0800581 if (unsafe_goal) {
582 status->grabbers = unsafe_goal->grabbers;
583 } else {
584 status->grabbers.top_front = false;
585 status->grabbers.top_back = false;
586 status->grabbers.bottom_front = false;
587 status->grabbers.bottom_back = false;
588 }
Adam Snaider3cd11c52015-02-16 02:16:09 +0000589 zeroing::PopulateEstimatorState(left_arm_estimator_, &status->left_arm_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800590 zeroing::PopulateEstimatorState(right_arm_estimator_,
591 &status->right_arm_state);
592 zeroing::PopulateEstimatorState(left_elevator_estimator_,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000593 &status->left_elevator_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800594 zeroing::PopulateEstimatorState(right_elevator_estimator_,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000595 &status->right_elevator_state);
Austin Schuh703b8d42015-02-01 14:56:34 -0800596 status->estopped = (state_ == ESTOP);
Austin Schuh482a4e12015-02-14 22:43:43 -0800597 status->state = state_;
Austin Schuh703b8d42015-02-01 14:56:34 -0800598 last_state_ = state_;
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800599}
600
601} // namespace control_loops
602} // namespace frc971