blob: c3155bf294bafea8249cf7fabc63224f5571f10d [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
John Park33858a32018-09-28 23:05:48 -07004#include "aos/commonmath.h"
5#include "aos/controls/control_loops.q.h"
6#include "aos/logging/logging.h"
Comran Morshed25f81a02016-01-23 13:40:10 +00007
Austin Schuh2fc10fa2016-02-08 00:44:34 -08008#include "y2016/control_loops/superstructure/integral_intake_plant.h"
9#include "y2016/control_loops/superstructure/integral_arm_plant.h"
Austin Schuh1defd262016-04-03 16:13:32 -070010#include "y2016/queues/ball_detector.q.h"
Austin Schuh2fc10fa2016-02-08 00:44:34 -080011
12#include "y2016/constants.h"
13
Comran Morshed25f81a02016-01-23 13:40:10 +000014namespace y2016 {
15namespace control_loops {
Austin Schuh2fc10fa2016-02-08 00:44:34 -080016namespace superstructure {
Comran Morshed25f81a02016-01-23 13:40:10 +000017
Austin Schuh2fc10fa2016-02-08 00:44:34 -080018namespace {
Austin Schuh2d7820b2016-02-16 13:47:42 -080019// The maximum voltage the intake roller will be allowed to use.
Austin Schuhbe041152016-02-28 22:01:52 -080020constexpr float kMaxIntakeTopVoltage = 12.0;
21constexpr float kMaxIntakeBottomVoltage = 12.0;
Comran Morshed71466fe2016-04-21 20:21:14 -070022constexpr float kMaxClimberVoltage = 12.0;
Comran Morshedf4cd7642016-02-15 20:40:49 +000023
Austin Schuh2d7820b2016-02-16 13:47:42 -080024// Aliases to reduce typing.
Adam Snaider06779722016-02-14 15:26:22 -080025constexpr double kIntakeEncoderIndexDifference =
26 constants::Values::kIntakeEncoderIndexDifference;
27constexpr double kWristEncoderIndexDifference =
28 constants::Values::kWristEncoderIndexDifference;
29constexpr double kShoulderEncoderIndexDifference =
30 constants::Values::kShoulderEncoderIndexDifference;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080031} // namespace
32
Philipp Schrader0119eb12016-02-15 18:16:21 +000033// ///// CollisionAvoidance /////
34
35void CollisionAvoidance::UpdateGoal(double shoulder_angle_goal,
36 double wrist_angle_goal,
37 double intake_angle_goal) {
Austin Schuh6ca0f792016-03-12 14:06:14 -080038 const double original_shoulder_angle_goal = shoulder_angle_goal;
39 const double original_intake_angle_goal = intake_angle_goal;
Austin Schuh2c717862016-03-13 15:32:53 -070040 const double original_wrist_angle_goal = wrist_angle_goal;
Austin Schuh6ca0f792016-03-12 14:06:14 -080041
Philipp Schrader0119eb12016-02-15 18:16:21 +000042 double shoulder_angle = arm_->shoulder_angle();
43 double wrist_angle = arm_->wrist_angle();
Austin Schuh3634ed32017-02-05 16:28:49 -080044 double intake_angle = intake_->position();
Philipp Schrader0119eb12016-02-15 18:16:21 +000045
46 // TODO(phil): This may need tuning to account for bounciness in the limbs or
47 // some other thing that I haven't thought of. At the very least,
48 // incorporating a small safety margin makes writing test cases much easier
49 // since you can directly compare statuses against the constants in the
50 // CollisionAvoidance class.
Austin Schuhfef64ac2016-04-24 19:08:01 -070051 constexpr double kSafetyMargin = 0.03; // radians
Philipp Schrader0119eb12016-02-15 18:16:21 +000052
53 // Avoid colliding the shooter with the frame.
54 // If the shoulder is below a certain angle or we want to move it below
55 // that angle, then the shooter has to stay level to the ground. Otherwise,
56 // it will crash into the frame.
Austin Schuh2c717862016-03-13 15:32:53 -070057 if (intake_angle < kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
58 if (shoulder_angle < kMinShoulderAngleForHorizontalShooter ||
59 original_shoulder_angle_goal < kMinShoulderAngleForHorizontalShooter) {
60 wrist_angle_goal = 0.0;
61 } else if (shoulder_angle < kMinShoulderAngleForIntakeInterference ||
62 original_shoulder_angle_goal <
63 kMinShoulderAngleForIntakeInterference) {
64 wrist_angle_goal =
65 aos::Clip(original_wrist_angle_goal,
66 kMinWristAngleForMovingByIntake + kSafetyMargin,
67 kMaxWristAngleForMovingByIntake - kSafetyMargin);
68 }
69 } else {
70 if (shoulder_angle < kMinShoulderAngleForIntakeUpInterference ||
71 original_shoulder_angle_goal <
72 kMinShoulderAngleForIntakeUpInterference) {
73 wrist_angle_goal = 0.0;
74 }
75 }
76
Austin Schuh6ca0f792016-03-12 14:06:14 -080077 if (shoulder_angle < kMinShoulderAngleForIntakeUpInterference ||
78 original_shoulder_angle_goal < kMinShoulderAngleForIntakeUpInterference) {
Philipp Schrader0119eb12016-02-15 18:16:21 +000079 // Make sure that we don't move the shoulder below a certain angle until
80 // the wrist is level with the ground.
Austin Schuh6802a9d2016-03-12 21:34:53 -080081 if (intake_angle < kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
Austin Schuh2c717862016-03-13 15:32:53 -070082 if (wrist_angle > kMaxWristAngleForMovingByIntake ||
83 wrist_angle < kMinWristAngleForMovingByIntake) {
Austin Schuh6802a9d2016-03-12 21:34:53 -080084 shoulder_angle_goal =
Comran Morshed71466fe2016-04-21 20:21:14 -070085 ::std::max(original_shoulder_angle_goal,
86 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
Austin Schuh6802a9d2016-03-12 21:34:53 -080087 }
88 } else {
Austin Schuh2c717862016-03-13 15:32:53 -070089 if (wrist_angle > kMaxWristAngleForMovingByIntake ||
90 wrist_angle < kMinWristAngleForMovingByIntake) {
Comran Morshed71466fe2016-04-21 20:21:14 -070091 shoulder_angle_goal = ::std::max(
92 original_shoulder_angle_goal,
93 kMinShoulderAngleForIntakeUpInterference + kSafetyMargin);
Austin Schuh6802a9d2016-03-12 21:34:53 -080094 }
Austin Schuh6ca0f792016-03-12 14:06:14 -080095 }
Philipp Schrader0119eb12016-02-15 18:16:21 +000096 if (::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
97 shoulder_angle_goal =
Austin Schuh6802a9d2016-03-12 21:34:53 -080098 ::std::max(shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +000099 kMinShoulderAngleForHorizontalShooter + kSafetyMargin);
100 }
101 }
102
103 // Is the arm where it could interfere with the intake right now?
104 bool shoulder_is_in_danger =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800105 (shoulder_angle < kMinShoulderAngleForIntakeUpInterference &&
Philipp Schrader0119eb12016-02-15 18:16:21 +0000106 shoulder_angle > kMaxShoulderAngleUntilSafeIntakeStowing);
107
108 // Is the arm moving into collision zone from above?
109 bool shoulder_moving_into_danger_from_above =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800110 (shoulder_angle >= kMinShoulderAngleForIntakeUpInterference &&
111 original_shoulder_angle_goal <=
112 kMinShoulderAngleForIntakeUpInterference);
Philipp Schrader0119eb12016-02-15 18:16:21 +0000113
114 // Is the arm moving into collision zone from below?
115 bool shoulder_moving_into_danger_from_below =
116 (shoulder_angle <= kMaxShoulderAngleUntilSafeIntakeStowing &&
Austin Schuh6ca0f792016-03-12 14:06:14 -0800117 original_shoulder_angle_goal >= kMaxShoulderAngleUntilSafeIntakeStowing);
Philipp Schrader0119eb12016-02-15 18:16:21 +0000118
119 // Avoid colliding the arm with the intake.
120 if (shoulder_is_in_danger || shoulder_moving_into_danger_from_above ||
121 shoulder_moving_into_danger_from_below) {
122 // If the arm could collide with the intake, we make sure to move the
123 // intake out of the way. The arm has priority.
124 intake_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800125 ::std::min(original_intake_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000126 kMaxIntakeAngleBeforeArmInterference - kSafetyMargin);
127
128 // Don't let the shoulder move into the collision area until the intake is
129 // out of the way.
130 if (intake_angle > kMaxIntakeAngleBeforeArmInterference) {
131 const double kHalfwayPointBetweenSafeZones =
132 (kMinShoulderAngleForIntakeInterference +
133 kMaxShoulderAngleUntilSafeIntakeStowing) /
134 2.0;
135
136 if (shoulder_angle >= kHalfwayPointBetweenSafeZones) {
137 // The shoulder is closer to being above the collision area. Move it up
138 // there.
Austin Schuh2c717862016-03-13 15:32:53 -0700139 if (intake_angle <
140 kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
141 shoulder_angle_goal = ::std::max(
142 original_shoulder_angle_goal,
143 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
144 } else {
145 shoulder_angle_goal = ::std::max(
146 original_shoulder_angle_goal,
147 kMinShoulderAngleForIntakeUpInterference + kSafetyMargin);
148 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000149 } else {
150 // The shoulder is closer to being below the collision zone (i.e. in
151 // stowing/intake position), keep it there for now.
152 shoulder_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800153 ::std::min(original_shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000154 kMaxShoulderAngleUntilSafeIntakeStowing - kSafetyMargin);
155 }
156 }
157 }
158
159 // Send the possibly adjusted goals to the components.
160 arm_->set_unprofiled_goal(shoulder_angle_goal, wrist_angle_goal);
161 intake_->set_unprofiled_goal(intake_angle_goal);
162}
163
Philipp Schrader07147532016-02-16 01:23:07 +0000164bool CollisionAvoidance::collided() const {
165 return collided_with_given_angles(arm_->shoulder_angle(), arm_->wrist_angle(),
Austin Schuh3634ed32017-02-05 16:28:49 -0800166 intake_->position());
Philipp Schrader07147532016-02-16 01:23:07 +0000167}
168
169bool CollisionAvoidance::collided_with_given_angles(double shoulder_angle,
170 double wrist_angle,
171 double intake_angle) {
172 // The arm and the intake must not hit.
173 if (shoulder_angle >=
174 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
175 shoulder_angle <=
Austin Schuh6ca0f792016-03-12 14:06:14 -0800176 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference &&
Comran Morshed71466fe2016-04-21 20:21:14 -0700177 intake_angle > CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference) {
Austin Schuh6ca0f792016-03-12 14:06:14 -0800178 LOG(DEBUG, "Collided: Intake %f > %f, and shoulder %f < %f < %f.\n",
179 intake_angle, CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
180 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
Austin Schuhc85cdd52016-02-16 13:05:35 -0800181 shoulder_angle,
Austin Schuh6ca0f792016-03-12 14:06:14 -0800182 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference);
183 return true;
184 }
185
186 if (shoulder_angle >=
187 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
188 shoulder_angle <=
189 CollisionAvoidance::kMinShoulderAngleForIntakeInterference &&
190 intake_angle < CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference &&
191 intake_angle > Superstructure::kIntakeLowerClear &&
Austin Schuh2c717862016-03-13 15:32:53 -0700192 (wrist_angle > CollisionAvoidance::kMaxWristAngleForMovingByIntake ||
193 wrist_angle < CollisionAvoidance::kMinWristAngleForMovingByIntake)) {
194 LOG(DEBUG,
195 "Collided: Intake %f < %f < %f, shoulder %f < %f < %f, and %f < %f < "
196 "%f.\n",
Austin Schuh6ca0f792016-03-12 14:06:14 -0800197 Superstructure::kIntakeLowerClear, intake_angle,
198 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
199 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
200 shoulder_angle,
Austin Schuh2c717862016-03-13 15:32:53 -0700201 CollisionAvoidance::kMinShoulderAngleForIntakeInterference,
202 CollisionAvoidance::kMinWristAngleForMovingByIntake, wrist_angle,
203 CollisionAvoidance::kMaxWristAngleForMovingByIntake);
Philipp Schrader07147532016-02-16 01:23:07 +0000204 return true;
205 }
206
207 // The wrist must go back to zero when the shoulder is moving the arm into
208 // a stowed/intaking position.
209 if (shoulder_angle <
210 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter &&
211 ::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
Brian Silverman9180fc12016-05-16 00:09:02 -0700212 LOG(DEBUG, "Collided: Shoulder %f < %f and wrist |%f| > %f.\n",
Austin Schuhc85cdd52016-02-16 13:05:35 -0800213 shoulder_angle,
214 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter, wrist_angle,
215 kMaxWristAngleForSafeArmStowing);
Philipp Schrader07147532016-02-16 01:23:07 +0000216 return true;
217 }
218
219 return false;
220}
221
Philipp Schrader0119eb12016-02-15 18:16:21 +0000222constexpr double CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
223constexpr double CollisionAvoidance::kMinShoulderAngleForIntakeInterference;
224constexpr double CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
225constexpr double CollisionAvoidance::kMaxWristAngleForSafeArmStowing;
226constexpr double CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing;
227
Austin Schuh55a13dc2019-01-27 22:39:03 -0800228Superstructure::Superstructure(::aos::EventLoop *event_loop,
229 const ::std::string &name)
230 : aos::controls::ControlLoop<control_loops::SuperstructureQueue>(event_loop,
231 name),
Austin Schuh4b652c92019-05-27 13:22:27 -0700232 ball_detector_fetcher_(
233 event_loop->MakeFetcher<::y2016::sensors::BallDetector>(
234 ".y2016.sensors.ball_detector")),
Philipp Schrader0119eb12016-02-15 18:16:21 +0000235 collision_avoidance_(&intake_, &arm_) {}
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800236
Adam Snaider06779722016-02-14 15:26:22 -0800237bool Superstructure::IsArmNear(double shoulder_tolerance,
238 double wrist_tolerance) {
239 return ((arm_.unprofiled_goal() - arm_.X_hat())
240 .block<2, 1>(0, 0)
241 .lpNorm<Eigen::Infinity>() < shoulder_tolerance) &&
242 ((arm_.unprofiled_goal() - arm_.X_hat())
243 .block<4, 1>(0, 0)
244 .lpNorm<Eigen::Infinity>() < wrist_tolerance) &&
245 ((arm_.unprofiled_goal() - arm_.goal())
246 .block<4, 1>(0, 0)
247 .lpNorm<Eigen::Infinity>() < 1e-6);
248}
249
250bool Superstructure::IsArmNear(double tolerance) {
251 return ((arm_.unprofiled_goal() - arm_.X_hat())
252 .block<4, 1>(0, 0)
253 .lpNorm<Eigen::Infinity>() < tolerance) &&
254 ((arm_.unprofiled_goal() - arm_.goal())
255 .block<4, 1>(0, 0)
256 .lpNorm<Eigen::Infinity>() < 1e-6);
257}
258
259bool Superstructure::IsIntakeNear(double tolerance) {
260 return ((intake_.unprofiled_goal() - intake_.X_hat())
261 .block<2, 1>(0, 0)
262 .lpNorm<Eigen::Infinity>() < tolerance);
263}
264
265double Superstructure::MoveButKeepAbove(double reference_angle,
266 double current_angle,
267 double move_distance) {
268 return -MoveButKeepBelow(-reference_angle, -current_angle, -move_distance);
269}
270
271double Superstructure::MoveButKeepBelow(double reference_angle,
272 double current_angle,
273 double move_distance) {
274 // There are 3 interesting places to move to.
275 const double small_negative_move = current_angle - move_distance;
276 const double small_positive_move = current_angle + move_distance;
277 // And the reference angle.
278
279 // Move the the highest one that is below reference_angle.
280 if (small_negative_move > reference_angle) {
281 return reference_angle;
282 } else if (small_positive_move > reference_angle) {
283 return small_negative_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800284 } else {
Adam Snaider06779722016-02-14 15:26:22 -0800285 return small_positive_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800286 }
287}
288
289void Superstructure::RunIteration(
290 const control_loops::SuperstructureQueue::Goal *unsafe_goal,
291 const control_loops::SuperstructureQueue::Position *position,
292 control_loops::SuperstructureQueue::Output *output,
293 control_loops::SuperstructureQueue::Status *status) {
Adam Snaider06779722016-02-14 15:26:22 -0800294 const State state_before_switch = state_;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800295 if (WasReset()) {
296 LOG(ERROR, "WPILib reset, restarting\n");
297 arm_.Reset();
298 intake_.Reset();
299 state_ = UNINITIALIZED;
300 }
301
302 // Bool to track if we should turn the motors on or not.
303 bool disable = output == nullptr;
304
305 arm_.Correct(position->shoulder, position->wrist);
306 intake_.Correct(position->intake);
307
Adam Snaider06779722016-02-14 15:26:22 -0800308 // There are 2 main zeroing paths, HIGH_ARM_ZERO and LOW_ARM_ZERO.
309 //
310 // HIGH_ARM_ZERO works by lifting the arm all the way up so it is clear,
311 // moving the shooter to be horizontal, moving the intake out, and then moving
312 // the arm back down.
313 //
314 // LOW_ARM_ZERO works by moving the intake out of the way, lifting the arm up,
315 // leveling the shooter, and then moving back down.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800316
Diana Vandenberge2843c62016-02-13 17:44:20 -0800317 if (arm_.error() || intake_.error()) {
318 state_ = ESTOP;
319 }
320
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800321 const bool is_collided = collided();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800322 switch (state_) {
323 case UNINITIALIZED:
Adam Snaider06779722016-02-14 15:26:22 -0800324 // Wait in the uninitialized state until both the arm and intake are
325 // initialized.
326 LOG(DEBUG, "Uninitialized, waiting for intake and arm\n");
327 if (arm_.initialized() && intake_.initialized()) {
328 state_ = DISABLED_INITIALIZED;
329 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800330 disable = true;
331 break;
332
Adam Snaider06779722016-02-14 15:26:22 -0800333 case DISABLED_INITIALIZED:
334 // Wait here until we are either fully zeroed while disabled, or we become
335 // enabled. At that point, figure out if we should HIGH_ARM_ZERO or
336 // LOW_ARM_ZERO.
337 if (disable) {
338 if (arm_.zeroed() && intake_.zeroed()) {
339 state_ = SLOW_RUNNING;
340 }
341 } else {
342 if (arm_.shoulder_angle() >= kShoulderMiddleAngle) {
343 state_ = HIGH_ARM_ZERO_LIFT_ARM;
344 } else {
345 state_ = LOW_ARM_ZERO_LOWER_INTAKE;
346 }
347 }
348
349 // Set the goals to where we are now so when we start back up, we don't
350 // jump.
Austin Schuh3634ed32017-02-05 16:28:49 -0800351 intake_.ForceGoal(intake_.position());
Adam Snaider06779722016-02-14 15:26:22 -0800352 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
353 // Set up the profile to be the zeroing profile.
354 intake_.AdjustProfile(0.5, 10);
355 arm_.AdjustProfile(0.5, 10, 0.5, 10);
356
357 // We are not ready to start doing anything yet.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800358 disable = true;
Adam Snaider06779722016-02-14 15:26:22 -0800359 break;
360
361 case HIGH_ARM_ZERO_LIFT_ARM:
362 if (disable) {
363 state_ = DISABLED_INITIALIZED;
364 } else {
365 // Raise the shoulder up out of the way.
366 arm_.set_unprofiled_goal(kShoulderUpAngle, arm_.wrist_angle());
367 if (IsArmNear(kLooseTolerance)) {
368 // Close enough, start the next move.
369 state_ = HIGH_ARM_ZERO_LEVEL_SHOOTER;
370 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800371 }
372 break;
373
Adam Snaider06779722016-02-14 15:26:22 -0800374 case HIGH_ARM_ZERO_LEVEL_SHOOTER:
375 if (disable) {
376 state_ = DISABLED_INITIALIZED;
377 } else {
378 // Move the shooter to be level.
379 arm_.set_unprofiled_goal(kShoulderUpAngle, 0.0);
380
381 if (IsArmNear(kLooseTolerance)) {
382 // Close enough, start the next move.
383 state_ = HIGH_ARM_ZERO_MOVE_INTAKE_OUT;
384 }
385 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800386 break;
387
Adam Snaider06779722016-02-14 15:26:22 -0800388 case HIGH_ARM_ZERO_MOVE_INTAKE_OUT:
389 if (disable) {
390 state_ = DISABLED_INITIALIZED;
391 } else {
392 // If we were just asked to move the intake, make sure it moves far
393 // enough.
394 if (last_state_ != HIGH_ARM_ZERO_MOVE_INTAKE_OUT) {
395 intake_.set_unprofiled_goal(
Austin Schuh3634ed32017-02-05 16:28:49 -0800396 MoveButKeepBelow(kIntakeUpperClear, intake_.position(),
Adam Snaider06779722016-02-14 15:26:22 -0800397 kIntakeEncoderIndexDifference * 2.5));
398 }
399
400 if (IsIntakeNear(kLooseTolerance)) {
401 // Close enough, start the next move.
402 state_ = HIGH_ARM_ZERO_LOWER_ARM;
403 }
404 }
405 break;
406
407 case HIGH_ARM_ZERO_LOWER_ARM:
408 if (disable) {
409 state_ = DISABLED_INITIALIZED;
410 } else {
411 // Land the shooter in the belly-pan. It should be zeroed by the time
412 // it gets there. If not, just estop.
413 arm_.set_unprofiled_goal(kShoulderLanded, 0.0);
414 if (arm_.zeroed() && intake_.zeroed()) {
415 state_ = RUNNING;
416 } else if (IsArmNear(kLooseTolerance)) {
417 LOG(ERROR,
418 "Failed to zero while executing the HIGH_ARM_ZERO sequence. "
419 "Arm: %d Intake %d\n",
420 arm_.zeroed(), intake_.zeroed());
421 state_ = ESTOP;
422 }
423 }
424 break;
425
426 case LOW_ARM_ZERO_LOWER_INTAKE:
427 if (disable) {
428 state_ = DISABLED_INITIALIZED;
429 } else {
430 // Move the intake down out of the way of the arm. Make sure to move it
431 // far enough to zero.
432 if (last_state_ != LOW_ARM_ZERO_LOWER_INTAKE) {
433 intake_.set_unprofiled_goal(
Austin Schuh3634ed32017-02-05 16:28:49 -0800434 MoveButKeepBelow(kIntakeLowerClear, intake_.position(),
Adam Snaider06779722016-02-14 15:26:22 -0800435 kIntakeEncoderIndexDifference * 2.5));
436 }
437 if (IsIntakeNear(kLooseTolerance)) {
438 if (::std::abs(arm_.wrist_angle()) < kWristAlmostLevel) {
439 state_ = LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER;
440 } else {
441 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
442 }
443 }
444 }
445 break;
446
447 case LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER:
448 if (disable) {
449 state_ = DISABLED_INITIALIZED;
450 } else {
451 // If we are supposed to level the shooter, set it to level, and wait
452 // until it is very close to level.
453 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
454 if (IsArmNear(kLooseTolerance, kTightTolerance)) {
455 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
456 }
457 }
458 break;
459
460 case LOW_ARM_ZERO_LIFT_SHOULDER:
461 if (disable) {
462 state_ = DISABLED_INITIALIZED;
463 } else {
464 // Decide where to move to. We need to move far enough to see an index
465 // pulse, but must also get high enough that we can safely level the
466 // shooter.
467 if (last_state_ != LOW_ARM_ZERO_LIFT_SHOULDER) {
468 arm_.set_unprofiled_goal(
469 MoveButKeepAbove(kShoulderWristClearAngle, arm_.shoulder_angle(),
470 ::std::max(kWristEncoderIndexDifference,
471 kShoulderEncoderIndexDifference) *
472 2.5),
473 arm_.unprofiled_goal(2, 0));
474 }
475
Brian Silverman741b27a2016-05-16 00:09:26 -0700476 // If we're about to ask the wrist to go past one of its limits, then
477 // move the goal so it will be just at the limit when we finish lifting
478 // the shoulder. If it wasn't intersecting something before, this can't
479 // cause it to crash into anything.
480 const double ungrounded_wrist = arm_.goal(2, 0) - arm_.goal(0, 0);
481 const double unprofiled_ungrounded_wrist =
482 arm_.unprofiled_goal(2, 0) - arm_.unprofiled_goal(0, 0);
483 if (unprofiled_ungrounded_wrist >
484 constants::Values::kWristRange.upper &&
485 ungrounded_wrist >
486 constants::Values::kWristRange.upper - kWristAlmostLevel) {
487 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0),
488 constants::Values::kWristRange.upper +
489 arm_.unprofiled_goal(0, 0));
490 } else if (unprofiled_ungrounded_wrist <
491 constants::Values::kWristRange.lower &&
492 ungrounded_wrist < constants::Values::kWristRange.lower +
493 kWristAlmostLevel) {
494 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0),
495 constants::Values::kWristRange.lower +
496 arm_.unprofiled_goal(0, 0));
497 }
498
Adam Snaider06779722016-02-14 15:26:22 -0800499 // Wait until we are level and then go for it.
500 if (IsArmNear(kLooseTolerance)) {
501 state_ = LOW_ARM_ZERO_LEVEL_SHOOTER;
502 }
503 }
504 break;
505
506 case LOW_ARM_ZERO_LEVEL_SHOOTER:
507 if (disable) {
508 state_ = DISABLED_INITIALIZED;
509 } else {
510 // Move the shooter level (and keep the same height). We don't want to
511 // got to RUNNING until we are completely level so that we don't
512 // give control back in a weird case where we might crash.
513 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
514 if (IsArmNear(kLooseTolerance)) {
515 if (arm_.zeroed() && intake_.zeroed()) {
516 state_ = RUNNING;
517 } else {
518 LOG(ERROR,
519 "Failed to zero while executing the LOW_ARM_ZERO sequence. "
520 "Arm: %d Intake %d\n",
521 arm_.zeroed(), intake_.zeroed());
522 state_ = ESTOP;
523 }
524 }
525 }
526 break;
527
Austin Schuhb1d682b2016-02-16 13:07:44 -0800528 // These 4 cases are very similar.
Adam Snaider06779722016-02-14 15:26:22 -0800529 case SLOW_RUNNING:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800530 case RUNNING:
Austin Schuhb1d682b2016-02-16 13:07:44 -0800531 case LANDING_SLOW_RUNNING:
532 case LANDING_RUNNING: {
Austin Schuh829fe572016-02-14 21:41:16 -0800533 if (disable) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800534 // If we are disabled, go to slow running (or landing slow running) if
535 // we are collided.
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800536 if (is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800537 if (state_ == RUNNING) {
538 state_ = SLOW_RUNNING;
539 } else if (state_ == LANDING_RUNNING) {
540 state_ = LANDING_SLOW_RUNNING;
541 }
542 }
Austin Schuh829fe572016-02-14 21:41:16 -0800543
Austin Schuhb1d682b2016-02-16 13:07:44 -0800544 // Reset the profile to the current position so it moves well from here.
Austin Schuh3634ed32017-02-05 16:28:49 -0800545 intake_.ForceGoal(intake_.position());
Austin Schuh829fe572016-02-14 21:41:16 -0800546 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
547 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800548 // If we are in slow_running and are no longer collided, let 'er rip.
Austin Schuh829fe572016-02-14 21:41:16 -0800549 if (state_ == SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800550 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800551 state_ = RUNNING;
552 }
553 } else if (state_ == LANDING_SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800554 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800555 state_ = LANDING_RUNNING;
556 }
Austin Schuh829fe572016-02-14 21:41:16 -0800557 }
558 }
559
Austin Schuhb1d682b2016-02-16 13:07:44 -0800560 double requested_shoulder = constants::Values::kShoulderRange.lower;
561 double requested_wrist = 0.0;
562 double requested_intake = M_PI / 2.0;
563
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800564 if (unsafe_goal) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800565 // If we are in one of the landing states, limit the accelerations and
566 // velocities to land cleanly.
567 if (state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING) {
568 arm_.AdjustProfile(0.5, // Shoulder Velocity
569 4.0, // Shoulder acceleration,
570 4.0, // Wrist velocity
571 10.0); // Wrist acceleration.
572 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
573 unsafe_goal->max_angular_acceleration_intake);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800574
Austin Schuhb1d682b2016-02-16 13:07:44 -0800575 requested_shoulder =
576 ::std::max(unsafe_goal->angle_shoulder,
577 constants::Values::kShoulderRange.lower);
578 requested_wrist = 0.0;
579 requested_intake = unsafe_goal->angle_intake;
Austin Schuha1dd5272016-02-16 15:39:38 -0800580 // Transition to landing once the profile is close to finished for the
581 // shoulder.
582 if (arm_.goal(0, 0) > kShoulderTransitionToLanded + 1e-4 ||
583 arm_.unprofiled_goal(0, 0) > kShoulderTransitionToLanded + 1e-4) {
584 if (state_ == LANDING_RUNNING) {
585 state_ = RUNNING;
586 } else {
587 state_ = SLOW_RUNNING;
588 }
589 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000590 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800591 // Otherwise, give the user what he asked for.
592 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder,
593 unsafe_goal->max_angular_acceleration_shoulder,
594 unsafe_goal->max_angular_velocity_wrist,
595 unsafe_goal->max_angular_acceleration_wrist);
596 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake,
597 unsafe_goal->max_angular_acceleration_intake);
598
599 // Except, don't let the shoulder go all the way down.
600 requested_shoulder = ::std::max(unsafe_goal->angle_shoulder,
601 kShoulderTransitionToLanded);
602 requested_wrist = unsafe_goal->angle_wrist;
603 requested_intake = unsafe_goal->angle_intake;
604
605 // Transition to landing once the profile is close to finished for the
606 // shoulder.
Austin Schuha1dd5272016-02-16 15:39:38 -0800607 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 &&
608 arm_.unprofiled_goal(0, 0) <=
609 kShoulderTransitionToLanded + 1e-4) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800610 if (state_ == RUNNING) {
611 state_ = LANDING_RUNNING;
612 } else {
613 state_ = LANDING_SLOW_RUNNING;
614 }
615 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000616 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800617 }
618
Austin Schuhb1d682b2016-02-16 13:07:44 -0800619 // Push the request out to hardware!
620 collision_avoidance_.UpdateGoal(requested_shoulder, requested_wrist,
621 requested_intake);
622
623 // ESTOP if we hit the hard limits.
624 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
625 state_ = ESTOP;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800626 }
Austin Schuhb1d682b2016-02-16 13:07:44 -0800627 } break;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800628
629 case ESTOP:
630 LOG(ERROR, "Estop\n");
631 disable = true;
632 break;
633 }
634
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800635 // Set the voltage limits.
Comran Morshed011f7d92016-02-16 23:03:06 +0000636 const double max_voltage = (state_ == RUNNING || state_ == LANDING_RUNNING)
637 ? kOperatingVoltage
638 : kZeroingVoltage;
Comran Morshed71466fe2016-04-21 20:21:14 -0700639 if (unsafe_goal) {
Austin Schuh0c001a82016-05-01 12:30:09 -0700640 constexpr float kTriggerThreshold = 12.0 * 0.90 / 0.005;
Comran Morshed71466fe2016-04-21 20:21:14 -0700641
642 if (unsafe_goal->voltage_climber > 1.0) {
643 kill_shoulder_accumulator_ +=
644 ::std::min(kMaxClimberVoltage, unsafe_goal->voltage_climber);
645 } else {
646 kill_shoulder_accumulator_ = 0.0;
647 }
648
649 if (kill_shoulder_accumulator_ > kTriggerThreshold) {
650 kill_shoulder_ = true;
651 }
652 }
Austin Schuh0c001a82016-05-01 12:30:09 -0700653 arm_.set_max_voltage(
Austin Schuh473a5652017-02-05 01:30:42 -0800654 {{kill_shoulder_ ? 0.0 : max_voltage,
655 kill_shoulder_ ? (arm_.X_hat(0, 0) < 0.05 ? kShooterHangingLowVoltage
656 : kShooterHangingVoltage)
657 : max_voltage}});
658 intake_.set_max_voltage({{max_voltage}});
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800659
Diana Vandenberg9cc9ab62016-04-20 21:27:47 -0700660 if (IsRunning() && !kill_shoulder_) {
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800661 // We don't want lots of negative voltage when we are near the bellypan on
662 // the shoulder...
663 // TODO(austin): Do I want to push negative power into the belly pan at this
664 // point? Maybe just put the goal slightly below the bellypan and call that
665 // good enough.
Austin Schuh6ca0f792016-03-12 14:06:14 -0800666 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 ||
667 arm_.X_hat(0, 0) <= kShoulderTransitionToLanded + 1e-4) {
Comran Morshed011f7d92016-02-16 23:03:06 +0000668 arm_.set_shoulder_asymetric_limits(kLandingShoulderDownVoltage,
669 max_voltage);
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800670 }
671 }
672
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800673 // Calculate the loops for a cycle.
Austin Schuhbe041152016-02-28 22:01:52 -0800674 {
675 Eigen::Matrix<double, 3, 1> error = intake_.controller().error();
Austin Schuh32501832017-02-25 18:32:56 -0800676 status->intake.position_power =
677 intake_.controller().controller().K(0, 0) * error(0, 0);
678 status->intake.velocity_power =
679 intake_.controller().controller().K(0, 1) * error(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800680 }
681
682 {
683 Eigen::Matrix<double, 6, 1> error = arm_.controller().error();
Austin Schuh32501832017-02-25 18:32:56 -0800684 status->shoulder.position_power =
685 arm_.controller().controller().K(0, 0) * error(0, 0);
686 status->shoulder.velocity_power =
687 arm_.controller().controller().K(0, 1) * error(1, 0);
688 status->wrist.position_power =
689 arm_.controller().controller().K(0, 2) * error(2, 0);
690 status->wrist.velocity_power =
691 arm_.controller().controller().K(0, 3) * error(3, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800692 }
693
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800694 arm_.Update(disable);
695 intake_.Update(disable);
696
697 // Write out all the voltages.
698 if (output) {
Austin Schuh3634ed32017-02-05 16:28:49 -0800699 output->voltage_intake = intake_.voltage();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800700 output->voltage_shoulder = arm_.shoulder_voltage();
701 output->voltage_wrist = arm_.wrist_voltage();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000702
Campbell Crowleyd4fd6552016-02-21 17:53:46 -0800703 output->voltage_top_rollers = 0.0;
704 output->voltage_bottom_rollers = 0.0;
Comran Morshed71466fe2016-04-21 20:21:14 -0700705
706 output->voltage_climber = 0.0;
707 output->unfold_climber = false;
Comran Morshedf4cd7642016-02-15 20:40:49 +0000708 if (unsafe_goal) {
Comran Morshed71466fe2016-04-21 20:21:14 -0700709 // Ball detector lights.
Austin Schuh4b652c92019-05-27 13:22:27 -0700710 ball_detector_fetcher_.Fetch();
Austin Schuh1defd262016-04-03 16:13:32 -0700711 bool ball_detected = false;
Austin Schuh4b652c92019-05-27 13:22:27 -0700712 if (ball_detector_fetcher_.get()) {
713 ball_detected = ball_detector_fetcher_->voltage > 2.5;
Austin Schuh1defd262016-04-03 16:13:32 -0700714 }
Comran Morshed71466fe2016-04-21 20:21:14 -0700715
716 // Climber.
717 output->voltage_climber = ::std::max(
718 static_cast<float>(0.0),
719 ::std::min(unsafe_goal->voltage_climber, kMaxClimberVoltage));
720 output->unfold_climber = unsafe_goal->unfold_climber;
721
722 // Intake.
Austin Schuh1defd262016-04-03 16:13:32 -0700723 if (unsafe_goal->force_intake || !ball_detected) {
724 output->voltage_top_rollers = ::std::max(
725 -kMaxIntakeTopVoltage,
726 ::std::min(unsafe_goal->voltage_top_rollers, kMaxIntakeTopVoltage));
727 output->voltage_bottom_rollers =
728 ::std::max(-kMaxIntakeBottomVoltage,
729 ::std::min(unsafe_goal->voltage_bottom_rollers,
730 kMaxIntakeBottomVoltage));
731 } else {
732 output->voltage_top_rollers = 0.0;
733 output->voltage_bottom_rollers = 0.0;
734 }
Comran Morshed71466fe2016-04-21 20:21:14 -0700735
736 // Traverse.
Austin Schuh843412b2016-03-20 16:48:46 -0700737 output->traverse_unlatched = unsafe_goal->traverse_unlatched;
738 output->traverse_down = unsafe_goal->traverse_down;
Comran Morshedf4cd7642016-02-15 20:40:49 +0000739 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800740 }
741
742 // Save debug/internal state.
Austin Schuha987f8b2016-02-28 22:02:20 -0800743 status->zeroed = arm_.zeroed() && intake_.zeroed();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800744
745 status->shoulder.angle = arm_.X_hat(0, 0);
746 status->shoulder.angular_velocity = arm_.X_hat(1, 0);
747 status->shoulder.goal_angle = arm_.goal(0, 0);
748 status->shoulder.goal_angular_velocity = arm_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800749 status->shoulder.unprofiled_goal_angle = arm_.unprofiled_goal(0, 0);
750 status->shoulder.unprofiled_goal_angular_velocity =
751 arm_.unprofiled_goal(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800752 status->shoulder.voltage_error = arm_.X_hat(4, 0);
753 status->shoulder.calculated_velocity =
754 (arm_.shoulder_angle() - last_shoulder_angle_) / 0.005;
Austin Schuh473a5652017-02-05 01:30:42 -0800755 status->shoulder.estimator_state = arm_.EstimatorState(0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800756
757 status->wrist.angle = arm_.X_hat(2, 0);
758 status->wrist.angular_velocity = arm_.X_hat(3, 0);
759 status->wrist.goal_angle = arm_.goal(2, 0);
760 status->wrist.goal_angular_velocity = arm_.goal(3, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800761 status->wrist.unprofiled_goal_angle = arm_.unprofiled_goal(2, 0);
762 status->wrist.unprofiled_goal_angular_velocity = arm_.unprofiled_goal(3, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800763 status->wrist.voltage_error = arm_.X_hat(5, 0);
764 status->wrist.calculated_velocity =
765 (arm_.wrist_angle() - last_wrist_angle_) / 0.005;
Austin Schuh473a5652017-02-05 01:30:42 -0800766 status->wrist.estimator_state = arm_.EstimatorState(1);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800767
768 status->intake.angle = intake_.X_hat(0, 0);
769 status->intake.angular_velocity = intake_.X_hat(1, 0);
770 status->intake.goal_angle = intake_.goal(0, 0);
771 status->intake.goal_angular_velocity = intake_.goal(1, 0);
Austin Schuh39fd6102016-02-13 22:59:48 -0800772 status->intake.unprofiled_goal_angle = intake_.unprofiled_goal(0, 0);
773 status->intake.unprofiled_goal_angular_velocity =
774 intake_.unprofiled_goal(1, 0);
Comran Morshed71466fe2016-04-21 20:21:14 -0700775 status->intake.calculated_velocity =
Austin Schuh3634ed32017-02-05 16:28:49 -0800776 (intake_.position() - last_intake_angle_) / 0.005;
Austin Schuhbe041152016-02-28 22:01:52 -0800777 status->intake.voltage_error = intake_.X_hat(2, 0);
Austin Schuh473a5652017-02-05 01:30:42 -0800778 status->intake.estimator_state = intake_.EstimatorState(0);
Austin Schuhbe041152016-02-28 22:01:52 -0800779 status->intake.feedforwards_power = intake_.controller().ff_U(0, 0);
780
Austin Schuhf59b6bc2016-03-11 21:26:19 -0800781 status->shoulder_controller_index = arm_.controller_index();
782
Austin Schuhbe041152016-02-28 22:01:52 -0800783 last_shoulder_angle_ = arm_.shoulder_angle();
784 last_wrist_angle_ = arm_.wrist_angle();
Austin Schuh3634ed32017-02-05 16:28:49 -0800785 last_intake_angle_ = intake_.position();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800786
787 status->estopped = (state_ == ESTOP);
788
789 status->state = state_;
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800790 status->is_collided = is_collided;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800791
Adam Snaider06779722016-02-14 15:26:22 -0800792 last_state_ = state_before_switch;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800793}
794
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000795constexpr double Superstructure::kZeroingVoltage;
796constexpr double Superstructure::kOperatingVoltage;
Austin Schuh6ca0f792016-03-12 14:06:14 -0800797constexpr double Superstructure::kLandingShoulderDownVoltage;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000798constexpr double Superstructure::kShoulderMiddleAngle;
799constexpr double Superstructure::kLooseTolerance;
800constexpr double Superstructure::kIntakeUpperClear;
801constexpr double Superstructure::kIntakeLowerClear;
802constexpr double Superstructure::kShoulderUpAngle;
803constexpr double Superstructure::kShoulderLanded;
804constexpr double Superstructure::kTightTolerance;
805constexpr double Superstructure::kWristAlmostLevel;
806constexpr double Superstructure::kShoulderWristClearAngle;
Austin Schuhb1d682b2016-02-16 13:07:44 -0800807constexpr double Superstructure::kShoulderTransitionToLanded;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000808
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800809} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000810} // namespace control_loops
811} // namespace y2016