blob: 1f1bbdff6bcc8b8ef82d440e8f95d4c0ee294ece [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 Schuh6ca0f792016-03-12 14:06:14 -080059 if (::std::abs(wrist_angle) > kMaxWristAngleForMovingByIntake) {
60 shoulder_angle_goal =
61 ::std::max(original_shoulder_angle_goal,
62 kMinShoulderAngleForIntakeUpInterference + kSafetyMargin);
63 }
Philipp Schrader0119eb12016-02-15 18:16:21 +000064 if (::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
65 shoulder_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -080066 ::std::max(original_shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +000067 kMinShoulderAngleForHorizontalShooter + kSafetyMargin);
68 }
69 }
70
71 // Is the arm where it could interfere with the intake right now?
72 bool shoulder_is_in_danger =
Austin Schuh6ca0f792016-03-12 14:06:14 -080073 (shoulder_angle < kMinShoulderAngleForIntakeUpInterference &&
Philipp Schrader0119eb12016-02-15 18:16:21 +000074 shoulder_angle > kMaxShoulderAngleUntilSafeIntakeStowing);
75
76 // Is the arm moving into collision zone from above?
77 bool shoulder_moving_into_danger_from_above =
Austin Schuh6ca0f792016-03-12 14:06:14 -080078 (shoulder_angle >= kMinShoulderAngleForIntakeUpInterference &&
79 original_shoulder_angle_goal <=
80 kMinShoulderAngleForIntakeUpInterference);
Philipp Schrader0119eb12016-02-15 18:16:21 +000081
82 // Is the arm moving into collision zone from below?
83 bool shoulder_moving_into_danger_from_below =
84 (shoulder_angle <= kMaxShoulderAngleUntilSafeIntakeStowing &&
Austin Schuh6ca0f792016-03-12 14:06:14 -080085 original_shoulder_angle_goal >= kMaxShoulderAngleUntilSafeIntakeStowing);
Philipp Schrader0119eb12016-02-15 18:16:21 +000086
87 // Avoid colliding the arm with the intake.
88 if (shoulder_is_in_danger || shoulder_moving_into_danger_from_above ||
89 shoulder_moving_into_danger_from_below) {
90 // If the arm could collide with the intake, we make sure to move the
91 // intake out of the way. The arm has priority.
92 intake_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -080093 ::std::min(original_intake_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +000094 kMaxIntakeAngleBeforeArmInterference - kSafetyMargin);
95
96 // Don't let the shoulder move into the collision area until the intake is
97 // out of the way.
98 if (intake_angle > kMaxIntakeAngleBeforeArmInterference) {
99 const double kHalfwayPointBetweenSafeZones =
100 (kMinShoulderAngleForIntakeInterference +
101 kMaxShoulderAngleUntilSafeIntakeStowing) /
102 2.0;
103
104 if (shoulder_angle >= kHalfwayPointBetweenSafeZones) {
105 // The shoulder is closer to being above the collision area. Move it up
106 // there.
107 shoulder_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800108 ::std::max(original_shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000109 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
110 } else {
111 // The shoulder is closer to being below the collision zone (i.e. in
112 // stowing/intake position), keep it there for now.
113 shoulder_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800114 ::std::min(original_shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000115 kMaxShoulderAngleUntilSafeIntakeStowing - kSafetyMargin);
116 }
117 }
118 }
119
120 // Send the possibly adjusted goals to the components.
121 arm_->set_unprofiled_goal(shoulder_angle_goal, wrist_angle_goal);
122 intake_->set_unprofiled_goal(intake_angle_goal);
123}
124
Philipp Schrader07147532016-02-16 01:23:07 +0000125bool CollisionAvoidance::collided() const {
126 return collided_with_given_angles(arm_->shoulder_angle(), arm_->wrist_angle(),
127 intake_->angle());
128}
129
130bool CollisionAvoidance::collided_with_given_angles(double shoulder_angle,
131 double wrist_angle,
132 double intake_angle) {
133 // The arm and the intake must not hit.
134 if (shoulder_angle >=
135 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
136 shoulder_angle <=
Austin Schuh6ca0f792016-03-12 14:06:14 -0800137 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference &&
138 intake_angle >
139 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference) {
140 LOG(DEBUG, "Collided: Intake %f > %f, and shoulder %f < %f < %f.\n",
141 intake_angle, CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
142 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
Austin Schuhc85cdd52016-02-16 13:05:35 -0800143 shoulder_angle,
Austin Schuh6ca0f792016-03-12 14:06:14 -0800144 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference);
145 return true;
146 }
147
148 if (shoulder_angle >=
149 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
150 shoulder_angle <=
151 CollisionAvoidance::kMinShoulderAngleForIntakeInterference &&
152 intake_angle < CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference &&
153 intake_angle > Superstructure::kIntakeLowerClear &&
154 ::std::abs(wrist_angle) >
155 CollisionAvoidance::kMaxWristAngleForMovingByIntake) {
156 LOG(DEBUG, "Collided: Intake %f < %f < %f, and shoulder %f < %f < %f.\n",
157 Superstructure::kIntakeLowerClear, intake_angle,
158 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
159 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
160 shoulder_angle,
161 CollisionAvoidance::kMinShoulderAngleForIntakeInterference);
Philipp Schrader07147532016-02-16 01:23:07 +0000162 return true;
163 }
164
165 // The wrist must go back to zero when the shoulder is moving the arm into
166 // a stowed/intaking position.
167 if (shoulder_angle <
168 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter &&
169 ::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
Austin Schuhc85cdd52016-02-16 13:05:35 -0800170 LOG(DEBUG, "Collided: Shoulder %f < %f and wrist |%f| < %f.\n",
171 shoulder_angle,
172 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter, wrist_angle,
173 kMaxWristAngleForSafeArmStowing);
Philipp Schrader07147532016-02-16 01:23:07 +0000174 return true;
175 }
176
177 return false;
178}
179
Philipp Schrader0119eb12016-02-15 18:16:21 +0000180constexpr double CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
181constexpr double CollisionAvoidance::kMinShoulderAngleForIntakeInterference;
182constexpr double CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
183constexpr double CollisionAvoidance::kMaxWristAngleForSafeArmStowing;
184constexpr double CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing;
185
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800186Superstructure::Superstructure(
187 control_loops::SuperstructureQueue *superstructure_queue)
188 : aos::controls::ControlLoop<control_loops::SuperstructureQueue>(
Philipp Schrader0119eb12016-02-15 18:16:21 +0000189 superstructure_queue),
190 collision_avoidance_(&intake_, &arm_) {}
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800191
Adam Snaider06779722016-02-14 15:26:22 -0800192bool Superstructure::IsArmNear(double shoulder_tolerance,
193 double wrist_tolerance) {
194 return ((arm_.unprofiled_goal() - arm_.X_hat())
195 .block<2, 1>(0, 0)
196 .lpNorm<Eigen::Infinity>() < shoulder_tolerance) &&
197 ((arm_.unprofiled_goal() - arm_.X_hat())
198 .block<4, 1>(0, 0)
199 .lpNorm<Eigen::Infinity>() < wrist_tolerance) &&
200 ((arm_.unprofiled_goal() - arm_.goal())
201 .block<4, 1>(0, 0)
202 .lpNorm<Eigen::Infinity>() < 1e-6);
203}
204
205bool Superstructure::IsArmNear(double tolerance) {
206 return ((arm_.unprofiled_goal() - arm_.X_hat())
207 .block<4, 1>(0, 0)
208 .lpNorm<Eigen::Infinity>() < tolerance) &&
209 ((arm_.unprofiled_goal() - arm_.goal())
210 .block<4, 1>(0, 0)
211 .lpNorm<Eigen::Infinity>() < 1e-6);
212}
213
214bool Superstructure::IsIntakeNear(double tolerance) {
215 return ((intake_.unprofiled_goal() - intake_.X_hat())
216 .block<2, 1>(0, 0)
217 .lpNorm<Eigen::Infinity>() < tolerance);
218}
219
220double Superstructure::MoveButKeepAbove(double reference_angle,
221 double current_angle,
222 double move_distance) {
223 return -MoveButKeepBelow(-reference_angle, -current_angle, -move_distance);
224}
225
226double Superstructure::MoveButKeepBelow(double reference_angle,
227 double current_angle,
228 double move_distance) {
229 // There are 3 interesting places to move to.
230 const double small_negative_move = current_angle - move_distance;
231 const double small_positive_move = current_angle + move_distance;
232 // And the reference angle.
233
234 // Move the the highest one that is below reference_angle.
235 if (small_negative_move > reference_angle) {
236 return reference_angle;
237 } else if (small_positive_move > reference_angle) {
238 return small_negative_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800239 } else {
Adam Snaider06779722016-02-14 15:26:22 -0800240 return small_positive_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800241 }
242}
243
244void Superstructure::RunIteration(
245 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
246 const control_loops::SuperstructureQueue::Position *position,
247 control_loops::SuperstructureQueue::Output *output,
248 control_loops::SuperstructureQueue::Status *status) {
Adam Snaider06779722016-02-14 15:26:22 -0800249 const State state_before_switch = state_;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800250 if (WasReset()) {
251 LOG(ERROR, "WPILib reset, restarting\n");
252 arm_.Reset();
253 intake_.Reset();
254 state_ = UNINITIALIZED;
255 }
256
257 // Bool to track if we should turn the motors on or not.
258 bool disable = output == nullptr;
259
260 arm_.Correct(position->shoulder, position->wrist);
261 intake_.Correct(position->intake);
262
Adam Snaider06779722016-02-14 15:26:22 -0800263 // There are 2 main zeroing paths, HIGH_ARM_ZERO and LOW_ARM_ZERO.
264 //
265 // HIGH_ARM_ZERO works by lifting the arm all the way up so it is clear,
266 // moving the shooter to be horizontal, moving the intake out, and then moving
267 // the arm back down.
268 //
269 // LOW_ARM_ZERO works by moving the intake out of the way, lifting the arm up,
270 // leveling the shooter, and then moving back down.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800271
Diana Vandenberge2843c62016-02-13 17:44:20 -0800272 if (arm_.error() || intake_.error()) {
273 state_ = ESTOP;
274 }
275
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800276 const bool is_collided = collided();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800277 switch (state_) {
278 case UNINITIALIZED:
Adam Snaider06779722016-02-14 15:26:22 -0800279 // Wait in the uninitialized state until both the arm and intake are
280 // initialized.
281 LOG(DEBUG, "Uninitialized, waiting for intake and arm\n");
282 if (arm_.initialized() && intake_.initialized()) {
283 state_ = DISABLED_INITIALIZED;
284 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800285 disable = true;
286 break;
287
Adam Snaider06779722016-02-14 15:26:22 -0800288 case DISABLED_INITIALIZED:
289 // Wait here until we are either fully zeroed while disabled, or we become
290 // enabled. At that point, figure out if we should HIGH_ARM_ZERO or
291 // LOW_ARM_ZERO.
292 if (disable) {
293 if (arm_.zeroed() && intake_.zeroed()) {
294 state_ = SLOW_RUNNING;
295 }
296 } else {
297 if (arm_.shoulder_angle() >= kShoulderMiddleAngle) {
298 state_ = HIGH_ARM_ZERO_LIFT_ARM;
299 } else {
300 state_ = LOW_ARM_ZERO_LOWER_INTAKE;
301 }
302 }
303
304 // Set the goals to where we are now so when we start back up, we don't
305 // jump.
306 intake_.ForceGoal(intake_.angle());
307 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
308 // Set up the profile to be the zeroing profile.
309 intake_.AdjustProfile(0.5, 10);
310 arm_.AdjustProfile(0.5, 10, 0.5, 10);
311
312 // We are not ready to start doing anything yet.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800313 disable = true;
Adam Snaider06779722016-02-14 15:26:22 -0800314 break;
315
316 case HIGH_ARM_ZERO_LIFT_ARM:
317 if (disable) {
318 state_ = DISABLED_INITIALIZED;
319 } else {
320 // Raise the shoulder up out of the way.
321 arm_.set_unprofiled_goal(kShoulderUpAngle, arm_.wrist_angle());
322 if (IsArmNear(kLooseTolerance)) {
323 // Close enough, start the next move.
324 state_ = HIGH_ARM_ZERO_LEVEL_SHOOTER;
325 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800326 }
327 break;
328
Adam Snaider06779722016-02-14 15:26:22 -0800329 case HIGH_ARM_ZERO_LEVEL_SHOOTER:
330 if (disable) {
331 state_ = DISABLED_INITIALIZED;
332 } else {
333 // Move the shooter to be level.
334 arm_.set_unprofiled_goal(kShoulderUpAngle, 0.0);
335
336 if (IsArmNear(kLooseTolerance)) {
337 // Close enough, start the next move.
338 state_ = HIGH_ARM_ZERO_MOVE_INTAKE_OUT;
339 }
340 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800341 break;
342
Adam Snaider06779722016-02-14 15:26:22 -0800343 case HIGH_ARM_ZERO_MOVE_INTAKE_OUT:
344 if (disable) {
345 state_ = DISABLED_INITIALIZED;
346 } else {
347 // If we were just asked to move the intake, make sure it moves far
348 // enough.
349 if (last_state_ != HIGH_ARM_ZERO_MOVE_INTAKE_OUT) {
350 intake_.set_unprofiled_goal(
351 MoveButKeepBelow(kIntakeUpperClear, intake_.angle(),
352 kIntakeEncoderIndexDifference * 2.5));
353 }
354
355 if (IsIntakeNear(kLooseTolerance)) {
356 // Close enough, start the next move.
357 state_ = HIGH_ARM_ZERO_LOWER_ARM;
358 }
359 }
360 break;
361
362 case HIGH_ARM_ZERO_LOWER_ARM:
363 if (disable) {
364 state_ = DISABLED_INITIALIZED;
365 } else {
366 // Land the shooter in the belly-pan. It should be zeroed by the time
367 // it gets there. If not, just estop.
368 arm_.set_unprofiled_goal(kShoulderLanded, 0.0);
369 if (arm_.zeroed() && intake_.zeroed()) {
370 state_ = RUNNING;
371 } else if (IsArmNear(kLooseTolerance)) {
372 LOG(ERROR,
373 "Failed to zero while executing the HIGH_ARM_ZERO sequence. "
374 "Arm: %d Intake %d\n",
375 arm_.zeroed(), intake_.zeroed());
376 state_ = ESTOP;
377 }
378 }
379 break;
380
381 case LOW_ARM_ZERO_LOWER_INTAKE:
382 if (disable) {
383 state_ = DISABLED_INITIALIZED;
384 } else {
385 // Move the intake down out of the way of the arm. Make sure to move it
386 // far enough to zero.
387 if (last_state_ != LOW_ARM_ZERO_LOWER_INTAKE) {
388 intake_.set_unprofiled_goal(
389 MoveButKeepBelow(kIntakeLowerClear, intake_.angle(),
390 kIntakeEncoderIndexDifference * 2.5));
391 }
392 if (IsIntakeNear(kLooseTolerance)) {
393 if (::std::abs(arm_.wrist_angle()) < kWristAlmostLevel) {
394 state_ = LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER;
395 } else {
396 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
397 }
398 }
399 }
400 break;
401
402 case LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER:
403 if (disable) {
404 state_ = DISABLED_INITIALIZED;
405 } else {
406 // If we are supposed to level the shooter, set it to level, and wait
407 // until it is very close to level.
408 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
409 if (IsArmNear(kLooseTolerance, kTightTolerance)) {
410 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
411 }
412 }
413 break;
414
415 case LOW_ARM_ZERO_LIFT_SHOULDER:
416 if (disable) {
417 state_ = DISABLED_INITIALIZED;
418 } else {
419 // Decide where to move to. We need to move far enough to see an index
420 // pulse, but must also get high enough that we can safely level the
421 // shooter.
422 if (last_state_ != LOW_ARM_ZERO_LIFT_SHOULDER) {
423 arm_.set_unprofiled_goal(
424 MoveButKeepAbove(kShoulderWristClearAngle, arm_.shoulder_angle(),
425 ::std::max(kWristEncoderIndexDifference,
426 kShoulderEncoderIndexDifference) *
427 2.5),
428 arm_.unprofiled_goal(2, 0));
429 }
430
431 // Wait until we are level and then go for it.
432 if (IsArmNear(kLooseTolerance)) {
433 state_ = LOW_ARM_ZERO_LEVEL_SHOOTER;
434 }
435 }
436 break;
437
438 case LOW_ARM_ZERO_LEVEL_SHOOTER:
439 if (disable) {
440 state_ = DISABLED_INITIALIZED;
441 } else {
442 // Move the shooter level (and keep the same height). We don't want to
443 // got to RUNNING until we are completely level so that we don't
444 // give control back in a weird case where we might crash.
445 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
446 if (IsArmNear(kLooseTolerance)) {
447 if (arm_.zeroed() && intake_.zeroed()) {
448 state_ = RUNNING;
449 } else {
450 LOG(ERROR,
451 "Failed to zero while executing the LOW_ARM_ZERO sequence. "
452 "Arm: %d Intake %d\n",
453 arm_.zeroed(), intake_.zeroed());
454 state_ = ESTOP;
455 }
456 }
457 }
458 break;
459
Austin Schuhb1d682b2016-02-16 13:07:44 -0800460 // These 4 cases are very similar.
Adam Snaider06779722016-02-14 15:26:22 -0800461 case SLOW_RUNNING:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800462 case RUNNING:
Austin Schuhb1d682b2016-02-16 13:07:44 -0800463 case LANDING_SLOW_RUNNING:
464 case LANDING_RUNNING: {
Austin Schuh829fe572016-02-14 21:41:16 -0800465 if (disable) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800466 // If we are disabled, go to slow running (or landing slow running) if
467 // we are collided.
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800468 if (is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800469 if (state_ == RUNNING) {
470 state_ = SLOW_RUNNING;
471 } else if (state_ == LANDING_RUNNING) {
472 state_ = LANDING_SLOW_RUNNING;
473 }
474 }
Austin Schuh829fe572016-02-14 21:41:16 -0800475
Austin Schuhb1d682b2016-02-16 13:07:44 -0800476 // Reset the profile to the current position so it moves well from here.
Austin Schuh829fe572016-02-14 21:41:16 -0800477 intake_.ForceGoal(intake_.angle());
478 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
479 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800480 // If we are in slow_running and are no longer collided, let 'er rip.
Austin Schuh829fe572016-02-14 21:41:16 -0800481 if (state_ == SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800482 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800483 state_ = RUNNING;
484 }
485 } else if (state_ == LANDING_SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800486 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800487 state_ = LANDING_RUNNING;
488 }
Austin Schuh829fe572016-02-14 21:41:16 -0800489 }
490 }
491
Austin Schuhb1d682b2016-02-16 13:07:44 -0800492 double requested_shoulder = constants::Values::kShoulderRange.lower;
493 double requested_wrist = 0.0;
494 double requested_intake = M_PI / 2.0;
495
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800496 if (unsafe_goal) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800497 // If we are in one of the landing states, limit the accelerations and
498 // velocities to land cleanly.
499 if (state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING) {
500 arm_.AdjustProfile(0.5, // Shoulder Velocity
501 4.0, // Shoulder acceleration,
502 4.0, // Wrist velocity
503 10.0); // Wrist acceleration.
504 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
505 unsafe_goal->max_angular_acceleration_intake);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800506
Austin Schuhb1d682b2016-02-16 13:07:44 -0800507 requested_shoulder =
508 ::std::max(unsafe_goal->angle_shoulder,
509 constants::Values::kShoulderRange.lower);
510 requested_wrist = 0.0;
511 requested_intake = unsafe_goal->angle_intake;
Austin Schuha1dd5272016-02-16 15:39:38 -0800512 // Transition to landing once the profile is close to finished for the
513 // shoulder.
514 if (arm_.goal(0, 0) > kShoulderTransitionToLanded + 1e-4 ||
515 arm_.unprofiled_goal(0, 0) > kShoulderTransitionToLanded + 1e-4) {
516 if (state_ == LANDING_RUNNING) {
517 state_ = RUNNING;
518 } else {
519 state_ = SLOW_RUNNING;
520 }
521 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000522 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800523 // Otherwise, give the user what he asked for.
524 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder,
525 unsafe_goal->max_angular_acceleration_shoulder,
526 unsafe_goal->max_angular_velocity_wrist,
527 unsafe_goal->max_angular_acceleration_wrist);
528 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
529 unsafe_goal->max_angular_acceleration_intake);
530
531 // Except, don't let the shoulder go all the way down.
532 requested_shoulder = ::std::max(unsafe_goal->angle_shoulder,
533 kShoulderTransitionToLanded);
534 requested_wrist = unsafe_goal->angle_wrist;
535 requested_intake = unsafe_goal->angle_intake;
536
537 // Transition to landing once the profile is close to finished for the
538 // shoulder.
Austin Schuha1dd5272016-02-16 15:39:38 -0800539 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 &&
540 arm_.unprofiled_goal(0, 0) <=
541 kShoulderTransitionToLanded + 1e-4) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800542 if (state_ == RUNNING) {
543 state_ = LANDING_RUNNING;
544 } else {
545 state_ = LANDING_SLOW_RUNNING;
546 }
547 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000548 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800549 }
550
Austin Schuhb1d682b2016-02-16 13:07:44 -0800551 // Push the request out to hardware!
552 collision_avoidance_.UpdateGoal(requested_shoulder, requested_wrist,
553 requested_intake);
554
555 // ESTOP if we hit the hard limits.
556 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
557 state_ = ESTOP;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800558 }
Austin Schuhb1d682b2016-02-16 13:07:44 -0800559 } break;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800560
561 case ESTOP:
562 LOG(ERROR, "Estop\n");
563 disable = true;
564 break;
565 }
566
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800567 // Set the voltage limits.
Comran Morshed011f7d92016-02-16 23:03:06 +0000568 const double max_voltage = (state_ == RUNNING || state_ == LANDING_RUNNING)
569 ? kOperatingVoltage
570 : kZeroingVoltage;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800571 arm_.set_max_voltage(max_voltage, max_voltage);
572 intake_.set_max_voltage(max_voltage);
573
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800574 if (IsRunning()) {
575 // We don't want lots of negative voltage when we are near the bellypan on
576 // the shoulder...
577 // TODO(austin): Do I want to push negative power into the belly pan at this
578 // point? Maybe just put the goal slightly below the bellypan and call that
579 // good enough.
Austin Schuh6ca0f792016-03-12 14:06:14 -0800580 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 ||
581 arm_.X_hat(0, 0) <= kShoulderTransitionToLanded + 1e-4) {
Comran Morshed011f7d92016-02-16 23:03:06 +0000582 arm_.set_shoulder_asymetric_limits(kLandingShoulderDownVoltage,
583 max_voltage);
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800584 }
585 }
586
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800587 // Calculate the loops for a cycle.
Austin Schuhbe041152016-02-28 22:01:52 -0800588 {
589 Eigen::Matrix<double, 3, 1> error = intake_.controller().error();
590 status->intake.position_power = intake_.controller().K(0, 0) * error(0, 0);
591 status->intake.velocity_power = intake_.controller().K(0, 1) * error(1, 0);
592 }
593
594 {
595 Eigen::Matrix<double, 6, 1> error = arm_.controller().error();
596 status->shoulder.position_power = arm_.controller().K(0, 0) * error(0, 0);
597 status->shoulder.velocity_power = arm_.controller().K(0, 1) * error(1, 0);
598 status->wrist.position_power = arm_.controller().K(0, 2) * error(2, 0);
599 status->wrist.velocity_power = arm_.controller().K(0, 3) * error(3, 0);
600 }
601
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800602 arm_.Update(disable);
603 intake_.Update(disable);
604
605 // Write out all the voltages.
606 if (output) {
607 output->voltage_intake = intake_.intake_voltage();
608 output->voltage_shoulder = arm_.shoulder_voltage();
609 output->voltage_wrist = arm_.wrist_voltage();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000610
611 // Logic to run our rollers on the intake.
Campbell Crowleyd4fd6552016-02-21 17:53:46 -0800612 output->voltage_top_rollers = 0.0;
613 output->voltage_bottom_rollers = 0.0;
Comran Morshedf4cd7642016-02-15 20:40:49 +0000614 if (unsafe_goal) {
Campbell Crowleyd4fd6552016-02-21 17:53:46 -0800615 output->voltage_top_rollers = ::std::max(
616 -kMaxIntakeTopVoltage,
617 ::std::min(unsafe_goal->voltage_top_rollers, kMaxIntakeTopVoltage));
618 output->voltage_bottom_rollers = ::std::max(
619 -kMaxIntakeBottomVoltage,
620 ::std::min(unsafe_goal->voltage_bottom_rollers, kMaxIntakeBottomVoltage));
Comran Morshedf4cd7642016-02-15 20:40:49 +0000621 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800622 }
623
624 // Save debug/internal state.
Austin Schuha987f8b2016-02-28 22:02:20 -0800625 status->zeroed = arm_.zeroed() && intake_.zeroed();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800626
627 status->shoulder.angle = arm_.X_hat(0, 0);
628 status->shoulder.angular_velocity = arm_.X_hat(1, 0);
629 status->shoulder.goal_angle = arm_.goal(0, 0);
630 status->shoulder.goal_angular_velocity = arm_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800631 status->shoulder.unprofiled_goal_angle = arm_.unprofiled_goal(0, 0);
632 status->shoulder.unprofiled_goal_angular_velocity =
633 arm_.unprofiled_goal(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800634 status->shoulder.voltage_error = arm_.X_hat(4, 0);
635 status->shoulder.calculated_velocity =
636 (arm_.shoulder_angle() - last_shoulder_angle_) / 0.005;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800637 status->shoulder.estimator_state = arm_.ShoulderEstimatorState();
638
639 status->wrist.angle = arm_.X_hat(2, 0);
640 status->wrist.angular_velocity = arm_.X_hat(3, 0);
641 status->wrist.goal_angle = arm_.goal(2, 0);
642 status->wrist.goal_angular_velocity = arm_.goal(3, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800643 status->wrist.unprofiled_goal_angle = arm_.unprofiled_goal(2, 0);
644 status->wrist.unprofiled_goal_angular_velocity = arm_.unprofiled_goal(3, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800645 status->wrist.voltage_error = arm_.X_hat(5, 0);
646 status->wrist.calculated_velocity =
647 (arm_.wrist_angle() - last_wrist_angle_) / 0.005;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800648 status->wrist.estimator_state = arm_.WristEstimatorState();
649
650 status->intake.angle = intake_.X_hat(0, 0);
651 status->intake.angular_velocity = intake_.X_hat(1, 0);
652 status->intake.goal_angle = intake_.goal(0, 0);
653 status->intake.goal_angular_velocity = intake_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800654 status->intake.unprofiled_goal_angle = intake_.unprofiled_goal(0, 0);
655 status->intake.unprofiled_goal_angular_velocity =
656 intake_.unprofiled_goal(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800657 status->intake.calculated_velocity = (intake_.angle() - last_intake_angle_) / 0.005;
658 status->intake.voltage_error = intake_.X_hat(2, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800659 status->intake.estimator_state = intake_.IntakeEstimatorState();
Austin Schuhbe041152016-02-28 22:01:52 -0800660 status->intake.feedforwards_power = intake_.controller().ff_U(0, 0);
661
Austin Schuhf59b6bc2016-03-11 21:26:19 -0800662 status->shoulder_controller_index = arm_.controller_index();
663
Austin Schuhbe041152016-02-28 22:01:52 -0800664 last_shoulder_angle_ = arm_.shoulder_angle();
665 last_wrist_angle_ = arm_.wrist_angle();
666 last_intake_angle_ = intake_.angle();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800667
668 status->estopped = (state_ == ESTOP);
669
670 status->state = state_;
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800671 status->is_collided = is_collided;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800672
Adam Snaider06779722016-02-14 15:26:22 -0800673 last_state_ = state_before_switch;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800674}
675
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000676constexpr double Superstructure::kZeroingVoltage;
677constexpr double Superstructure::kOperatingVoltage;
Austin Schuh6ca0f792016-03-12 14:06:14 -0800678constexpr double Superstructure::kLandingShoulderDownVoltage;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000679constexpr double Superstructure::kShoulderMiddleAngle;
680constexpr double Superstructure::kLooseTolerance;
681constexpr double Superstructure::kIntakeUpperClear;
682constexpr double Superstructure::kIntakeLowerClear;
683constexpr double Superstructure::kShoulderUpAngle;
684constexpr double Superstructure::kShoulderLanded;
685constexpr double Superstructure::kTightTolerance;
686constexpr double Superstructure::kWristAlmostLevel;
687constexpr double Superstructure::kShoulderWristClearAngle;
Austin Schuhb1d682b2016-02-16 13:07:44 -0800688constexpr double Superstructure::kShoulderTransitionToLanded;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000689
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800690} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000691} // namespace control_loops
692} // namespace y2016