blob: 925faab944e10d2ed54678117101d4a5d1bbc280 [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 Schuhbe041152016-02-28 22:01:52 -080017constexpr double kLandingShoulderDownVoltage = -2.0;
Austin Schuh2d7820b2016-02-16 13:47:42 -080018// The maximum voltage the intake roller will be allowed to use.
Austin Schuhbe041152016-02-28 22:01:52 -080019constexpr float kMaxIntakeTopVoltage = 12.0;
20constexpr float kMaxIntakeBottomVoltage = 12.0;
Comran Morshedf4cd7642016-02-15 20:40:49 +000021
Austin Schuh2d7820b2016-02-16 13:47:42 -080022// Aliases to reduce typing.
Adam Snaider06779722016-02-14 15:26:22 -080023constexpr double kIntakeEncoderIndexDifference =
24 constants::Values::kIntakeEncoderIndexDifference;
25constexpr double kWristEncoderIndexDifference =
26 constants::Values::kWristEncoderIndexDifference;
27constexpr double kShoulderEncoderIndexDifference =
28 constants::Values::kShoulderEncoderIndexDifference;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080029} // namespace
30
Philipp Schrader0119eb12016-02-15 18:16:21 +000031// ///// CollisionAvoidance /////
32
33void CollisionAvoidance::UpdateGoal(double shoulder_angle_goal,
34 double wrist_angle_goal,
35 double intake_angle_goal) {
36 double shoulder_angle = arm_->shoulder_angle();
37 double wrist_angle = arm_->wrist_angle();
38 double intake_angle = intake_->angle();
39
40 // TODO(phil): This may need tuning to account for bounciness in the limbs or
41 // some other thing that I haven't thought of. At the very least,
42 // incorporating a small safety margin makes writing test cases much easier
43 // since you can directly compare statuses against the constants in the
44 // CollisionAvoidance class.
45 constexpr double kSafetyMargin = 0.01; // radians
46
47 // Avoid colliding the shooter with the frame.
48 // If the shoulder is below a certain angle or we want to move it below
49 // that angle, then the shooter has to stay level to the ground. Otherwise,
50 // it will crash into the frame.
51 if (shoulder_angle < kMinShoulderAngleForHorizontalShooter ||
52 shoulder_angle_goal < kMinShoulderAngleForHorizontalShooter) {
53 wrist_angle_goal = 0.0;
54
55 // Make sure that we don't move the shoulder below a certain angle until
56 // the wrist is level with the ground.
57 if (::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
58 shoulder_angle_goal =
59 ::std::max(shoulder_angle_goal,
60 kMinShoulderAngleForHorizontalShooter + kSafetyMargin);
61 }
62 }
63
64 // Is the arm where it could interfere with the intake right now?
65 bool shoulder_is_in_danger =
66 (shoulder_angle < kMinShoulderAngleForIntakeInterference &&
67 shoulder_angle > kMaxShoulderAngleUntilSafeIntakeStowing);
68
69 // Is the arm moving into collision zone from above?
70 bool shoulder_moving_into_danger_from_above =
71 (shoulder_angle >= kMinShoulderAngleForIntakeInterference &&
72 shoulder_angle_goal <= kMinShoulderAngleForIntakeInterference);
73
74 // Is the arm moving into collision zone from below?
75 bool shoulder_moving_into_danger_from_below =
76 (shoulder_angle <= kMaxShoulderAngleUntilSafeIntakeStowing &&
77 shoulder_angle_goal >= kMaxShoulderAngleUntilSafeIntakeStowing);
78
79 // Avoid colliding the arm with the intake.
80 if (shoulder_is_in_danger || shoulder_moving_into_danger_from_above ||
81 shoulder_moving_into_danger_from_below) {
82 // If the arm could collide with the intake, we make sure to move the
83 // intake out of the way. The arm has priority.
84 intake_angle_goal =
85 ::std::min(intake_angle_goal,
86 kMaxIntakeAngleBeforeArmInterference - kSafetyMargin);
87
88 // Don't let the shoulder move into the collision area until the intake is
89 // out of the way.
90 if (intake_angle > kMaxIntakeAngleBeforeArmInterference) {
91 const double kHalfwayPointBetweenSafeZones =
92 (kMinShoulderAngleForIntakeInterference +
93 kMaxShoulderAngleUntilSafeIntakeStowing) /
94 2.0;
95
96 if (shoulder_angle >= kHalfwayPointBetweenSafeZones) {
97 // The shoulder is closer to being above the collision area. Move it up
98 // there.
99 shoulder_angle_goal =
100 ::std::max(shoulder_angle_goal,
101 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
102 } else {
103 // The shoulder is closer to being below the collision zone (i.e. in
104 // stowing/intake position), keep it there for now.
105 shoulder_angle_goal =
106 ::std::min(shoulder_angle_goal,
107 kMaxShoulderAngleUntilSafeIntakeStowing - kSafetyMargin);
108 }
109 }
110 }
111
112 // Send the possibly adjusted goals to the components.
113 arm_->set_unprofiled_goal(shoulder_angle_goal, wrist_angle_goal);
114 intake_->set_unprofiled_goal(intake_angle_goal);
115}
116
Philipp Schrader07147532016-02-16 01:23:07 +0000117bool CollisionAvoidance::collided() const {
118 return collided_with_given_angles(arm_->shoulder_angle(), arm_->wrist_angle(),
119 intake_->angle());
120}
121
122bool CollisionAvoidance::collided_with_given_angles(double shoulder_angle,
123 double wrist_angle,
124 double intake_angle) {
125 // The arm and the intake must not hit.
126 if (shoulder_angle >=
127 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
128 shoulder_angle <=
129 CollisionAvoidance::kMinShoulderAngleForIntakeInterference &&
130 intake_angle > CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference) {
Austin Schuhc85cdd52016-02-16 13:05:35 -0800131 LOG(DEBUG, "Collided: Intake %f > %f, and shoulder %f < %f < %f.\n", intake_angle,
132 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
133 CollisionAvoidance::kMinShoulderAngleForIntakeInterference,
134 shoulder_angle,
135 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing);
Philipp Schrader07147532016-02-16 01:23:07 +0000136 return true;
137 }
138
139 // The wrist must go back to zero when the shoulder is moving the arm into
140 // a stowed/intaking position.
141 if (shoulder_angle <
142 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter &&
143 ::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
Austin Schuhc85cdd52016-02-16 13:05:35 -0800144 LOG(DEBUG, "Collided: Shoulder %f < %f and wrist |%f| < %f.\n",
145 shoulder_angle,
146 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter, wrist_angle,
147 kMaxWristAngleForSafeArmStowing);
Philipp Schrader07147532016-02-16 01:23:07 +0000148 return true;
149 }
150
151 return false;
152}
153
Philipp Schrader0119eb12016-02-15 18:16:21 +0000154constexpr double CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
155constexpr double CollisionAvoidance::kMinShoulderAngleForIntakeInterference;
156constexpr double CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
157constexpr double CollisionAvoidance::kMaxWristAngleForSafeArmStowing;
158constexpr double CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing;
159
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800160Superstructure::Superstructure(
161 control_loops::SuperstructureQueue *superstructure_queue)
162 : aos::controls::ControlLoop<control_loops::SuperstructureQueue>(
Philipp Schrader0119eb12016-02-15 18:16:21 +0000163 superstructure_queue),
164 collision_avoidance_(&intake_, &arm_) {}
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800165
Adam Snaider06779722016-02-14 15:26:22 -0800166bool Superstructure::IsArmNear(double shoulder_tolerance,
167 double wrist_tolerance) {
168 return ((arm_.unprofiled_goal() - arm_.X_hat())
169 .block<2, 1>(0, 0)
170 .lpNorm<Eigen::Infinity>() < shoulder_tolerance) &&
171 ((arm_.unprofiled_goal() - arm_.X_hat())
172 .block<4, 1>(0, 0)
173 .lpNorm<Eigen::Infinity>() < wrist_tolerance) &&
174 ((arm_.unprofiled_goal() - arm_.goal())
175 .block<4, 1>(0, 0)
176 .lpNorm<Eigen::Infinity>() < 1e-6);
177}
178
179bool Superstructure::IsArmNear(double tolerance) {
180 return ((arm_.unprofiled_goal() - arm_.X_hat())
181 .block<4, 1>(0, 0)
182 .lpNorm<Eigen::Infinity>() < tolerance) &&
183 ((arm_.unprofiled_goal() - arm_.goal())
184 .block<4, 1>(0, 0)
185 .lpNorm<Eigen::Infinity>() < 1e-6);
186}
187
188bool Superstructure::IsIntakeNear(double tolerance) {
189 return ((intake_.unprofiled_goal() - intake_.X_hat())
190 .block<2, 1>(0, 0)
191 .lpNorm<Eigen::Infinity>() < tolerance);
192}
193
194double Superstructure::MoveButKeepAbove(double reference_angle,
195 double current_angle,
196 double move_distance) {
197 return -MoveButKeepBelow(-reference_angle, -current_angle, -move_distance);
198}
199
200double Superstructure::MoveButKeepBelow(double reference_angle,
201 double current_angle,
202 double move_distance) {
203 // There are 3 interesting places to move to.
204 const double small_negative_move = current_angle - move_distance;
205 const double small_positive_move = current_angle + move_distance;
206 // And the reference angle.
207
208 // Move the the highest one that is below reference_angle.
209 if (small_negative_move > reference_angle) {
210 return reference_angle;
211 } else if (small_positive_move > reference_angle) {
212 return small_negative_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800213 } else {
Adam Snaider06779722016-02-14 15:26:22 -0800214 return small_positive_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800215 }
216}
217
218void Superstructure::RunIteration(
219 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
220 const control_loops::SuperstructureQueue::Position *position,
221 control_loops::SuperstructureQueue::Output *output,
222 control_loops::SuperstructureQueue::Status *status) {
Adam Snaider06779722016-02-14 15:26:22 -0800223 const State state_before_switch = state_;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800224 if (WasReset()) {
225 LOG(ERROR, "WPILib reset, restarting\n");
226 arm_.Reset();
227 intake_.Reset();
228 state_ = UNINITIALIZED;
229 }
230
231 // Bool to track if we should turn the motors on or not.
232 bool disable = output == nullptr;
233
234 arm_.Correct(position->shoulder, position->wrist);
235 intake_.Correct(position->intake);
236
Adam Snaider06779722016-02-14 15:26:22 -0800237 // There are 2 main zeroing paths, HIGH_ARM_ZERO and LOW_ARM_ZERO.
238 //
239 // HIGH_ARM_ZERO works by lifting the arm all the way up so it is clear,
240 // moving the shooter to be horizontal, moving the intake out, and then moving
241 // the arm back down.
242 //
243 // LOW_ARM_ZERO works by moving the intake out of the way, lifting the arm up,
244 // leveling the shooter, and then moving back down.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800245
Diana Vandenberge2843c62016-02-13 17:44:20 -0800246 if (arm_.error() || intake_.error()) {
247 state_ = ESTOP;
248 }
249
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800250 const bool is_collided = collided();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800251 switch (state_) {
252 case UNINITIALIZED:
Adam Snaider06779722016-02-14 15:26:22 -0800253 // Wait in the uninitialized state until both the arm and intake are
254 // initialized.
255 LOG(DEBUG, "Uninitialized, waiting for intake and arm\n");
256 if (arm_.initialized() && intake_.initialized()) {
257 state_ = DISABLED_INITIALIZED;
258 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800259 disable = true;
260 break;
261
Adam Snaider06779722016-02-14 15:26:22 -0800262 case DISABLED_INITIALIZED:
263 // Wait here until we are either fully zeroed while disabled, or we become
264 // enabled. At that point, figure out if we should HIGH_ARM_ZERO or
265 // LOW_ARM_ZERO.
266 if (disable) {
267 if (arm_.zeroed() && intake_.zeroed()) {
268 state_ = SLOW_RUNNING;
269 }
270 } else {
271 if (arm_.shoulder_angle() >= kShoulderMiddleAngle) {
272 state_ = HIGH_ARM_ZERO_LIFT_ARM;
273 } else {
274 state_ = LOW_ARM_ZERO_LOWER_INTAKE;
275 }
276 }
277
278 // Set the goals to where we are now so when we start back up, we don't
279 // jump.
280 intake_.ForceGoal(intake_.angle());
281 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
282 // Set up the profile to be the zeroing profile.
283 intake_.AdjustProfile(0.5, 10);
284 arm_.AdjustProfile(0.5, 10, 0.5, 10);
285
286 // We are not ready to start doing anything yet.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800287 disable = true;
Adam Snaider06779722016-02-14 15:26:22 -0800288 break;
289
290 case HIGH_ARM_ZERO_LIFT_ARM:
291 if (disable) {
292 state_ = DISABLED_INITIALIZED;
293 } else {
294 // Raise the shoulder up out of the way.
295 arm_.set_unprofiled_goal(kShoulderUpAngle, arm_.wrist_angle());
296 if (IsArmNear(kLooseTolerance)) {
297 // Close enough, start the next move.
298 state_ = HIGH_ARM_ZERO_LEVEL_SHOOTER;
299 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800300 }
301 break;
302
Adam Snaider06779722016-02-14 15:26:22 -0800303 case HIGH_ARM_ZERO_LEVEL_SHOOTER:
304 if (disable) {
305 state_ = DISABLED_INITIALIZED;
306 } else {
307 // Move the shooter to be level.
308 arm_.set_unprofiled_goal(kShoulderUpAngle, 0.0);
309
310 if (IsArmNear(kLooseTolerance)) {
311 // Close enough, start the next move.
312 state_ = HIGH_ARM_ZERO_MOVE_INTAKE_OUT;
313 }
314 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800315 break;
316
Adam Snaider06779722016-02-14 15:26:22 -0800317 case HIGH_ARM_ZERO_MOVE_INTAKE_OUT:
318 if (disable) {
319 state_ = DISABLED_INITIALIZED;
320 } else {
321 // If we were just asked to move the intake, make sure it moves far
322 // enough.
323 if (last_state_ != HIGH_ARM_ZERO_MOVE_INTAKE_OUT) {
324 intake_.set_unprofiled_goal(
325 MoveButKeepBelow(kIntakeUpperClear, intake_.angle(),
326 kIntakeEncoderIndexDifference * 2.5));
327 }
328
329 if (IsIntakeNear(kLooseTolerance)) {
330 // Close enough, start the next move.
331 state_ = HIGH_ARM_ZERO_LOWER_ARM;
332 }
333 }
334 break;
335
336 case HIGH_ARM_ZERO_LOWER_ARM:
337 if (disable) {
338 state_ = DISABLED_INITIALIZED;
339 } else {
340 // Land the shooter in the belly-pan. It should be zeroed by the time
341 // it gets there. If not, just estop.
342 arm_.set_unprofiled_goal(kShoulderLanded, 0.0);
343 if (arm_.zeroed() && intake_.zeroed()) {
344 state_ = RUNNING;
345 } else if (IsArmNear(kLooseTolerance)) {
346 LOG(ERROR,
347 "Failed to zero while executing the HIGH_ARM_ZERO sequence. "
348 "Arm: %d Intake %d\n",
349 arm_.zeroed(), intake_.zeroed());
350 state_ = ESTOP;
351 }
352 }
353 break;
354
355 case LOW_ARM_ZERO_LOWER_INTAKE:
356 if (disable) {
357 state_ = DISABLED_INITIALIZED;
358 } else {
359 // Move the intake down out of the way of the arm. Make sure to move it
360 // far enough to zero.
361 if (last_state_ != LOW_ARM_ZERO_LOWER_INTAKE) {
362 intake_.set_unprofiled_goal(
363 MoveButKeepBelow(kIntakeLowerClear, intake_.angle(),
364 kIntakeEncoderIndexDifference * 2.5));
365 }
366 if (IsIntakeNear(kLooseTolerance)) {
367 if (::std::abs(arm_.wrist_angle()) < kWristAlmostLevel) {
368 state_ = LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER;
369 } else {
370 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
371 }
372 }
373 }
374 break;
375
376 case LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER:
377 if (disable) {
378 state_ = DISABLED_INITIALIZED;
379 } else {
380 // If we are supposed to level the shooter, set it to level, and wait
381 // until it is very close to level.
382 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
383 if (IsArmNear(kLooseTolerance, kTightTolerance)) {
384 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
385 }
386 }
387 break;
388
389 case LOW_ARM_ZERO_LIFT_SHOULDER:
390 if (disable) {
391 state_ = DISABLED_INITIALIZED;
392 } else {
393 // Decide where to move to. We need to move far enough to see an index
394 // pulse, but must also get high enough that we can safely level the
395 // shooter.
396 if (last_state_ != LOW_ARM_ZERO_LIFT_SHOULDER) {
397 arm_.set_unprofiled_goal(
398 MoveButKeepAbove(kShoulderWristClearAngle, arm_.shoulder_angle(),
399 ::std::max(kWristEncoderIndexDifference,
400 kShoulderEncoderIndexDifference) *
401 2.5),
402 arm_.unprofiled_goal(2, 0));
403 }
404
405 // Wait until we are level and then go for it.
406 if (IsArmNear(kLooseTolerance)) {
407 state_ = LOW_ARM_ZERO_LEVEL_SHOOTER;
408 }
409 }
410 break;
411
412 case LOW_ARM_ZERO_LEVEL_SHOOTER:
413 if (disable) {
414 state_ = DISABLED_INITIALIZED;
415 } else {
416 // Move the shooter level (and keep the same height). We don't want to
417 // got to RUNNING until we are completely level so that we don't
418 // give control back in a weird case where we might crash.
419 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
420 if (IsArmNear(kLooseTolerance)) {
421 if (arm_.zeroed() && intake_.zeroed()) {
422 state_ = RUNNING;
423 } else {
424 LOG(ERROR,
425 "Failed to zero while executing the LOW_ARM_ZERO sequence. "
426 "Arm: %d Intake %d\n",
427 arm_.zeroed(), intake_.zeroed());
428 state_ = ESTOP;
429 }
430 }
431 }
432 break;
433
Austin Schuhb1d682b2016-02-16 13:07:44 -0800434 // These 4 cases are very similar.
Adam Snaider06779722016-02-14 15:26:22 -0800435 case SLOW_RUNNING:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800436 case RUNNING:
Austin Schuhb1d682b2016-02-16 13:07:44 -0800437 case LANDING_SLOW_RUNNING:
438 case LANDING_RUNNING: {
Austin Schuh829fe572016-02-14 21:41:16 -0800439 if (disable) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800440 // If we are disabled, go to slow running (or landing slow running) if
441 // we are collided.
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800442 if (is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800443 if (state_ == RUNNING) {
444 state_ = SLOW_RUNNING;
445 } else if (state_ == LANDING_RUNNING) {
446 state_ = LANDING_SLOW_RUNNING;
447 }
448 }
Austin Schuh829fe572016-02-14 21:41:16 -0800449
Austin Schuhb1d682b2016-02-16 13:07:44 -0800450 // Reset the profile to the current position so it moves well from here.
Austin Schuh829fe572016-02-14 21:41:16 -0800451 intake_.ForceGoal(intake_.angle());
452 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
453 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800454 // If we are in slow_running and are no longer collided, let 'er rip.
Austin Schuh829fe572016-02-14 21:41:16 -0800455 if (state_ == SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800456 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800457 state_ = RUNNING;
458 }
459 } else if (state_ == LANDING_SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800460 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800461 state_ = LANDING_RUNNING;
462 }
Austin Schuh829fe572016-02-14 21:41:16 -0800463 }
464 }
465
Austin Schuhb1d682b2016-02-16 13:07:44 -0800466 double requested_shoulder = constants::Values::kShoulderRange.lower;
467 double requested_wrist = 0.0;
468 double requested_intake = M_PI / 2.0;
469
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800470 if (unsafe_goal) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800471 // If we are in one of the landing states, limit the accelerations and
472 // velocities to land cleanly.
473 if (state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING) {
474 arm_.AdjustProfile(0.5, // Shoulder Velocity
475 4.0, // Shoulder acceleration,
476 4.0, // Wrist velocity
477 10.0); // Wrist acceleration.
478 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
479 unsafe_goal->max_angular_acceleration_intake);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800480
Austin Schuhb1d682b2016-02-16 13:07:44 -0800481 requested_shoulder =
482 ::std::max(unsafe_goal->angle_shoulder,
483 constants::Values::kShoulderRange.lower);
484 requested_wrist = 0.0;
485 requested_intake = unsafe_goal->angle_intake;
Austin Schuha1dd5272016-02-16 15:39:38 -0800486 // Transition to landing once the profile is close to finished for the
487 // shoulder.
488 if (arm_.goal(0, 0) > kShoulderTransitionToLanded + 1e-4 ||
489 arm_.unprofiled_goal(0, 0) > kShoulderTransitionToLanded + 1e-4) {
490 if (state_ == LANDING_RUNNING) {
491 state_ = RUNNING;
492 } else {
493 state_ = SLOW_RUNNING;
494 }
495 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000496 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800497 // Otherwise, give the user what he asked for.
498 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder,
499 unsafe_goal->max_angular_acceleration_shoulder,
500 unsafe_goal->max_angular_velocity_wrist,
501 unsafe_goal->max_angular_acceleration_wrist);
502 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
503 unsafe_goal->max_angular_acceleration_intake);
504
505 // Except, don't let the shoulder go all the way down.
506 requested_shoulder = ::std::max(unsafe_goal->angle_shoulder,
507 kShoulderTransitionToLanded);
508 requested_wrist = unsafe_goal->angle_wrist;
509 requested_intake = unsafe_goal->angle_intake;
510
511 // Transition to landing once the profile is close to finished for the
512 // shoulder.
Austin Schuha1dd5272016-02-16 15:39:38 -0800513 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 &&
514 arm_.unprofiled_goal(0, 0) <=
515 kShoulderTransitionToLanded + 1e-4) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800516 if (state_ == RUNNING) {
517 state_ = LANDING_RUNNING;
518 } else {
519 state_ = LANDING_SLOW_RUNNING;
520 }
521 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000522 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800523 }
524
Austin Schuhb1d682b2016-02-16 13:07:44 -0800525 // Push the request out to hardware!
526 collision_avoidance_.UpdateGoal(requested_shoulder, requested_wrist,
527 requested_intake);
528
529 // ESTOP if we hit the hard limits.
530 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
531 state_ = ESTOP;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800532 }
Austin Schuhb1d682b2016-02-16 13:07:44 -0800533 } break;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800534
535 case ESTOP:
536 LOG(ERROR, "Estop\n");
537 disable = true;
538 break;
539 }
540
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800541 // Set the voltage limits.
Comran Morshed011f7d92016-02-16 23:03:06 +0000542 const double max_voltage = (state_ == RUNNING || state_ == LANDING_RUNNING)
543 ? kOperatingVoltage
544 : kZeroingVoltage;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800545 arm_.set_max_voltage(max_voltage, max_voltage);
546 intake_.set_max_voltage(max_voltage);
547
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800548 if (IsRunning()) {
549 // We don't want lots of negative voltage when we are near the bellypan on
550 // the shoulder...
551 // TODO(austin): Do I want to push negative power into the belly pan at this
552 // point? Maybe just put the goal slightly below the bellypan and call that
553 // good enough.
554 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4) {
Comran Morshed011f7d92016-02-16 23:03:06 +0000555 arm_.set_shoulder_asymetric_limits(kLandingShoulderDownVoltage,
556 max_voltage);
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800557 }
558 }
559
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800560 // Calculate the loops for a cycle.
Austin Schuhbe041152016-02-28 22:01:52 -0800561 {
562 Eigen::Matrix<double, 3, 1> error = intake_.controller().error();
563 status->intake.position_power = intake_.controller().K(0, 0) * error(0, 0);
564 status->intake.velocity_power = intake_.controller().K(0, 1) * error(1, 0);
565 }
566
567 {
568 Eigen::Matrix<double, 6, 1> error = arm_.controller().error();
569 status->shoulder.position_power = arm_.controller().K(0, 0) * error(0, 0);
570 status->shoulder.velocity_power = arm_.controller().K(0, 1) * error(1, 0);
571 status->wrist.position_power = arm_.controller().K(0, 2) * error(2, 0);
572 status->wrist.velocity_power = arm_.controller().K(0, 3) * error(3, 0);
573 }
574
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800575 arm_.Update(disable);
576 intake_.Update(disable);
577
578 // Write out all the voltages.
579 if (output) {
580 output->voltage_intake = intake_.intake_voltage();
581 output->voltage_shoulder = arm_.shoulder_voltage();
582 output->voltage_wrist = arm_.wrist_voltage();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000583
584 // Logic to run our rollers on the intake.
Campbell Crowleyd4fd6552016-02-21 17:53:46 -0800585 output->voltage_top_rollers = 0.0;
586 output->voltage_bottom_rollers = 0.0;
Comran Morshedf4cd7642016-02-15 20:40:49 +0000587 if (unsafe_goal) {
Campbell Crowleyd4fd6552016-02-21 17:53:46 -0800588 output->voltage_top_rollers = ::std::max(
589 -kMaxIntakeTopVoltage,
590 ::std::min(unsafe_goal->voltage_top_rollers, kMaxIntakeTopVoltage));
591 output->voltage_bottom_rollers = ::std::max(
592 -kMaxIntakeBottomVoltage,
593 ::std::min(unsafe_goal->voltage_bottom_rollers, kMaxIntakeBottomVoltage));
Comran Morshedf4cd7642016-02-15 20:40:49 +0000594 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800595 }
596
597 // Save debug/internal state.
Austin Schuha987f8b2016-02-28 22:02:20 -0800598 status->zeroed = arm_.zeroed() && intake_.zeroed();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800599
600 status->shoulder.angle = arm_.X_hat(0, 0);
601 status->shoulder.angular_velocity = arm_.X_hat(1, 0);
602 status->shoulder.goal_angle = arm_.goal(0, 0);
603 status->shoulder.goal_angular_velocity = arm_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800604 status->shoulder.unprofiled_goal_angle = arm_.unprofiled_goal(0, 0);
605 status->shoulder.unprofiled_goal_angular_velocity =
606 arm_.unprofiled_goal(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800607 status->shoulder.voltage_error = arm_.X_hat(4, 0);
608 status->shoulder.calculated_velocity =
609 (arm_.shoulder_angle() - last_shoulder_angle_) / 0.005;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800610 status->shoulder.estimator_state = arm_.ShoulderEstimatorState();
611
612 status->wrist.angle = arm_.X_hat(2, 0);
613 status->wrist.angular_velocity = arm_.X_hat(3, 0);
614 status->wrist.goal_angle = arm_.goal(2, 0);
615 status->wrist.goal_angular_velocity = arm_.goal(3, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800616 status->wrist.unprofiled_goal_angle = arm_.unprofiled_goal(2, 0);
617 status->wrist.unprofiled_goal_angular_velocity = arm_.unprofiled_goal(3, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800618 status->wrist.voltage_error = arm_.X_hat(5, 0);
619 status->wrist.calculated_velocity =
620 (arm_.wrist_angle() - last_wrist_angle_) / 0.005;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800621 status->wrist.estimator_state = arm_.WristEstimatorState();
622
623 status->intake.angle = intake_.X_hat(0, 0);
624 status->intake.angular_velocity = intake_.X_hat(1, 0);
625 status->intake.goal_angle = intake_.goal(0, 0);
626 status->intake.goal_angular_velocity = intake_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800627 status->intake.unprofiled_goal_angle = intake_.unprofiled_goal(0, 0);
628 status->intake.unprofiled_goal_angular_velocity =
629 intake_.unprofiled_goal(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800630 status->intake.calculated_velocity = (intake_.angle() - last_intake_angle_) / 0.005;
631 status->intake.voltage_error = intake_.X_hat(2, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800632 status->intake.estimator_state = intake_.IntakeEstimatorState();
Austin Schuhbe041152016-02-28 22:01:52 -0800633 status->intake.feedforwards_power = intake_.controller().ff_U(0, 0);
634
Austin Schuhf59b6bc2016-03-11 21:26:19 -0800635 status->shoulder_controller_index = arm_.controller_index();
636
Austin Schuhbe041152016-02-28 22:01:52 -0800637 last_shoulder_angle_ = arm_.shoulder_angle();
638 last_wrist_angle_ = arm_.wrist_angle();
639 last_intake_angle_ = intake_.angle();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800640
641 status->estopped = (state_ == ESTOP);
642
643 status->state = state_;
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800644 status->is_collided = is_collided;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800645
Adam Snaider06779722016-02-14 15:26:22 -0800646 last_state_ = state_before_switch;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800647}
648
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000649constexpr double Superstructure::kZeroingVoltage;
650constexpr double Superstructure::kOperatingVoltage;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000651constexpr double Superstructure::kShoulderMiddleAngle;
652constexpr double Superstructure::kLooseTolerance;
653constexpr double Superstructure::kIntakeUpperClear;
654constexpr double Superstructure::kIntakeLowerClear;
655constexpr double Superstructure::kShoulderUpAngle;
656constexpr double Superstructure::kShoulderLanded;
657constexpr double Superstructure::kTightTolerance;
658constexpr double Superstructure::kWristAlmostLevel;
659constexpr double Superstructure::kShoulderWristClearAngle;
Austin Schuhb1d682b2016-02-16 13:07:44 -0800660constexpr double Superstructure::kShoulderTransitionToLanded;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000661
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800662} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000663} // namespace control_loops
664} // namespace y2016