blob: b82d2d94176c310afbcdb61cb027ffb029042e9a [file] [log] [blame]
Comran Morshed25f81a02016-01-23 13:40:10 +00001#include "y2016/control_loops/superstructure/superstructure.h"
Austin Schuh10c2d112016-02-14 13:42:28 -08002#include "y2016/control_loops/superstructure/superstructure_controls.h"
Comran Morshed25f81a02016-01-23 13:40:10 +00003
4#include "aos/common/controls/control_loops.q.h"
5#include "aos/common/logging/logging.h"
6
Austin Schuh2fc10fa2016-02-08 00:44:34 -08007#include "y2016/control_loops/superstructure/integral_intake_plant.h"
8#include "y2016/control_loops/superstructure/integral_arm_plant.h"
9
10#include "y2016/constants.h"
11
Comran Morshed25f81a02016-01-23 13:40:10 +000012namespace y2016 {
13namespace control_loops {
Austin Schuh2fc10fa2016-02-08 00:44:34 -080014namespace superstructure {
Comran Morshed25f81a02016-01-23 13:40:10 +000015
Austin Schuh2fc10fa2016-02-08 00:44:34 -080016namespace {
Austin Schuh2d7820b2016-02-16 13:47:42 -080017// The maximum voltage the intake roller will be allowed to use.
Austin Schuhbe041152016-02-28 22:01:52 -080018constexpr float kMaxIntakeTopVoltage = 12.0;
19constexpr float kMaxIntakeBottomVoltage = 12.0;
Comran Morshedf4cd7642016-02-15 20:40:49 +000020
Austin Schuh2d7820b2016-02-16 13:47:42 -080021// Aliases to reduce typing.
Adam Snaider06779722016-02-14 15:26:22 -080022constexpr double kIntakeEncoderIndexDifference =
23 constants::Values::kIntakeEncoderIndexDifference;
24constexpr double kWristEncoderIndexDifference =
25 constants::Values::kWristEncoderIndexDifference;
26constexpr double kShoulderEncoderIndexDifference =
27 constants::Values::kShoulderEncoderIndexDifference;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080028} // namespace
29
Philipp Schrader0119eb12016-02-15 18:16:21 +000030// ///// CollisionAvoidance /////
31
32void CollisionAvoidance::UpdateGoal(double shoulder_angle_goal,
33 double wrist_angle_goal,
34 double intake_angle_goal) {
Austin Schuh6ca0f792016-03-12 14:06:14 -080035 const double original_shoulder_angle_goal = shoulder_angle_goal;
36 const double original_intake_angle_goal = intake_angle_goal;
37
Philipp Schrader0119eb12016-02-15 18:16:21 +000038 double shoulder_angle = arm_->shoulder_angle();
39 double wrist_angle = arm_->wrist_angle();
40 double intake_angle = intake_->angle();
41
42 // TODO(phil): This may need tuning to account for bounciness in the limbs or
43 // some other thing that I haven't thought of. At the very least,
44 // incorporating a small safety margin makes writing test cases much easier
45 // since you can directly compare statuses against the constants in the
46 // CollisionAvoidance class.
47 constexpr double kSafetyMargin = 0.01; // radians
48
49 // Avoid colliding the shooter with the frame.
50 // If the shoulder is below a certain angle or we want to move it below
51 // that angle, then the shooter has to stay level to the ground. Otherwise,
52 // it will crash into the frame.
Austin Schuh6ca0f792016-03-12 14:06:14 -080053 if (shoulder_angle < kMinShoulderAngleForIntakeUpInterference ||
54 original_shoulder_angle_goal < kMinShoulderAngleForIntakeUpInterference) {
Philipp Schrader0119eb12016-02-15 18:16:21 +000055 wrist_angle_goal = 0.0;
56
57 // Make sure that we don't move the shoulder below a certain angle until
58 // the wrist is level with the ground.
Austin Schuh6802a9d2016-03-12 21:34:53 -080059 if (intake_angle < kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
60 if (::std::abs(wrist_angle) > kMaxWristAngleForMovingByIntake) {
61 shoulder_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -080062 ::std::max(original_shoulder_angle_goal,
Austin Schuh6802a9d2016-03-12 21:34:53 -080063 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
64 }
65 } else {
66 if (::std::abs(wrist_angle) > kMaxWristAngleForMovingByIntake) {
67 shoulder_angle_goal =
68 ::std::max(original_shoulder_angle_goal,
69 kMinShoulderAngleForIntakeUpInterference + kSafetyMargin);
70 }
Austin Schuh6ca0f792016-03-12 14:06:14 -080071 }
Philipp Schrader0119eb12016-02-15 18:16:21 +000072 if (::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
73 shoulder_angle_goal =
Austin Schuh6802a9d2016-03-12 21:34:53 -080074 ::std::max(shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +000075 kMinShoulderAngleForHorizontalShooter + kSafetyMargin);
76 }
77 }
78
79 // Is the arm where it could interfere with the intake right now?
80 bool shoulder_is_in_danger =
Austin Schuh6ca0f792016-03-12 14:06:14 -080081 (shoulder_angle < kMinShoulderAngleForIntakeUpInterference &&
Philipp Schrader0119eb12016-02-15 18:16:21 +000082 shoulder_angle > kMaxShoulderAngleUntilSafeIntakeStowing);
83
84 // Is the arm moving into collision zone from above?
85 bool shoulder_moving_into_danger_from_above =
Austin Schuh6ca0f792016-03-12 14:06:14 -080086 (shoulder_angle >= kMinShoulderAngleForIntakeUpInterference &&
87 original_shoulder_angle_goal <=
88 kMinShoulderAngleForIntakeUpInterference);
Philipp Schrader0119eb12016-02-15 18:16:21 +000089
90 // Is the arm moving into collision zone from below?
91 bool shoulder_moving_into_danger_from_below =
92 (shoulder_angle <= kMaxShoulderAngleUntilSafeIntakeStowing &&
Austin Schuh6ca0f792016-03-12 14:06:14 -080093 original_shoulder_angle_goal >= kMaxShoulderAngleUntilSafeIntakeStowing);
Philipp Schrader0119eb12016-02-15 18:16:21 +000094
95 // Avoid colliding the arm with the intake.
96 if (shoulder_is_in_danger || shoulder_moving_into_danger_from_above ||
97 shoulder_moving_into_danger_from_below) {
98 // If the arm could collide with the intake, we make sure to move the
99 // intake out of the way. The arm has priority.
100 intake_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800101 ::std::min(original_intake_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000102 kMaxIntakeAngleBeforeArmInterference - kSafetyMargin);
103
104 // Don't let the shoulder move into the collision area until the intake is
105 // out of the way.
106 if (intake_angle > kMaxIntakeAngleBeforeArmInterference) {
107 const double kHalfwayPointBetweenSafeZones =
108 (kMinShoulderAngleForIntakeInterference +
109 kMaxShoulderAngleUntilSafeIntakeStowing) /
110 2.0;
111
112 if (shoulder_angle >= kHalfwayPointBetweenSafeZones) {
113 // The shoulder is closer to being above the collision area. Move it up
114 // there.
115 shoulder_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800116 ::std::max(original_shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000117 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
118 } else {
119 // The shoulder is closer to being below the collision zone (i.e. in
120 // stowing/intake position), keep it there for now.
121 shoulder_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800122 ::std::min(original_shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000123 kMaxShoulderAngleUntilSafeIntakeStowing - kSafetyMargin);
124 }
125 }
126 }
127
128 // Send the possibly adjusted goals to the components.
129 arm_->set_unprofiled_goal(shoulder_angle_goal, wrist_angle_goal);
130 intake_->set_unprofiled_goal(intake_angle_goal);
131}
132
Philipp Schrader07147532016-02-16 01:23:07 +0000133bool CollisionAvoidance::collided() const {
134 return collided_with_given_angles(arm_->shoulder_angle(), arm_->wrist_angle(),
135 intake_->angle());
136}
137
138bool CollisionAvoidance::collided_with_given_angles(double shoulder_angle,
139 double wrist_angle,
140 double intake_angle) {
141 // The arm and the intake must not hit.
142 if (shoulder_angle >=
143 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
144 shoulder_angle <=
Austin Schuh6ca0f792016-03-12 14:06:14 -0800145 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference &&
146 intake_angle >
147 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference) {
148 LOG(DEBUG, "Collided: Intake %f > %f, and shoulder %f < %f < %f.\n",
149 intake_angle, CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
150 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
Austin Schuhc85cdd52016-02-16 13:05:35 -0800151 shoulder_angle,
Austin Schuh6ca0f792016-03-12 14:06:14 -0800152 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference);
153 return true;
154 }
155
156 if (shoulder_angle >=
157 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
158 shoulder_angle <=
159 CollisionAvoidance::kMinShoulderAngleForIntakeInterference &&
160 intake_angle < CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference &&
161 intake_angle > Superstructure::kIntakeLowerClear &&
162 ::std::abs(wrist_angle) >
163 CollisionAvoidance::kMaxWristAngleForMovingByIntake) {
164 LOG(DEBUG, "Collided: Intake %f < %f < %f, and shoulder %f < %f < %f.\n",
165 Superstructure::kIntakeLowerClear, intake_angle,
166 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
167 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
168 shoulder_angle,
169 CollisionAvoidance::kMinShoulderAngleForIntakeInterference);
Philipp Schrader07147532016-02-16 01:23:07 +0000170 return true;
171 }
172
173 // The wrist must go back to zero when the shoulder is moving the arm into
174 // a stowed/intaking position.
175 if (shoulder_angle <
176 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter &&
177 ::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
Austin Schuhc85cdd52016-02-16 13:05:35 -0800178 LOG(DEBUG, "Collided: Shoulder %f < %f and wrist |%f| < %f.\n",
179 shoulder_angle,
180 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter, wrist_angle,
181 kMaxWristAngleForSafeArmStowing);
Philipp Schrader07147532016-02-16 01:23:07 +0000182 return true;
183 }
184
185 return false;
186}
187
Philipp Schrader0119eb12016-02-15 18:16:21 +0000188constexpr double CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
189constexpr double CollisionAvoidance::kMinShoulderAngleForIntakeInterference;
190constexpr double CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
191constexpr double CollisionAvoidance::kMaxWristAngleForSafeArmStowing;
192constexpr double CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing;
193
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800194Superstructure::Superstructure(
195 control_loops::SuperstructureQueue *superstructure_queue)
196 : aos::controls::ControlLoop<control_loops::SuperstructureQueue>(
Philipp Schrader0119eb12016-02-15 18:16:21 +0000197 superstructure_queue),
198 collision_avoidance_(&intake_, &arm_) {}
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800199
Adam Snaider06779722016-02-14 15:26:22 -0800200bool Superstructure::IsArmNear(double shoulder_tolerance,
201 double wrist_tolerance) {
202 return ((arm_.unprofiled_goal() - arm_.X_hat())
203 .block<2, 1>(0, 0)
204 .lpNorm<Eigen::Infinity>() < shoulder_tolerance) &&
205 ((arm_.unprofiled_goal() - arm_.X_hat())
206 .block<4, 1>(0, 0)
207 .lpNorm<Eigen::Infinity>() < wrist_tolerance) &&
208 ((arm_.unprofiled_goal() - arm_.goal())
209 .block<4, 1>(0, 0)
210 .lpNorm<Eigen::Infinity>() < 1e-6);
211}
212
213bool Superstructure::IsArmNear(double tolerance) {
214 return ((arm_.unprofiled_goal() - arm_.X_hat())
215 .block<4, 1>(0, 0)
216 .lpNorm<Eigen::Infinity>() < tolerance) &&
217 ((arm_.unprofiled_goal() - arm_.goal())
218 .block<4, 1>(0, 0)
219 .lpNorm<Eigen::Infinity>() < 1e-6);
220}
221
222bool Superstructure::IsIntakeNear(double tolerance) {
223 return ((intake_.unprofiled_goal() - intake_.X_hat())
224 .block<2, 1>(0, 0)
225 .lpNorm<Eigen::Infinity>() < tolerance);
226}
227
228double Superstructure::MoveButKeepAbove(double reference_angle,
229 double current_angle,
230 double move_distance) {
231 return -MoveButKeepBelow(-reference_angle, -current_angle, -move_distance);
232}
233
234double Superstructure::MoveButKeepBelow(double reference_angle,
235 double current_angle,
236 double move_distance) {
237 // There are 3 interesting places to move to.
238 const double small_negative_move = current_angle - move_distance;
239 const double small_positive_move = current_angle + move_distance;
240 // And the reference angle.
241
242 // Move the the highest one that is below reference_angle.
243 if (small_negative_move > reference_angle) {
244 return reference_angle;
245 } else if (small_positive_move > reference_angle) {
246 return small_negative_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800247 } else {
Adam Snaider06779722016-02-14 15:26:22 -0800248 return small_positive_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800249 }
250}
251
252void Superstructure::RunIteration(
253 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
254 const control_loops::SuperstructureQueue::Position *position,
255 control_loops::SuperstructureQueue::Output *output,
256 control_loops::SuperstructureQueue::Status *status) {
Adam Snaider06779722016-02-14 15:26:22 -0800257 const State state_before_switch = state_;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800258 if (WasReset()) {
259 LOG(ERROR, "WPILib reset, restarting\n");
260 arm_.Reset();
261 intake_.Reset();
262 state_ = UNINITIALIZED;
263 }
264
265 // Bool to track if we should turn the motors on or not.
266 bool disable = output == nullptr;
267
268 arm_.Correct(position->shoulder, position->wrist);
269 intake_.Correct(position->intake);
270
Adam Snaider06779722016-02-14 15:26:22 -0800271 // There are 2 main zeroing paths, HIGH_ARM_ZERO and LOW_ARM_ZERO.
272 //
273 // HIGH_ARM_ZERO works by lifting the arm all the way up so it is clear,
274 // moving the shooter to be horizontal, moving the intake out, and then moving
275 // the arm back down.
276 //
277 // LOW_ARM_ZERO works by moving the intake out of the way, lifting the arm up,
278 // leveling the shooter, and then moving back down.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800279
Diana Vandenberge2843c62016-02-13 17:44:20 -0800280 if (arm_.error() || intake_.error()) {
281 state_ = ESTOP;
282 }
283
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800284 const bool is_collided = collided();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800285 switch (state_) {
286 case UNINITIALIZED:
Adam Snaider06779722016-02-14 15:26:22 -0800287 // Wait in the uninitialized state until both the arm and intake are
288 // initialized.
289 LOG(DEBUG, "Uninitialized, waiting for intake and arm\n");
290 if (arm_.initialized() && intake_.initialized()) {
291 state_ = DISABLED_INITIALIZED;
292 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800293 disable = true;
294 break;
295
Adam Snaider06779722016-02-14 15:26:22 -0800296 case DISABLED_INITIALIZED:
297 // Wait here until we are either fully zeroed while disabled, or we become
298 // enabled. At that point, figure out if we should HIGH_ARM_ZERO or
299 // LOW_ARM_ZERO.
300 if (disable) {
301 if (arm_.zeroed() && intake_.zeroed()) {
302 state_ = SLOW_RUNNING;
303 }
304 } else {
305 if (arm_.shoulder_angle() >= kShoulderMiddleAngle) {
306 state_ = HIGH_ARM_ZERO_LIFT_ARM;
307 } else {
308 state_ = LOW_ARM_ZERO_LOWER_INTAKE;
309 }
310 }
311
312 // Set the goals to where we are now so when we start back up, we don't
313 // jump.
314 intake_.ForceGoal(intake_.angle());
315 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
316 // Set up the profile to be the zeroing profile.
317 intake_.AdjustProfile(0.5, 10);
318 arm_.AdjustProfile(0.5, 10, 0.5, 10);
319
320 // We are not ready to start doing anything yet.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800321 disable = true;
Adam Snaider06779722016-02-14 15:26:22 -0800322 break;
323
324 case HIGH_ARM_ZERO_LIFT_ARM:
325 if (disable) {
326 state_ = DISABLED_INITIALIZED;
327 } else {
328 // Raise the shoulder up out of the way.
329 arm_.set_unprofiled_goal(kShoulderUpAngle, arm_.wrist_angle());
330 if (IsArmNear(kLooseTolerance)) {
331 // Close enough, start the next move.
332 state_ = HIGH_ARM_ZERO_LEVEL_SHOOTER;
333 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800334 }
335 break;
336
Adam Snaider06779722016-02-14 15:26:22 -0800337 case HIGH_ARM_ZERO_LEVEL_SHOOTER:
338 if (disable) {
339 state_ = DISABLED_INITIALIZED;
340 } else {
341 // Move the shooter to be level.
342 arm_.set_unprofiled_goal(kShoulderUpAngle, 0.0);
343
344 if (IsArmNear(kLooseTolerance)) {
345 // Close enough, start the next move.
346 state_ = HIGH_ARM_ZERO_MOVE_INTAKE_OUT;
347 }
348 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800349 break;
350
Adam Snaider06779722016-02-14 15:26:22 -0800351 case HIGH_ARM_ZERO_MOVE_INTAKE_OUT:
352 if (disable) {
353 state_ = DISABLED_INITIALIZED;
354 } else {
355 // If we were just asked to move the intake, make sure it moves far
356 // enough.
357 if (last_state_ != HIGH_ARM_ZERO_MOVE_INTAKE_OUT) {
358 intake_.set_unprofiled_goal(
359 MoveButKeepBelow(kIntakeUpperClear, intake_.angle(),
360 kIntakeEncoderIndexDifference * 2.5));
361 }
362
363 if (IsIntakeNear(kLooseTolerance)) {
364 // Close enough, start the next move.
365 state_ = HIGH_ARM_ZERO_LOWER_ARM;
366 }
367 }
368 break;
369
370 case HIGH_ARM_ZERO_LOWER_ARM:
371 if (disable) {
372 state_ = DISABLED_INITIALIZED;
373 } else {
374 // Land the shooter in the belly-pan. It should be zeroed by the time
375 // it gets there. If not, just estop.
376 arm_.set_unprofiled_goal(kShoulderLanded, 0.0);
377 if (arm_.zeroed() && intake_.zeroed()) {
378 state_ = RUNNING;
379 } else if (IsArmNear(kLooseTolerance)) {
380 LOG(ERROR,
381 "Failed to zero while executing the HIGH_ARM_ZERO sequence. "
382 "Arm: %d Intake %d\n",
383 arm_.zeroed(), intake_.zeroed());
384 state_ = ESTOP;
385 }
386 }
387 break;
388
389 case LOW_ARM_ZERO_LOWER_INTAKE:
390 if (disable) {
391 state_ = DISABLED_INITIALIZED;
392 } else {
393 // Move the intake down out of the way of the arm. Make sure to move it
394 // far enough to zero.
395 if (last_state_ != LOW_ARM_ZERO_LOWER_INTAKE) {
396 intake_.set_unprofiled_goal(
397 MoveButKeepBelow(kIntakeLowerClear, intake_.angle(),
398 kIntakeEncoderIndexDifference * 2.5));
399 }
400 if (IsIntakeNear(kLooseTolerance)) {
401 if (::std::abs(arm_.wrist_angle()) < kWristAlmostLevel) {
402 state_ = LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER;
403 } else {
404 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
405 }
406 }
407 }
408 break;
409
410 case LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER:
411 if (disable) {
412 state_ = DISABLED_INITIALIZED;
413 } else {
414 // If we are supposed to level the shooter, set it to level, and wait
415 // until it is very close to level.
416 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
417 if (IsArmNear(kLooseTolerance, kTightTolerance)) {
418 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
419 }
420 }
421 break;
422
423 case LOW_ARM_ZERO_LIFT_SHOULDER:
424 if (disable) {
425 state_ = DISABLED_INITIALIZED;
426 } else {
427 // Decide where to move to. We need to move far enough to see an index
428 // pulse, but must also get high enough that we can safely level the
429 // shooter.
430 if (last_state_ != LOW_ARM_ZERO_LIFT_SHOULDER) {
431 arm_.set_unprofiled_goal(
432 MoveButKeepAbove(kShoulderWristClearAngle, arm_.shoulder_angle(),
433 ::std::max(kWristEncoderIndexDifference,
434 kShoulderEncoderIndexDifference) *
435 2.5),
436 arm_.unprofiled_goal(2, 0));
437 }
438
439 // Wait until we are level and then go for it.
440 if (IsArmNear(kLooseTolerance)) {
441 state_ = LOW_ARM_ZERO_LEVEL_SHOOTER;
442 }
443 }
444 break;
445
446 case LOW_ARM_ZERO_LEVEL_SHOOTER:
447 if (disable) {
448 state_ = DISABLED_INITIALIZED;
449 } else {
450 // Move the shooter level (and keep the same height). We don't want to
451 // got to RUNNING until we are completely level so that we don't
452 // give control back in a weird case where we might crash.
453 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
454 if (IsArmNear(kLooseTolerance)) {
455 if (arm_.zeroed() && intake_.zeroed()) {
456 state_ = RUNNING;
457 } else {
458 LOG(ERROR,
459 "Failed to zero while executing the LOW_ARM_ZERO sequence. "
460 "Arm: %d Intake %d\n",
461 arm_.zeroed(), intake_.zeroed());
462 state_ = ESTOP;
463 }
464 }
465 }
466 break;
467
Austin Schuhb1d682b2016-02-16 13:07:44 -0800468 // These 4 cases are very similar.
Adam Snaider06779722016-02-14 15:26:22 -0800469 case SLOW_RUNNING:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800470 case RUNNING:
Austin Schuhb1d682b2016-02-16 13:07:44 -0800471 case LANDING_SLOW_RUNNING:
472 case LANDING_RUNNING: {
Austin Schuh829fe572016-02-14 21:41:16 -0800473 if (disable) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800474 // If we are disabled, go to slow running (or landing slow running) if
475 // we are collided.
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800476 if (is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800477 if (state_ == RUNNING) {
478 state_ = SLOW_RUNNING;
479 } else if (state_ == LANDING_RUNNING) {
480 state_ = LANDING_SLOW_RUNNING;
481 }
482 }
Austin Schuh829fe572016-02-14 21:41:16 -0800483
Austin Schuhb1d682b2016-02-16 13:07:44 -0800484 // Reset the profile to the current position so it moves well from here.
Austin Schuh829fe572016-02-14 21:41:16 -0800485 intake_.ForceGoal(intake_.angle());
486 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
487 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800488 // If we are in slow_running and are no longer collided, let 'er rip.
Austin Schuh829fe572016-02-14 21:41:16 -0800489 if (state_ == SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800490 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800491 state_ = RUNNING;
492 }
493 } else if (state_ == LANDING_SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800494 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800495 state_ = LANDING_RUNNING;
496 }
Austin Schuh829fe572016-02-14 21:41:16 -0800497 }
498 }
499
Austin Schuhb1d682b2016-02-16 13:07:44 -0800500 double requested_shoulder = constants::Values::kShoulderRange.lower;
501 double requested_wrist = 0.0;
502 double requested_intake = M_PI / 2.0;
503
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800504 if (unsafe_goal) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800505 // If we are in one of the landing states, limit the accelerations and
506 // velocities to land cleanly.
507 if (state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING) {
508 arm_.AdjustProfile(0.5, // Shoulder Velocity
509 4.0, // Shoulder acceleration,
510 4.0, // Wrist velocity
511 10.0); // Wrist acceleration.
512 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
513 unsafe_goal->max_angular_acceleration_intake);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800514
Austin Schuhb1d682b2016-02-16 13:07:44 -0800515 requested_shoulder =
516 ::std::max(unsafe_goal->angle_shoulder,
517 constants::Values::kShoulderRange.lower);
518 requested_wrist = 0.0;
519 requested_intake = unsafe_goal->angle_intake;
Austin Schuha1dd5272016-02-16 15:39:38 -0800520 // Transition to landing once the profile is close to finished for the
521 // shoulder.
522 if (arm_.goal(0, 0) > kShoulderTransitionToLanded + 1e-4 ||
523 arm_.unprofiled_goal(0, 0) > kShoulderTransitionToLanded + 1e-4) {
524 if (state_ == LANDING_RUNNING) {
525 state_ = RUNNING;
526 } else {
527 state_ = SLOW_RUNNING;
528 }
529 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000530 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800531 // Otherwise, give the user what he asked for.
532 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder,
533 unsafe_goal->max_angular_acceleration_shoulder,
534 unsafe_goal->max_angular_velocity_wrist,
535 unsafe_goal->max_angular_acceleration_wrist);
536 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
537 unsafe_goal->max_angular_acceleration_intake);
538
539 // Except, don't let the shoulder go all the way down.
540 requested_shoulder = ::std::max(unsafe_goal->angle_shoulder,
541 kShoulderTransitionToLanded);
542 requested_wrist = unsafe_goal->angle_wrist;
543 requested_intake = unsafe_goal->angle_intake;
544
545 // Transition to landing once the profile is close to finished for the
546 // shoulder.
Austin Schuha1dd5272016-02-16 15:39:38 -0800547 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 &&
548 arm_.unprofiled_goal(0, 0) <=
549 kShoulderTransitionToLanded + 1e-4) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800550 if (state_ == RUNNING) {
551 state_ = LANDING_RUNNING;
552 } else {
553 state_ = LANDING_SLOW_RUNNING;
554 }
555 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000556 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800557 }
558
Austin Schuhb1d682b2016-02-16 13:07:44 -0800559 // Push the request out to hardware!
560 collision_avoidance_.UpdateGoal(requested_shoulder, requested_wrist,
561 requested_intake);
562
563 // ESTOP if we hit the hard limits.
564 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
565 state_ = ESTOP;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800566 }
Austin Schuhb1d682b2016-02-16 13:07:44 -0800567 } break;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800568
569 case ESTOP:
570 LOG(ERROR, "Estop\n");
571 disable = true;
572 break;
573 }
574
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800575 // Set the voltage limits.
Comran Morshed011f7d92016-02-16 23:03:06 +0000576 const double max_voltage = (state_ == RUNNING || state_ == LANDING_RUNNING)
577 ? kOperatingVoltage
578 : kZeroingVoltage;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800579 arm_.set_max_voltage(max_voltage, max_voltage);
580 intake_.set_max_voltage(max_voltage);
581
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800582 if (IsRunning()) {
583 // We don't want lots of negative voltage when we are near the bellypan on
584 // the shoulder...
585 // TODO(austin): Do I want to push negative power into the belly pan at this
586 // point? Maybe just put the goal slightly below the bellypan and call that
587 // good enough.
Austin Schuh6ca0f792016-03-12 14:06:14 -0800588 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 ||
589 arm_.X_hat(0, 0) <= kShoulderTransitionToLanded + 1e-4) {
Comran Morshed011f7d92016-02-16 23:03:06 +0000590 arm_.set_shoulder_asymetric_limits(kLandingShoulderDownVoltage,
591 max_voltage);
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800592 }
593 }
594
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800595 // Calculate the loops for a cycle.
Austin Schuhbe041152016-02-28 22:01:52 -0800596 {
597 Eigen::Matrix<double, 3, 1> error = intake_.controller().error();
598 status->intake.position_power = intake_.controller().K(0, 0) * error(0, 0);
599 status->intake.velocity_power = intake_.controller().K(0, 1) * error(1, 0);
600 }
601
602 {
603 Eigen::Matrix<double, 6, 1> error = arm_.controller().error();
604 status->shoulder.position_power = arm_.controller().K(0, 0) * error(0, 0);
605 status->shoulder.velocity_power = arm_.controller().K(0, 1) * error(1, 0);
606 status->wrist.position_power = arm_.controller().K(0, 2) * error(2, 0);
607 status->wrist.velocity_power = arm_.controller().K(0, 3) * error(3, 0);
608 }
609
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800610 arm_.Update(disable);
611 intake_.Update(disable);
612
613 // Write out all the voltages.
614 if (output) {
615 output->voltage_intake = intake_.intake_voltage();
616 output->voltage_shoulder = arm_.shoulder_voltage();
617 output->voltage_wrist = arm_.wrist_voltage();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000618
619 // Logic to run our rollers on the intake.
Campbell Crowleyd4fd6552016-02-21 17:53:46 -0800620 output->voltage_top_rollers = 0.0;
621 output->voltage_bottom_rollers = 0.0;
Comran Morshedf4cd7642016-02-15 20:40:49 +0000622 if (unsafe_goal) {
Campbell Crowleyd4fd6552016-02-21 17:53:46 -0800623 output->voltage_top_rollers = ::std::max(
624 -kMaxIntakeTopVoltage,
625 ::std::min(unsafe_goal->voltage_top_rollers, kMaxIntakeTopVoltage));
626 output->voltage_bottom_rollers = ::std::max(
627 -kMaxIntakeBottomVoltage,
628 ::std::min(unsafe_goal->voltage_bottom_rollers, kMaxIntakeBottomVoltage));
Comran Morshedf4cd7642016-02-15 20:40:49 +0000629 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800630 }
631
632 // Save debug/internal state.
Austin Schuha987f8b2016-02-28 22:02:20 -0800633 status->zeroed = arm_.zeroed() && intake_.zeroed();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800634
635 status->shoulder.angle = arm_.X_hat(0, 0);
636 status->shoulder.angular_velocity = arm_.X_hat(1, 0);
637 status->shoulder.goal_angle = arm_.goal(0, 0);
638 status->shoulder.goal_angular_velocity = arm_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800639 status->shoulder.unprofiled_goal_angle = arm_.unprofiled_goal(0, 0);
640 status->shoulder.unprofiled_goal_angular_velocity =
641 arm_.unprofiled_goal(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800642 status->shoulder.voltage_error = arm_.X_hat(4, 0);
643 status->shoulder.calculated_velocity =
644 (arm_.shoulder_angle() - last_shoulder_angle_) / 0.005;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800645 status->shoulder.estimator_state = arm_.ShoulderEstimatorState();
646
647 status->wrist.angle = arm_.X_hat(2, 0);
648 status->wrist.angular_velocity = arm_.X_hat(3, 0);
649 status->wrist.goal_angle = arm_.goal(2, 0);
650 status->wrist.goal_angular_velocity = arm_.goal(3, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800651 status->wrist.unprofiled_goal_angle = arm_.unprofiled_goal(2, 0);
652 status->wrist.unprofiled_goal_angular_velocity = arm_.unprofiled_goal(3, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800653 status->wrist.voltage_error = arm_.X_hat(5, 0);
654 status->wrist.calculated_velocity =
655 (arm_.wrist_angle() - last_wrist_angle_) / 0.005;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800656 status->wrist.estimator_state = arm_.WristEstimatorState();
657
658 status->intake.angle = intake_.X_hat(0, 0);
659 status->intake.angular_velocity = intake_.X_hat(1, 0);
660 status->intake.goal_angle = intake_.goal(0, 0);
661 status->intake.goal_angular_velocity = intake_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800662 status->intake.unprofiled_goal_angle = intake_.unprofiled_goal(0, 0);
663 status->intake.unprofiled_goal_angular_velocity =
664 intake_.unprofiled_goal(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800665 status->intake.calculated_velocity = (intake_.angle() - last_intake_angle_) / 0.005;
666 status->intake.voltage_error = intake_.X_hat(2, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800667 status->intake.estimator_state = intake_.IntakeEstimatorState();
Austin Schuhbe041152016-02-28 22:01:52 -0800668 status->intake.feedforwards_power = intake_.controller().ff_U(0, 0);
669
Austin Schuhf59b6bc2016-03-11 21:26:19 -0800670 status->shoulder_controller_index = arm_.controller_index();
671
Austin Schuhbe041152016-02-28 22:01:52 -0800672 last_shoulder_angle_ = arm_.shoulder_angle();
673 last_wrist_angle_ = arm_.wrist_angle();
674 last_intake_angle_ = intake_.angle();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800675
676 status->estopped = (state_ == ESTOP);
677
678 status->state = state_;
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800679 status->is_collided = is_collided;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800680
Adam Snaider06779722016-02-14 15:26:22 -0800681 last_state_ = state_before_switch;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800682}
683
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000684constexpr double Superstructure::kZeroingVoltage;
685constexpr double Superstructure::kOperatingVoltage;
Austin Schuh6ca0f792016-03-12 14:06:14 -0800686constexpr double Superstructure::kLandingShoulderDownVoltage;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000687constexpr double Superstructure::kShoulderMiddleAngle;
688constexpr double Superstructure::kLooseTolerance;
689constexpr double Superstructure::kIntakeUpperClear;
690constexpr double Superstructure::kIntakeLowerClear;
691constexpr double Superstructure::kShoulderUpAngle;
692constexpr double Superstructure::kShoulderLanded;
693constexpr double Superstructure::kTightTolerance;
694constexpr double Superstructure::kWristAlmostLevel;
695constexpr double Superstructure::kShoulderWristClearAngle;
Austin Schuhb1d682b2016-02-16 13:07:44 -0800696constexpr double Superstructure::kShoulderTransitionToLanded;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000697
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800698} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000699} // namespace control_loops
700} // namespace y2016