blob: a6acae723d15dc629ab7fef2541db7cda07cd053 [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
Austin Schuh88af0852016-12-04 20:31:32 -080015namespace y2015 {
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080016namespace control_loops {
Austin Schuh88af0852016-12-04 20:31:32 -080017namespace fridge {
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080018
Austin Schuh214e9c12016-11-25 17:26:20 -080019namespace chrono = ::std::chrono;
20
Austin Schuh703b8d42015-02-01 14:56:34 -080021namespace {
Austin Schuhb966c432015-02-16 15:47:18 -080022constexpr double kZeroingVoltage = 4.0;
23constexpr double kElevatorZeroingVelocity = 0.10;
Daniel Pettie1bb13e2015-02-17 13:59:15 -080024// What speed we move to our safe height at.
Austin Schuhb3ae2592015-03-15 00:33:38 -070025constexpr double kElevatorSafeHeightVelocity = 0.3;
Austin Schuhb966c432015-02-16 15:47:18 -080026constexpr double kArmZeroingVelocity = 0.20;
Austin Schuh703b8d42015-02-01 14:56:34 -080027} // namespace
28
Austin Schuh8de10c72015-02-27 23:33:40 -080029template <int S>
30void CappedStateFeedbackLoop<S>::CapU() {
Austin Schuh88af0852016-12-04 20:31:32 -080031 ::frc971::control_loops::VoltageCap(max_voltage_, this->U(0, 0),
32 this->U(1, 0), &this->mutable_U(0, 0),
33 &this->mutable_U(1, 0));
Austin Schuh703b8d42015-02-01 14:56:34 -080034}
35
Austin Schuh8de10c72015-02-27 23:33:40 -080036template <int S>
Austin Schuh703b8d42015-02-01 14:56:34 -080037Eigen::Matrix<double, 2, 1>
Austin Schuh8de10c72015-02-27 23:33:40 -080038CappedStateFeedbackLoop<S>::UnsaturateOutputGoalChange() {
Austin Schuh703b8d42015-02-01 14:56:34 -080039 // Compute the K matrix used to compensate for position errors.
40 Eigen::Matrix<double, 2, 2> Kp;
41 Kp.setZero();
Austin Schuh8de10c72015-02-27 23:33:40 -080042 Kp.col(0) = this->K().col(0);
43 Kp.col(1) = this->K().col(2);
Austin Schuh703b8d42015-02-01 14:56:34 -080044
45 Eigen::Matrix<double, 2, 2> Kp_inv = Kp.inverse();
46
47 // Compute how much we need to change R in order to achieve the change in U
48 // that was observed.
Austin Schuh8de10c72015-02-27 23:33:40 -080049 Eigen::Matrix<double, 2, 1> deltaR =
50 -Kp_inv * (this->U_uncapped() - this->U());
Austin Schuh703b8d42015-02-01 14:56:34 -080051 return deltaR;
52}
53
Austin Schuh88af0852016-12-04 20:31:32 -080054Fridge::Fridge(FridgeQueue *fridge)
55 : aos::controls::ControlLoop<FridgeQueue>(fridge),
56 arm_loop_(new CappedStateFeedbackLoop<5>(StateFeedbackLoop<5, 2, 2>(
Austin Schuh6c20f202017-02-18 22:31:44 -080057 MakeIntegralArmLoop()))),
Austin Schuh8de10c72015-02-27 23:33:40 -080058 elevator_loop_(new CappedStateFeedbackLoop<4>(
Austin Schuh703b8d42015-02-01 14:56:34 -080059 StateFeedbackLoop<4, 2, 2>(MakeElevatorLoop()))),
Daniel Pettia7827412015-02-13 20:55:57 -080060 left_arm_estimator_(constants::GetValues().fridge.left_arm_zeroing),
61 right_arm_estimator_(constants::GetValues().fridge.right_arm_zeroing),
Austin Schuh482a4e12015-02-14 22:43:43 -080062 left_elevator_estimator_(constants::GetValues().fridge.left_elev_zeroing),
Austin Schuh703b8d42015-02-01 14:56:34 -080063 right_elevator_estimator_(
Philipp Schrader3e281412015-03-01 23:48:23 +000064 constants::GetValues().fridge.right_elev_zeroing),
Philipp Schrader085bf012015-03-15 01:43:11 +000065 last_profiling_type_(ProfilingType::ANGLE_HEIGHT_PROFILING),
66 kinematics_(constants::GetValues().fridge.arm_length,
67 constants::GetValues().fridge.elevator.upper_limit,
68 constants::GetValues().fridge.elevator.lower_limit,
69 constants::GetValues().fridge.arm.upper_limit,
70 constants::GetValues().fridge.arm.lower_limit),
Philipp Schrader3e281412015-03-01 23:48:23 +000071 arm_profile_(::aos::controls::kLoopFrequency),
Philipp Schrader085bf012015-03-15 01:43:11 +000072 elevator_profile_(::aos::controls::kLoopFrequency),
73 x_profile_(::aos::controls::kLoopFrequency),
74 y_profile_(::aos::controls::kLoopFrequency) {}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080075
Austin Schuh703b8d42015-02-01 14:56:34 -080076void Fridge::UpdateZeroingState() {
Austin Schuh47bf6902015-02-14 22:46:22 -080077 if (left_elevator_estimator_.offset_ratio_ready() < 1.0 ||
78 right_elevator_estimator_.offset_ratio_ready() < 1.0 ||
79 left_arm_estimator_.offset_ratio_ready() < 1.0 ||
80 right_arm_estimator_.offset_ratio_ready() < 1.0) {
Austin Schuh703b8d42015-02-01 14:56:34 -080081 state_ = INITIALIZING;
82 } else if (!left_elevator_estimator_.zeroed() ||
Adam Snaider3cd11c52015-02-16 02:16:09 +000083 !right_elevator_estimator_.zeroed()) {
Austin Schuh703b8d42015-02-01 14:56:34 -080084 state_ = ZEROING_ELEVATOR;
Adam Snaider3cd11c52015-02-16 02:16:09 +000085 } else if (!left_arm_estimator_.zeroed() || !right_arm_estimator_.zeroed()) {
Austin Schuh703b8d42015-02-01 14:56:34 -080086 state_ = ZEROING_ARM;
87 } else {
88 state_ = RUNNING;
89 }
90}
Ben Fredrickson6b5ba792015-01-25 17:14:40 -080091
Austin Schuh703b8d42015-02-01 14:56:34 -080092void Fridge::Correct() {
93 {
94 Eigen::Matrix<double, 2, 1> Y;
95 Y << left_elevator(), right_elevator();
96 elevator_loop_->Correct(Y);
97 }
98
99 {
100 Eigen::Matrix<double, 2, 1> Y;
101 Y << left_arm(), right_arm();
102 arm_loop_->Correct(Y);
103 }
104}
105
106void Fridge::SetElevatorOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800107 LOG(INFO, "Changing Elevator offset from %f, %f to %f, %f\n",
108 left_elevator_offset_, right_elevator_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -0800109 double left_doffset = left_offset - left_elevator_offset_;
110 double right_doffset = right_offset - right_elevator_offset_;
111
112 // Adjust the average height and height difference between the two sides.
113 // The derivatives of both should not need to be updated since the speeds
114 // haven't changed.
115 // The height difference is calculated as left - right, not right - left.
116 elevator_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
117 elevator_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
118
119 // Modify the zeroing goal.
120 elevator_goal_ += (left_doffset + right_doffset) / 2;
121
122 // Update the cached offset values to the actual values.
123 left_elevator_offset_ = left_offset;
124 right_elevator_offset_ = right_offset;
125}
126
127void Fridge::SetArmOffset(double left_offset, double right_offset) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800128 LOG(INFO, "Changing Arm offset from %f, %f to %f, %f\n", left_arm_offset_,
129 right_arm_offset_, left_offset, right_offset);
Austin Schuh703b8d42015-02-01 14:56:34 -0800130 double left_doffset = left_offset - left_arm_offset_;
131 double right_doffset = right_offset - right_arm_offset_;
132
133 // Adjust the average angle and angle difference between the two sides.
134 // The derivatives of both should not need to be updated since the speeds
135 // haven't changed.
136 arm_loop_->mutable_X_hat(0, 0) += (left_doffset + right_doffset) / 2;
137 arm_loop_->mutable_X_hat(2, 0) += (left_doffset - right_doffset) / 2;
138
139 // Modify the zeroing goal.
140 arm_goal_ += (left_doffset + right_doffset) / 2;
141
142 // Update the cached offset values to the actual values.
143 left_arm_offset_ = left_offset;
144 right_arm_offset_ = right_offset;
145}
146
147double Fridge::estimated_left_elevator() {
148 return current_position_.elevator.left.encoder +
149 left_elevator_estimator_.offset();
150}
151double Fridge::estimated_right_elevator() {
152 return current_position_.elevator.right.encoder +
153 right_elevator_estimator_.offset();
154}
155
156double Fridge::estimated_elevator() {
157 return (estimated_left_elevator() + estimated_right_elevator()) / 2.0;
158}
159
160double Fridge::estimated_left_arm() {
Austin Schuhb3ae2592015-03-15 00:33:38 -0700161 return current_position_.arm.left.encoder + left_arm_estimator_.offset();
Austin Schuh703b8d42015-02-01 14:56:34 -0800162}
163double Fridge::estimated_right_arm() {
Austin Schuhb3ae2592015-03-15 00:33:38 -0700164 return current_position_.arm.right.encoder + right_arm_estimator_.offset();
Austin Schuh703b8d42015-02-01 14:56:34 -0800165}
166double Fridge::estimated_arm() {
167 return (estimated_left_arm() + estimated_right_arm()) / 2.0;
168}
169
170double Fridge::left_elevator() {
171 return current_position_.elevator.left.encoder + left_elevator_offset_;
172}
173double Fridge::right_elevator() {
174 return current_position_.elevator.right.encoder + right_elevator_offset_;
175}
176
177double Fridge::elevator() { return (left_elevator() + right_elevator()) / 2.0; }
178
179double Fridge::left_arm() {
180 return current_position_.arm.left.encoder + left_arm_offset_;
181}
182double Fridge::right_arm() {
183 return current_position_.arm.right.encoder + right_arm_offset_;
184}
185double Fridge::arm() { return (left_arm() + right_arm()) / 2.0; }
186
187double Fridge::elevator_zeroing_velocity() {
188 double average_elevator =
189 (constants::GetValues().fridge.elevator.lower_limit +
190 constants::GetValues().fridge.elevator.upper_limit) /
191 2.0;
192
193 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800194 constants::GetValues().fridge.left_elev_zeroing.index_difference,
195 constants::GetValues().fridge.right_elev_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800196
197 if (elevator_zeroing_velocity_ == 0) {
198 if (estimated_elevator() > average_elevator) {
199 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
200 } else {
201 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
202 }
203 } else if (elevator_zeroing_velocity_ > 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800204 estimated_elevator() > average_elevator + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800205 elevator_zeroing_velocity_ = -kElevatorZeroingVelocity;
206 } else if (elevator_zeroing_velocity_ < 0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800207 estimated_elevator() < average_elevator - 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800208 elevator_zeroing_velocity_ = kElevatorZeroingVelocity;
209 }
210 return elevator_zeroing_velocity_;
211}
212
Philipp Schrader3e281412015-03-01 23:48:23 +0000213double Fridge::UseUnlessZero(double target_value, double default_value) {
214 if (target_value != 0.0) {
215 return target_value;
216 } else {
217 return default_value;
218 }
219}
220
Austin Schuh703b8d42015-02-01 14:56:34 -0800221double Fridge::arm_zeroing_velocity() {
222 const double average_arm = (constants::GetValues().fridge.arm.lower_limit +
223 constants::GetValues().fridge.arm.upper_limit) /
224 2.0;
225 const double pulse_width = ::std::max(
Daniel Pettia7827412015-02-13 20:55:57 -0800226 constants::GetValues().fridge.right_arm_zeroing.index_difference,
227 constants::GetValues().fridge.left_arm_zeroing.index_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800228
229 if (arm_zeroing_velocity_ == 0) {
230 if (estimated_arm() > average_arm) {
231 arm_zeroing_velocity_ = -kArmZeroingVelocity;
232 } else {
233 arm_zeroing_velocity_ = kArmZeroingVelocity;
234 }
235 } else if (arm_zeroing_velocity_ > 0.0 &&
Austin Schuh9e37c322015-02-16 15:47:49 -0800236 estimated_arm() > average_arm + 1.1 * pulse_width) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800237 arm_zeroing_velocity_ = -kArmZeroingVelocity;
Austin Schuhb3ae2592015-03-15 00:33:38 -0700238 } else if (arm_zeroing_velocity_ < 0.0 && estimated_arm() < average_arm) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800239 arm_zeroing_velocity_ = kArmZeroingVelocity;
240 }
241 return arm_zeroing_velocity_;
242}
243
Austin Schuh88af0852016-12-04 20:31:32 -0800244void Fridge::RunIteration(const FridgeQueue::Goal *unsafe_goal,
245 const FridgeQueue::Position *position,
246 FridgeQueue::Output *output,
247 FridgeQueue::Status *status) {
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800248 if (WasReset()) {
249 LOG(ERROR, "WPILib reset, restarting\n");
250 left_elevator_estimator_.Reset();
251 right_elevator_estimator_.Reset();
252 left_arm_estimator_.Reset();
253 right_arm_estimator_.Reset();
254 state_ = UNINITIALIZED;
255 }
256
Austin Schuh703b8d42015-02-01 14:56:34 -0800257 // Get a reference to the constants struct since we use it so often in this
258 // code.
Philipp Schrader82c65072015-02-16 00:47:09 +0000259 const auto &values = constants::GetValues();
Austin Schuh703b8d42015-02-01 14:56:34 -0800260
261 // Bool to track if we should turn the motors on or not.
262 bool disable = output == nullptr;
Austin Schuh703b8d42015-02-01 14:56:34 -0800263
264 // Save the current position so it can be used easily in the class.
265 current_position_ = *position;
266
267 left_elevator_estimator_.UpdateEstimate(position->elevator.left);
268 right_elevator_estimator_.UpdateEstimate(position->elevator.right);
269 left_arm_estimator_.UpdateEstimate(position->arm.left);
270 right_arm_estimator_.UpdateEstimate(position->arm.right);
271
272 if (state_ != UNINITIALIZED) {
273 Correct();
274 }
275
276 // Zeroing will work as follows:
277 // At startup, record the offset of the two halves of the two subsystems.
278 // Then, start moving the elevator towards the center until both halves are
279 // zeroed.
280 // Then, start moving the claw towards the center until both halves are
281 // zeroed.
282 // Then, done!
283
284 // We'll then need code to do sanity checking on values.
285
286 // Now, we need to figure out which way to go.
287
288 switch (state_) {
289 case UNINITIALIZED:
290 LOG(DEBUG, "Uninitialized\n");
291 // Startup. Assume that we are at the origin everywhere.
292 // This records the encoder offset between the two sides of the elevator.
293 left_elevator_offset_ = -position->elevator.left.encoder;
294 right_elevator_offset_ = -position->elevator.right.encoder;
295 left_arm_offset_ = -position->arm.left.encoder;
296 right_arm_offset_ = -position->arm.right.encoder;
Austin Schuhaab01572015-02-15 00:12:35 -0800297 elevator_loop_->mutable_X_hat().setZero();
298 arm_loop_->mutable_X_hat().setZero();
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800299 LOG(INFO, "Initializing arm offsets to %f, %f\n", left_arm_offset_,
300 right_arm_offset_);
301 LOG(INFO, "Initializing elevator offsets to %f, %f\n",
302 left_elevator_offset_, right_elevator_offset_);
Austin Schuh703b8d42015-02-01 14:56:34 -0800303 Correct();
304 state_ = INITIALIZING;
305 disable = true;
306 break;
307
308 case INITIALIZING:
309 LOG(DEBUG, "Waiting for accurate initial position.\n");
310 disable = true;
311 // Update state_ to accurately represent the state of the zeroing
312 // estimators.
313 UpdateZeroingState();
314 if (state_ != INITIALIZING) {
315 // Set the goals to where we are now.
316 elevator_goal_ = elevator();
317 arm_goal_ = arm();
318 }
319 break;
320
321 case ZEROING_ELEVATOR:
322 LOG(DEBUG, "Zeroing elevator\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800323
324 // Update state_ to accurately represent the state of the zeroing
325 // estimators.
326 UpdateZeroingState();
327 if (left_elevator_estimator_.zeroed() &&
328 right_elevator_estimator_.zeroed()) {
329 SetElevatorOffset(left_elevator_estimator_.offset(),
330 right_elevator_estimator_.offset());
331 LOG(DEBUG, "Zeroed the elevator!\n");
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800332
Austin Schuh5e872c82015-02-17 22:59:08 -0800333 if (elevator() < values.fridge.arm_zeroing_height &&
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800334 state_ != INITIALIZING) {
335 // Move the elevator to a safe height before we start zeroing the arm,
336 // so that we don't crash anything.
337 LOG(DEBUG, "Moving elevator to safe height.\n");
Brian Silverman283849f2015-03-28 01:25:16 -0400338 if (elevator_goal_ < values.fridge.arm_zeroing_height) {
339 elevator_goal_ += kElevatorSafeHeightVelocity *
Austin Schuh214e9c12016-11-25 17:26:20 -0800340 chrono::duration_cast<chrono::duration<double>>(
341 ::aos::controls::kLoopFrequency).count();
Brian Silverman283849f2015-03-28 01:25:16 -0400342 elevator_goal_velocity_ = kElevatorSafeHeightVelocity;
Austin Schuh34aad422015-03-28 14:23:31 -0700343 state_ = ZEROING_ELEVATOR;
Brian Silverman283849f2015-03-28 01:25:16 -0400344 } else {
345 // We want it stopped at whatever height it's currently set to.
346 elevator_goal_velocity_ = 0;
347 }
Daniel Pettie1bb13e2015-02-17 13:59:15 -0800348 }
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800349 } else if (!disable) {
Philipp Schrader085bf012015-03-15 01:43:11 +0000350 elevator_goal_velocity_ = elevator_zeroing_velocity();
351 elevator_goal_ += elevator_goal_velocity_ *
Austin Schuh214e9c12016-11-25 17:26:20 -0800352 chrono::duration_cast<chrono::duration<double>>(
353 ::aos::controls::kLoopFrequency).count();
Austin Schuh703b8d42015-02-01 14:56:34 -0800354 }
Philipp Schrader3e281412015-03-01 23:48:23 +0000355
356 // Bypass motion profiles while we are zeroing.
357 // This is also an important step right after the elevator is zeroed and
358 // we reach into the elevator's state matrix and change it based on the
359 // newly-obtained offset.
360 {
361 Eigen::Matrix<double, 2, 1> current;
362 current.setZero();
Philipp Schrader085bf012015-03-15 01:43:11 +0000363 current << elevator_goal_, elevator_goal_velocity_;
Philipp Schrader3e281412015-03-01 23:48:23 +0000364 elevator_profile_.MoveCurrentState(current);
365 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800366 break;
367
368 case ZEROING_ARM:
369 LOG(DEBUG, "Zeroing the arm\n");
Austin Schuh703b8d42015-02-01 14:56:34 -0800370
Austin Schuh34aad422015-03-28 14:23:31 -0700371 if (elevator() < values.fridge.arm_zeroing_height - 0.10 ||
372 elevator_goal_ < values.fridge.arm_zeroing_height) {
Brian Silverman283849f2015-03-28 01:25:16 -0400373 LOG(INFO,
374 "Going back to ZEROING_ELEVATOR until it gets high enough to "
375 "safely zero the arm\n");
376 state_ = ZEROING_ELEVATOR;
377 break;
378 }
379
Austin Schuh703b8d42015-02-01 14:56:34 -0800380 // Update state_ to accurately represent the state of the zeroing
381 // estimators.
382 UpdateZeroingState();
383 if (left_arm_estimator_.zeroed() && right_arm_estimator_.zeroed()) {
384 SetArmOffset(left_arm_estimator_.offset(),
385 right_arm_estimator_.offset());
386 LOG(DEBUG, "Zeroed the arm!\n");
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800387 } else if (!disable) {
Philipp Schrader085bf012015-03-15 01:43:11 +0000388 arm_goal_velocity_ = arm_zeroing_velocity();
Austin Schuh214e9c12016-11-25 17:26:20 -0800389 arm_goal_ += arm_goal_velocity_ *
390 chrono::duration_cast<chrono::duration<double>>(
391 ::aos::controls::kLoopFrequency).count();
Austin Schuh703b8d42015-02-01 14:56:34 -0800392 }
Philipp Schrader3e281412015-03-01 23:48:23 +0000393
394 // Bypass motion profiles while we are zeroing.
395 // This is also an important step right after the arm is zeroed and
396 // we reach into the arm's state matrix and change it based on the
397 // newly-obtained offset.
398 {
399 Eigen::Matrix<double, 2, 1> current;
400 current.setZero();
Philipp Schrader085bf012015-03-15 01:43:11 +0000401 current << arm_goal_, arm_goal_velocity_;
Philipp Schrader3e281412015-03-01 23:48:23 +0000402 arm_profile_.MoveCurrentState(current);
403 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800404 break;
405
406 case RUNNING:
407 LOG(DEBUG, "Running!\n");
Austin Schuh482a4e12015-02-14 22:43:43 -0800408 if (unsafe_goal) {
Philipp Schrader085bf012015-03-15 01:43:11 +0000409 // Handle the case where we switch between the types of profiling.
410 ProfilingType new_profiling_type =
411 static_cast<ProfilingType>(unsafe_goal->profiling_type);
Philipp Schrader3e281412015-03-01 23:48:23 +0000412
Philipp Schrader085bf012015-03-15 01:43:11 +0000413 if (last_profiling_type_ != new_profiling_type) {
414 // Reset the height/angle profiles.
415 Eigen::Matrix<double, 2, 1> current;
416 current.setZero();
417 current << arm_goal_, arm_goal_velocity_;
418 arm_profile_.MoveCurrentState(current);
419 current << elevator_goal_, elevator_goal_velocity_;
420 elevator_profile_.MoveCurrentState(current);
Philipp Schrader3e281412015-03-01 23:48:23 +0000421
Philipp Schrader085bf012015-03-15 01:43:11 +0000422 // Reset the x/y profiles.
423 aos::util::ElevatorArmKinematics::KinematicResult x_y_result;
424 kinematics_.ForwardKinematic(elevator_goal_, arm_goal_,
425 elevator_goal_velocity_,
426 arm_goal_velocity_, &x_y_result);
427 current << x_y_result.fridge_x, x_y_result.fridge_x_velocity;
428 x_profile_.MoveCurrentState(current);
429 current << x_y_result.fridge_h, x_y_result.fridge_h_velocity;
430 y_profile_.MoveCurrentState(current);
431
432 last_profiling_type_ = new_profiling_type;
433 }
434
435 if (new_profiling_type == ProfilingType::ANGLE_HEIGHT_PROFILING) {
436 // Pick a set of sane arm defaults if none are specified.
437 arm_profile_.set_maximum_velocity(
438 UseUnlessZero(unsafe_goal->max_angular_velocity, 1.0));
439 arm_profile_.set_maximum_acceleration(
440 UseUnlessZero(unsafe_goal->max_angular_acceleration, 3.0));
441 elevator_profile_.set_maximum_velocity(
442 UseUnlessZero(unsafe_goal->max_velocity, 0.50));
443 elevator_profile_.set_maximum_acceleration(
444 UseUnlessZero(unsafe_goal->max_acceleration, 2.0));
445
446 // Use the profiles to limit the arm's movements.
447 const double unfiltered_arm_goal = ::std::max(
448 ::std::min(unsafe_goal->angle, values.fridge.arm.upper_limit),
449 values.fridge.arm.lower_limit);
450 ::Eigen::Matrix<double, 2, 1> arm_goal_state = arm_profile_.Update(
451 unfiltered_arm_goal, unsafe_goal->angular_velocity);
452 arm_goal_ = arm_goal_state(0, 0);
453 arm_goal_velocity_ = arm_goal_state(1, 0);
454
455 // Use the profiles to limit the elevator's movements.
456 const double unfiltered_elevator_goal =
457 ::std::max(::std::min(unsafe_goal->height,
458 values.fridge.elevator.upper_limit),
459 values.fridge.elevator.lower_limit);
460 ::Eigen::Matrix<double, 2, 1> elevator_goal_state =
461 elevator_profile_.Update(unfiltered_elevator_goal,
462 unsafe_goal->velocity);
463 elevator_goal_ = elevator_goal_state(0, 0);
464 elevator_goal_velocity_ = elevator_goal_state(1, 0);
465 } else if (new_profiling_type == ProfilingType::X_Y_PROFILING) {
466 // Use x/y profiling
467 aos::util::ElevatorArmKinematics::KinematicResult kinematic_result;
468
469 x_profile_.set_maximum_velocity(
470 UseUnlessZero(unsafe_goal->max_x_velocity, 0.5));
471 x_profile_.set_maximum_acceleration(
472 UseUnlessZero(unsafe_goal->max_x_acceleration, 2.0));
473 y_profile_.set_maximum_velocity(
474 UseUnlessZero(unsafe_goal->max_y_velocity, 0.50));
475 y_profile_.set_maximum_acceleration(
476 UseUnlessZero(unsafe_goal->max_y_acceleration, 2.0));
477
478 // Limit the goals before we update the profiles.
479 kinematics_.InverseKinematic(
480 unsafe_goal->x, unsafe_goal->y, unsafe_goal->x_velocity,
481 unsafe_goal->y_velocity, &kinematic_result);
482
483 // Use the profiles to limit the x movements.
484 ::Eigen::Matrix<double, 2, 1> x_goal_state = x_profile_.Update(
485 kinematic_result.fridge_x, kinematic_result.fridge_x_velocity);
486
487 // Use the profiles to limit the y movements.
488 ::Eigen::Matrix<double, 2, 1> y_goal_state = y_profile_.Update(
489 kinematic_result.fridge_h, kinematic_result.fridge_h_velocity);
490
491 // Convert x/y goal states into arm/elevator goals.
492 // The inverse kinematics functions automatically perform range
493 // checking and adjust the results so that they're always valid.
494 kinematics_.InverseKinematic(x_goal_state(0, 0), y_goal_state(0, 0),
495 x_goal_state(1, 0), y_goal_state(1, 0),
496 &kinematic_result);
497
498 // Store the appropriate inverse kinematic results in the
499 // arm/elevator goals.
500 arm_goal_ = kinematic_result.arm_angle;
501 arm_goal_velocity_ = kinematic_result.arm_velocity;
502
503 elevator_goal_ = kinematic_result.elevator_height;
504 elevator_goal_velocity_ = kinematic_result.arm_velocity;
505 } else {
506 LOG(ERROR, "Unknown profiling_type: %d\n",
507 unsafe_goal->profiling_type);
508 }
Austin Schuh482a4e12015-02-14 22:43:43 -0800509 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800510
511 // Update state_ to accurately represent the state of the zeroing
512 // estimators.
513 UpdateZeroingState();
Austin Schuh703b8d42015-02-01 14:56:34 -0800514
515 if (state_ != RUNNING && state_ != ESTOP) {
516 state_ = UNINITIALIZED;
517 }
518 break;
519
520 case ESTOP:
521 LOG(ERROR, "Estop\n");
522 disable = true;
523 break;
524 }
525
526 // Commence death if either left/right tracking error gets too big. This
527 // should run immediately after the SetArmOffset and SetElevatorOffset
528 // functions to double-check that the hardware is in a sane state.
529 if (::std::abs(left_arm() - right_arm()) >=
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800530 values.max_allowed_left_right_arm_difference) {
531 LOG(ERROR, "The arms are too far apart. |%f - %f| > %f\n", left_arm(),
532 right_arm(), values.max_allowed_left_right_arm_difference);
533
534 // Indicate an ESTOP condition and stop the motors.
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700535 if (output) {
536 state_ = ESTOP;
537 }
Austin Schuhdbd6bfa2015-02-14 21:25:16 -0800538 disable = true;
539 }
540
541 if (::std::abs(left_elevator() - right_elevator()) >=
542 values.max_allowed_left_right_elevator_difference) {
543 LOG(ERROR, "The elevators are too far apart. |%f - %f| > %f\n",
544 left_elevator(), right_elevator(),
545 values.max_allowed_left_right_elevator_difference);
Austin Schuh703b8d42015-02-01 14:56:34 -0800546
547 // Indicate an ESTOP condition and stop the motors.
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700548 if (output) {
549 state_ = ESTOP;
550 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800551 disable = true;
552 }
553
554 // Limit the goals so we can't exceed the hardware limits if we are RUNNING.
555 if (state_ == RUNNING) {
556 // Limit the arm goal to min/max allowable angles.
557 if (arm_goal_ >= values.fridge.arm.upper_limit) {
558 LOG(WARNING, "Arm goal above limit, %f > %f\n", arm_goal_,
559 values.fridge.arm.upper_limit);
560 arm_goal_ = values.fridge.arm.upper_limit;
561 }
562 if (arm_goal_ <= values.fridge.arm.lower_limit) {
563 LOG(WARNING, "Arm goal below limit, %f < %f\n", arm_goal_,
564 values.fridge.arm.lower_limit);
565 arm_goal_ = values.fridge.arm.lower_limit;
566 }
567
568 // Limit the elevator goal to min/max allowable heights.
569 if (elevator_goal_ >= values.fridge.elevator.upper_limit) {
570 LOG(WARNING, "Elevator goal above limit, %f > %f\n", elevator_goal_,
571 values.fridge.elevator.upper_limit);
572 elevator_goal_ = values.fridge.elevator.upper_limit;
573 }
574 if (elevator_goal_ <= values.fridge.elevator.lower_limit) {
575 LOG(WARNING, "Elevator goal below limit, %f < %f\n", elevator_goal_,
576 values.fridge.elevator.lower_limit);
577 elevator_goal_ = values.fridge.elevator.lower_limit;
578 }
579 }
580
581 // Check the lower level hardware limit as well.
582 if (state_ == RUNNING) {
583 if (left_arm() >= values.fridge.arm.upper_hard_limit ||
584 left_arm() <= values.fridge.arm.lower_hard_limit) {
585 LOG(ERROR, "Left arm at %f out of bounds [%f, %f], ESTOPing\n",
586 left_arm(), values.fridge.arm.lower_hard_limit,
587 values.fridge.arm.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700588 if (output) {
589 state_ = ESTOP;
590 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800591 }
592
593 if (right_arm() >= values.fridge.arm.upper_hard_limit ||
594 right_arm() <= values.fridge.arm.lower_hard_limit) {
595 LOG(ERROR, "Right arm at %f out of bounds [%f, %f], ESTOPing\n",
596 right_arm(), values.fridge.arm.lower_hard_limit,
597 values.fridge.arm.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700598 if (output) {
599 state_ = ESTOP;
600 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800601 }
602
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700603 if (left_elevator() >= values.fridge.elevator.upper_hard_limit) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800604 LOG(ERROR, "Left elevator at %f out of bounds [%f, %f], ESTOPing\n",
605 left_elevator(), values.fridge.elevator.lower_hard_limit,
606 values.fridge.elevator.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700607 if (output) {
608 state_ = ESTOP;
609 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800610 }
611
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700612 if (right_elevator() >= values.fridge.elevator.upper_hard_limit) {
Austin Schuh703b8d42015-02-01 14:56:34 -0800613 LOG(ERROR, "Right elevator at %f out of bounds [%f, %f], ESTOPing\n",
614 right_elevator(), values.fridge.elevator.lower_hard_limit,
615 values.fridge.elevator.upper_hard_limit);
Brian Silverman10b8f4c2015-03-21 23:44:46 -0700616 if (output) {
617 state_ = ESTOP;
618 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800619 }
620 }
621
622 // Set the goals.
Philipp Schrader085bf012015-03-15 01:43:11 +0000623 arm_loop_->mutable_R() << arm_goal_, arm_goal_velocity_, 0.0, 0.0, 0.0;
624 elevator_loop_->mutable_R() << elevator_goal_, elevator_goal_velocity_, 0.0,
Austin Schuh703b8d42015-02-01 14:56:34 -0800625 0.0;
626
627 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
628 arm_loop_->set_max_voltage(max_voltage);
629 elevator_loop_->set_max_voltage(max_voltage);
630
631 if (state_ == ESTOP) {
632 disable = true;
633 }
634 arm_loop_->Update(disable);
635 elevator_loop_->Update(disable);
636
637 if (state_ == INITIALIZING || state_ == ZEROING_ELEVATOR ||
638 state_ == ZEROING_ARM) {
639 if (arm_loop_->U() != arm_loop_->U_uncapped()) {
640 Eigen::Matrix<double, 2, 1> deltaR =
641 arm_loop_->UnsaturateOutputGoalChange();
642
643 // Move the average arm goal by the amount observed.
644 LOG(WARNING, "Moving arm goal by %f to handle saturation\n",
645 deltaR(0, 0));
646 arm_goal_ += deltaR(0, 0);
647 }
648
649 if (elevator_loop_->U() != elevator_loop_->U_uncapped()) {
650 Eigen::Matrix<double, 2, 1> deltaR =
651 elevator_loop_->UnsaturateOutputGoalChange();
652
653 // Move the average elevator goal by the amount observed.
654 LOG(WARNING, "Moving elevator goal by %f to handle saturation\n",
655 deltaR(0, 0));
656 elevator_goal_ += deltaR(0, 0);
657 }
658 }
659
660 if (output) {
661 output->left_arm = arm_loop_->U(0, 0);
662 output->right_arm = arm_loop_->U(1, 0);
663 output->left_elevator = elevator_loop_->U(0, 0);
664 output->right_elevator = elevator_loop_->U(1, 0);
Austin Schuh482a4e12015-02-14 22:43:43 -0800665 if (unsafe_goal) {
666 output->grabbers = unsafe_goal->grabbers;
667 } else {
668 output->grabbers.top_front = false;
669 output->grabbers.top_back = false;
670 output->grabbers.bottom_front = false;
671 output->grabbers.bottom_back = false;
672 }
Austin Schuh703b8d42015-02-01 14:56:34 -0800673 }
674
675 // TODO(austin): Populate these fully.
Austin Schuh5ae4efd2015-02-15 23:34:22 -0800676 status->zeroed = state_ == RUNNING;
Philipp Schrader2a25b612015-03-28 23:49:06 +0000677
Austin Schuh703b8d42015-02-01 14:56:34 -0800678 status->angle = arm_loop_->X_hat(0, 0);
Philipp Schrader3e281412015-03-01 23:48:23 +0000679 status->angular_velocity = arm_loop_->X_hat(1, 0);
Austin Schuh703b8d42015-02-01 14:56:34 -0800680 status->height = elevator_loop_->X_hat(0, 0);
Philipp Schrader3e281412015-03-01 23:48:23 +0000681 status->velocity = elevator_loop_->X_hat(1, 0);
Philipp Schrader2a25b612015-03-28 23:49:06 +0000682
Austin Schuh5e872c82015-02-17 22:59:08 -0800683 status->goal_angle = arm_goal_;
Philipp Schrader085bf012015-03-15 01:43:11 +0000684 status->goal_angular_velocity = arm_goal_velocity_;
Austin Schuh5e872c82015-02-17 22:59:08 -0800685 status->goal_height = elevator_goal_;
Philipp Schrader085bf012015-03-15 01:43:11 +0000686 status->goal_velocity = elevator_goal_velocity_;
Philipp Schrader2a25b612015-03-28 23:49:06 +0000687
688 // Populate the same status, but in X/Y co-ordinates.
689 aos::util::ElevatorArmKinematics::KinematicResult x_y_status;
690 kinematics_.ForwardKinematic(status->height, status->angle,
691 status->velocity, status->angular_velocity,
692 &x_y_status);
693 status->x = x_y_status.fridge_x;
694 status->y = x_y_status.fridge_h;
695 status->x_velocity = x_y_status.fridge_x_velocity;
696 status->y_velocity = x_y_status.fridge_h_velocity;
697
698 kinematics_.ForwardKinematic(status->goal_height, status->goal_angle,
699 status->goal_velocity, status->goal_angular_velocity,
700 &x_y_status);
701 status->goal_x = x_y_status.fridge_x;
702 status->goal_y = x_y_status.fridge_h;
703 status->goal_x_velocity = x_y_status.fridge_x_velocity;
704 status->goal_y_velocity = x_y_status.fridge_h_velocity;
705
Austin Schuh482a4e12015-02-14 22:43:43 -0800706 if (unsafe_goal) {
707 status->grabbers = unsafe_goal->grabbers;
708 } else {
709 status->grabbers.top_front = false;
710 status->grabbers.top_back = false;
711 status->grabbers.bottom_front = false;
712 status->grabbers.bottom_back = false;
713 }
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800714 status->left_arm_state = left_arm_estimator_.GetEstimatorState();
715 status->right_arm_state = right_arm_estimator_.GetEstimatorState();
716 status->left_elevator_state = left_elevator_estimator_.GetEstimatorState();
717 status->right_elevator_state = right_elevator_estimator_.GetEstimatorState();
Austin Schuh703b8d42015-02-01 14:56:34 -0800718 status->estopped = (state_ == ESTOP);
Austin Schuh482a4e12015-02-14 22:43:43 -0800719 status->state = state_;
Austin Schuh703b8d42015-02-01 14:56:34 -0800720 last_state_ = state_;
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800721}
722
Austin Schuh88af0852016-12-04 20:31:32 -0800723} // namespace fridge
Ben Fredrickson6b5ba792015-01-25 17:14:40 -0800724} // namespace control_loops
Austin Schuh88af0852016-12-04 20:31:32 -0800725} // namespace y2015