blob: 9f2241a6820ec93fb18a647861b48a661e7d10da [file] [log] [blame]
Brian Silvermanb691f5e2015-08-02 11:37:55 -07001#include "y2015/control_loops/fridge/fridge.h"
Ben Fredrickson6b5ba792015-01-25 17:14:40 -08002
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
Brian Silvermanb691f5e2015-08-02 11:37:55 -07008#include "y2015/control_loops/fridge/elevator_motor_plant.h"
9#include "y2015/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
Brian Silvermanb691f5e2015-08-02 11:37:55 -070013#include "y2015/constants.h"
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080014
15namespace frc971 {
16namespace control_loops {
17
Austin Schuh214e9c12016-11-25 17:26:20 -080018namespace chrono = ::std::chrono;
19
Austin Schuh703b8d42015-02-01 14:56:34 -080020namespace {
Austin Schuhb966c432015-02-16 15:47:18 -080021constexpr double kZeroingVoltage = 4.0;
22constexpr double kElevatorZeroingVelocity = 0.10;
Daniel Pettie1bb13e2015-02-17 13:59:15 -080023// What speed we move to our safe height at.
Austin Schuhb3ae2592015-03-15 00:33:38 -070024constexpr double kElevatorSafeHeightVelocity = 0.3;
Austin Schuhb966c432015-02-16 15:47:18 -080025constexpr double kArmZeroingVelocity = 0.20;
Austin Schuh703b8d42015-02-01 14:56:34 -080026} // namespace
27
Austin Schuh8de10c72015-02-27 23:33:40 -080028template <int S>
29void CappedStateFeedbackLoop<S>::CapU() {
30 VoltageCap(max_voltage_, this->U(0, 0), this->U(1, 0), &this->mutable_U(0, 0),
31 &this->mutable_U(1, 0));
Austin Schuh703b8d42015-02-01 14:56:34 -080032}
33
Austin Schuh8de10c72015-02-27 23:33:40 -080034template <int S>
Austin Schuh703b8d42015-02-01 14:56:34 -080035Eigen::Matrix<double, 2, 1>
Austin Schuh8de10c72015-02-27 23:33:40 -080036CappedStateFeedbackLoop<S>::UnsaturateOutputGoalChange() {
Austin Schuh703b8d42015-02-01 14:56:34 -080037 // Compute the K matrix used to compensate for position errors.
38 Eigen::Matrix<double, 2, 2> Kp;
39 Kp.setZero();
Austin Schuh8de10c72015-02-27 23:33:40 -080040 Kp.col(0) = this->K().col(0);
41 Kp.col(1) = this->K().col(2);
Austin Schuh703b8d42015-02-01 14:56:34 -080042
43 Eigen::Matrix<double, 2, 2> Kp_inv = Kp.inverse();
44
45 // Compute how much we need to change R in order to achieve the change in U
46 // that was observed.
Austin Schuh8de10c72015-02-27 23:33:40 -080047 Eigen::Matrix<double, 2, 1> deltaR =
48 -Kp_inv * (this->U_uncapped() - this->U());
Austin Schuh703b8d42015-02-01 14:56:34 -080049 return deltaR;
50}
51
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080052Fridge::Fridge(control_loops::FridgeQueue *fridge)
Brian Silverman089f5812015-02-15 01:58:19 -050053 : aos::controls::ControlLoop<control_loops::FridgeQueue>(fridge),
Austin Schuh8de10c72015-02-27 23:33:40 -080054 arm_loop_(new CappedStateFeedbackLoop<5>(
55 StateFeedbackLoop<5, 2, 2>(MakeIntegralArmLoop()))),
56 elevator_loop_(new CappedStateFeedbackLoop<4>(
Austin Schuh703b8d42015-02-01 14:56:34 -080057 StateFeedbackLoop<4, 2, 2>(MakeElevatorLoop()))),
Daniel Pettia7827412015-02-13 20:55:57 -080058 left_arm_estimator_(constants::GetValues().fridge.left_arm_zeroing),
59 right_arm_estimator_(constants::GetValues().fridge.right_arm_zeroing),
Austin Schuh482a4e12015-02-14 22:43:43 -080060 left_elevator_estimator_(constants::GetValues().fridge.left_elev_zeroing),
Austin Schuh703b8d42015-02-01 14:56:34 -080061 right_elevator_estimator_(
Philipp Schrader3e281412015-03-01 23:48:23 +000062 constants::GetValues().fridge.right_elev_zeroing),
Philipp Schrader085bf012015-03-15 01:43:11 +000063 last_profiling_type_(ProfilingType::ANGLE_HEIGHT_PROFILING),
64 kinematics_(constants::GetValues().fridge.arm_length,
65 constants::GetValues().fridge.elevator.upper_limit,
66 constants::GetValues().fridge.elevator.lower_limit,
67 constants::GetValues().fridge.arm.upper_limit,
68 constants::GetValues().fridge.arm.lower_limit),
Philipp Schrader3e281412015-03-01 23:48:23 +000069 arm_profile_(::aos::controls::kLoopFrequency),
Philipp Schrader085bf012015-03-15 01:43:11 +000070 elevator_profile_(::aos::controls::kLoopFrequency),
71 x_profile_(::aos::controls::kLoopFrequency),
72 y_profile_(::aos::controls::kLoopFrequency) {}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080073
Austin Schuh703b8d42015-02-01 14:56:34 -080074void Fridge::UpdateZeroingState() {
Austin Schuh47bf6902015-02-14 22:46:22 -080075 if (left_elevator_estimator_.offset_ratio_ready() < 1.0 ||
76 right_elevator_estimator_.offset_ratio_ready() < 1.0 ||
77 left_arm_estimator_.offset_ratio_ready() < 1.0 ||
78 right_arm_estimator_.offset_ratio_ready() < 1.0) {
Austin Schuh703b8d42015-02-01 14:56:34 -080079 state_ = INITIALIZING;
80 } else if (!left_elevator_estimator_.zeroed() ||
Adam Snaider3cd11c52015-02-16 02:16:09 +000081 !right_elevator_estimator_.zeroed()) {
Austin Schuh703b8d42015-02-01 14:56:34 -080082 state_ = ZEROING_ELEVATOR;
Adam Snaider3cd11c52015-02-16 02:16:09 +000083 } else if (!left_arm_estimator_.zeroed() || !right_arm_estimator_.zeroed()) {
Austin Schuh703b8d42015-02-01 14:56:34 -080084 state_ = ZEROING_ARM;
85 } else {
86 state_ = RUNNING;
87 }
88}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080089
Austin Schuh703b8d42015-02-01 14:56:34 -080090void Fridge::Correct() {
91 {
92 Eigen::Matrix<double, 2, 1> Y;
93 Y << left_elevator(), right_elevator();
94 elevator_loop_->Correct(Y);
95 }
96
97 {
98 Eigen::Matrix<double, 2, 1> Y;
99 Y << left_arm(), right_arm();
100 arm_loop_->Correct(Y);
101 }
102}
103
104void Fridge::SetElevatorOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800105 LOG(INFO, "Changing Elevator offset from %f, %f to %f, %f\n",
106 left_elevator_offset_, right_elevator_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -0800107 double left_doffset = left_offset - left_elevator_offset_;
108 double right_doffset = right_offset - right_elevator_offset_;
109
110 // Adjust the average height and height difference between the two sides.
111 // The derivatives of both should not need to be updated since the speeds
112 // haven't changed.
113 // The height difference is calculated as left - right, not right - left.
114 elevator_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
115 elevator_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
116
117 // Modify the zeroing goal.
118 elevator_goal_ += (left_doffset + right_doffset) / 2;
119
120 // Update the cached offset values to the actual values.
121 left_elevator_offset_ = left_offset;
122 right_elevator_offset_ = right_offset;
123}
124
125void Fridge::SetArmOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800126 LOG(INFO, "Changing Arm offset from %f, %f to %f, %f\n", left_arm_offset_,
127 right_arm_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -0800128 double left_doffset = left_offset - left_arm_offset_;
129 double right_doffset = right_offset - right_arm_offset_;
130
131 // Adjust the average angle and angle difference between the two sides.
132 // The derivatives of both should not need to be updated since the speeds
133 // haven't changed.
134 arm_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
135 arm_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
136
137 // Modify the zeroing goal.
138 arm_goal_ += (left_doffset + right_doffset) / 2;
139
140 // Update the cached offset values to the actual values.
141 left_arm_offset_ = left_offset;
142 right_arm_offset_ = right_offset;
143}
144
145double Fridge::estimated_left_elevator() {
146 return current_position_.elevator.left.encoder +
147 left_elevator_estimator_.offset();
148}
149double Fridge::estimated_right_elevator() {
150 return current_position_.elevator.right.encoder +
151 right_elevator_estimator_.offset();
152}
153
154double Fridge::estimated_elevator() {
155 return (estimated_left_elevator() + estimated_right_elevator()) / 2.0;
156}
157
158double Fridge::estimated_left_arm() {
Austin Schuhb3ae2592015-03-15 00:33:38 -0700159 return current_position_.arm.left.encoder + left_arm_estimator_.offset();
Austin Schuh703b8d42015-02-01 14:56:34 -0800160}
161double Fridge::estimated_right_arm() {
Austin Schuhb3ae2592015-03-15 00:33:38 -0700162 return current_position_.arm.right.encoder + right_arm_estimator_.offset();
Austin Schuh703b8d42015-02-01 14:56:34 -0800163}
164double Fridge::estimated_arm() {
165 return (estimated_left_arm() + estimated_right_arm()) / 2.0;
166}
167
168double Fridge::left_elevator() {
169 return current_position_.elevator.left.encoder + left_elevator_offset_;
170}
171double Fridge::right_elevator() {
172 return current_position_.elevator.right.encoder + right_elevator_offset_;
173}
174
175double Fridge::elevator() { return (left_elevator() + right_elevator()) / 2.0; }
176
177double Fridge::left_arm() {
178 return current_position_.arm.left.encoder + left_arm_offset_;
179}
180double Fridge::right_arm() {
181 return current_position_.arm.right.encoder + right_arm_offset_;
182}
183double Fridge::arm() { return (left_arm() + right_arm()) / 2.0; }
184
185double Fridge::elevator_zeroing_velocity() {
186 double average_elevator =
187 (constants::GetValues().fridge.elevator.lower_limit +
188 constants::GetValues().fridge.elevator.upper_limit) /
189 2.0;
190
191 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800192 constants::GetValues().fridge.left_elev_zeroing.index_difference,
193 constants::GetValues().fridge.right_elev_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800194
195 if (elevator_zeroing_velocity_ == 0) {
196 if (estimated_elevator() > average_elevator) {
197 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
198 } else {
199 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
200 }
201 } else if (elevator_zeroing_velocity_ > 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800202 estimated_elevator() > average_elevator + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800203 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
204 } else if (elevator_zeroing_velocity_ < 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800205 estimated_elevator() < average_elevator - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800206 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
207 }
208 return elevator_zeroing_velocity_;
209}
210
Philipp Schrader3e281412015-03-01 23:48:23 +0000211double Fridge::UseUnlessZero(double target_value, double default_value) {
212 if (target_value != 0.0) {
213 return target_value;
214 } else {
215 return default_value;
216 }
217}
218
Austin Schuh703b8d42015-02-01 14:56:34 -0800219double Fridge::arm_zeroing_velocity() {
220 const double average_arm = (constants::GetValues().fridge.arm.lower_limit +
221 constants::GetValues().fridge.arm.upper_limit) /
222 2.0;
223 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800224 constants::GetValues().fridge.right_arm_zeroing.index_difference,
225 constants::GetValues().fridge.left_arm_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800226
227 if (arm_zeroing_velocity_ == 0) {
228 if (estimated_arm() > average_arm) {
229 arm_zeroing_velocity_ = -kArmZeroingVelocity;
230 } else {
231 arm_zeroing_velocity_ = kArmZeroingVelocity;
232 }
233 } else if (arm_zeroing_velocity_ > 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800234 estimated_arm() > average_arm + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800235 arm_zeroing_velocity_ = -kArmZeroingVelocity;
Austin Schuhb3ae2592015-03-15 00:33:38 -0700236 } else if (arm_zeroing_velocity_ < 0.0 && estimated_arm() < average_arm) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800237 arm_zeroing_velocity_ = kArmZeroingVelocity;
238 }
239 return arm_zeroing_velocity_;
240}
241
Austin Schuh482a4e12015-02-14 22:43:43 -0800242void Fridge::RunIteration(const control_loops::FridgeQueue::Goal *unsafe_goal,
Austin Schuh703b8d42015-02-01 14:56:34 -0800243 const control_loops::FridgeQueue::Position *position,
244 control_loops::FridgeQueue::Output *output,
245 control_loops::FridgeQueue::Status *status) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800246 if (WasReset()) {
247 LOG(ERROR, "WPILib reset, restarting\n");
248 left_elevator_estimator_.Reset();
249 right_elevator_estimator_.Reset();
250 left_arm_estimator_.Reset();
251 right_arm_estimator_.Reset();
252 state_ = UNINITIALIZED;
253 }
254
Austin Schuh703b8d42015-02-01 14:56:34 -0800255 // Get a reference to the constants struct since we use it so often in this
256 // code.
Philipp Schrader82c65072015-02-16 00:47:09 +0000257 const auto &values = constants::GetValues();
Austin Schuh703b8d42015-02-01 14:56:34 -0800258
259 // Bool to track if we should turn the motors on or not.
260 bool disable = output == nullptr;
Austin Schuh703b8d42015-02-01 14:56:34 -0800261
262 // Save the current position so it can be used easily in the class.
263 current_position_ = *position;
264
265 left_elevator_estimator_.UpdateEstimate(position->elevator.left);
266 right_elevator_estimator_.UpdateEstimate(position->elevator.right);
267 left_arm_estimator_.UpdateEstimate(position->arm.left);
268 right_arm_estimator_.UpdateEstimate(position->arm.right);
269
270 if (state_ != UNINITIALIZED) {
271 Correct();
272 }
273
274 // Zeroing will work as follows:
275 // At startup, record the offset of the two halves of the two subsystems.
276 // Then, start moving the elevator towards the center until both halves are
277 // zeroed.
278 // Then, start moving the claw towards the center until both halves are
279 // zeroed.
280 // Then, done!
281
282 // We'll then need code to do sanity checking on values.
283
284 // Now, we need to figure out which way to go.
285
286 switch (state_) {
287 case UNINITIALIZED:
288 LOG(DEBUG, "Uninitialized\n");
289 // Startup. Assume that we are at the origin everywhere.
290 // This records the encoder offset between the two sides of the elevator.
291 left_elevator_offset_ = -position->elevator.left.encoder;
292 right_elevator_offset_ = -position->elevator.right.encoder;
293 left_arm_offset_ = -position->arm.left.encoder;
294 right_arm_offset_ = -position->arm.right.encoder;
Austin Schuhaab01572015-02-15 00:12:35 -0800295 elevator_loop_->mutable_X_hat().setZero();
296 arm_loop_->mutable_X_hat().setZero();
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800297 LOG(INFO, "Initializing arm offsets to %f, %f\n", left_arm_offset_,
298 right_arm_offset_);
299 LOG(INFO, "Initializing elevator offsets to %f, %f\n",
300 left_elevator_offset_, right_elevator_offset_);
Austin Schuh703b8d42015-02-01 14:56:34 -0800301 Correct();
302 state_ = INITIALIZING;
303 disable = true;
304 break;
305
306 case INITIALIZING:
307 LOG(DEBUG, "Waiting for accurate initial position.\n");
308 disable = true;
309 // Update state_ to accurately represent the state of the zeroing
310 // estimators.
311 UpdateZeroingState();
312 if (state_ != INITIALIZING) {
313 // Set the goals to where we are now.
314 elevator_goal_ = elevator();
315 arm_goal_ = arm();
316 }
317 break;
318
319 case ZEROING_ELEVATOR:
320 LOG(DEBUG, "Zeroing elevator\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800321
322 // Update state_ to accurately represent the state of the zeroing
323 // estimators.
324 UpdateZeroingState();
325 if (left_elevator_estimator_.zeroed() &&
326 right_elevator_estimator_.zeroed()) {
327 SetElevatorOffset(left_elevator_estimator_.offset(),
328 right_elevator_estimator_.offset());
329 LOG(DEBUG, "Zeroed the elevator!\n");
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800330
Austin Schuh5e872c82015-02-17 22:59:08 -0800331 if (elevator() < values.fridge.arm_zeroing_height &&
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800332 state_ != INITIALIZING) {
333 // Move the elevator to a safe height before we start zeroing the arm,
334 // so that we don't crash anything.
335 LOG(DEBUG, "Moving elevator to safe height.\n");
Brian Silverman283849f2015-03-28 01:25:16 -0400336 if (elevator_goal_ < values.fridge.arm_zeroing_height) {
337 elevator_goal_ += kElevatorSafeHeightVelocity *
Austin Schuh214e9c12016-11-25 17:26:20 -0800338 chrono::duration_cast<chrono::duration<double>>(
339 ::aos::controls::kLoopFrequency).count();
Brian Silverman283849f2015-03-28 01:25:16 -0400340 elevator_goal_velocity_ = kElevatorSafeHeightVelocity;
Austin Schuh34aad422015-03-28 14:23:31 -0700341 state_ = ZEROING_ELEVATOR;
Brian Silverman283849f2015-03-28 01:25:16 -0400342 } else {
343 // We want it stopped at whatever height it's currently set to.
344 elevator_goal_velocity_ = 0;
345 }
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800346 }
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800347 } else if (!disable) {
Philipp Schrader085bf012015-03-15 01:43:11 +0000348 elevator_goal_velocity_ = elevator_zeroing_velocity();
349 elevator_goal_ += elevator_goal_velocity_ *
Austin Schuh214e9c12016-11-25 17:26:20 -0800350 chrono::duration_cast<chrono::duration<double>>(
351 ::aos::controls::kLoopFrequency).count();
Austin Schuh703b8d42015-02-01 14:56:34 -0800352 }
Philipp Schrader3e281412015-03-01 23:48:23 +0000353
354 // Bypass motion profiles while we are zeroing.
355 // This is also an important step right after the elevator is zeroed and
356 // we reach into the elevator's state matrix and change it based on the
357 // newly-obtained offset.
358 {
359 Eigen::Matrix<double, 2, 1> current;
360 current.setZero();
Philipp Schrader085bf012015-03-15 01:43:11 +0000361 current << elevator_goal_, elevator_goal_velocity_;
Philipp Schrader3e281412015-03-01 23:48:23 +0000362 elevator_profile_.MoveCurrentState(current);
363 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800364 break;
365
366 case ZEROING_ARM:
367 LOG(DEBUG, "Zeroing the arm\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800368
Austin Schuh34aad422015-03-28 14:23:31 -0700369 if (elevator() < values.fridge.arm_zeroing_height - 0.10 ||
370 elevator_goal_ < values.fridge.arm_zeroing_height) {
Brian Silverman283849f2015-03-28 01:25:16 -0400371 LOG(INFO,
372 "Going back to ZEROING_ELEVATOR until it gets high enough to "
373 "safely zero the arm\n");
374 state_ = ZEROING_ELEVATOR;
375 break;
376 }
377
Austin Schuh703b8d42015-02-01 14:56:34 -0800378 // Update state_ to accurately represent the state of the zeroing
379 // estimators.
380 UpdateZeroingState();
381 if (left_arm_estimator_.zeroed() && right_arm_estimator_.zeroed()) {
382 SetArmOffset(left_arm_estimator_.offset(),
383 right_arm_estimator_.offset());
384 LOG(DEBUG, "Zeroed the arm!\n");
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800385 } else if (!disable) {
Philipp Schrader085bf012015-03-15 01:43:11 +0000386 arm_goal_velocity_ = arm_zeroing_velocity();
Austin Schuh214e9c12016-11-25 17:26:20 -0800387 arm_goal_ += arm_goal_velocity_ *
388 chrono::duration_cast<chrono::duration<double>>(
389 ::aos::controls::kLoopFrequency).count();
Austin Schuh703b8d42015-02-01 14:56:34 -0800390 }
Philipp Schrader3e281412015-03-01 23:48:23 +0000391
392 // Bypass motion profiles while we are zeroing.
393 // This is also an important step right after the arm is zeroed and
394 // we reach into the arm's state matrix and change it based on the
395 // newly-obtained offset.
396 {
397 Eigen::Matrix<double, 2, 1> current;
398 current.setZero();
Philipp Schrader085bf012015-03-15 01:43:11 +0000399 current << arm_goal_, arm_goal_velocity_;
Philipp Schrader3e281412015-03-01 23:48:23 +0000400 arm_profile_.MoveCurrentState(current);
401 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800402 break;
403
404 case RUNNING:
405 LOG(DEBUG, "Running!\n");
Austin Schuh482a4e12015-02-14 22:43:43 -0800406 if (unsafe_goal) {
Philipp Schrader085bf012015-03-15 01:43:11 +0000407 // Handle the case where we switch between the types of profiling.
408 ProfilingType new_profiling_type =
409 static_cast<ProfilingType>(unsafe_goal->profiling_type);
Philipp Schrader3e281412015-03-01 23:48:23 +0000410
Philipp Schrader085bf012015-03-15 01:43:11 +0000411 if (last_profiling_type_ != new_profiling_type) {
412 // Reset the height/angle profiles.
413 Eigen::Matrix<double, 2, 1> current;
414 current.setZero();
415 current << arm_goal_, arm_goal_velocity_;
416 arm_profile_.MoveCurrentState(current);
417 current << elevator_goal_, elevator_goal_velocity_;
418 elevator_profile_.MoveCurrentState(current);
Philipp Schrader3e281412015-03-01 23:48:23 +0000419
Philipp Schrader085bf012015-03-15 01:43:11 +0000420 // Reset the x/y profiles.
421 aos::util::ElevatorArmKinematics::KinematicResult x_y_result;
422 kinematics_.ForwardKinematic(elevator_goal_, arm_goal_,
423 elevator_goal_velocity_,
424 arm_goal_velocity_, &x_y_result);
425 current << x_y_result.fridge_x, x_y_result.fridge_x_velocity;
426 x_profile_.MoveCurrentState(current);
427 current << x_y_result.fridge_h, x_y_result.fridge_h_velocity;
428 y_profile_.MoveCurrentState(current);
429
430 last_profiling_type_ = new_profiling_type;
431 }
432
433 if (new_profiling_type == ProfilingType::ANGLE_HEIGHT_PROFILING) {
434 // Pick a set of sane arm defaults if none are specified.
435 arm_profile_.set_maximum_velocity(
436 UseUnlessZero(unsafe_goal->max_angular_velocity, 1.0));
437 arm_profile_.set_maximum_acceleration(
438 UseUnlessZero(unsafe_goal->max_angular_acceleration, 3.0));
439 elevator_profile_.set_maximum_velocity(
440 UseUnlessZero(unsafe_goal->max_velocity, 0.50));
441 elevator_profile_.set_maximum_acceleration(
442 UseUnlessZero(unsafe_goal->max_acceleration, 2.0));
443
444 // Use the profiles to limit the arm's movements.
445 const double unfiltered_arm_goal = ::std::max(
446 ::std::min(unsafe_goal->angle, values.fridge.arm.upper_limit),
447 values.fridge.arm.lower_limit);
448 ::Eigen::Matrix<double, 2, 1> arm_goal_state = arm_profile_.Update(
449 unfiltered_arm_goal, unsafe_goal->angular_velocity);
450 arm_goal_ = arm_goal_state(0, 0);
451 arm_goal_velocity_ = arm_goal_state(1, 0);
452
453 // Use the profiles to limit the elevator's movements.
454 const double unfiltered_elevator_goal =
455 ::std::max(::std::min(unsafe_goal->height,
456 values.fridge.elevator.upper_limit),
457 values.fridge.elevator.lower_limit);
458 ::Eigen::Matrix<double, 2, 1> elevator_goal_state =
459 elevator_profile_.Update(unfiltered_elevator_goal,
460 unsafe_goal->velocity);
461 elevator_goal_ = elevator_goal_state(0, 0);
462 elevator_goal_velocity_ = elevator_goal_state(1, 0);
463 } else if (new_profiling_type == ProfilingType::X_Y_PROFILING) {
464 // Use x/y profiling
465 aos::util::ElevatorArmKinematics::KinematicResult kinematic_result;
466
467 x_profile_.set_maximum_velocity(
468 UseUnlessZero(unsafe_goal->max_x_velocity, 0.5));
469 x_profile_.set_maximum_acceleration(
470 UseUnlessZero(unsafe_goal->max_x_acceleration, 2.0));
471 y_profile_.set_maximum_velocity(
472 UseUnlessZero(unsafe_goal->max_y_velocity, 0.50));
473 y_profile_.set_maximum_acceleration(
474 UseUnlessZero(unsafe_goal->max_y_acceleration, 2.0));
475
476 // Limit the goals before we update the profiles.
477 kinematics_.InverseKinematic(
478 unsafe_goal->x, unsafe_goal->y, unsafe_goal->x_velocity,
479 unsafe_goal->y_velocity, &kinematic_result);
480
481 // Use the profiles to limit the x movements.
482 ::Eigen::Matrix<double, 2, 1> x_goal_state = x_profile_.Update(
483 kinematic_result.fridge_x, kinematic_result.fridge_x_velocity);
484
485 // Use the profiles to limit the y movements.
486 ::Eigen::Matrix<double, 2, 1> y_goal_state = y_profile_.Update(
487 kinematic_result.fridge_h, kinematic_result.fridge_h_velocity);
488
489 // Convert x/y goal states into arm/elevator goals.
490 // The inverse kinematics functions automatically perform range
491 // checking and adjust the results so that they're always valid.
492 kinematics_.InverseKinematic(x_goal_state(0, 0), y_goal_state(0, 0),
493 x_goal_state(1, 0), y_goal_state(1, 0),
494 &kinematic_result);
495
496 // Store the appropriate inverse kinematic results in the
497 // arm/elevator goals.
498 arm_goal_ = kinematic_result.arm_angle;
499 arm_goal_velocity_ = kinematic_result.arm_velocity;
500
501 elevator_goal_ = kinematic_result.elevator_height;
502 elevator_goal_velocity_ = kinematic_result.arm_velocity;
503 } else {
504 LOG(ERROR, "Unknown profiling_type: %d\n",
505 unsafe_goal->profiling_type);
506 }
Austin Schuh482a4e12015-02-14 22:43:43 -0800507 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800508
509 // Update state_ to accurately represent the state of the zeroing
510 // estimators.
511 UpdateZeroingState();
Austin Schuh703b8d42015-02-01 14:56:34 -0800512
513 if (state_ != RUNNING && state_ != ESTOP) {
514 state_ = UNINITIALIZED;
515 }
516 break;
517
518 case ESTOP:
519 LOG(ERROR, "Estop\n");
520 disable = true;
521 break;
522 }
523
524 // Commence death if either left/right tracking error gets too big. This
525 // should run immediately after the SetArmOffset and SetElevatorOffset
526 // functions to double-check that the hardware is in a sane state.
527 if (::std::abs(left_arm() - right_arm()) >=
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800528 values.max_allowed_left_right_arm_difference) {
529 LOG(ERROR, "The arms are too far apart. |%f - %f| > %f\n", left_arm(),
530 right_arm(), values.max_allowed_left_right_arm_difference);
531
532 // Indicate an ESTOP condition and stop the motors.
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700533 if (output) {
534 state_ = ESTOP;
535 }
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800536 disable = true;
537 }
538
539 if (::std::abs(left_elevator() - right_elevator()) >=
540 values.max_allowed_left_right_elevator_difference) {
541 LOG(ERROR, "The elevators are too far apart. |%f - %f| > %f\n",
542 left_elevator(), right_elevator(),
543 values.max_allowed_left_right_elevator_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800544
545 // Indicate an ESTOP condition and stop the motors.
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700546 if (output) {
547 state_ = ESTOP;
548 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800549 disable = true;
550 }
551
552 // Limit the goals so we can't exceed the hardware limits if we are RUNNING.
553 if (state_ == RUNNING) {
554 // Limit the arm goal to min/max allowable angles.
555 if (arm_goal_ >= values.fridge.arm.upper_limit) {
556 LOG(WARNING, "Arm goal above limit, %f > %f\n", arm_goal_,
557 values.fridge.arm.upper_limit);
558 arm_goal_ = values.fridge.arm.upper_limit;
559 }
560 if (arm_goal_ <= values.fridge.arm.lower_limit) {
561 LOG(WARNING, "Arm goal below limit, %f < %f\n", arm_goal_,
562 values.fridge.arm.lower_limit);
563 arm_goal_ = values.fridge.arm.lower_limit;
564 }
565
566 // Limit the elevator goal to min/max allowable heights.
567 if (elevator_goal_ >= values.fridge.elevator.upper_limit) {
568 LOG(WARNING, "Elevator goal above limit, %f > %f\n", elevator_goal_,
569 values.fridge.elevator.upper_limit);
570 elevator_goal_ = values.fridge.elevator.upper_limit;
571 }
572 if (elevator_goal_ <= values.fridge.elevator.lower_limit) {
573 LOG(WARNING, "Elevator goal below limit, %f < %f\n", elevator_goal_,
574 values.fridge.elevator.lower_limit);
575 elevator_goal_ = values.fridge.elevator.lower_limit;
576 }
577 }
578
579 // Check the lower level hardware limit as well.
580 if (state_ == RUNNING) {
581 if (left_arm() >= values.fridge.arm.upper_hard_limit ||
582 left_arm() <= values.fridge.arm.lower_hard_limit) {
583 LOG(ERROR, "Left arm at %f out of bounds [%f, %f], ESTOPing\n",
584 left_arm(), values.fridge.arm.lower_hard_limit,
585 values.fridge.arm.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700586 if (output) {
587 state_ = ESTOP;
588 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800589 }
590
591 if (right_arm() >= values.fridge.arm.upper_hard_limit ||
592 right_arm() <= values.fridge.arm.lower_hard_limit) {
593 LOG(ERROR, "Right arm at %f out of bounds [%f, %f], ESTOPing\n",
594 right_arm(), values.fridge.arm.lower_hard_limit,
595 values.fridge.arm.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700596 if (output) {
597 state_ = ESTOP;
598 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800599 }
600
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700601 if (left_elevator() >= values.fridge.elevator.upper_hard_limit) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800602 LOG(ERROR, "Left elevator at %f out of bounds [%f, %f], ESTOPing\n",
603 left_elevator(), values.fridge.elevator.lower_hard_limit,
604 values.fridge.elevator.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700605 if (output) {
606 state_ = ESTOP;
607 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800608 }
609
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700610 if (right_elevator() >= values.fridge.elevator.upper_hard_limit) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800611 LOG(ERROR, "Right elevator at %f out of bounds [%f, %f], ESTOPing\n",
612 right_elevator(), values.fridge.elevator.lower_hard_limit,
613 values.fridge.elevator.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700614 if (output) {
615 state_ = ESTOP;
616 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800617 }
618 }
619
620 // Set the goals.
Philipp Schrader085bf012015-03-15 01:43:11 +0000621 arm_loop_->mutable_R() << arm_goal_, arm_goal_velocity_, 0.0, 0.0, 0.0;
622 elevator_loop_->mutable_R() << elevator_goal_, elevator_goal_velocity_, 0.0,
Austin Schuh703b8d42015-02-01 14:56:34 -0800623 0.0;
624
625 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
626 arm_loop_->set_max_voltage(max_voltage);
627 elevator_loop_->set_max_voltage(max_voltage);
628
629 if (state_ == ESTOP) {
630 disable = true;
631 }
632 arm_loop_->Update(disable);
633 elevator_loop_->Update(disable);
634
635 if (state_ == INITIALIZING || state_ == ZEROING_ELEVATOR ||
636 state_ == ZEROING_ARM) {
637 if (arm_loop_->U() != arm_loop_->U_uncapped()) {
638 Eigen::Matrix<double, 2, 1> deltaR =
639 arm_loop_->UnsaturateOutputGoalChange();
640
641 // Move the average arm goal by the amount observed.
642 LOG(WARNING, "Moving arm goal by %f to handle saturation\n",
643 deltaR(0, 0));
644 arm_goal_ += deltaR(0, 0);
645 }
646
647 if (elevator_loop_->U() != elevator_loop_->U_uncapped()) {
648 Eigen::Matrix<double, 2, 1> deltaR =
649 elevator_loop_->UnsaturateOutputGoalChange();
650
651 // Move the average elevator goal by the amount observed.
652 LOG(WARNING, "Moving elevator goal by %f to handle saturation\n",
653 deltaR(0, 0));
654 elevator_goal_ += deltaR(0, 0);
655 }
656 }
657
658 if (output) {
659 output->left_arm = arm_loop_->U(0, 0);
660 output->right_arm = arm_loop_->U(1, 0);
661 output->left_elevator = elevator_loop_->U(0, 0);
662 output->right_elevator = elevator_loop_->U(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800663 if (unsafe_goal) {
664 output->grabbers = unsafe_goal->grabbers;
665 } else {
666 output->grabbers.top_front = false;
667 output->grabbers.top_back = false;
668 output->grabbers.bottom_front = false;
669 output->grabbers.bottom_back = false;
670 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800671 }
672
673 // TODO(austin): Populate these fully.
Austin Schuh5ae4efd2015-02-15 23:34:22 -0800674 status->zeroed = state_ == RUNNING;
Philipp Schrader2a25b612015-03-28 23:49:06 +0000675
Austin Schuh703b8d42015-02-01 14:56:34 -0800676 status->angle = arm_loop_->X_hat(0, 0);
Philipp Schrader3e281412015-03-01 23:48:23 +0000677 status->angular_velocity = arm_loop_->X_hat(1, 0);
Austin Schuh703b8d42015-02-01 14:56:34 -0800678 status->height = elevator_loop_->X_hat(0, 0);
Philipp Schrader3e281412015-03-01 23:48:23 +0000679 status->velocity = elevator_loop_->X_hat(1, 0);
Philipp Schrader2a25b612015-03-28 23:49:06 +0000680
Austin Schuh5e872c82015-02-17 22:59:08 -0800681 status->goal_angle = arm_goal_;
Philipp Schrader085bf012015-03-15 01:43:11 +0000682 status->goal_angular_velocity = arm_goal_velocity_;
Austin Schuh5e872c82015-02-17 22:59:08 -0800683 status->goal_height = elevator_goal_;
Philipp Schrader085bf012015-03-15 01:43:11 +0000684 status->goal_velocity = elevator_goal_velocity_;
Philipp Schrader2a25b612015-03-28 23:49:06 +0000685
686 // Populate the same status, but in X/Y co-ordinates.
687 aos::util::ElevatorArmKinematics::KinematicResult x_y_status;
688 kinematics_.ForwardKinematic(status->height, status->angle,
689 status->velocity, status->angular_velocity,
690 &x_y_status);
691 status->x = x_y_status.fridge_x;
692 status->y = x_y_status.fridge_h;
693 status->x_velocity = x_y_status.fridge_x_velocity;
694 status->y_velocity = x_y_status.fridge_h_velocity;
695
696 kinematics_.ForwardKinematic(status->goal_height, status->goal_angle,
697 status->goal_velocity, status->goal_angular_velocity,
698 &x_y_status);
699 status->goal_x = x_y_status.fridge_x;
700 status->goal_y = x_y_status.fridge_h;
701 status->goal_x_velocity = x_y_status.fridge_x_velocity;
702 status->goal_y_velocity = x_y_status.fridge_h_velocity;
703
Austin Schuh482a4e12015-02-14 22:43:43 -0800704 if (unsafe_goal) {
705 status->grabbers = unsafe_goal->grabbers;
706 } else {
707 status->grabbers.top_front = false;
708 status->grabbers.top_back = false;
709 status->grabbers.bottom_front = false;
710 status->grabbers.bottom_back = false;
711 }
Adam Snaider3cd11c52015-02-16 02:16:09 +0000712 zeroing::PopulateEstimatorState(left_arm_estimator_, &status->left_arm_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800713 zeroing::PopulateEstimatorState(right_arm_estimator_,
714 &status->right_arm_state);
715 zeroing::PopulateEstimatorState(left_elevator_estimator_,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000716 &status->left_elevator_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800717 zeroing::PopulateEstimatorState(right_elevator_estimator_,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000718 &status->right_elevator_state);
Austin Schuh703b8d42015-02-01 14:56:34 -0800719 status->estopped = (state_ == ESTOP);
Austin Schuh482a4e12015-02-14 22:43:43 -0800720 status->state = state_;
Austin Schuh703b8d42015-02-01 14:56:34 -0800721 last_state_ = state_;
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800722}
723
724} // namespace control_loops
725} // namespace frc971