blob: 0e97762e3af9c3f26e6e6d3d4c76c98d35dc595d [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 {
Comran Morshedf4cd7642016-02-15 20:40:49 +000017constexpr double kZeroingVoltage = 4.0;
18
Adam Snaider06779722016-02-14 15:26:22 -080019constexpr double kIntakeEncoderIndexDifference =
20 constants::Values::kIntakeEncoderIndexDifference;
21constexpr double kWristEncoderIndexDifference =
22 constants::Values::kWristEncoderIndexDifference;
23constexpr double kShoulderEncoderIndexDifference =
24 constants::Values::kShoulderEncoderIndexDifference;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080025} // namespace
26
Philipp Schrader0119eb12016-02-15 18:16:21 +000027// ///// CollisionAvoidance /////
28
29void CollisionAvoidance::UpdateGoal(double shoulder_angle_goal,
30 double wrist_angle_goal,
31 double intake_angle_goal) {
32 double shoulder_angle = arm_->shoulder_angle();
33 double wrist_angle = arm_->wrist_angle();
34 double intake_angle = intake_->angle();
35
36 // TODO(phil): This may need tuning to account for bounciness in the limbs or
37 // some other thing that I haven't thought of. At the very least,
38 // incorporating a small safety margin makes writing test cases much easier
39 // since you can directly compare statuses against the constants in the
40 // CollisionAvoidance class.
41 constexpr double kSafetyMargin = 0.01; // radians
42
43 // Avoid colliding the shooter with the frame.
44 // If the shoulder is below a certain angle or we want to move it below
45 // that angle, then the shooter has to stay level to the ground. Otherwise,
46 // it will crash into the frame.
47 if (shoulder_angle < kMinShoulderAngleForHorizontalShooter ||
48 shoulder_angle_goal < kMinShoulderAngleForHorizontalShooter) {
49 wrist_angle_goal = 0.0;
50
51 // Make sure that we don't move the shoulder below a certain angle until
52 // the wrist is level with the ground.
53 if (::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
54 shoulder_angle_goal =
55 ::std::max(shoulder_angle_goal,
56 kMinShoulderAngleForHorizontalShooter + kSafetyMargin);
57 }
58 }
59
60 // Is the arm where it could interfere with the intake right now?
61 bool shoulder_is_in_danger =
62 (shoulder_angle < kMinShoulderAngleForIntakeInterference &&
63 shoulder_angle > kMaxShoulderAngleUntilSafeIntakeStowing);
64
65 // Is the arm moving into collision zone from above?
66 bool shoulder_moving_into_danger_from_above =
67 (shoulder_angle >= kMinShoulderAngleForIntakeInterference &&
68 shoulder_angle_goal <= kMinShoulderAngleForIntakeInterference);
69
70 // Is the arm moving into collision zone from below?
71 bool shoulder_moving_into_danger_from_below =
72 (shoulder_angle <= kMaxShoulderAngleUntilSafeIntakeStowing &&
73 shoulder_angle_goal >= kMaxShoulderAngleUntilSafeIntakeStowing);
74
75 // Avoid colliding the arm with the intake.
76 if (shoulder_is_in_danger || shoulder_moving_into_danger_from_above ||
77 shoulder_moving_into_danger_from_below) {
78 // If the arm could collide with the intake, we make sure to move the
79 // intake out of the way. The arm has priority.
80 intake_angle_goal =
81 ::std::min(intake_angle_goal,
82 kMaxIntakeAngleBeforeArmInterference - kSafetyMargin);
83
84 // Don't let the shoulder move into the collision area until the intake is
85 // out of the way.
86 if (intake_angle > kMaxIntakeAngleBeforeArmInterference) {
87 const double kHalfwayPointBetweenSafeZones =
88 (kMinShoulderAngleForIntakeInterference +
89 kMaxShoulderAngleUntilSafeIntakeStowing) /
90 2.0;
91
92 if (shoulder_angle >= kHalfwayPointBetweenSafeZones) {
93 // The shoulder is closer to being above the collision area. Move it up
94 // there.
95 shoulder_angle_goal =
96 ::std::max(shoulder_angle_goal,
97 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
98 } else {
99 // The shoulder is closer to being below the collision zone (i.e. in
100 // stowing/intake position), keep it there for now.
101 shoulder_angle_goal =
102 ::std::min(shoulder_angle_goal,
103 kMaxShoulderAngleUntilSafeIntakeStowing - kSafetyMargin);
104 }
105 }
106 }
107
108 // Send the possibly adjusted goals to the components.
109 arm_->set_unprofiled_goal(shoulder_angle_goal, wrist_angle_goal);
110 intake_->set_unprofiled_goal(intake_angle_goal);
111}
112
Philipp Schrader07147532016-02-16 01:23:07 +0000113bool CollisionAvoidance::collided() const {
114 return collided_with_given_angles(arm_->shoulder_angle(), arm_->wrist_angle(),
115 intake_->angle());
116}
117
118bool CollisionAvoidance::collided_with_given_angles(double shoulder_angle,
119 double wrist_angle,
120 double intake_angle) {
121 // The arm and the intake must not hit.
122 if (shoulder_angle >=
123 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
124 shoulder_angle <=
125 CollisionAvoidance::kMinShoulderAngleForIntakeInterference &&
126 intake_angle > CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference) {
Austin Schuhc85cdd52016-02-16 13:05:35 -0800127 LOG(DEBUG, "Collided: Intake %f > %f, and shoulder %f < %f < %f.\n", intake_angle,
128 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
129 CollisionAvoidance::kMinShoulderAngleForIntakeInterference,
130 shoulder_angle,
131 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing);
Philipp Schrader07147532016-02-16 01:23:07 +0000132 return true;
133 }
134
135 // The wrist must go back to zero when the shoulder is moving the arm into
136 // a stowed/intaking position.
137 if (shoulder_angle <
138 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter &&
139 ::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
Austin Schuhc85cdd52016-02-16 13:05:35 -0800140 LOG(DEBUG, "Collided: Shoulder %f < %f and wrist |%f| < %f.\n",
141 shoulder_angle,
142 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter, wrist_angle,
143 kMaxWristAngleForSafeArmStowing);
Philipp Schrader07147532016-02-16 01:23:07 +0000144 return true;
145 }
146
147 return false;
148}
149
Philipp Schrader0119eb12016-02-15 18:16:21 +0000150constexpr double CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
151constexpr double CollisionAvoidance::kMinShoulderAngleForIntakeInterference;
152constexpr double CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
153constexpr double CollisionAvoidance::kMaxWristAngleForSafeArmStowing;
154constexpr double CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing;
155
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800156Superstructure::Superstructure(
157 control_loops::SuperstructureQueue *superstructure_queue)
158 : aos::controls::ControlLoop<control_loops::SuperstructureQueue>(
Philipp Schrader0119eb12016-02-15 18:16:21 +0000159 superstructure_queue),
160 collision_avoidance_(&intake_, &arm_) {}
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800161
Adam Snaider06779722016-02-14 15:26:22 -0800162bool Superstructure::IsArmNear(double shoulder_tolerance,
163 double wrist_tolerance) {
164 return ((arm_.unprofiled_goal() - arm_.X_hat())
165 .block<2, 1>(0, 0)
166 .lpNorm<Eigen::Infinity>() < shoulder_tolerance) &&
167 ((arm_.unprofiled_goal() - arm_.X_hat())
168 .block<4, 1>(0, 0)
169 .lpNorm<Eigen::Infinity>() < wrist_tolerance) &&
170 ((arm_.unprofiled_goal() - arm_.goal())
171 .block<4, 1>(0, 0)
172 .lpNorm<Eigen::Infinity>() < 1e-6);
173}
174
175bool Superstructure::IsArmNear(double tolerance) {
176 return ((arm_.unprofiled_goal() - arm_.X_hat())
177 .block<4, 1>(0, 0)
178 .lpNorm<Eigen::Infinity>() < tolerance) &&
179 ((arm_.unprofiled_goal() - arm_.goal())
180 .block<4, 1>(0, 0)
181 .lpNorm<Eigen::Infinity>() < 1e-6);
182}
183
184bool Superstructure::IsIntakeNear(double tolerance) {
185 return ((intake_.unprofiled_goal() - intake_.X_hat())
186 .block<2, 1>(0, 0)
187 .lpNorm<Eigen::Infinity>() < tolerance);
188}
189
190double Superstructure::MoveButKeepAbove(double reference_angle,
191 double current_angle,
192 double move_distance) {
193 return -MoveButKeepBelow(-reference_angle, -current_angle, -move_distance);
194}
195
196double Superstructure::MoveButKeepBelow(double reference_angle,
197 double current_angle,
198 double move_distance) {
199 // There are 3 interesting places to move to.
200 const double small_negative_move = current_angle - move_distance;
201 const double small_positive_move = current_angle + move_distance;
202 // And the reference angle.
203
204 // Move the the highest one that is below reference_angle.
205 if (small_negative_move > reference_angle) {
206 return reference_angle;
207 } else if (small_positive_move > reference_angle) {
208 return small_negative_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800209 } else {
Adam Snaider06779722016-02-14 15:26:22 -0800210 return small_positive_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800211 }
212}
213
214void Superstructure::RunIteration(
215 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
216 const control_loops::SuperstructureQueue::Position *position,
217 control_loops::SuperstructureQueue::Output *output,
218 control_loops::SuperstructureQueue::Status *status) {
Adam Snaider06779722016-02-14 15:26:22 -0800219 const State state_before_switch = state_;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800220 if (WasReset()) {
221 LOG(ERROR, "WPILib reset, restarting\n");
222 arm_.Reset();
223 intake_.Reset();
224 state_ = UNINITIALIZED;
225 }
226
227 // Bool to track if we should turn the motors on or not.
228 bool disable = output == nullptr;
229
230 arm_.Correct(position->shoulder, position->wrist);
231 intake_.Correct(position->intake);
232
Adam Snaider06779722016-02-14 15:26:22 -0800233 // There are 2 main zeroing paths, HIGH_ARM_ZERO and LOW_ARM_ZERO.
234 //
235 // HIGH_ARM_ZERO works by lifting the arm all the way up so it is clear,
236 // moving the shooter to be horizontal, moving the intake out, and then moving
237 // the arm back down.
238 //
239 // LOW_ARM_ZERO works by moving the intake out of the way, lifting the arm up,
240 // leveling the shooter, and then moving back down.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800241
Diana Vandenberge2843c62016-02-13 17:44:20 -0800242 if (arm_.error() || intake_.error()) {
243 state_ = ESTOP;
244 }
245
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800246 switch (state_) {
247 case UNINITIALIZED:
Adam Snaider06779722016-02-14 15:26:22 -0800248 // Wait in the uninitialized state until both the arm and intake are
249 // initialized.
250 LOG(DEBUG, "Uninitialized, waiting for intake and arm\n");
251 if (arm_.initialized() && intake_.initialized()) {
252 state_ = DISABLED_INITIALIZED;
253 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800254 disable = true;
255 break;
256
Adam Snaider06779722016-02-14 15:26:22 -0800257 case DISABLED_INITIALIZED:
258 // Wait here until we are either fully zeroed while disabled, or we become
259 // enabled. At that point, figure out if we should HIGH_ARM_ZERO or
260 // LOW_ARM_ZERO.
261 if (disable) {
262 if (arm_.zeroed() && intake_.zeroed()) {
263 state_ = SLOW_RUNNING;
264 }
265 } else {
266 if (arm_.shoulder_angle() >= kShoulderMiddleAngle) {
267 state_ = HIGH_ARM_ZERO_LIFT_ARM;
268 } else {
269 state_ = LOW_ARM_ZERO_LOWER_INTAKE;
270 }
271 }
272
273 // Set the goals to where we are now so when we start back up, we don't
274 // jump.
275 intake_.ForceGoal(intake_.angle());
276 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
277 // Set up the profile to be the zeroing profile.
278 intake_.AdjustProfile(0.5, 10);
279 arm_.AdjustProfile(0.5, 10, 0.5, 10);
280
281 // We are not ready to start doing anything yet.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800282 disable = true;
Adam Snaider06779722016-02-14 15:26:22 -0800283 break;
284
285 case HIGH_ARM_ZERO_LIFT_ARM:
286 if (disable) {
287 state_ = DISABLED_INITIALIZED;
288 } else {
289 // Raise the shoulder up out of the way.
290 arm_.set_unprofiled_goal(kShoulderUpAngle, arm_.wrist_angle());
291 if (IsArmNear(kLooseTolerance)) {
292 // Close enough, start the next move.
293 state_ = HIGH_ARM_ZERO_LEVEL_SHOOTER;
294 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800295 }
296 break;
297
Adam Snaider06779722016-02-14 15:26:22 -0800298 case HIGH_ARM_ZERO_LEVEL_SHOOTER:
299 if (disable) {
300 state_ = DISABLED_INITIALIZED;
301 } else {
302 // Move the shooter to be level.
303 arm_.set_unprofiled_goal(kShoulderUpAngle, 0.0);
304
305 if (IsArmNear(kLooseTolerance)) {
306 // Close enough, start the next move.
307 state_ = HIGH_ARM_ZERO_MOVE_INTAKE_OUT;
308 }
309 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800310 break;
311
Adam Snaider06779722016-02-14 15:26:22 -0800312 case HIGH_ARM_ZERO_MOVE_INTAKE_OUT:
313 if (disable) {
314 state_ = DISABLED_INITIALIZED;
315 } else {
316 // If we were just asked to move the intake, make sure it moves far
317 // enough.
318 if (last_state_ != HIGH_ARM_ZERO_MOVE_INTAKE_OUT) {
319 intake_.set_unprofiled_goal(
320 MoveButKeepBelow(kIntakeUpperClear, intake_.angle(),
321 kIntakeEncoderIndexDifference * 2.5));
322 }
323
324 if (IsIntakeNear(kLooseTolerance)) {
325 // Close enough, start the next move.
326 state_ = HIGH_ARM_ZERO_LOWER_ARM;
327 }
328 }
329 break;
330
331 case HIGH_ARM_ZERO_LOWER_ARM:
332 if (disable) {
333 state_ = DISABLED_INITIALIZED;
334 } else {
335 // Land the shooter in the belly-pan. It should be zeroed by the time
336 // it gets there. If not, just estop.
337 arm_.set_unprofiled_goal(kShoulderLanded, 0.0);
338 if (arm_.zeroed() && intake_.zeroed()) {
339 state_ = RUNNING;
340 } else if (IsArmNear(kLooseTolerance)) {
341 LOG(ERROR,
342 "Failed to zero while executing the HIGH_ARM_ZERO sequence. "
343 "Arm: %d Intake %d\n",
344 arm_.zeroed(), intake_.zeroed());
345 state_ = ESTOP;
346 }
347 }
348 break;
349
350 case LOW_ARM_ZERO_LOWER_INTAKE:
351 if (disable) {
352 state_ = DISABLED_INITIALIZED;
353 } else {
354 // Move the intake down out of the way of the arm. Make sure to move it
355 // far enough to zero.
356 if (last_state_ != LOW_ARM_ZERO_LOWER_INTAKE) {
357 intake_.set_unprofiled_goal(
358 MoveButKeepBelow(kIntakeLowerClear, intake_.angle(),
359 kIntakeEncoderIndexDifference * 2.5));
360 }
361 if (IsIntakeNear(kLooseTolerance)) {
362 if (::std::abs(arm_.wrist_angle()) < kWristAlmostLevel) {
363 state_ = LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER;
364 } else {
365 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
366 }
367 }
368 }
369 break;
370
371 case LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER:
372 if (disable) {
373 state_ = DISABLED_INITIALIZED;
374 } else {
375 // If we are supposed to level the shooter, set it to level, and wait
376 // until it is very close to level.
377 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
378 if (IsArmNear(kLooseTolerance, kTightTolerance)) {
379 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
380 }
381 }
382 break;
383
384 case LOW_ARM_ZERO_LIFT_SHOULDER:
385 if (disable) {
386 state_ = DISABLED_INITIALIZED;
387 } else {
388 // Decide where to move to. We need to move far enough to see an index
389 // pulse, but must also get high enough that we can safely level the
390 // shooter.
391 if (last_state_ != LOW_ARM_ZERO_LIFT_SHOULDER) {
392 arm_.set_unprofiled_goal(
393 MoveButKeepAbove(kShoulderWristClearAngle, arm_.shoulder_angle(),
394 ::std::max(kWristEncoderIndexDifference,
395 kShoulderEncoderIndexDifference) *
396 2.5),
397 arm_.unprofiled_goal(2, 0));
398 }
399
400 // Wait until we are level and then go for it.
401 if (IsArmNear(kLooseTolerance)) {
402 state_ = LOW_ARM_ZERO_LEVEL_SHOOTER;
403 }
404 }
405 break;
406
407 case LOW_ARM_ZERO_LEVEL_SHOOTER:
408 if (disable) {
409 state_ = DISABLED_INITIALIZED;
410 } else {
411 // Move the shooter level (and keep the same height). We don't want to
412 // got to RUNNING until we are completely level so that we don't
413 // give control back in a weird case where we might crash.
414 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
415 if (IsArmNear(kLooseTolerance)) {
416 if (arm_.zeroed() && intake_.zeroed()) {
417 state_ = RUNNING;
418 } else {
419 LOG(ERROR,
420 "Failed to zero while executing the LOW_ARM_ZERO sequence. "
421 "Arm: %d Intake %d\n",
422 arm_.zeroed(), intake_.zeroed());
423 state_ = ESTOP;
424 }
425 }
426 }
427 break;
428
Austin Schuhb1d682b2016-02-16 13:07:44 -0800429 // These 4 cases are very similar.
Adam Snaider06779722016-02-14 15:26:22 -0800430 case SLOW_RUNNING:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800431 case RUNNING:
Austin Schuhb1d682b2016-02-16 13:07:44 -0800432 case LANDING_SLOW_RUNNING:
433 case LANDING_RUNNING: {
Austin Schuh829fe572016-02-14 21:41:16 -0800434 if (disable) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800435 // If we are disabled, go to slow running (or landing slow running) if
436 // we are collided.
437 if (collided()) {
438 if (state_ == RUNNING) {
439 state_ = SLOW_RUNNING;
440 } else if (state_ == LANDING_RUNNING) {
441 state_ = LANDING_SLOW_RUNNING;
442 }
443 }
Austin Schuh829fe572016-02-14 21:41:16 -0800444
Austin Schuhb1d682b2016-02-16 13:07:44 -0800445 // Reset the profile to the current position so it moves well from here.
Austin Schuh829fe572016-02-14 21:41:16 -0800446 intake_.ForceGoal(intake_.angle());
447 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
448 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800449 // If we are in slow_running and are no longer collided, let 'er rip.
Austin Schuh829fe572016-02-14 21:41:16 -0800450 if (state_ == SLOW_RUNNING) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800451 if (!collided()) {
452 state_ = RUNNING;
453 }
454 } else if (state_ == LANDING_SLOW_RUNNING) {
455 if (!collided()) {
456 state_ = LANDING_RUNNING;
457 }
Austin Schuh829fe572016-02-14 21:41:16 -0800458 }
459 }
460
Austin Schuhb1d682b2016-02-16 13:07:44 -0800461 double requested_shoulder = constants::Values::kShoulderRange.lower;
462 double requested_wrist = 0.0;
463 double requested_intake = M_PI / 2.0;
464
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800465 if (unsafe_goal) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800466 // If we are in one of the landing states, limit the accelerations and
467 // velocities to land cleanly.
468 if (state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING) {
469 arm_.AdjustProfile(0.5, // Shoulder Velocity
470 4.0, // Shoulder acceleration,
471 4.0, // Wrist velocity
472 10.0); // Wrist acceleration.
473 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
474 unsafe_goal->max_angular_acceleration_intake);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800475
Austin Schuhb1d682b2016-02-16 13:07:44 -0800476 requested_shoulder =
477 ::std::max(unsafe_goal->angle_shoulder,
478 constants::Values::kShoulderRange.lower);
479 requested_wrist = 0.0;
480 requested_intake = unsafe_goal->angle_intake;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000481 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800482 // Otherwise, give the user what he asked for.
483 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder,
484 unsafe_goal->max_angular_acceleration_shoulder,
485 unsafe_goal->max_angular_velocity_wrist,
486 unsafe_goal->max_angular_acceleration_wrist);
487 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
488 unsafe_goal->max_angular_acceleration_intake);
489
490 // Except, don't let the shoulder go all the way down.
491 requested_shoulder = ::std::max(unsafe_goal->angle_shoulder,
492 kShoulderTransitionToLanded);
493 requested_wrist = unsafe_goal->angle_wrist;
494 requested_intake = unsafe_goal->angle_intake;
495
496 // Transition to landing once the profile is close to finished for the
497 // shoulder.
498 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4) {
499 if (state_ == RUNNING) {
500 state_ = LANDING_RUNNING;
501 } else {
502 state_ = LANDING_SLOW_RUNNING;
503 }
504 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000505 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800506 }
507
Austin Schuhb1d682b2016-02-16 13:07:44 -0800508 // Push the request out to hardware!
509 collision_avoidance_.UpdateGoal(requested_shoulder, requested_wrist,
510 requested_intake);
511
512 // ESTOP if we hit the hard limits.
513 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
514 state_ = ESTOP;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800515 }
Austin Schuhb1d682b2016-02-16 13:07:44 -0800516 } break;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800517
518 case ESTOP:
519 LOG(ERROR, "Estop\n");
520 disable = true;
521 break;
522 }
523
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800524 // Set the voltage limits.
Austin Schuhb1d682b2016-02-16 13:07:44 -0800525 const double max_voltage =
526 (state_ == RUNNING || state_ == LANDING_RUNNING) ? 12.0 : kZeroingVoltage;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800527 arm_.set_max_voltage(max_voltage, max_voltage);
528 intake_.set_max_voltage(max_voltage);
529
530 // Calculate the loops for a cycle.
531 arm_.Update(disable);
532 intake_.Update(disable);
533
534 // Write out all the voltages.
535 if (output) {
536 output->voltage_intake = intake_.intake_voltage();
537 output->voltage_shoulder = arm_.shoulder_voltage();
538 output->voltage_wrist = arm_.wrist_voltage();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000539
540 // Logic to run our rollers on the intake.
541 output->voltage_rollers = 0.0;
542 if (unsafe_goal) {
543 output->voltage_rollers =
544 ::std::max(-8.0, ::std::min(8.0, unsafe_goal->voltage_rollers));
545 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800546 }
547
548 // Save debug/internal state.
549 // TODO(austin): Save the voltage errors.
Austin Schuhb1d682b2016-02-16 13:07:44 -0800550 status->zeroed = (state_ == RUNNING || state_ == LANDING_RUNNING ||
551 state_ == SLOW_RUNNING || state_ == LANDING_SLOW_RUNNING);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800552
553 status->shoulder.angle = arm_.X_hat(0, 0);
554 status->shoulder.angular_velocity = arm_.X_hat(1, 0);
555 status->shoulder.goal_angle = arm_.goal(0, 0);
556 status->shoulder.goal_angular_velocity = arm_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800557 status->shoulder.unprofiled_goal_angle = arm_.unprofiled_goal(0, 0);
558 status->shoulder.unprofiled_goal_angular_velocity =
559 arm_.unprofiled_goal(1, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800560 status->shoulder.estimator_state = arm_.ShoulderEstimatorState();
561
562 status->wrist.angle = arm_.X_hat(2, 0);
563 status->wrist.angular_velocity = arm_.X_hat(3, 0);
564 status->wrist.goal_angle = arm_.goal(2, 0);
565 status->wrist.goal_angular_velocity = arm_.goal(3, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800566 status->wrist.unprofiled_goal_angle = arm_.unprofiled_goal(2, 0);
567 status->wrist.unprofiled_goal_angular_velocity = arm_.unprofiled_goal(3, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800568 status->wrist.estimator_state = arm_.WristEstimatorState();
569
570 status->intake.angle = intake_.X_hat(0, 0);
571 status->intake.angular_velocity = intake_.X_hat(1, 0);
572 status->intake.goal_angle = intake_.goal(0, 0);
573 status->intake.goal_angular_velocity = intake_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800574 status->intake.unprofiled_goal_angle = intake_.unprofiled_goal(0, 0);
575 status->intake.unprofiled_goal_angular_velocity =
576 intake_.unprofiled_goal(1, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800577 status->intake.estimator_state = intake_.IntakeEstimatorState();
578
579 status->estopped = (state_ == ESTOP);
580
581 status->state = state_;
582
Adam Snaider06779722016-02-14 15:26:22 -0800583 last_state_ = state_before_switch;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800584}
585
Philipp Schrader0119eb12016-02-15 18:16:21 +0000586constexpr double Superstructure::kShoulderMiddleAngle;
587constexpr double Superstructure::kLooseTolerance;
588constexpr double Superstructure::kIntakeUpperClear;
589constexpr double Superstructure::kIntakeLowerClear;
590constexpr double Superstructure::kShoulderUpAngle;
591constexpr double Superstructure::kShoulderLanded;
592constexpr double Superstructure::kTightTolerance;
593constexpr double Superstructure::kWristAlmostLevel;
594constexpr double Superstructure::kShoulderWristClearAngle;
Austin Schuhb1d682b2016-02-16 13:07:44 -0800595constexpr double Superstructure::kShoulderTransitionToLanded;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000596
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800597} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000598} // namespace control_loops
599} // namespace y2016