blob: 2a1e4841c5602d624f1b7247dbada28c4c1955cf [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.
Austin Schuhb3ae2592015-03-15 00:33:38 -070022constexpr double kElevatorSafeHeightVelocity = 0.3;
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() {
Austin Schuhb3ae2592015-03-15 00:33:38 -0700149 return current_position_.arm.left.encoder + left_arm_estimator_.offset();
Austin Schuh703b8d42015-02-01 14:56:34 -0800150}
151double Fridge::estimated_right_arm() {
Austin Schuhb3ae2592015-03-15 00:33:38 -0700152 return current_position_.arm.right.encoder + right_arm_estimator_.offset();
Austin Schuh703b8d42015-02-01 14:56:34 -0800153}
154double Fridge::estimated_arm() {
155 return (estimated_left_arm() + estimated_right_arm()) / 2.0;
156}
157
158double Fridge::left_elevator() {
159 return current_position_.elevator.left.encoder + left_elevator_offset_;
160}
161double Fridge::right_elevator() {
162 return current_position_.elevator.right.encoder + right_elevator_offset_;
163}
164
165double Fridge::elevator() { return (left_elevator() + right_elevator()) / 2.0; }
166
167double Fridge::left_arm() {
168 return current_position_.arm.left.encoder + left_arm_offset_;
169}
170double Fridge::right_arm() {
171 return current_position_.arm.right.encoder + right_arm_offset_;
172}
173double Fridge::arm() { return (left_arm() + right_arm()) / 2.0; }
174
175double Fridge::elevator_zeroing_velocity() {
176 double average_elevator =
177 (constants::GetValues().fridge.elevator.lower_limit +
178 constants::GetValues().fridge.elevator.upper_limit) /
179 2.0;
180
181 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800182 constants::GetValues().fridge.left_elev_zeroing.index_difference,
183 constants::GetValues().fridge.right_elev_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800184
185 if (elevator_zeroing_velocity_ == 0) {
186 if (estimated_elevator() > average_elevator) {
187 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
188 } else {
189 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
190 }
191 } else if (elevator_zeroing_velocity_ > 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800192 estimated_elevator() > average_elevator + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800193 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
194 } else if (elevator_zeroing_velocity_ < 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800195 estimated_elevator() < average_elevator - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800196 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
197 }
198 return elevator_zeroing_velocity_;
199}
200
Philipp Schrader3e281412015-03-01 23:48:23 +0000201double Fridge::UseUnlessZero(double target_value, double default_value) {
202 if (target_value != 0.0) {
203 return target_value;
204 } else {
205 return default_value;
206 }
207}
208
Austin Schuh703b8d42015-02-01 14:56:34 -0800209double Fridge::arm_zeroing_velocity() {
210 const double average_arm = (constants::GetValues().fridge.arm.lower_limit +
211 constants::GetValues().fridge.arm.upper_limit) /
212 2.0;
213 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800214 constants::GetValues().fridge.right_arm_zeroing.index_difference,
215 constants::GetValues().fridge.left_arm_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800216
217 if (arm_zeroing_velocity_ == 0) {
218 if (estimated_arm() > average_arm) {
219 arm_zeroing_velocity_ = -kArmZeroingVelocity;
220 } else {
221 arm_zeroing_velocity_ = kArmZeroingVelocity;
222 }
223 } else if (arm_zeroing_velocity_ > 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800224 estimated_arm() > average_arm + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800225 arm_zeroing_velocity_ = -kArmZeroingVelocity;
Austin Schuhb3ae2592015-03-15 00:33:38 -0700226 } else if (arm_zeroing_velocity_ < 0.0 && estimated_arm() < average_arm) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800227 arm_zeroing_velocity_ = kArmZeroingVelocity;
228 }
229 return arm_zeroing_velocity_;
230}
231
Austin Schuh482a4e12015-02-14 22:43:43 -0800232void Fridge::RunIteration(const control_loops::FridgeQueue::Goal *unsafe_goal,
Austin Schuh703b8d42015-02-01 14:56:34 -0800233 const control_loops::FridgeQueue::Position *position,
234 control_loops::FridgeQueue::Output *output,
235 control_loops::FridgeQueue::Status *status) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800236 if (WasReset()) {
237 LOG(ERROR, "WPILib reset, restarting\n");
238 left_elevator_estimator_.Reset();
239 right_elevator_estimator_.Reset();
240 left_arm_estimator_.Reset();
241 right_arm_estimator_.Reset();
242 state_ = UNINITIALIZED;
243 }
244
Austin Schuh703b8d42015-02-01 14:56:34 -0800245 // Get a reference to the constants struct since we use it so often in this
246 // code.
Philipp Schrader82c65072015-02-16 00:47:09 +0000247 const auto &values = constants::GetValues();
Austin Schuh703b8d42015-02-01 14:56:34 -0800248
249 // Bool to track if we should turn the motors on or not.
250 bool disable = output == nullptr;
251 double elevator_goal_velocity = 0.0;
252 double arm_goal_velocity = 0.0;
253
254 // Save the current position so it can be used easily in the class.
255 current_position_ = *position;
256
257 left_elevator_estimator_.UpdateEstimate(position->elevator.left);
258 right_elevator_estimator_.UpdateEstimate(position->elevator.right);
259 left_arm_estimator_.UpdateEstimate(position->arm.left);
260 right_arm_estimator_.UpdateEstimate(position->arm.right);
261
262 if (state_ != UNINITIALIZED) {
263 Correct();
264 }
265
266 // Zeroing will work as follows:
267 // At startup, record the offset of the two halves of the two subsystems.
268 // Then, start moving the elevator towards the center until both halves are
269 // zeroed.
270 // Then, start moving the claw towards the center until both halves are
271 // zeroed.
272 // Then, done!
273
274 // We'll then need code to do sanity checking on values.
275
276 // Now, we need to figure out which way to go.
277
278 switch (state_) {
279 case UNINITIALIZED:
280 LOG(DEBUG, "Uninitialized\n");
281 // Startup. Assume that we are at the origin everywhere.
282 // This records the encoder offset between the two sides of the elevator.
283 left_elevator_offset_ = -position->elevator.left.encoder;
284 right_elevator_offset_ = -position->elevator.right.encoder;
285 left_arm_offset_ = -position->arm.left.encoder;
286 right_arm_offset_ = -position->arm.right.encoder;
Austin Schuhaab01572015-02-15 00:12:35 -0800287 elevator_loop_->mutable_X_hat().setZero();
288 arm_loop_->mutable_X_hat().setZero();
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800289 LOG(INFO, "Initializing arm offsets to %f, %f\n", left_arm_offset_,
290 right_arm_offset_);
291 LOG(INFO, "Initializing elevator offsets to %f, %f\n",
292 left_elevator_offset_, right_elevator_offset_);
Austin Schuh703b8d42015-02-01 14:56:34 -0800293 Correct();
294 state_ = INITIALIZING;
295 disable = true;
296 break;
297
298 case INITIALIZING:
299 LOG(DEBUG, "Waiting for accurate initial position.\n");
300 disable = true;
301 // Update state_ to accurately represent the state of the zeroing
302 // estimators.
303 UpdateZeroingState();
304 if (state_ != INITIALIZING) {
305 // Set the goals to where we are now.
306 elevator_goal_ = elevator();
307 arm_goal_ = arm();
308 }
309 break;
310
311 case ZEROING_ELEVATOR:
312 LOG(DEBUG, "Zeroing elevator\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800313
314 // Update state_ to accurately represent the state of the zeroing
315 // estimators.
316 UpdateZeroingState();
317 if (left_elevator_estimator_.zeroed() &&
318 right_elevator_estimator_.zeroed()) {
319 SetElevatorOffset(left_elevator_estimator_.offset(),
320 right_elevator_estimator_.offset());
321 LOG(DEBUG, "Zeroed the elevator!\n");
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800322
Austin Schuh5e872c82015-02-17 22:59:08 -0800323 if (elevator() < values.fridge.arm_zeroing_height &&
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800324 state_ != INITIALIZING) {
325 // Move the elevator to a safe height before we start zeroing the arm,
326 // so that we don't crash anything.
327 LOG(DEBUG, "Moving elevator to safe height.\n");
328 elevator_goal_ += kElevatorSafeHeightVelocity *
329 ::aos::controls::kLoopFrequency.ToSeconds();
330 elevator_goal_velocity = kElevatorSafeHeightVelocity;
331
332 state_ = ZEROING_ELEVATOR;
333 break;
334 }
335
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800336 } else if (!disable) {
337 elevator_goal_velocity = elevator_zeroing_velocity();
Daniel Petti9cf68c82015-02-14 14:57:17 -0800338 elevator_goal_ += elevator_goal_velocity *
Adam Snaider3cd11c52015-02-16 02:16:09 +0000339 ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800340 }
Philipp Schrader3e281412015-03-01 23:48:23 +0000341
342 // Bypass motion profiles while we are zeroing.
343 // This is also an important step right after the elevator is zeroed and
344 // we reach into the elevator's state matrix and change it based on the
345 // newly-obtained offset.
346 {
347 Eigen::Matrix<double, 2, 1> current;
348 current.setZero();
349 current << elevator_goal_, elevator_goal_velocity;
350 elevator_profile_.MoveCurrentState(current);
351 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800352 break;
353
354 case ZEROING_ARM:
355 LOG(DEBUG, "Zeroing the arm\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800356
357 // Update state_ to accurately represent the state of the zeroing
358 // estimators.
359 UpdateZeroingState();
360 if (left_arm_estimator_.zeroed() && right_arm_estimator_.zeroed()) {
361 SetArmOffset(left_arm_estimator_.offset(),
362 right_arm_estimator_.offset());
363 LOG(DEBUG, "Zeroed the arm!\n");
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800364 } else if (!disable) {
365 arm_goal_velocity = arm_zeroing_velocity();
Adam Snaider3cd11c52015-02-16 02:16:09 +0000366 arm_goal_ +=
367 arm_goal_velocity * ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800368 }
Philipp Schrader3e281412015-03-01 23:48:23 +0000369
370 // Bypass motion profiles while we are zeroing.
371 // This is also an important step right after the arm is zeroed and
372 // we reach into the arm's state matrix and change it based on the
373 // newly-obtained offset.
374 {
375 Eigen::Matrix<double, 2, 1> current;
376 current.setZero();
377 current << arm_goal_, arm_goal_velocity;
378 arm_profile_.MoveCurrentState(current);
379 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800380 break;
381
382 case RUNNING:
383 LOG(DEBUG, "Running!\n");
Austin Schuh482a4e12015-02-14 22:43:43 -0800384 if (unsafe_goal) {
Philipp Schrader3e281412015-03-01 23:48:23 +0000385 // Pick a set of sane arm defaults if none are specified.
386 arm_profile_.set_maximum_velocity(
387 UseUnlessZero(unsafe_goal->max_angular_velocity, 1.0));
388 arm_profile_.set_maximum_acceleration(
389 UseUnlessZero(unsafe_goal->max_angular_acceleration, 3.0));
390 elevator_profile_.set_maximum_velocity(
391 UseUnlessZero(unsafe_goal->max_velocity, 0.50));
392 elevator_profile_.set_maximum_acceleration(
393 UseUnlessZero(unsafe_goal->max_acceleration, 2.0));
394
395 // Use the profiles to limit the arm's movements.
396 const double unfiltered_arm_goal = ::std::max(
397 ::std::min(unsafe_goal->angle, values.fridge.arm.upper_limit),
398 values.fridge.arm.lower_limit);
399 ::Eigen::Matrix<double, 2, 1> arm_goal_state = arm_profile_.Update(
400 unfiltered_arm_goal, unsafe_goal->angular_velocity);
401 arm_goal_ = arm_goal_state(0, 0);
402 arm_goal_velocity = arm_goal_state(1, 0);
403
404 // Use the profiles to limit the elevator's movements.
405 const double unfiltered_elevator_goal = ::std::max(
406 ::std::min(unsafe_goal->height, values.fridge.elevator.upper_limit),
407 values.fridge.elevator.lower_limit);
408 ::Eigen::Matrix<double, 2, 1> elevator_goal_state =
409 elevator_profile_.Update(unfiltered_elevator_goal,
410 unsafe_goal->velocity);
411 elevator_goal_ = elevator_goal_state(0, 0);
412 elevator_goal_velocity = elevator_goal_state(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800413 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800414
415 // Update state_ to accurately represent the state of the zeroing
416 // estimators.
417 UpdateZeroingState();
Austin Schuh703b8d42015-02-01 14:56:34 -0800418
419 if (state_ != RUNNING && state_ != ESTOP) {
420 state_ = UNINITIALIZED;
421 }
422 break;
423
424 case ESTOP:
425 LOG(ERROR, "Estop\n");
426 disable = true;
427 break;
428 }
429
430 // Commence death if either left/right tracking error gets too big. This
431 // should run immediately after the SetArmOffset and SetElevatorOffset
432 // functions to double-check that the hardware is in a sane state.
433 if (::std::abs(left_arm() - right_arm()) >=
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800434 values.max_allowed_left_right_arm_difference) {
435 LOG(ERROR, "The arms are too far apart. |%f - %f| > %f\n", left_arm(),
436 right_arm(), values.max_allowed_left_right_arm_difference);
437
438 // Indicate an ESTOP condition and stop the motors.
439 state_ = ESTOP;
440 disable = true;
441 }
442
443 if (::std::abs(left_elevator() - right_elevator()) >=
444 values.max_allowed_left_right_elevator_difference) {
445 LOG(ERROR, "The elevators are too far apart. |%f - %f| > %f\n",
446 left_elevator(), right_elevator(),
447 values.max_allowed_left_right_elevator_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800448
449 // Indicate an ESTOP condition and stop the motors.
450 state_ = ESTOP;
451 disable = true;
452 }
453
454 // Limit the goals so we can't exceed the hardware limits if we are RUNNING.
455 if (state_ == RUNNING) {
456 // Limit the arm goal to min/max allowable angles.
457 if (arm_goal_ >= values.fridge.arm.upper_limit) {
458 LOG(WARNING, "Arm goal above limit, %f > %f\n", arm_goal_,
459 values.fridge.arm.upper_limit);
460 arm_goal_ = values.fridge.arm.upper_limit;
461 }
462 if (arm_goal_ <= values.fridge.arm.lower_limit) {
463 LOG(WARNING, "Arm goal below limit, %f < %f\n", arm_goal_,
464 values.fridge.arm.lower_limit);
465 arm_goal_ = values.fridge.arm.lower_limit;
466 }
467
468 // Limit the elevator goal to min/max allowable heights.
469 if (elevator_goal_ >= values.fridge.elevator.upper_limit) {
470 LOG(WARNING, "Elevator goal above limit, %f > %f\n", elevator_goal_,
471 values.fridge.elevator.upper_limit);
472 elevator_goal_ = values.fridge.elevator.upper_limit;
473 }
474 if (elevator_goal_ <= values.fridge.elevator.lower_limit) {
475 LOG(WARNING, "Elevator goal below limit, %f < %f\n", elevator_goal_,
476 values.fridge.elevator.lower_limit);
477 elevator_goal_ = values.fridge.elevator.lower_limit;
478 }
479 }
480
481 // Check the lower level hardware limit as well.
482 if (state_ == RUNNING) {
483 if (left_arm() >= values.fridge.arm.upper_hard_limit ||
484 left_arm() <= values.fridge.arm.lower_hard_limit) {
485 LOG(ERROR, "Left arm at %f out of bounds [%f, %f], ESTOPing\n",
486 left_arm(), values.fridge.arm.lower_hard_limit,
487 values.fridge.arm.upper_hard_limit);
488 state_ = ESTOP;
489 }
490
491 if (right_arm() >= values.fridge.arm.upper_hard_limit ||
492 right_arm() <= values.fridge.arm.lower_hard_limit) {
493 LOG(ERROR, "Right arm at %f out of bounds [%f, %f], ESTOPing\n",
494 right_arm(), values.fridge.arm.lower_hard_limit,
495 values.fridge.arm.upper_hard_limit);
496 state_ = ESTOP;
497 }
498
499 if (left_elevator() >= values.fridge.elevator.upper_hard_limit ||
500 left_elevator() <= values.fridge.elevator.lower_hard_limit) {
501 LOG(ERROR, "Left elevator at %f out of bounds [%f, %f], ESTOPing\n",
502 left_elevator(), values.fridge.elevator.lower_hard_limit,
503 values.fridge.elevator.upper_hard_limit);
504 state_ = ESTOP;
505 }
506
507 if (right_elevator() >= values.fridge.elevator.upper_hard_limit ||
508 right_elevator() <= values.fridge.elevator.lower_hard_limit) {
509 LOG(ERROR, "Right elevator at %f out of bounds [%f, %f], ESTOPing\n",
510 right_elevator(), values.fridge.elevator.lower_hard_limit,
511 values.fridge.elevator.upper_hard_limit);
512 state_ = ESTOP;
513 }
514 }
515
516 // Set the goals.
Austin Schuh8de10c72015-02-27 23:33:40 -0800517 arm_loop_->mutable_R() << arm_goal_, arm_goal_velocity, 0.0, 0.0, 0.0;
Austin Schuh703b8d42015-02-01 14:56:34 -0800518 elevator_loop_->mutable_R() << elevator_goal_, elevator_goal_velocity, 0.0,
519 0.0;
520
521 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
522 arm_loop_->set_max_voltage(max_voltage);
523 elevator_loop_->set_max_voltage(max_voltage);
524
525 if (state_ == ESTOP) {
526 disable = true;
527 }
528 arm_loop_->Update(disable);
529 elevator_loop_->Update(disable);
530
531 if (state_ == INITIALIZING || state_ == ZEROING_ELEVATOR ||
532 state_ == ZEROING_ARM) {
533 if (arm_loop_->U() != arm_loop_->U_uncapped()) {
534 Eigen::Matrix<double, 2, 1> deltaR =
535 arm_loop_->UnsaturateOutputGoalChange();
536
537 // Move the average arm goal by the amount observed.
538 LOG(WARNING, "Moving arm goal by %f to handle saturation\n",
539 deltaR(0, 0));
540 arm_goal_ += deltaR(0, 0);
541 }
542
543 if (elevator_loop_->U() != elevator_loop_->U_uncapped()) {
544 Eigen::Matrix<double, 2, 1> deltaR =
545 elevator_loop_->UnsaturateOutputGoalChange();
546
547 // Move the average elevator goal by the amount observed.
548 LOG(WARNING, "Moving elevator goal by %f to handle saturation\n",
549 deltaR(0, 0));
550 elevator_goal_ += deltaR(0, 0);
551 }
552 }
553
554 if (output) {
555 output->left_arm = arm_loop_->U(0, 0);
556 output->right_arm = arm_loop_->U(1, 0);
557 output->left_elevator = elevator_loop_->U(0, 0);
558 output->right_elevator = elevator_loop_->U(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800559 if (unsafe_goal) {
560 output->grabbers = unsafe_goal->grabbers;
561 } else {
562 output->grabbers.top_front = false;
563 output->grabbers.top_back = false;
564 output->grabbers.bottom_front = false;
565 output->grabbers.bottom_back = false;
566 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800567 }
568
569 // TODO(austin): Populate these fully.
Austin Schuh5ae4efd2015-02-15 23:34:22 -0800570 status->zeroed = state_ == RUNNING;
Austin Schuh703b8d42015-02-01 14:56:34 -0800571 status->angle = arm_loop_->X_hat(0, 0);
Philipp Schrader3e281412015-03-01 23:48:23 +0000572 status->angular_velocity = arm_loop_->X_hat(1, 0);
Austin Schuh703b8d42015-02-01 14:56:34 -0800573 status->height = elevator_loop_->X_hat(0, 0);
Philipp Schrader3e281412015-03-01 23:48:23 +0000574 status->velocity = elevator_loop_->X_hat(1, 0);
Austin Schuh5e872c82015-02-17 22:59:08 -0800575 status->goal_angle = arm_goal_;
Philipp Schrader3e281412015-03-01 23:48:23 +0000576 status->goal_angular_velocity = arm_goal_velocity;
Austin Schuh5e872c82015-02-17 22:59:08 -0800577 status->goal_height = elevator_goal_;
Philipp Schrader3e281412015-03-01 23:48:23 +0000578 status->goal_velocity = elevator_goal_velocity;
Austin Schuh482a4e12015-02-14 22:43:43 -0800579 if (unsafe_goal) {
580 status->grabbers = unsafe_goal->grabbers;
581 } else {
582 status->grabbers.top_front = false;
583 status->grabbers.top_back = false;
584 status->grabbers.bottom_front = false;
585 status->grabbers.bottom_back = false;
586 }
Adam Snaider3cd11c52015-02-16 02:16:09 +0000587 zeroing::PopulateEstimatorState(left_arm_estimator_, &status->left_arm_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800588 zeroing::PopulateEstimatorState(right_arm_estimator_,
589 &status->right_arm_state);
590 zeroing::PopulateEstimatorState(left_elevator_estimator_,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000591 &status->left_elevator_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800592 zeroing::PopulateEstimatorState(right_elevator_estimator_,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000593 &status->right_elevator_state);
Austin Schuh703b8d42015-02-01 14:56:34 -0800594 status->estopped = (state_ == ESTOP);
Austin Schuh482a4e12015-02-14 22:43:43 -0800595 status->state = state_;
Austin Schuh703b8d42015-02-01 14:56:34 -0800596 last_state_ = state_;
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800597}
598
599} // namespace control_loops
600} // namespace frc971