blob: 083baf23b4a70a7dfae4abb1c84e67ac6c06544c [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 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),
Philipp Schrader085bf012015-03-15 01:43:11 +000061 last_profiling_type_(ProfilingType::ANGLE_HEIGHT_PROFILING),
62 kinematics_(constants::GetValues().fridge.arm_length,
63 constants::GetValues().fridge.elevator.upper_limit,
64 constants::GetValues().fridge.elevator.lower_limit,
65 constants::GetValues().fridge.arm.upper_limit,
66 constants::GetValues().fridge.arm.lower_limit),
Philipp Schrader3e281412015-03-01 23:48:23 +000067 arm_profile_(::aos::controls::kLoopFrequency),
Philipp Schrader085bf012015-03-15 01:43:11 +000068 elevator_profile_(::aos::controls::kLoopFrequency),
69 x_profile_(::aos::controls::kLoopFrequency),
70 y_profile_(::aos::controls::kLoopFrequency) {}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080071
Austin Schuh703b8d42015-02-01 14:56:34 -080072void Fridge::UpdateZeroingState() {
Austin Schuh47bf6902015-02-14 22:46:22 -080073 if (left_elevator_estimator_.offset_ratio_ready() < 1.0 ||
74 right_elevator_estimator_.offset_ratio_ready() < 1.0 ||
75 left_arm_estimator_.offset_ratio_ready() < 1.0 ||
76 right_arm_estimator_.offset_ratio_ready() < 1.0) {
Austin Schuh703b8d42015-02-01 14:56:34 -080077 state_ = INITIALIZING;
78 } else if (!left_elevator_estimator_.zeroed() ||
Adam Snaider3cd11c52015-02-16 02:16:09 +000079 !right_elevator_estimator_.zeroed()) {
Austin Schuh703b8d42015-02-01 14:56:34 -080080 state_ = ZEROING_ELEVATOR;
Adam Snaider3cd11c52015-02-16 02:16:09 +000081 } else if (!left_arm_estimator_.zeroed() || !right_arm_estimator_.zeroed()) {
Austin Schuh703b8d42015-02-01 14:56:34 -080082 state_ = ZEROING_ARM;
83 } else {
84 state_ = RUNNING;
85 }
86}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080087
Austin Schuh703b8d42015-02-01 14:56:34 -080088void Fridge::Correct() {
89 {
90 Eigen::Matrix<double, 2, 1> Y;
91 Y << left_elevator(), right_elevator();
92 elevator_loop_->Correct(Y);
93 }
94
95 {
96 Eigen::Matrix<double, 2, 1> Y;
97 Y << left_arm(), right_arm();
98 arm_loop_->Correct(Y);
99 }
100}
101
102void Fridge::SetElevatorOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800103 LOG(INFO, "Changing Elevator offset from %f, %f to %f, %f\n",
104 left_elevator_offset_, right_elevator_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -0800105 double left_doffset = left_offset - left_elevator_offset_;
106 double right_doffset = right_offset - right_elevator_offset_;
107
108 // Adjust the average height and height difference between the two sides.
109 // The derivatives of both should not need to be updated since the speeds
110 // haven't changed.
111 // The height difference is calculated as left - right, not right - left.
112 elevator_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
113 elevator_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
114
115 // Modify the zeroing goal.
116 elevator_goal_ += (left_doffset + right_doffset) / 2;
117
118 // Update the cached offset values to the actual values.
119 left_elevator_offset_ = left_offset;
120 right_elevator_offset_ = right_offset;
121}
122
123void Fridge::SetArmOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800124 LOG(INFO, "Changing Arm offset from %f, %f to %f, %f\n", left_arm_offset_,
125 right_arm_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -0800126 double left_doffset = left_offset - left_arm_offset_;
127 double right_doffset = right_offset - right_arm_offset_;
128
129 // Adjust the average angle and angle difference between the two sides.
130 // The derivatives of both should not need to be updated since the speeds
131 // haven't changed.
132 arm_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
133 arm_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
134
135 // Modify the zeroing goal.
136 arm_goal_ += (left_doffset + right_doffset) / 2;
137
138 // Update the cached offset values to the actual values.
139 left_arm_offset_ = left_offset;
140 right_arm_offset_ = right_offset;
141}
142
143double Fridge::estimated_left_elevator() {
144 return current_position_.elevator.left.encoder +
145 left_elevator_estimator_.offset();
146}
147double Fridge::estimated_right_elevator() {
148 return current_position_.elevator.right.encoder +
149 right_elevator_estimator_.offset();
150}
151
152double Fridge::estimated_elevator() {
153 return (estimated_left_elevator() + estimated_right_elevator()) / 2.0;
154}
155
156double Fridge::estimated_left_arm() {
Austin Schuhb3ae2592015-03-15 00:33:38 -0700157 return current_position_.arm.left.encoder + left_arm_estimator_.offset();
Austin Schuh703b8d42015-02-01 14:56:34 -0800158}
159double Fridge::estimated_right_arm() {
Austin Schuhb3ae2592015-03-15 00:33:38 -0700160 return current_position_.arm.right.encoder + right_arm_estimator_.offset();
Austin Schuh703b8d42015-02-01 14:56:34 -0800161}
162double Fridge::estimated_arm() {
163 return (estimated_left_arm() + estimated_right_arm()) / 2.0;
164}
165
166double Fridge::left_elevator() {
167 return current_position_.elevator.left.encoder + left_elevator_offset_;
168}
169double Fridge::right_elevator() {
170 return current_position_.elevator.right.encoder + right_elevator_offset_;
171}
172
173double Fridge::elevator() { return (left_elevator() + right_elevator()) / 2.0; }
174
175double Fridge::left_arm() {
176 return current_position_.arm.left.encoder + left_arm_offset_;
177}
178double Fridge::right_arm() {
179 return current_position_.arm.right.encoder + right_arm_offset_;
180}
181double Fridge::arm() { return (left_arm() + right_arm()) / 2.0; }
182
183double Fridge::elevator_zeroing_velocity() {
184 double average_elevator =
185 (constants::GetValues().fridge.elevator.lower_limit +
186 constants::GetValues().fridge.elevator.upper_limit) /
187 2.0;
188
189 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800190 constants::GetValues().fridge.left_elev_zeroing.index_difference,
191 constants::GetValues().fridge.right_elev_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800192
193 if (elevator_zeroing_velocity_ == 0) {
194 if (estimated_elevator() > average_elevator) {
195 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
196 } else {
197 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
198 }
199 } else if (elevator_zeroing_velocity_ > 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800200 estimated_elevator() > average_elevator + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800201 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
202 } else if (elevator_zeroing_velocity_ < 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800203 estimated_elevator() < average_elevator - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800204 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
205 }
206 return elevator_zeroing_velocity_;
207}
208
Philipp Schrader3e281412015-03-01 23:48:23 +0000209double Fridge::UseUnlessZero(double target_value, double default_value) {
210 if (target_value != 0.0) {
211 return target_value;
212 } else {
213 return default_value;
214 }
215}
216
Austin Schuh703b8d42015-02-01 14:56:34 -0800217double Fridge::arm_zeroing_velocity() {
218 const double average_arm = (constants::GetValues().fridge.arm.lower_limit +
219 constants::GetValues().fridge.arm.upper_limit) /
220 2.0;
221 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800222 constants::GetValues().fridge.right_arm_zeroing.index_difference,
223 constants::GetValues().fridge.left_arm_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800224
225 if (arm_zeroing_velocity_ == 0) {
226 if (estimated_arm() > average_arm) {
227 arm_zeroing_velocity_ = -kArmZeroingVelocity;
228 } else {
229 arm_zeroing_velocity_ = kArmZeroingVelocity;
230 }
231 } else if (arm_zeroing_velocity_ > 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800232 estimated_arm() > average_arm + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800233 arm_zeroing_velocity_ = -kArmZeroingVelocity;
Austin Schuhb3ae2592015-03-15 00:33:38 -0700234 } else if (arm_zeroing_velocity_ < 0.0 && estimated_arm() < average_arm) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800235 arm_zeroing_velocity_ = kArmZeroingVelocity;
236 }
237 return arm_zeroing_velocity_;
238}
239
Austin Schuh482a4e12015-02-14 22:43:43 -0800240void Fridge::RunIteration(const control_loops::FridgeQueue::Goal *unsafe_goal,
Austin Schuh703b8d42015-02-01 14:56:34 -0800241 const control_loops::FridgeQueue::Position *position,
242 control_loops::FridgeQueue::Output *output,
243 control_loops::FridgeQueue::Status *status) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800244 if (WasReset()) {
245 LOG(ERROR, "WPILib reset, restarting\n");
246 left_elevator_estimator_.Reset();
247 right_elevator_estimator_.Reset();
248 left_arm_estimator_.Reset();
249 right_arm_estimator_.Reset();
250 state_ = UNINITIALIZED;
251 }
252
Austin Schuh703b8d42015-02-01 14:56:34 -0800253 // Get a reference to the constants struct since we use it so often in this
254 // code.
Philipp Schrader82c65072015-02-16 00:47:09 +0000255 const auto &values = constants::GetValues();
Austin Schuh703b8d42015-02-01 14:56:34 -0800256
257 // Bool to track if we should turn the motors on or not.
258 bool disable = output == nullptr;
Austin Schuh703b8d42015-02-01 14:56:34 -0800259
260 // Save the current position so it can be used easily in the class.
261 current_position_ = *position;
262
263 left_elevator_estimator_.UpdateEstimate(position->elevator.left);
264 right_elevator_estimator_.UpdateEstimate(position->elevator.right);
265 left_arm_estimator_.UpdateEstimate(position->arm.left);
266 right_arm_estimator_.UpdateEstimate(position->arm.right);
267
268 if (state_ != UNINITIALIZED) {
269 Correct();
270 }
271
272 // Zeroing will work as follows:
273 // At startup, record the offset of the two halves of the two subsystems.
274 // Then, start moving the elevator towards the center until both halves are
275 // zeroed.
276 // Then, start moving the claw towards the center until both halves are
277 // zeroed.
278 // Then, done!
279
280 // We'll then need code to do sanity checking on values.
281
282 // Now, we need to figure out which way to go.
283
284 switch (state_) {
285 case UNINITIALIZED:
286 LOG(DEBUG, "Uninitialized\n");
287 // Startup. Assume that we are at the origin everywhere.
288 // This records the encoder offset between the two sides of the elevator.
289 left_elevator_offset_ = -position->elevator.left.encoder;
290 right_elevator_offset_ = -position->elevator.right.encoder;
291 left_arm_offset_ = -position->arm.left.encoder;
292 right_arm_offset_ = -position->arm.right.encoder;
Austin Schuhaab01572015-02-15 00:12:35 -0800293 elevator_loop_->mutable_X_hat().setZero();
294 arm_loop_->mutable_X_hat().setZero();
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800295 LOG(INFO, "Initializing arm offsets to %f, %f\n", left_arm_offset_,
296 right_arm_offset_);
297 LOG(INFO, "Initializing elevator offsets to %f, %f\n",
298 left_elevator_offset_, right_elevator_offset_);
Austin Schuh703b8d42015-02-01 14:56:34 -0800299 Correct();
300 state_ = INITIALIZING;
301 disable = true;
302 break;
303
304 case INITIALIZING:
305 LOG(DEBUG, "Waiting for accurate initial position.\n");
306 disable = true;
307 // Update state_ to accurately represent the state of the zeroing
308 // estimators.
309 UpdateZeroingState();
310 if (state_ != INITIALIZING) {
311 // Set the goals to where we are now.
312 elevator_goal_ = elevator();
313 arm_goal_ = arm();
314 }
315 break;
316
317 case ZEROING_ELEVATOR:
318 LOG(DEBUG, "Zeroing elevator\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800319
320 // Update state_ to accurately represent the state of the zeroing
321 // estimators.
322 UpdateZeroingState();
323 if (left_elevator_estimator_.zeroed() &&
324 right_elevator_estimator_.zeroed()) {
325 SetElevatorOffset(left_elevator_estimator_.offset(),
326 right_elevator_estimator_.offset());
327 LOG(DEBUG, "Zeroed the elevator!\n");
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800328
Austin Schuh5e872c82015-02-17 22:59:08 -0800329 if (elevator() < values.fridge.arm_zeroing_height &&
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800330 state_ != INITIALIZING) {
331 // Move the elevator to a safe height before we start zeroing the arm,
332 // so that we don't crash anything.
333 LOG(DEBUG, "Moving elevator to safe height.\n");
Brian Silverman283849f2015-03-28 01:25:16 -0400334 if (elevator_goal_ < values.fridge.arm_zeroing_height) {
335 elevator_goal_ += kElevatorSafeHeightVelocity *
336 ::aos::controls::kLoopFrequency.ToSeconds();
337 elevator_goal_velocity_ = kElevatorSafeHeightVelocity;
Austin Schuh34aad422015-03-28 14:23:31 -0700338 state_ = ZEROING_ELEVATOR;
Brian Silverman283849f2015-03-28 01:25:16 -0400339 } else {
340 // We want it stopped at whatever height it's currently set to.
341 elevator_goal_velocity_ = 0;
342 }
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800343 }
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800344 } else if (!disable) {
Philipp Schrader085bf012015-03-15 01:43:11 +0000345 elevator_goal_velocity_ = elevator_zeroing_velocity();
346 elevator_goal_ += elevator_goal_velocity_ *
Adam Snaider3cd11c52015-02-16 02:16:09 +0000347 ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800348 }
Philipp Schrader3e281412015-03-01 23:48:23 +0000349
350 // Bypass motion profiles while we are zeroing.
351 // This is also an important step right after the elevator is zeroed and
352 // we reach into the elevator's state matrix and change it based on the
353 // newly-obtained offset.
354 {
355 Eigen::Matrix<double, 2, 1> current;
356 current.setZero();
Philipp Schrader085bf012015-03-15 01:43:11 +0000357 current << elevator_goal_, elevator_goal_velocity_;
Philipp Schrader3e281412015-03-01 23:48:23 +0000358 elevator_profile_.MoveCurrentState(current);
359 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800360 break;
361
362 case ZEROING_ARM:
363 LOG(DEBUG, "Zeroing the arm\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800364
Austin Schuh34aad422015-03-28 14:23:31 -0700365 if (elevator() < values.fridge.arm_zeroing_height - 0.10 ||
366 elevator_goal_ < values.fridge.arm_zeroing_height) {
Brian Silverman283849f2015-03-28 01:25:16 -0400367 LOG(INFO,
368 "Going back to ZEROING_ELEVATOR until it gets high enough to "
369 "safely zero the arm\n");
370 state_ = ZEROING_ELEVATOR;
371 break;
372 }
373
Austin Schuh703b8d42015-02-01 14:56:34 -0800374 // Update state_ to accurately represent the state of the zeroing
375 // estimators.
376 UpdateZeroingState();
377 if (left_arm_estimator_.zeroed() && right_arm_estimator_.zeroed()) {
378 SetArmOffset(left_arm_estimator_.offset(),
379 right_arm_estimator_.offset());
380 LOG(DEBUG, "Zeroed the arm!\n");
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800381 } else if (!disable) {
Philipp Schrader085bf012015-03-15 01:43:11 +0000382 arm_goal_velocity_ = arm_zeroing_velocity();
Adam Snaider3cd11c52015-02-16 02:16:09 +0000383 arm_goal_ +=
Philipp Schrader085bf012015-03-15 01:43:11 +0000384 arm_goal_velocity_ * ::aos::controls::kLoopFrequency.ToSeconds();
Austin Schuh703b8d42015-02-01 14:56:34 -0800385 }
Philipp Schrader3e281412015-03-01 23:48:23 +0000386
387 // Bypass motion profiles while we are zeroing.
388 // This is also an important step right after the arm is zeroed and
389 // we reach into the arm's state matrix and change it based on the
390 // newly-obtained offset.
391 {
392 Eigen::Matrix<double, 2, 1> current;
393 current.setZero();
Philipp Schrader085bf012015-03-15 01:43:11 +0000394 current << arm_goal_, arm_goal_velocity_;
Philipp Schrader3e281412015-03-01 23:48:23 +0000395 arm_profile_.MoveCurrentState(current);
396 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800397 break;
398
399 case RUNNING:
400 LOG(DEBUG, "Running!\n");
Austin Schuh482a4e12015-02-14 22:43:43 -0800401 if (unsafe_goal) {
Philipp Schrader085bf012015-03-15 01:43:11 +0000402 // Handle the case where we switch between the types of profiling.
403 ProfilingType new_profiling_type =
404 static_cast<ProfilingType>(unsafe_goal->profiling_type);
Philipp Schrader3e281412015-03-01 23:48:23 +0000405
Philipp Schrader085bf012015-03-15 01:43:11 +0000406 if (last_profiling_type_ != new_profiling_type) {
407 // Reset the height/angle profiles.
408 Eigen::Matrix<double, 2, 1> current;
409 current.setZero();
410 current << arm_goal_, arm_goal_velocity_;
411 arm_profile_.MoveCurrentState(current);
412 current << elevator_goal_, elevator_goal_velocity_;
413 elevator_profile_.MoveCurrentState(current);
Philipp Schrader3e281412015-03-01 23:48:23 +0000414
Philipp Schrader085bf012015-03-15 01:43:11 +0000415 // Reset the x/y profiles.
416 aos::util::ElevatorArmKinematics::KinematicResult x_y_result;
417 kinematics_.ForwardKinematic(elevator_goal_, arm_goal_,
418 elevator_goal_velocity_,
419 arm_goal_velocity_, &x_y_result);
420 current << x_y_result.fridge_x, x_y_result.fridge_x_velocity;
421 x_profile_.MoveCurrentState(current);
422 current << x_y_result.fridge_h, x_y_result.fridge_h_velocity;
423 y_profile_.MoveCurrentState(current);
424
425 last_profiling_type_ = new_profiling_type;
426 }
427
428 if (new_profiling_type == ProfilingType::ANGLE_HEIGHT_PROFILING) {
429 // Pick a set of sane arm defaults if none are specified.
430 arm_profile_.set_maximum_velocity(
431 UseUnlessZero(unsafe_goal->max_angular_velocity, 1.0));
432 arm_profile_.set_maximum_acceleration(
433 UseUnlessZero(unsafe_goal->max_angular_acceleration, 3.0));
434 elevator_profile_.set_maximum_velocity(
435 UseUnlessZero(unsafe_goal->max_velocity, 0.50));
436 elevator_profile_.set_maximum_acceleration(
437 UseUnlessZero(unsafe_goal->max_acceleration, 2.0));
438
439 // Use the profiles to limit the arm's movements.
440 const double unfiltered_arm_goal = ::std::max(
441 ::std::min(unsafe_goal->angle, values.fridge.arm.upper_limit),
442 values.fridge.arm.lower_limit);
443 ::Eigen::Matrix<double, 2, 1> arm_goal_state = arm_profile_.Update(
444 unfiltered_arm_goal, unsafe_goal->angular_velocity);
445 arm_goal_ = arm_goal_state(0, 0);
446 arm_goal_velocity_ = arm_goal_state(1, 0);
447
448 // Use the profiles to limit the elevator's movements.
449 const double unfiltered_elevator_goal =
450 ::std::max(::std::min(unsafe_goal->height,
451 values.fridge.elevator.upper_limit),
452 values.fridge.elevator.lower_limit);
453 ::Eigen::Matrix<double, 2, 1> elevator_goal_state =
454 elevator_profile_.Update(unfiltered_elevator_goal,
455 unsafe_goal->velocity);
456 elevator_goal_ = elevator_goal_state(0, 0);
457 elevator_goal_velocity_ = elevator_goal_state(1, 0);
458 } else if (new_profiling_type == ProfilingType::X_Y_PROFILING) {
459 // Use x/y profiling
460 aos::util::ElevatorArmKinematics::KinematicResult kinematic_result;
461
462 x_profile_.set_maximum_velocity(
463 UseUnlessZero(unsafe_goal->max_x_velocity, 0.5));
464 x_profile_.set_maximum_acceleration(
465 UseUnlessZero(unsafe_goal->max_x_acceleration, 2.0));
466 y_profile_.set_maximum_velocity(
467 UseUnlessZero(unsafe_goal->max_y_velocity, 0.50));
468 y_profile_.set_maximum_acceleration(
469 UseUnlessZero(unsafe_goal->max_y_acceleration, 2.0));
470
471 // Limit the goals before we update the profiles.
472 kinematics_.InverseKinematic(
473 unsafe_goal->x, unsafe_goal->y, unsafe_goal->x_velocity,
474 unsafe_goal->y_velocity, &kinematic_result);
475
476 // Use the profiles to limit the x movements.
477 ::Eigen::Matrix<double, 2, 1> x_goal_state = x_profile_.Update(
478 kinematic_result.fridge_x, kinematic_result.fridge_x_velocity);
479
480 // Use the profiles to limit the y movements.
481 ::Eigen::Matrix<double, 2, 1> y_goal_state = y_profile_.Update(
482 kinematic_result.fridge_h, kinematic_result.fridge_h_velocity);
483
484 // Convert x/y goal states into arm/elevator goals.
485 // The inverse kinematics functions automatically perform range
486 // checking and adjust the results so that they're always valid.
487 kinematics_.InverseKinematic(x_goal_state(0, 0), y_goal_state(0, 0),
488 x_goal_state(1, 0), y_goal_state(1, 0),
489 &kinematic_result);
490
491 // Store the appropriate inverse kinematic results in the
492 // arm/elevator goals.
493 arm_goal_ = kinematic_result.arm_angle;
494 arm_goal_velocity_ = kinematic_result.arm_velocity;
495
496 elevator_goal_ = kinematic_result.elevator_height;
497 elevator_goal_velocity_ = kinematic_result.arm_velocity;
498 } else {
499 LOG(ERROR, "Unknown profiling_type: %d\n",
500 unsafe_goal->profiling_type);
501 }
Austin Schuh482a4e12015-02-14 22:43:43 -0800502 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800503
504 // Update state_ to accurately represent the state of the zeroing
505 // estimators.
506 UpdateZeroingState();
Austin Schuh703b8d42015-02-01 14:56:34 -0800507
508 if (state_ != RUNNING && state_ != ESTOP) {
509 state_ = UNINITIALIZED;
510 }
511 break;
512
513 case ESTOP:
514 LOG(ERROR, "Estop\n");
515 disable = true;
516 break;
517 }
518
519 // Commence death if either left/right tracking error gets too big. This
520 // should run immediately after the SetArmOffset and SetElevatorOffset
521 // functions to double-check that the hardware is in a sane state.
522 if (::std::abs(left_arm() - right_arm()) >=
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800523 values.max_allowed_left_right_arm_difference) {
524 LOG(ERROR, "The arms are too far apart. |%f - %f| > %f\n", left_arm(),
525 right_arm(), values.max_allowed_left_right_arm_difference);
526
527 // Indicate an ESTOP condition and stop the motors.
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700528 if (output) {
529 state_ = ESTOP;
530 }
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800531 disable = true;
532 }
533
534 if (::std::abs(left_elevator() - right_elevator()) >=
535 values.max_allowed_left_right_elevator_difference) {
536 LOG(ERROR, "The elevators are too far apart. |%f - %f| > %f\n",
537 left_elevator(), right_elevator(),
538 values.max_allowed_left_right_elevator_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800539
540 // Indicate an ESTOP condition and stop the motors.
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700541 if (output) {
542 state_ = ESTOP;
543 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800544 disable = true;
545 }
546
547 // Limit the goals so we can't exceed the hardware limits if we are RUNNING.
548 if (state_ == RUNNING) {
549 // Limit the arm goal to min/max allowable angles.
550 if (arm_goal_ >= values.fridge.arm.upper_limit) {
551 LOG(WARNING, "Arm goal above limit, %f > %f\n", arm_goal_,
552 values.fridge.arm.upper_limit);
553 arm_goal_ = values.fridge.arm.upper_limit;
554 }
555 if (arm_goal_ <= values.fridge.arm.lower_limit) {
556 LOG(WARNING, "Arm goal below limit, %f < %f\n", arm_goal_,
557 values.fridge.arm.lower_limit);
558 arm_goal_ = values.fridge.arm.lower_limit;
559 }
560
561 // Limit the elevator goal to min/max allowable heights.
562 if (elevator_goal_ >= values.fridge.elevator.upper_limit) {
563 LOG(WARNING, "Elevator goal above limit, %f > %f\n", elevator_goal_,
564 values.fridge.elevator.upper_limit);
565 elevator_goal_ = values.fridge.elevator.upper_limit;
566 }
567 if (elevator_goal_ <= values.fridge.elevator.lower_limit) {
568 LOG(WARNING, "Elevator goal below limit, %f < %f\n", elevator_goal_,
569 values.fridge.elevator.lower_limit);
570 elevator_goal_ = values.fridge.elevator.lower_limit;
571 }
572 }
573
574 // Check the lower level hardware limit as well.
575 if (state_ == RUNNING) {
576 if (left_arm() >= values.fridge.arm.upper_hard_limit ||
577 left_arm() <= values.fridge.arm.lower_hard_limit) {
578 LOG(ERROR, "Left arm at %f out of bounds [%f, %f], ESTOPing\n",
579 left_arm(), values.fridge.arm.lower_hard_limit,
580 values.fridge.arm.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700581 if (output) {
582 state_ = ESTOP;
583 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800584 }
585
586 if (right_arm() >= values.fridge.arm.upper_hard_limit ||
587 right_arm() <= values.fridge.arm.lower_hard_limit) {
588 LOG(ERROR, "Right arm at %f out of bounds [%f, %f], ESTOPing\n",
589 right_arm(), values.fridge.arm.lower_hard_limit,
590 values.fridge.arm.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700591 if (output) {
592 state_ = ESTOP;
593 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800594 }
595
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700596 if (left_elevator() >= values.fridge.elevator.upper_hard_limit) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800597 LOG(ERROR, "Left elevator at %f out of bounds [%f, %f], ESTOPing\n",
598 left_elevator(), values.fridge.elevator.lower_hard_limit,
599 values.fridge.elevator.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700600 if (output) {
601 state_ = ESTOP;
602 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800603 }
604
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700605 if (right_elevator() >= values.fridge.elevator.upper_hard_limit) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800606 LOG(ERROR, "Right elevator at %f out of bounds [%f, %f], ESTOPing\n",
607 right_elevator(), values.fridge.elevator.lower_hard_limit,
608 values.fridge.elevator.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700609 if (output) {
610 state_ = ESTOP;
611 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800612 }
613 }
614
615 // Set the goals.
Philipp Schrader085bf012015-03-15 01:43:11 +0000616 arm_loop_->mutable_R() << arm_goal_, arm_goal_velocity_, 0.0, 0.0, 0.0;
617 elevator_loop_->mutable_R() << elevator_goal_, elevator_goal_velocity_, 0.0,
Austin Schuh703b8d42015-02-01 14:56:34 -0800618 0.0;
619
620 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
621 arm_loop_->set_max_voltage(max_voltage);
622 elevator_loop_->set_max_voltage(max_voltage);
623
624 if (state_ == ESTOP) {
625 disable = true;
626 }
627 arm_loop_->Update(disable);
628 elevator_loop_->Update(disable);
629
630 if (state_ == INITIALIZING || state_ == ZEROING_ELEVATOR ||
631 state_ == ZEROING_ARM) {
632 if (arm_loop_->U() != arm_loop_->U_uncapped()) {
633 Eigen::Matrix<double, 2, 1> deltaR =
634 arm_loop_->UnsaturateOutputGoalChange();
635
636 // Move the average arm goal by the amount observed.
637 LOG(WARNING, "Moving arm goal by %f to handle saturation\n",
638 deltaR(0, 0));
639 arm_goal_ += deltaR(0, 0);
640 }
641
642 if (elevator_loop_->U() != elevator_loop_->U_uncapped()) {
643 Eigen::Matrix<double, 2, 1> deltaR =
644 elevator_loop_->UnsaturateOutputGoalChange();
645
646 // Move the average elevator goal by the amount observed.
647 LOG(WARNING, "Moving elevator goal by %f to handle saturation\n",
648 deltaR(0, 0));
649 elevator_goal_ += deltaR(0, 0);
650 }
651 }
652
653 if (output) {
654 output->left_arm = arm_loop_->U(0, 0);
655 output->right_arm = arm_loop_->U(1, 0);
656 output->left_elevator = elevator_loop_->U(0, 0);
657 output->right_elevator = elevator_loop_->U(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800658 if (unsafe_goal) {
659 output->grabbers = unsafe_goal->grabbers;
660 } else {
661 output->grabbers.top_front = false;
662 output->grabbers.top_back = false;
663 output->grabbers.bottom_front = false;
664 output->grabbers.bottom_back = false;
665 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800666 }
667
668 // TODO(austin): Populate these fully.
Austin Schuh5ae4efd2015-02-15 23:34:22 -0800669 status->zeroed = state_ == RUNNING;
Philipp Schrader2a25b612015-03-28 23:49:06 +0000670
Austin Schuh703b8d42015-02-01 14:56:34 -0800671 status->angle = arm_loop_->X_hat(0, 0);
Philipp Schrader3e281412015-03-01 23:48:23 +0000672 status->angular_velocity = arm_loop_->X_hat(1, 0);
Austin Schuh703b8d42015-02-01 14:56:34 -0800673 status->height = elevator_loop_->X_hat(0, 0);
Philipp Schrader3e281412015-03-01 23:48:23 +0000674 status->velocity = elevator_loop_->X_hat(1, 0);
Philipp Schrader2a25b612015-03-28 23:49:06 +0000675
Austin Schuh5e872c82015-02-17 22:59:08 -0800676 status->goal_angle = arm_goal_;
Philipp Schrader085bf012015-03-15 01:43:11 +0000677 status->goal_angular_velocity = arm_goal_velocity_;
Austin Schuh5e872c82015-02-17 22:59:08 -0800678 status->goal_height = elevator_goal_;
Philipp Schrader085bf012015-03-15 01:43:11 +0000679 status->goal_velocity = elevator_goal_velocity_;
Philipp Schrader2a25b612015-03-28 23:49:06 +0000680
681 // Populate the same status, but in X/Y co-ordinates.
682 aos::util::ElevatorArmKinematics::KinematicResult x_y_status;
683 kinematics_.ForwardKinematic(status->height, status->angle,
684 status->velocity, status->angular_velocity,
685 &x_y_status);
686 status->x = x_y_status.fridge_x;
687 status->y = x_y_status.fridge_h;
688 status->x_velocity = x_y_status.fridge_x_velocity;
689 status->y_velocity = x_y_status.fridge_h_velocity;
690
691 kinematics_.ForwardKinematic(status->goal_height, status->goal_angle,
692 status->goal_velocity, status->goal_angular_velocity,
693 &x_y_status);
694 status->goal_x = x_y_status.fridge_x;
695 status->goal_y = x_y_status.fridge_h;
696 status->goal_x_velocity = x_y_status.fridge_x_velocity;
697 status->goal_y_velocity = x_y_status.fridge_h_velocity;
698
Austin Schuh482a4e12015-02-14 22:43:43 -0800699 if (unsafe_goal) {
700 status->grabbers = unsafe_goal->grabbers;
701 } else {
702 status->grabbers.top_front = false;
703 status->grabbers.top_back = false;
704 status->grabbers.bottom_front = false;
705 status->grabbers.bottom_back = false;
706 }
Adam Snaider3cd11c52015-02-16 02:16:09 +0000707 zeroing::PopulateEstimatorState(left_arm_estimator_, &status->left_arm_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800708 zeroing::PopulateEstimatorState(right_arm_estimator_,
709 &status->right_arm_state);
710 zeroing::PopulateEstimatorState(left_elevator_estimator_,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000711 &status->left_elevator_state);
Daniel Pettiab274232015-02-16 19:15:34 -0800712 zeroing::PopulateEstimatorState(right_elevator_estimator_,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000713 &status->right_elevator_state);
Austin Schuh703b8d42015-02-01 14:56:34 -0800714 status->estopped = (state_ == ESTOP);
Austin Schuh482a4e12015-02-14 22:43:43 -0800715 status->state = state_;
Austin Schuh703b8d42015-02-01 14:56:34 -0800716 last_state_ = state_;
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800717}
718
719} // namespace control_loops
720} // namespace frc971