blob: c64fac121f84f19fa7129b9b1508ef4faa0221b1 [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 Schuh2fc10fa2016-02-08 00:44:34 -0800250 switch (state_) {
251 case UNINITIALIZED:
Adam Snaider06779722016-02-14 15:26:22 -0800252 // Wait in the uninitialized state until both the arm and intake are
253 // initialized.
254 LOG(DEBUG, "Uninitialized, waiting for intake and arm\n");
255 if (arm_.initialized() && intake_.initialized()) {
256 state_ = DISABLED_INITIALIZED;
257 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800258 disable = true;
259 break;
260
Adam Snaider06779722016-02-14 15:26:22 -0800261 case DISABLED_INITIALIZED:
262 // Wait here until we are either fully zeroed while disabled, or we become
263 // enabled. At that point, figure out if we should HIGH_ARM_ZERO or
264 // LOW_ARM_ZERO.
265 if (disable) {
266 if (arm_.zeroed() && intake_.zeroed()) {
267 state_ = SLOW_RUNNING;
268 }
269 } else {
270 if (arm_.shoulder_angle() >= kShoulderMiddleAngle) {
271 state_ = HIGH_ARM_ZERO_LIFT_ARM;
272 } else {
273 state_ = LOW_ARM_ZERO_LOWER_INTAKE;
274 }
275 }
276
277 // Set the goals to where we are now so when we start back up, we don't
278 // jump.
279 intake_.ForceGoal(intake_.angle());
280 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
281 // Set up the profile to be the zeroing profile.
282 intake_.AdjustProfile(0.5, 10);
283 arm_.AdjustProfile(0.5, 10, 0.5, 10);
284
285 // We are not ready to start doing anything yet.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800286 disable = true;
Adam Snaider06779722016-02-14 15:26:22 -0800287 break;
288
289 case HIGH_ARM_ZERO_LIFT_ARM:
290 if (disable) {
291 state_ = DISABLED_INITIALIZED;
292 } else {
293 // Raise the shoulder up out of the way.
294 arm_.set_unprofiled_goal(kShoulderUpAngle, arm_.wrist_angle());
295 if (IsArmNear(kLooseTolerance)) {
296 // Close enough, start the next move.
297 state_ = HIGH_ARM_ZERO_LEVEL_SHOOTER;
298 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800299 }
300 break;
301
Adam Snaider06779722016-02-14 15:26:22 -0800302 case HIGH_ARM_ZERO_LEVEL_SHOOTER:
303 if (disable) {
304 state_ = DISABLED_INITIALIZED;
305 } else {
306 // Move the shooter to be level.
307 arm_.set_unprofiled_goal(kShoulderUpAngle, 0.0);
308
309 if (IsArmNear(kLooseTolerance)) {
310 // Close enough, start the next move.
311 state_ = HIGH_ARM_ZERO_MOVE_INTAKE_OUT;
312 }
313 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800314 break;
315
Adam Snaider06779722016-02-14 15:26:22 -0800316 case HIGH_ARM_ZERO_MOVE_INTAKE_OUT:
317 if (disable) {
318 state_ = DISABLED_INITIALIZED;
319 } else {
320 // If we were just asked to move the intake, make sure it moves far
321 // enough.
322 if (last_state_ != HIGH_ARM_ZERO_MOVE_INTAKE_OUT) {
323 intake_.set_unprofiled_goal(
324 MoveButKeepBelow(kIntakeUpperClear, intake_.angle(),
325 kIntakeEncoderIndexDifference * 2.5));
326 }
327
328 if (IsIntakeNear(kLooseTolerance)) {
329 // Close enough, start the next move.
330 state_ = HIGH_ARM_ZERO_LOWER_ARM;
331 }
332 }
333 break;
334
335 case HIGH_ARM_ZERO_LOWER_ARM:
336 if (disable) {
337 state_ = DISABLED_INITIALIZED;
338 } else {
339 // Land the shooter in the belly-pan. It should be zeroed by the time
340 // it gets there. If not, just estop.
341 arm_.set_unprofiled_goal(kShoulderLanded, 0.0);
342 if (arm_.zeroed() && intake_.zeroed()) {
343 state_ = RUNNING;
344 } else if (IsArmNear(kLooseTolerance)) {
345 LOG(ERROR,
346 "Failed to zero while executing the HIGH_ARM_ZERO sequence. "
347 "Arm: %d Intake %d\n",
348 arm_.zeroed(), intake_.zeroed());
349 state_ = ESTOP;
350 }
351 }
352 break;
353
354 case LOW_ARM_ZERO_LOWER_INTAKE:
355 if (disable) {
356 state_ = DISABLED_INITIALIZED;
357 } else {
358 // Move the intake down out of the way of the arm. Make sure to move it
359 // far enough to zero.
360 if (last_state_ != LOW_ARM_ZERO_LOWER_INTAKE) {
361 intake_.set_unprofiled_goal(
362 MoveButKeepBelow(kIntakeLowerClear, intake_.angle(),
363 kIntakeEncoderIndexDifference * 2.5));
364 }
365 if (IsIntakeNear(kLooseTolerance)) {
366 if (::std::abs(arm_.wrist_angle()) < kWristAlmostLevel) {
367 state_ = LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER;
368 } else {
369 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
370 }
371 }
372 }
373 break;
374
375 case LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER:
376 if (disable) {
377 state_ = DISABLED_INITIALIZED;
378 } else {
379 // If we are supposed to level the shooter, set it to level, and wait
380 // until it is very close to level.
381 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
382 if (IsArmNear(kLooseTolerance, kTightTolerance)) {
383 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
384 }
385 }
386 break;
387
388 case LOW_ARM_ZERO_LIFT_SHOULDER:
389 if (disable) {
390 state_ = DISABLED_INITIALIZED;
391 } else {
392 // Decide where to move to. We need to move far enough to see an index
393 // pulse, but must also get high enough that we can safely level the
394 // shooter.
395 if (last_state_ != LOW_ARM_ZERO_LIFT_SHOULDER) {
396 arm_.set_unprofiled_goal(
397 MoveButKeepAbove(kShoulderWristClearAngle, arm_.shoulder_angle(),
398 ::std::max(kWristEncoderIndexDifference,
399 kShoulderEncoderIndexDifference) *
400 2.5),
401 arm_.unprofiled_goal(2, 0));
402 }
403
404 // Wait until we are level and then go for it.
405 if (IsArmNear(kLooseTolerance)) {
406 state_ = LOW_ARM_ZERO_LEVEL_SHOOTER;
407 }
408 }
409 break;
410
411 case LOW_ARM_ZERO_LEVEL_SHOOTER:
412 if (disable) {
413 state_ = DISABLED_INITIALIZED;
414 } else {
415 // Move the shooter level (and keep the same height). We don't want to
416 // got to RUNNING until we are completely level so that we don't
417 // give control back in a weird case where we might crash.
418 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
419 if (IsArmNear(kLooseTolerance)) {
420 if (arm_.zeroed() && intake_.zeroed()) {
421 state_ = RUNNING;
422 } else {
423 LOG(ERROR,
424 "Failed to zero while executing the LOW_ARM_ZERO sequence. "
425 "Arm: %d Intake %d\n",
426 arm_.zeroed(), intake_.zeroed());
427 state_ = ESTOP;
428 }
429 }
430 }
431 break;
432
Austin Schuhb1d682b2016-02-16 13:07:44 -0800433 // These 4 cases are very similar.
Adam Snaider06779722016-02-14 15:26:22 -0800434 case SLOW_RUNNING:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800435 case RUNNING:
Austin Schuhb1d682b2016-02-16 13:07:44 -0800436 case LANDING_SLOW_RUNNING:
437 case LANDING_RUNNING: {
Austin Schuh829fe572016-02-14 21:41:16 -0800438 if (disable) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800439 // If we are disabled, go to slow running (or landing slow running) if
440 // we are collided.
441 if (collided()) {
442 if (state_ == RUNNING) {
443 state_ = SLOW_RUNNING;
444 } else if (state_ == LANDING_RUNNING) {
445 state_ = LANDING_SLOW_RUNNING;
446 }
447 }
Austin Schuh829fe572016-02-14 21:41:16 -0800448
Austin Schuhb1d682b2016-02-16 13:07:44 -0800449 // Reset the profile to the current position so it moves well from here.
Austin Schuh829fe572016-02-14 21:41:16 -0800450 intake_.ForceGoal(intake_.angle());
451 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
452 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800453 // If we are in slow_running and are no longer collided, let 'er rip.
Austin Schuh829fe572016-02-14 21:41:16 -0800454 if (state_ == SLOW_RUNNING) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800455 if (!collided()) {
456 state_ = RUNNING;
457 }
458 } else if (state_ == LANDING_SLOW_RUNNING) {
459 if (!collided()) {
460 state_ = LANDING_RUNNING;
461 }
Austin Schuh829fe572016-02-14 21:41:16 -0800462 }
463 }
464
Austin Schuhb1d682b2016-02-16 13:07:44 -0800465 double requested_shoulder = constants::Values::kShoulderRange.lower;
466 double requested_wrist = 0.0;
467 double requested_intake = M_PI / 2.0;
468
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800469 if (unsafe_goal) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800470 // If we are in one of the landing states, limit the accelerations and
471 // velocities to land cleanly.
472 if (state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING) {
473 arm_.AdjustProfile(0.5, // Shoulder Velocity
474 4.0, // Shoulder acceleration,
475 4.0, // Wrist velocity
476 10.0); // Wrist acceleration.
477 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
478 unsafe_goal->max_angular_acceleration_intake);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800479
Austin Schuhb1d682b2016-02-16 13:07:44 -0800480 requested_shoulder =
481 ::std::max(unsafe_goal->angle_shoulder,
482 constants::Values::kShoulderRange.lower);
483 requested_wrist = 0.0;
484 requested_intake = unsafe_goal->angle_intake;
Austin Schuha1dd5272016-02-16 15:39:38 -0800485 // Transition to landing once the profile is close to finished for the
486 // shoulder.
487 if (arm_.goal(0, 0) > kShoulderTransitionToLanded + 1e-4 ||
488 arm_.unprofiled_goal(0, 0) > kShoulderTransitionToLanded + 1e-4) {
489 if (state_ == LANDING_RUNNING) {
490 state_ = RUNNING;
491 } else {
492 state_ = SLOW_RUNNING;
493 }
494 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000495 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800496 // Otherwise, give the user what he asked for.
497 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder,
498 unsafe_goal->max_angular_acceleration_shoulder,
499 unsafe_goal->max_angular_velocity_wrist,
500 unsafe_goal->max_angular_acceleration_wrist);
501 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
502 unsafe_goal->max_angular_acceleration_intake);
503
504 // Except, don't let the shoulder go all the way down.
505 requested_shoulder = ::std::max(unsafe_goal->angle_shoulder,
506 kShoulderTransitionToLanded);
507 requested_wrist = unsafe_goal->angle_wrist;
508 requested_intake = unsafe_goal->angle_intake;
509
510 // Transition to landing once the profile is close to finished for the
511 // shoulder.
Austin Schuha1dd5272016-02-16 15:39:38 -0800512 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 &&
513 arm_.unprofiled_goal(0, 0) <=
514 kShoulderTransitionToLanded + 1e-4) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800515 if (state_ == RUNNING) {
516 state_ = LANDING_RUNNING;
517 } else {
518 state_ = LANDING_SLOW_RUNNING;
519 }
520 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000521 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800522 }
523
Austin Schuhb1d682b2016-02-16 13:07:44 -0800524 // Push the request out to hardware!
525 collision_avoidance_.UpdateGoal(requested_shoulder, requested_wrist,
526 requested_intake);
527
528 // ESTOP if we hit the hard limits.
529 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
530 state_ = ESTOP;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800531 }
Austin Schuhb1d682b2016-02-16 13:07:44 -0800532 } break;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800533
534 case ESTOP:
535 LOG(ERROR, "Estop\n");
536 disable = true;
537 break;
538 }
539
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800540 // Set the voltage limits.
Comran Morshed011f7d92016-02-16 23:03:06 +0000541 const double max_voltage = (state_ == RUNNING || state_ == LANDING_RUNNING)
542 ? kOperatingVoltage
543 : kZeroingVoltage;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800544 arm_.set_max_voltage(max_voltage, max_voltage);
545 intake_.set_max_voltage(max_voltage);
546
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800547 if (IsRunning()) {
548 // We don't want lots of negative voltage when we are near the bellypan on
549 // the shoulder...
550 // TODO(austin): Do I want to push negative power into the belly pan at this
551 // point? Maybe just put the goal slightly below the bellypan and call that
552 // good enough.
553 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4) {
Comran Morshed011f7d92016-02-16 23:03:06 +0000554 arm_.set_shoulder_asymetric_limits(kLandingShoulderDownVoltage,
555 max_voltage);
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800556 }
557 }
558
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800559 // Calculate the loops for a cycle.
Austin Schuhbe041152016-02-28 22:01:52 -0800560 {
561 Eigen::Matrix<double, 3, 1> error = intake_.controller().error();
562 status->intake.position_power = intake_.controller().K(0, 0) * error(0, 0);
563 status->intake.velocity_power = intake_.controller().K(0, 1) * error(1, 0);
564 }
565
566 {
567 Eigen::Matrix<double, 6, 1> error = arm_.controller().error();
568 status->shoulder.position_power = arm_.controller().K(0, 0) * error(0, 0);
569 status->shoulder.velocity_power = arm_.controller().K(0, 1) * error(1, 0);
570 status->wrist.position_power = arm_.controller().K(0, 2) * error(2, 0);
571 status->wrist.velocity_power = arm_.controller().K(0, 3) * error(3, 0);
572 }
573
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800574 arm_.Update(disable);
575 intake_.Update(disable);
576
577 // Write out all the voltages.
578 if (output) {
579 output->voltage_intake = intake_.intake_voltage();
580 output->voltage_shoulder = arm_.shoulder_voltage();
581 output->voltage_wrist = arm_.wrist_voltage();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000582
583 // Logic to run our rollers on the intake.
Campbell Crowleyd4fd6552016-02-21 17:53:46 -0800584 output->voltage_top_rollers = 0.0;
585 output->voltage_bottom_rollers = 0.0;
Comran Morshedf4cd7642016-02-15 20:40:49 +0000586 if (unsafe_goal) {
Campbell Crowleyd4fd6552016-02-21 17:53:46 -0800587 output->voltage_top_rollers = ::std::max(
588 -kMaxIntakeTopVoltage,
589 ::std::min(unsafe_goal->voltage_top_rollers, kMaxIntakeTopVoltage));
590 output->voltage_bottom_rollers = ::std::max(
591 -kMaxIntakeBottomVoltage,
592 ::std::min(unsafe_goal->voltage_bottom_rollers, kMaxIntakeBottomVoltage));
Comran Morshedf4cd7642016-02-15 20:40:49 +0000593 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800594 }
595
596 // Save debug/internal state.
Austin Schuha987f8b2016-02-28 22:02:20 -0800597 status->zeroed = arm_.zeroed() && intake_.zeroed();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800598
599 status->shoulder.angle = arm_.X_hat(0, 0);
600 status->shoulder.angular_velocity = arm_.X_hat(1, 0);
601 status->shoulder.goal_angle = arm_.goal(0, 0);
602 status->shoulder.goal_angular_velocity = arm_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800603 status->shoulder.unprofiled_goal_angle = arm_.unprofiled_goal(0, 0);
604 status->shoulder.unprofiled_goal_angular_velocity =
605 arm_.unprofiled_goal(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800606 status->shoulder.voltage_error = arm_.X_hat(4, 0);
607 status->shoulder.calculated_velocity =
608 (arm_.shoulder_angle() - last_shoulder_angle_) / 0.005;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800609 status->shoulder.estimator_state = arm_.ShoulderEstimatorState();
610
611 status->wrist.angle = arm_.X_hat(2, 0);
612 status->wrist.angular_velocity = arm_.X_hat(3, 0);
613 status->wrist.goal_angle = arm_.goal(2, 0);
614 status->wrist.goal_angular_velocity = arm_.goal(3, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800615 status->wrist.unprofiled_goal_angle = arm_.unprofiled_goal(2, 0);
616 status->wrist.unprofiled_goal_angular_velocity = arm_.unprofiled_goal(3, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800617 status->wrist.voltage_error = arm_.X_hat(5, 0);
618 status->wrist.calculated_velocity =
619 (arm_.wrist_angle() - last_wrist_angle_) / 0.005;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800620 status->wrist.estimator_state = arm_.WristEstimatorState();
621
622 status->intake.angle = intake_.X_hat(0, 0);
623 status->intake.angular_velocity = intake_.X_hat(1, 0);
624 status->intake.goal_angle = intake_.goal(0, 0);
625 status->intake.goal_angular_velocity = intake_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800626 status->intake.unprofiled_goal_angle = intake_.unprofiled_goal(0, 0);
627 status->intake.unprofiled_goal_angular_velocity =
628 intake_.unprofiled_goal(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800629 status->intake.calculated_velocity = (intake_.angle() - last_intake_angle_) / 0.005;
630 status->intake.voltage_error = intake_.X_hat(2, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800631 status->intake.estimator_state = intake_.IntakeEstimatorState();
Austin Schuhbe041152016-02-28 22:01:52 -0800632 status->intake.feedforwards_power = intake_.controller().ff_U(0, 0);
633
Austin Schuhf59b6bc2016-03-11 21:26:19 -0800634 status->shoulder_controller_index = arm_.controller_index();
635
Austin Schuhbe041152016-02-28 22:01:52 -0800636 last_shoulder_angle_ = arm_.shoulder_angle();
637 last_wrist_angle_ = arm_.wrist_angle();
638 last_intake_angle_ = intake_.angle();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800639
640 status->estopped = (state_ == ESTOP);
641
642 status->state = state_;
643
Adam Snaider06779722016-02-14 15:26:22 -0800644 last_state_ = state_before_switch;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800645}
646
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000647constexpr double Superstructure::kZeroingVoltage;
648constexpr double Superstructure::kOperatingVoltage;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000649constexpr double Superstructure::kShoulderMiddleAngle;
650constexpr double Superstructure::kLooseTolerance;
651constexpr double Superstructure::kIntakeUpperClear;
652constexpr double Superstructure::kIntakeLowerClear;
653constexpr double Superstructure::kShoulderUpAngle;
654constexpr double Superstructure::kShoulderLanded;
655constexpr double Superstructure::kTightTolerance;
656constexpr double Superstructure::kWristAlmostLevel;
657constexpr double Superstructure::kShoulderWristClearAngle;
Austin Schuhb1d682b2016-02-16 13:07:44 -0800658constexpr double Superstructure::kShoulderTransitionToLanded;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000659
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800660} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000661} // namespace control_loops
662} // namespace y2016