blob: d4b5578d22c8a674325a66fc98b763759453b9df [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 {
Adam Snaider06779722016-02-14 15:26:22 -080017constexpr double kIntakeEncoderIndexDifference =
18 constants::Values::kIntakeEncoderIndexDifference;
19constexpr double kWristEncoderIndexDifference =
20 constants::Values::kWristEncoderIndexDifference;
21constexpr double kShoulderEncoderIndexDifference =
22 constants::Values::kShoulderEncoderIndexDifference;
23
Austin Schuh2fc10fa2016-02-08 00:44:34 -080024constexpr double kZeroingVoltage = 4.0;
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) {
127 return true;
128 }
129
130 // The wrist must go back to zero when the shoulder is moving the arm into
131 // a stowed/intaking position.
132 if (shoulder_angle <
133 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter &&
134 ::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
135 return true;
136 }
137
138 return false;
139}
140
Philipp Schrader0119eb12016-02-15 18:16:21 +0000141constexpr double CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
142constexpr double CollisionAvoidance::kMinShoulderAngleForIntakeInterference;
143constexpr double CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
144constexpr double CollisionAvoidance::kMaxWristAngleForSafeArmStowing;
145constexpr double CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing;
146
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800147Superstructure::Superstructure(
148 control_loops::SuperstructureQueue *superstructure_queue)
149 : aos::controls::ControlLoop<control_loops::SuperstructureQueue>(
Philipp Schrader0119eb12016-02-15 18:16:21 +0000150 superstructure_queue),
151 collision_avoidance_(&intake_, &arm_) {}
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800152
Adam Snaider06779722016-02-14 15:26:22 -0800153bool Superstructure::IsArmNear(double shoulder_tolerance,
154 double wrist_tolerance) {
155 return ((arm_.unprofiled_goal() - arm_.X_hat())
156 .block<2, 1>(0, 0)
157 .lpNorm<Eigen::Infinity>() < shoulder_tolerance) &&
158 ((arm_.unprofiled_goal() - arm_.X_hat())
159 .block<4, 1>(0, 0)
160 .lpNorm<Eigen::Infinity>() < wrist_tolerance) &&
161 ((arm_.unprofiled_goal() - arm_.goal())
162 .block<4, 1>(0, 0)
163 .lpNorm<Eigen::Infinity>() < 1e-6);
164}
165
166bool Superstructure::IsArmNear(double tolerance) {
167 return ((arm_.unprofiled_goal() - arm_.X_hat())
168 .block<4, 1>(0, 0)
169 .lpNorm<Eigen::Infinity>() < tolerance) &&
170 ((arm_.unprofiled_goal() - arm_.goal())
171 .block<4, 1>(0, 0)
172 .lpNorm<Eigen::Infinity>() < 1e-6);
173}
174
175bool Superstructure::IsIntakeNear(double tolerance) {
176 return ((intake_.unprofiled_goal() - intake_.X_hat())
177 .block<2, 1>(0, 0)
178 .lpNorm<Eigen::Infinity>() < tolerance);
179}
180
181double Superstructure::MoveButKeepAbove(double reference_angle,
182 double current_angle,
183 double move_distance) {
184 return -MoveButKeepBelow(-reference_angle, -current_angle, -move_distance);
185}
186
187double Superstructure::MoveButKeepBelow(double reference_angle,
188 double current_angle,
189 double move_distance) {
190 // There are 3 interesting places to move to.
191 const double small_negative_move = current_angle - move_distance;
192 const double small_positive_move = current_angle + move_distance;
193 // And the reference angle.
194
195 // Move the the highest one that is below reference_angle.
196 if (small_negative_move > reference_angle) {
197 return reference_angle;
198 } else if (small_positive_move > reference_angle) {
199 return small_negative_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800200 } else {
Adam Snaider06779722016-02-14 15:26:22 -0800201 return small_positive_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800202 }
203}
204
205void Superstructure::RunIteration(
206 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
207 const control_loops::SuperstructureQueue::Position *position,
208 control_loops::SuperstructureQueue::Output *output,
209 control_loops::SuperstructureQueue::Status *status) {
Adam Snaider06779722016-02-14 15:26:22 -0800210 const State state_before_switch = state_;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800211 if (WasReset()) {
212 LOG(ERROR, "WPILib reset, restarting\n");
213 arm_.Reset();
214 intake_.Reset();
215 state_ = UNINITIALIZED;
216 }
217
218 // Bool to track if we should turn the motors on or not.
219 bool disable = output == nullptr;
220
221 arm_.Correct(position->shoulder, position->wrist);
222 intake_.Correct(position->intake);
223
Adam Snaider06779722016-02-14 15:26:22 -0800224 // There are 2 main zeroing paths, HIGH_ARM_ZERO and LOW_ARM_ZERO.
225 //
226 // HIGH_ARM_ZERO works by lifting the arm all the way up so it is clear,
227 // moving the shooter to be horizontal, moving the intake out, and then moving
228 // the arm back down.
229 //
230 // LOW_ARM_ZERO works by moving the intake out of the way, lifting the arm up,
231 // leveling the shooter, and then moving back down.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800232
Diana Vandenberge2843c62016-02-13 17:44:20 -0800233 if (arm_.error() || intake_.error()) {
234 state_ = ESTOP;
235 }
236
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800237 switch (state_) {
238 case UNINITIALIZED:
Adam Snaider06779722016-02-14 15:26:22 -0800239 // Wait in the uninitialized state until both the arm and intake are
240 // initialized.
241 LOG(DEBUG, "Uninitialized, waiting for intake and arm\n");
242 if (arm_.initialized() && intake_.initialized()) {
243 state_ = DISABLED_INITIALIZED;
244 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800245 disable = true;
246 break;
247
Adam Snaider06779722016-02-14 15:26:22 -0800248 case DISABLED_INITIALIZED:
249 // Wait here until we are either fully zeroed while disabled, or we become
250 // enabled. At that point, figure out if we should HIGH_ARM_ZERO or
251 // LOW_ARM_ZERO.
252 if (disable) {
253 if (arm_.zeroed() && intake_.zeroed()) {
254 state_ = SLOW_RUNNING;
255 }
256 } else {
257 if (arm_.shoulder_angle() >= kShoulderMiddleAngle) {
258 state_ = HIGH_ARM_ZERO_LIFT_ARM;
259 } else {
260 state_ = LOW_ARM_ZERO_LOWER_INTAKE;
261 }
262 }
263
264 // Set the goals to where we are now so when we start back up, we don't
265 // jump.
266 intake_.ForceGoal(intake_.angle());
267 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
268 // Set up the profile to be the zeroing profile.
269 intake_.AdjustProfile(0.5, 10);
270 arm_.AdjustProfile(0.5, 10, 0.5, 10);
271
272 // We are not ready to start doing anything yet.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800273 disable = true;
Adam Snaider06779722016-02-14 15:26:22 -0800274 break;
275
276 case HIGH_ARM_ZERO_LIFT_ARM:
277 if (disable) {
278 state_ = DISABLED_INITIALIZED;
279 } else {
280 // Raise the shoulder up out of the way.
281 arm_.set_unprofiled_goal(kShoulderUpAngle, arm_.wrist_angle());
282 if (IsArmNear(kLooseTolerance)) {
283 // Close enough, start the next move.
284 state_ = HIGH_ARM_ZERO_LEVEL_SHOOTER;
285 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800286 }
287 break;
288
Adam Snaider06779722016-02-14 15:26:22 -0800289 case HIGH_ARM_ZERO_LEVEL_SHOOTER:
290 if (disable) {
291 state_ = DISABLED_INITIALIZED;
292 } else {
293 // Move the shooter to be level.
294 arm_.set_unprofiled_goal(kShoulderUpAngle, 0.0);
295
296 if (IsArmNear(kLooseTolerance)) {
297 // Close enough, start the next move.
298 state_ = HIGH_ARM_ZERO_MOVE_INTAKE_OUT;
299 }
300 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800301 break;
302
Adam Snaider06779722016-02-14 15:26:22 -0800303 case HIGH_ARM_ZERO_MOVE_INTAKE_OUT:
304 if (disable) {
305 state_ = DISABLED_INITIALIZED;
306 } else {
307 // If we were just asked to move the intake, make sure it moves far
308 // enough.
309 if (last_state_ != HIGH_ARM_ZERO_MOVE_INTAKE_OUT) {
310 intake_.set_unprofiled_goal(
311 MoveButKeepBelow(kIntakeUpperClear, intake_.angle(),
312 kIntakeEncoderIndexDifference * 2.5));
313 }
314
315 if (IsIntakeNear(kLooseTolerance)) {
316 // Close enough, start the next move.
317 state_ = HIGH_ARM_ZERO_LOWER_ARM;
318 }
319 }
320 break;
321
322 case HIGH_ARM_ZERO_LOWER_ARM:
323 if (disable) {
324 state_ = DISABLED_INITIALIZED;
325 } else {
326 // Land the shooter in the belly-pan. It should be zeroed by the time
327 // it gets there. If not, just estop.
328 arm_.set_unprofiled_goal(kShoulderLanded, 0.0);
329 if (arm_.zeroed() && intake_.zeroed()) {
330 state_ = RUNNING;
331 } else if (IsArmNear(kLooseTolerance)) {
332 LOG(ERROR,
333 "Failed to zero while executing the HIGH_ARM_ZERO sequence. "
334 "Arm: %d Intake %d\n",
335 arm_.zeroed(), intake_.zeroed());
336 state_ = ESTOP;
337 }
338 }
339 break;
340
341 case LOW_ARM_ZERO_LOWER_INTAKE:
342 if (disable) {
343 state_ = DISABLED_INITIALIZED;
344 } else {
345 // Move the intake down out of the way of the arm. Make sure to move it
346 // far enough to zero.
347 if (last_state_ != LOW_ARM_ZERO_LOWER_INTAKE) {
348 intake_.set_unprofiled_goal(
349 MoveButKeepBelow(kIntakeLowerClear, intake_.angle(),
350 kIntakeEncoderIndexDifference * 2.5));
351 }
352 if (IsIntakeNear(kLooseTolerance)) {
353 if (::std::abs(arm_.wrist_angle()) < kWristAlmostLevel) {
354 state_ = LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER;
355 } else {
356 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
357 }
358 }
359 }
360 break;
361
362 case LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER:
363 if (disable) {
364 state_ = DISABLED_INITIALIZED;
365 } else {
366 // If we are supposed to level the shooter, set it to level, and wait
367 // until it is very close to level.
368 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
369 if (IsArmNear(kLooseTolerance, kTightTolerance)) {
370 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
371 }
372 }
373 break;
374
375 case LOW_ARM_ZERO_LIFT_SHOULDER:
376 if (disable) {
377 state_ = DISABLED_INITIALIZED;
378 } else {
379 // Decide where to move to. We need to move far enough to see an index
380 // pulse, but must also get high enough that we can safely level the
381 // shooter.
382 if (last_state_ != LOW_ARM_ZERO_LIFT_SHOULDER) {
383 arm_.set_unprofiled_goal(
384 MoveButKeepAbove(kShoulderWristClearAngle, arm_.shoulder_angle(),
385 ::std::max(kWristEncoderIndexDifference,
386 kShoulderEncoderIndexDifference) *
387 2.5),
388 arm_.unprofiled_goal(2, 0));
389 }
390
391 // Wait until we are level and then go for it.
392 if (IsArmNear(kLooseTolerance)) {
393 state_ = LOW_ARM_ZERO_LEVEL_SHOOTER;
394 }
395 }
396 break;
397
398 case LOW_ARM_ZERO_LEVEL_SHOOTER:
399 if (disable) {
400 state_ = DISABLED_INITIALIZED;
401 } else {
402 // Move the shooter level (and keep the same height). We don't want to
403 // got to RUNNING until we are completely level so that we don't
404 // give control back in a weird case where we might crash.
405 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
406 if (IsArmNear(kLooseTolerance)) {
407 if (arm_.zeroed() && intake_.zeroed()) {
408 state_ = RUNNING;
409 } else {
410 LOG(ERROR,
411 "Failed to zero while executing the LOW_ARM_ZERO sequence. "
412 "Arm: %d Intake %d\n",
413 arm_.zeroed(), intake_.zeroed());
414 state_ = ESTOP;
415 }
416 }
417 }
418 break;
419
420 case SLOW_RUNNING:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800421 case RUNNING:
Austin Schuh829fe572016-02-14 21:41:16 -0800422 if (disable) {
423 // TODO(austin): Enter SLOW_RUNNING if we are collided.
424
425 // If we are disabled, reset the profile to the current position.
426 intake_.ForceGoal(intake_.angle());
427 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
428 } else {
429 if (state_ == SLOW_RUNNING) {
430 // TODO(austin): Exit SLOW_RUNNING if we are not collided.
431 LOG(ERROR, "Need to transition on non-collided, not all the time.\n");
432 state_ = RUNNING;
433 }
434 }
435
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800436 if (unsafe_goal) {
437 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder,
Austin Schuh829fe572016-02-14 21:41:16 -0800438 unsafe_goal->max_angular_acceleration_shoulder,
439 unsafe_goal->max_angular_velocity_wrist,
440 unsafe_goal->max_angular_acceleration_wrist);
Austin Schuhe7e40892016-02-15 18:21:04 -0800441 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
Austin Schuh829fe572016-02-14 21:41:16 -0800442 unsafe_goal->max_angular_acceleration_intake);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800443
Philipp Schrader0119eb12016-02-15 18:16:21 +0000444 if (collision_avoidance_enabled()) {
445 collision_avoidance_.UpdateGoal(unsafe_goal->angle_shoulder,
446 unsafe_goal->angle_wrist,
447 unsafe_goal->angle_intake);
448 } else {
449 arm_.set_unprofiled_goal(unsafe_goal->angle_shoulder,
450 unsafe_goal->angle_wrist);
451 intake_.set_unprofiled_goal(unsafe_goal->angle_intake);
452 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800453 }
454
Adam Snaider06779722016-02-14 15:26:22 -0800455 // ESTOP if we hit any of the limits. It is safe(ish) to hit the limits
456 // while zeroing since we use such low power.
457 if (state_ == RUNNING || state_ == SLOW_RUNNING) {
458 // ESTOP if we hit the hard limits.
459 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
460 state_ = ESTOP;
461 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800462 }
463 break;
464
465 case ESTOP:
466 LOG(ERROR, "Estop\n");
467 disable = true;
468 break;
469 }
470
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800471 // Set the voltage limits.
472 const double max_voltage = state_ == RUNNING ? 12.0 : kZeroingVoltage;
473 arm_.set_max_voltage(max_voltage, max_voltage);
474 intake_.set_max_voltage(max_voltage);
475
476 // Calculate the loops for a cycle.
477 arm_.Update(disable);
478 intake_.Update(disable);
479
480 // Write out all the voltages.
481 if (output) {
482 output->voltage_intake = intake_.intake_voltage();
483 output->voltage_shoulder = arm_.shoulder_voltage();
484 output->voltage_wrist = arm_.wrist_voltage();
485 }
486
487 // Save debug/internal state.
488 // TODO(austin): Save the voltage errors.
489 status->zeroed = state_ == RUNNING;
490
491 status->shoulder.angle = arm_.X_hat(0, 0);
492 status->shoulder.angular_velocity = arm_.X_hat(1, 0);
493 status->shoulder.goal_angle = arm_.goal(0, 0);
494 status->shoulder.goal_angular_velocity = arm_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800495 status->shoulder.unprofiled_goal_angle = arm_.unprofiled_goal(0, 0);
496 status->shoulder.unprofiled_goal_angular_velocity =
497 arm_.unprofiled_goal(1, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800498 status->shoulder.estimator_state = arm_.ShoulderEstimatorState();
499
500 status->wrist.angle = arm_.X_hat(2, 0);
501 status->wrist.angular_velocity = arm_.X_hat(3, 0);
502 status->wrist.goal_angle = arm_.goal(2, 0);
503 status->wrist.goal_angular_velocity = arm_.goal(3, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800504 status->wrist.unprofiled_goal_angle = arm_.unprofiled_goal(2, 0);
505 status->wrist.unprofiled_goal_angular_velocity = arm_.unprofiled_goal(3, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800506 status->wrist.estimator_state = arm_.WristEstimatorState();
507
508 status->intake.angle = intake_.X_hat(0, 0);
509 status->intake.angular_velocity = intake_.X_hat(1, 0);
510 status->intake.goal_angle = intake_.goal(0, 0);
511 status->intake.goal_angular_velocity = intake_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800512 status->intake.unprofiled_goal_angle = intake_.unprofiled_goal(0, 0);
513 status->intake.unprofiled_goal_angular_velocity =
514 intake_.unprofiled_goal(1, 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800515 status->intake.estimator_state = intake_.IntakeEstimatorState();
516
517 status->estopped = (state_ == ESTOP);
518
519 status->state = state_;
520
Adam Snaider06779722016-02-14 15:26:22 -0800521 last_state_ = state_before_switch;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800522}
523
Philipp Schrader0119eb12016-02-15 18:16:21 +0000524constexpr double Superstructure::kShoulderMiddleAngle;
525constexpr double Superstructure::kLooseTolerance;
526constexpr double Superstructure::kIntakeUpperClear;
527constexpr double Superstructure::kIntakeLowerClear;
528constexpr double Superstructure::kShoulderUpAngle;
529constexpr double Superstructure::kShoulderLanded;
530constexpr double Superstructure::kTightTolerance;
531constexpr double Superstructure::kWristAlmostLevel;
532constexpr double Superstructure::kShoulderWristClearAngle;
533
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800534} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000535} // namespace control_loops
536} // namespace y2016