blob: 8f4d02ec6f26095f485fd96e151a82ff7fe35749 [file] [log] [blame]
Comran Morshed25f81a02016-01-23 13:40:10 +00001#include "y2016/control_loops/superstructure/superstructure.h"
2
John Park33858a32018-09-28 23:05:48 -07003#include "aos/commonmath.h"
John Park33858a32018-09-28 23:05:48 -07004#include "aos/logging/logging.h"
Comran Morshed25f81a02016-01-23 13:40:10 +00005
Austin Schuh2fc10fa2016-02-08 00:44:34 -08006#include "y2016/control_loops/superstructure/integral_arm_plant.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07007#include "y2016/control_loops/superstructure/integral_intake_plant.h"
8#include "y2016/control_loops/superstructure/superstructure_controls.h"
9#include "y2016/queues/ball_detector_generated.h"
Austin Schuh2fc10fa2016-02-08 00:44:34 -080010
11#include "y2016/constants.h"
12
Comran Morshed25f81a02016-01-23 13:40:10 +000013namespace y2016 {
14namespace control_loops {
Austin Schuh2fc10fa2016-02-08 00:44:34 -080015namespace superstructure {
Comran Morshed25f81a02016-01-23 13:40:10 +000016
Austin Schuh2fc10fa2016-02-08 00:44:34 -080017namespace {
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 Morshed71466fe2016-04-21 20:21:14 -070021constexpr float kMaxClimberVoltage = 12.0;
Comran Morshedf4cd7642016-02-15 20:40:49 +000022
Austin Schuh2d7820b2016-02-16 13:47:42 -080023// Aliases to reduce typing.
Adam Snaider06779722016-02-14 15:26:22 -080024constexpr double kIntakeEncoderIndexDifference =
25 constants::Values::kIntakeEncoderIndexDifference;
26constexpr double kWristEncoderIndexDifference =
27 constants::Values::kWristEncoderIndexDifference;
28constexpr double kShoulderEncoderIndexDifference =
29 constants::Values::kShoulderEncoderIndexDifference;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080030} // namespace
31
Philipp Schrader0119eb12016-02-15 18:16:21 +000032// ///// CollisionAvoidance /////
33
34void CollisionAvoidance::UpdateGoal(double shoulder_angle_goal,
35 double wrist_angle_goal,
36 double intake_angle_goal) {
Austin Schuh6ca0f792016-03-12 14:06:14 -080037 const double original_shoulder_angle_goal = shoulder_angle_goal;
38 const double original_intake_angle_goal = intake_angle_goal;
Austin Schuh2c717862016-03-13 15:32:53 -070039 const double original_wrist_angle_goal = wrist_angle_goal;
Austin Schuh6ca0f792016-03-12 14:06:14 -080040
Philipp Schrader0119eb12016-02-15 18:16:21 +000041 double shoulder_angle = arm_->shoulder_angle();
42 double wrist_angle = arm_->wrist_angle();
Austin Schuh3634ed32017-02-05 16:28:49 -080043 double intake_angle = intake_->position();
Philipp Schrader0119eb12016-02-15 18:16:21 +000044
45 // TODO(phil): This may need tuning to account for bounciness in the limbs or
46 // some other thing that I haven't thought of. At the very least,
47 // incorporating a small safety margin makes writing test cases much easier
48 // since you can directly compare statuses against the constants in the
49 // CollisionAvoidance class.
Austin Schuhfef64ac2016-04-24 19:08:01 -070050 constexpr double kSafetyMargin = 0.03; // radians
Philipp Schrader0119eb12016-02-15 18:16:21 +000051
52 // Avoid colliding the shooter with the frame.
53 // If the shoulder is below a certain angle or we want to move it below
54 // that angle, then the shooter has to stay level to the ground. Otherwise,
55 // it will crash into the frame.
Austin Schuh2c717862016-03-13 15:32:53 -070056 if (intake_angle < kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
57 if (shoulder_angle < kMinShoulderAngleForHorizontalShooter ||
58 original_shoulder_angle_goal < kMinShoulderAngleForHorizontalShooter) {
59 wrist_angle_goal = 0.0;
60 } else if (shoulder_angle < kMinShoulderAngleForIntakeInterference ||
61 original_shoulder_angle_goal <
62 kMinShoulderAngleForIntakeInterference) {
63 wrist_angle_goal =
64 aos::Clip(original_wrist_angle_goal,
65 kMinWristAngleForMovingByIntake + kSafetyMargin,
66 kMaxWristAngleForMovingByIntake - kSafetyMargin);
67 }
68 } else {
69 if (shoulder_angle < kMinShoulderAngleForIntakeUpInterference ||
70 original_shoulder_angle_goal <
71 kMinShoulderAngleForIntakeUpInterference) {
72 wrist_angle_goal = 0.0;
73 }
74 }
75
Austin Schuh6ca0f792016-03-12 14:06:14 -080076 if (shoulder_angle < kMinShoulderAngleForIntakeUpInterference ||
77 original_shoulder_angle_goal < kMinShoulderAngleForIntakeUpInterference) {
Philipp Schrader0119eb12016-02-15 18:16:21 +000078 // Make sure that we don't move the shoulder below a certain angle until
79 // the wrist is level with the ground.
Austin Schuh6802a9d2016-03-12 21:34:53 -080080 if (intake_angle < kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
Austin Schuh2c717862016-03-13 15:32:53 -070081 if (wrist_angle > kMaxWristAngleForMovingByIntake ||
82 wrist_angle < kMinWristAngleForMovingByIntake) {
Austin Schuh6802a9d2016-03-12 21:34:53 -080083 shoulder_angle_goal =
Comran Morshed71466fe2016-04-21 20:21:14 -070084 ::std::max(original_shoulder_angle_goal,
85 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
Austin Schuh6802a9d2016-03-12 21:34:53 -080086 }
87 } else {
Austin Schuh2c717862016-03-13 15:32:53 -070088 if (wrist_angle > kMaxWristAngleForMovingByIntake ||
89 wrist_angle < kMinWristAngleForMovingByIntake) {
Comran Morshed71466fe2016-04-21 20:21:14 -070090 shoulder_angle_goal = ::std::max(
91 original_shoulder_angle_goal,
92 kMinShoulderAngleForIntakeUpInterference + kSafetyMargin);
Austin Schuh6802a9d2016-03-12 21:34:53 -080093 }
Austin Schuh6ca0f792016-03-12 14:06:14 -080094 }
Philipp Schrader0119eb12016-02-15 18:16:21 +000095 if (::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
96 shoulder_angle_goal =
Austin Schuh6802a9d2016-03-12 21:34:53 -080097 ::std::max(shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +000098 kMinShoulderAngleForHorizontalShooter + kSafetyMargin);
99 }
100 }
101
102 // Is the arm where it could interfere with the intake right now?
103 bool shoulder_is_in_danger =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800104 (shoulder_angle < kMinShoulderAngleForIntakeUpInterference &&
Philipp Schrader0119eb12016-02-15 18:16:21 +0000105 shoulder_angle > kMaxShoulderAngleUntilSafeIntakeStowing);
106
107 // Is the arm moving into collision zone from above?
108 bool shoulder_moving_into_danger_from_above =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800109 (shoulder_angle >= kMinShoulderAngleForIntakeUpInterference &&
110 original_shoulder_angle_goal <=
111 kMinShoulderAngleForIntakeUpInterference);
Philipp Schrader0119eb12016-02-15 18:16:21 +0000112
113 // Is the arm moving into collision zone from below?
114 bool shoulder_moving_into_danger_from_below =
115 (shoulder_angle <= kMaxShoulderAngleUntilSafeIntakeStowing &&
Austin Schuh6ca0f792016-03-12 14:06:14 -0800116 original_shoulder_angle_goal >= kMaxShoulderAngleUntilSafeIntakeStowing);
Philipp Schrader0119eb12016-02-15 18:16:21 +0000117
118 // Avoid colliding the arm with the intake.
119 if (shoulder_is_in_danger || shoulder_moving_into_danger_from_above ||
120 shoulder_moving_into_danger_from_below) {
121 // If the arm could collide with the intake, we make sure to move the
122 // intake out of the way. The arm has priority.
123 intake_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800124 ::std::min(original_intake_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000125 kMaxIntakeAngleBeforeArmInterference - kSafetyMargin);
126
127 // Don't let the shoulder move into the collision area until the intake is
128 // out of the way.
129 if (intake_angle > kMaxIntakeAngleBeforeArmInterference) {
130 const double kHalfwayPointBetweenSafeZones =
131 (kMinShoulderAngleForIntakeInterference +
132 kMaxShoulderAngleUntilSafeIntakeStowing) /
133 2.0;
134
135 if (shoulder_angle >= kHalfwayPointBetweenSafeZones) {
136 // The shoulder is closer to being above the collision area. Move it up
137 // there.
Austin Schuh2c717862016-03-13 15:32:53 -0700138 if (intake_angle <
139 kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
140 shoulder_angle_goal = ::std::max(
141 original_shoulder_angle_goal,
142 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
143 } else {
144 shoulder_angle_goal = ::std::max(
145 original_shoulder_angle_goal,
146 kMinShoulderAngleForIntakeUpInterference + kSafetyMargin);
147 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000148 } else {
149 // The shoulder is closer to being below the collision zone (i.e. in
150 // stowing/intake position), keep it there for now.
151 shoulder_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800152 ::std::min(original_shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000153 kMaxShoulderAngleUntilSafeIntakeStowing - kSafetyMargin);
154 }
155 }
156 }
157
158 // Send the possibly adjusted goals to the components.
159 arm_->set_unprofiled_goal(shoulder_angle_goal, wrist_angle_goal);
160 intake_->set_unprofiled_goal(intake_angle_goal);
161}
162
Philipp Schrader07147532016-02-16 01:23:07 +0000163bool CollisionAvoidance::collided() const {
164 return collided_with_given_angles(arm_->shoulder_angle(), arm_->wrist_angle(),
Austin Schuh3634ed32017-02-05 16:28:49 -0800165 intake_->position());
Philipp Schrader07147532016-02-16 01:23:07 +0000166}
167
168bool CollisionAvoidance::collided_with_given_angles(double shoulder_angle,
169 double wrist_angle,
170 double intake_angle) {
171 // The arm and the intake must not hit.
172 if (shoulder_angle >=
173 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
174 shoulder_angle <=
Austin Schuh6ca0f792016-03-12 14:06:14 -0800175 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference &&
Comran Morshed71466fe2016-04-21 20:21:14 -0700176 intake_angle > CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700177 AOS_LOG(DEBUG, "Collided: Intake %f > %f, and shoulder %f < %f < %f.\n",
178 intake_angle,
179 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
180 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
181 shoulder_angle,
182 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference);
Austin Schuh6ca0f792016-03-12 14:06:14 -0800183 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)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700194 AOS_LOG(
195 DEBUG,
Austin Schuh2c717862016-03-13 15:32:53 -0700196 "Collided: Intake %f < %f < %f, shoulder %f < %f < %f, and %f < %f < "
197 "%f.\n",
Austin Schuh6ca0f792016-03-12 14:06:14 -0800198 Superstructure::kIntakeLowerClear, intake_angle,
199 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
200 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
201 shoulder_angle,
Austin Schuh2c717862016-03-13 15:32:53 -0700202 CollisionAvoidance::kMinShoulderAngleForIntakeInterference,
203 CollisionAvoidance::kMinWristAngleForMovingByIntake, wrist_angle,
204 CollisionAvoidance::kMaxWristAngleForMovingByIntake);
Philipp Schrader07147532016-02-16 01:23:07 +0000205 return true;
206 }
207
208 // The wrist must go back to zero when the shoulder is moving the arm into
209 // a stowed/intaking position.
210 if (shoulder_angle <
211 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter &&
212 ::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700213 AOS_LOG(DEBUG, "Collided: Shoulder %f < %f and wrist |%f| > %f.\n",
214 shoulder_angle,
215 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter,
216 wrist_angle, kMaxWristAngleForSafeArmStowing);
Philipp Schrader07147532016-02-16 01:23:07 +0000217 return true;
218 }
219
220 return false;
221}
222
Philipp Schrader0119eb12016-02-15 18:16:21 +0000223constexpr double CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
224constexpr double CollisionAvoidance::kMinShoulderAngleForIntakeInterference;
225constexpr double CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
226constexpr double CollisionAvoidance::kMaxWristAngleForSafeArmStowing;
227constexpr double CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing;
228
Austin Schuh55a13dc2019-01-27 22:39:03 -0800229Superstructure::Superstructure(::aos::EventLoop *event_loop,
230 const ::std::string &name)
Alex Perrycb7da4b2019-08-28 19:35:56 -0700231 : aos::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
232 name),
Austin Schuh4b652c92019-05-27 13:22:27 -0700233 ball_detector_fetcher_(
234 event_loop->MakeFetcher<::y2016::sensors::BallDetector>(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700235 "/superstructure")),
Philipp Schrader0119eb12016-02-15 18:16:21 +0000236 collision_avoidance_(&intake_, &arm_) {}
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800237
Adam Snaider06779722016-02-14 15:26:22 -0800238bool Superstructure::IsArmNear(double shoulder_tolerance,
239 double wrist_tolerance) {
240 return ((arm_.unprofiled_goal() - arm_.X_hat())
241 .block<2, 1>(0, 0)
242 .lpNorm<Eigen::Infinity>() < shoulder_tolerance) &&
243 ((arm_.unprofiled_goal() - arm_.X_hat())
244 .block<4, 1>(0, 0)
245 .lpNorm<Eigen::Infinity>() < wrist_tolerance) &&
246 ((arm_.unprofiled_goal() - arm_.goal())
247 .block<4, 1>(0, 0)
248 .lpNorm<Eigen::Infinity>() < 1e-6);
249}
250
251bool Superstructure::IsArmNear(double tolerance) {
252 return ((arm_.unprofiled_goal() - arm_.X_hat())
253 .block<4, 1>(0, 0)
254 .lpNorm<Eigen::Infinity>() < tolerance) &&
255 ((arm_.unprofiled_goal() - arm_.goal())
256 .block<4, 1>(0, 0)
257 .lpNorm<Eigen::Infinity>() < 1e-6);
258}
259
260bool Superstructure::IsIntakeNear(double tolerance) {
261 return ((intake_.unprofiled_goal() - intake_.X_hat())
262 .block<2, 1>(0, 0)
263 .lpNorm<Eigen::Infinity>() < tolerance);
264}
265
266double Superstructure::MoveButKeepAbove(double reference_angle,
267 double current_angle,
268 double move_distance) {
269 return -MoveButKeepBelow(-reference_angle, -current_angle, -move_distance);
270}
271
272double Superstructure::MoveButKeepBelow(double reference_angle,
273 double current_angle,
274 double move_distance) {
275 // There are 3 interesting places to move to.
276 const double small_negative_move = current_angle - move_distance;
277 const double small_positive_move = current_angle + move_distance;
278 // And the reference angle.
279
280 // Move the the highest one that is below reference_angle.
281 if (small_negative_move > reference_angle) {
282 return reference_angle;
283 } else if (small_positive_move > reference_angle) {
284 return small_negative_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800285 } else {
Adam Snaider06779722016-02-14 15:26:22 -0800286 return small_positive_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800287 }
288}
289
290void Superstructure::RunIteration(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700291 const Goal *unsafe_goal,
292 const Position *position,
293 aos::Sender<Output>::Builder *output,
294 aos::Sender<Status>::Builder *status) {
Adam Snaider06779722016-02-14 15:26:22 -0800295 const State state_before_switch = state_;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800296 if (WasReset()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700297 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800298 arm_.Reset();
299 intake_.Reset();
300 state_ = UNINITIALIZED;
301 }
302
303 // Bool to track if we should turn the motors on or not.
304 bool disable = output == nullptr;
305
Alex Perrycb7da4b2019-08-28 19:35:56 -0700306 arm_.Correct(position->shoulder(), position->wrist());
307 intake_.Correct(*position->intake());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800308
Adam Snaider06779722016-02-14 15:26:22 -0800309 // There are 2 main zeroing paths, HIGH_ARM_ZERO and LOW_ARM_ZERO.
310 //
311 // HIGH_ARM_ZERO works by lifting the arm all the way up so it is clear,
312 // moving the shooter to be horizontal, moving the intake out, and then moving
313 // the arm back down.
314 //
315 // LOW_ARM_ZERO works by moving the intake out of the way, lifting the arm up,
316 // leveling the shooter, and then moving back down.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800317
Diana Vandenberge2843c62016-02-13 17:44:20 -0800318 if (arm_.error() || intake_.error()) {
319 state_ = ESTOP;
320 }
321
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800322 const bool is_collided = collided();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800323 switch (state_) {
324 case UNINITIALIZED:
Adam Snaider06779722016-02-14 15:26:22 -0800325 // Wait in the uninitialized state until both the arm and intake are
326 // initialized.
Austin Schuhf257f3c2019-10-27 21:00:43 -0700327 AOS_LOG(DEBUG, "Uninitialized, waiting for intake and arm\n");
Adam Snaider06779722016-02-14 15:26:22 -0800328 if (arm_.initialized() && intake_.initialized()) {
329 state_ = DISABLED_INITIALIZED;
330 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800331 disable = true;
332 break;
333
Adam Snaider06779722016-02-14 15:26:22 -0800334 case DISABLED_INITIALIZED:
335 // Wait here until we are either fully zeroed while disabled, or we become
336 // enabled. At that point, figure out if we should HIGH_ARM_ZERO or
337 // LOW_ARM_ZERO.
338 if (disable) {
339 if (arm_.zeroed() && intake_.zeroed()) {
340 state_ = SLOW_RUNNING;
341 }
342 } else {
343 if (arm_.shoulder_angle() >= kShoulderMiddleAngle) {
344 state_ = HIGH_ARM_ZERO_LIFT_ARM;
345 } else {
346 state_ = LOW_ARM_ZERO_LOWER_INTAKE;
347 }
348 }
349
350 // Set the goals to where we are now so when we start back up, we don't
351 // jump.
Austin Schuh3634ed32017-02-05 16:28:49 -0800352 intake_.ForceGoal(intake_.position());
Adam Snaider06779722016-02-14 15:26:22 -0800353 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
354 // Set up the profile to be the zeroing profile.
355 intake_.AdjustProfile(0.5, 10);
356 arm_.AdjustProfile(0.5, 10, 0.5, 10);
357
358 // We are not ready to start doing anything yet.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800359 disable = true;
Adam Snaider06779722016-02-14 15:26:22 -0800360 break;
361
362 case HIGH_ARM_ZERO_LIFT_ARM:
363 if (disable) {
364 state_ = DISABLED_INITIALIZED;
365 } else {
366 // Raise the shoulder up out of the way.
367 arm_.set_unprofiled_goal(kShoulderUpAngle, arm_.wrist_angle());
368 if (IsArmNear(kLooseTolerance)) {
369 // Close enough, start the next move.
370 state_ = HIGH_ARM_ZERO_LEVEL_SHOOTER;
371 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800372 }
373 break;
374
Adam Snaider06779722016-02-14 15:26:22 -0800375 case HIGH_ARM_ZERO_LEVEL_SHOOTER:
376 if (disable) {
377 state_ = DISABLED_INITIALIZED;
378 } else {
379 // Move the shooter to be level.
380 arm_.set_unprofiled_goal(kShoulderUpAngle, 0.0);
381
382 if (IsArmNear(kLooseTolerance)) {
383 // Close enough, start the next move.
384 state_ = HIGH_ARM_ZERO_MOVE_INTAKE_OUT;
385 }
386 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800387 break;
388
Adam Snaider06779722016-02-14 15:26:22 -0800389 case HIGH_ARM_ZERO_MOVE_INTAKE_OUT:
390 if (disable) {
391 state_ = DISABLED_INITIALIZED;
392 } else {
393 // If we were just asked to move the intake, make sure it moves far
394 // enough.
395 if (last_state_ != HIGH_ARM_ZERO_MOVE_INTAKE_OUT) {
396 intake_.set_unprofiled_goal(
Austin Schuh3634ed32017-02-05 16:28:49 -0800397 MoveButKeepBelow(kIntakeUpperClear, intake_.position(),
Adam Snaider06779722016-02-14 15:26:22 -0800398 kIntakeEncoderIndexDifference * 2.5));
399 }
400
401 if (IsIntakeNear(kLooseTolerance)) {
402 // Close enough, start the next move.
403 state_ = HIGH_ARM_ZERO_LOWER_ARM;
404 }
405 }
406 break;
407
408 case HIGH_ARM_ZERO_LOWER_ARM:
409 if (disable) {
410 state_ = DISABLED_INITIALIZED;
411 } else {
412 // Land the shooter in the belly-pan. It should be zeroed by the time
413 // it gets there. If not, just estop.
414 arm_.set_unprofiled_goal(kShoulderLanded, 0.0);
415 if (arm_.zeroed() && intake_.zeroed()) {
416 state_ = RUNNING;
417 } else if (IsArmNear(kLooseTolerance)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700418 AOS_LOG(ERROR,
419 "Failed to zero while executing the HIGH_ARM_ZERO sequence. "
420 "Arm: %d Intake %d\n",
421 arm_.zeroed(), intake_.zeroed());
Adam Snaider06779722016-02-14 15:26:22 -0800422 state_ = ESTOP;
423 }
424 }
425 break;
426
427 case LOW_ARM_ZERO_LOWER_INTAKE:
428 if (disable) {
429 state_ = DISABLED_INITIALIZED;
430 } else {
431 // Move the intake down out of the way of the arm. Make sure to move it
432 // far enough to zero.
433 if (last_state_ != LOW_ARM_ZERO_LOWER_INTAKE) {
434 intake_.set_unprofiled_goal(
Austin Schuh3634ed32017-02-05 16:28:49 -0800435 MoveButKeepBelow(kIntakeLowerClear, intake_.position(),
Adam Snaider06779722016-02-14 15:26:22 -0800436 kIntakeEncoderIndexDifference * 2.5));
437 }
438 if (IsIntakeNear(kLooseTolerance)) {
439 if (::std::abs(arm_.wrist_angle()) < kWristAlmostLevel) {
440 state_ = LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER;
441 } else {
442 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
443 }
444 }
445 }
446 break;
447
448 case LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER:
449 if (disable) {
450 state_ = DISABLED_INITIALIZED;
451 } else {
452 // If we are supposed to level the shooter, set it to level, and wait
453 // until it is very close to level.
454 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
455 if (IsArmNear(kLooseTolerance, kTightTolerance)) {
456 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
457 }
458 }
459 break;
460
461 case LOW_ARM_ZERO_LIFT_SHOULDER:
462 if (disable) {
463 state_ = DISABLED_INITIALIZED;
464 } else {
465 // Decide where to move to. We need to move far enough to see an index
466 // pulse, but must also get high enough that we can safely level the
467 // shooter.
468 if (last_state_ != LOW_ARM_ZERO_LIFT_SHOULDER) {
469 arm_.set_unprofiled_goal(
470 MoveButKeepAbove(kShoulderWristClearAngle, arm_.shoulder_angle(),
471 ::std::max(kWristEncoderIndexDifference,
472 kShoulderEncoderIndexDifference) *
473 2.5),
474 arm_.unprofiled_goal(2, 0));
475 }
476
Brian Silverman741b27a2016-05-16 00:09:26 -0700477 // If we're about to ask the wrist to go past one of its limits, then
478 // move the goal so it will be just at the limit when we finish lifting
479 // the shoulder. If it wasn't intersecting something before, this can't
480 // cause it to crash into anything.
481 const double ungrounded_wrist = arm_.goal(2, 0) - arm_.goal(0, 0);
482 const double unprofiled_ungrounded_wrist =
483 arm_.unprofiled_goal(2, 0) - arm_.unprofiled_goal(0, 0);
484 if (unprofiled_ungrounded_wrist >
485 constants::Values::kWristRange.upper &&
486 ungrounded_wrist >
487 constants::Values::kWristRange.upper - kWristAlmostLevel) {
488 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0),
489 constants::Values::kWristRange.upper +
490 arm_.unprofiled_goal(0, 0));
491 } else if (unprofiled_ungrounded_wrist <
492 constants::Values::kWristRange.lower &&
493 ungrounded_wrist < constants::Values::kWristRange.lower +
494 kWristAlmostLevel) {
495 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0),
496 constants::Values::kWristRange.lower +
497 arm_.unprofiled_goal(0, 0));
498 }
499
Adam Snaider06779722016-02-14 15:26:22 -0800500 // Wait until we are level and then go for it.
501 if (IsArmNear(kLooseTolerance)) {
502 state_ = LOW_ARM_ZERO_LEVEL_SHOOTER;
503 }
504 }
505 break;
506
507 case LOW_ARM_ZERO_LEVEL_SHOOTER:
508 if (disable) {
509 state_ = DISABLED_INITIALIZED;
510 } else {
511 // Move the shooter level (and keep the same height). We don't want to
512 // got to RUNNING until we are completely level so that we don't
513 // give control back in a weird case where we might crash.
514 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
515 if (IsArmNear(kLooseTolerance)) {
516 if (arm_.zeroed() && intake_.zeroed()) {
517 state_ = RUNNING;
518 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700519 AOS_LOG(ERROR,
520 "Failed to zero while executing the LOW_ARM_ZERO sequence. "
521 "Arm: %d Intake %d\n",
522 arm_.zeroed(), intake_.zeroed());
Adam Snaider06779722016-02-14 15:26:22 -0800523 state_ = ESTOP;
524 }
525 }
526 }
527 break;
528
Austin Schuhb1d682b2016-02-16 13:07:44 -0800529 // These 4 cases are very similar.
Adam Snaider06779722016-02-14 15:26:22 -0800530 case SLOW_RUNNING:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800531 case RUNNING:
Austin Schuhb1d682b2016-02-16 13:07:44 -0800532 case LANDING_SLOW_RUNNING:
533 case LANDING_RUNNING: {
Austin Schuh829fe572016-02-14 21:41:16 -0800534 if (disable) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800535 // If we are disabled, go to slow running (or landing slow running) if
536 // we are collided.
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800537 if (is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800538 if (state_ == RUNNING) {
539 state_ = SLOW_RUNNING;
540 } else if (state_ == LANDING_RUNNING) {
541 state_ = LANDING_SLOW_RUNNING;
542 }
543 }
Austin Schuh829fe572016-02-14 21:41:16 -0800544
Austin Schuhb1d682b2016-02-16 13:07:44 -0800545 // Reset the profile to the current position so it moves well from here.
Austin Schuh3634ed32017-02-05 16:28:49 -0800546 intake_.ForceGoal(intake_.position());
Austin Schuh829fe572016-02-14 21:41:16 -0800547 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
548 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800549 // If we are in slow_running and are no longer collided, let 'er rip.
Austin Schuh829fe572016-02-14 21:41:16 -0800550 if (state_ == SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800551 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800552 state_ = RUNNING;
553 }
554 } else if (state_ == LANDING_SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800555 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800556 state_ = LANDING_RUNNING;
557 }
Austin Schuh829fe572016-02-14 21:41:16 -0800558 }
559 }
560
Austin Schuhb1d682b2016-02-16 13:07:44 -0800561 double requested_shoulder = constants::Values::kShoulderRange.lower;
562 double requested_wrist = 0.0;
563 double requested_intake = M_PI / 2.0;
564
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800565 if (unsafe_goal) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800566 // If we are in one of the landing states, limit the accelerations and
567 // velocities to land cleanly.
568 if (state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING) {
569 arm_.AdjustProfile(0.5, // Shoulder Velocity
570 4.0, // Shoulder acceleration,
571 4.0, // Wrist velocity
572 10.0); // Wrist acceleration.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700573 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake(),
574 unsafe_goal->max_angular_acceleration_intake());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800575
Austin Schuhb1d682b2016-02-16 13:07:44 -0800576 requested_shoulder =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700577 ::std::max(unsafe_goal->angle_shoulder(),
Austin Schuhb1d682b2016-02-16 13:07:44 -0800578 constants::Values::kShoulderRange.lower);
579 requested_wrist = 0.0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700580 requested_intake = unsafe_goal->angle_intake();
Austin Schuha1dd5272016-02-16 15:39:38 -0800581 // Transition to landing once the profile is close to finished for the
582 // shoulder.
583 if (arm_.goal(0, 0) > kShoulderTransitionToLanded + 1e-4 ||
584 arm_.unprofiled_goal(0, 0) > kShoulderTransitionToLanded + 1e-4) {
585 if (state_ == LANDING_RUNNING) {
586 state_ = RUNNING;
587 } else {
588 state_ = SLOW_RUNNING;
589 }
590 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000591 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800592 // Otherwise, give the user what he asked for.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700593 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder(),
594 unsafe_goal->max_angular_acceleration_shoulder(),
595 unsafe_goal->max_angular_velocity_wrist(),
596 unsafe_goal->max_angular_acceleration_wrist());
597 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake(),
598 unsafe_goal->max_angular_acceleration_intake());
Austin Schuhb1d682b2016-02-16 13:07:44 -0800599
600 // Except, don't let the shoulder go all the way down.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700601 requested_shoulder = ::std::max(unsafe_goal->angle_shoulder(),
Austin Schuhb1d682b2016-02-16 13:07:44 -0800602 kShoulderTransitionToLanded);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700603 requested_wrist = unsafe_goal->angle_wrist();
604 requested_intake = unsafe_goal->angle_intake();
Austin Schuhb1d682b2016-02-16 13:07:44 -0800605
606 // Transition to landing once the profile is close to finished for the
607 // shoulder.
Austin Schuha1dd5272016-02-16 15:39:38 -0800608 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 &&
609 arm_.unprofiled_goal(0, 0) <=
610 kShoulderTransitionToLanded + 1e-4) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800611 if (state_ == RUNNING) {
612 state_ = LANDING_RUNNING;
613 } else {
614 state_ = LANDING_SLOW_RUNNING;
615 }
616 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000617 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800618 }
619
Austin Schuhb1d682b2016-02-16 13:07:44 -0800620 // Push the request out to hardware!
621 collision_avoidance_.UpdateGoal(requested_shoulder, requested_wrist,
622 requested_intake);
623
624 // ESTOP if we hit the hard limits.
625 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
626 state_ = ESTOP;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800627 }
Austin Schuhb1d682b2016-02-16 13:07:44 -0800628 } break;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800629
630 case ESTOP:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700631 AOS_LOG(ERROR, "Estop\n");
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800632 disable = true;
633 break;
634 }
635
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800636 // Set the voltage limits.
Comran Morshed011f7d92016-02-16 23:03:06 +0000637 const double max_voltage = (state_ == RUNNING || state_ == LANDING_RUNNING)
638 ? kOperatingVoltage
639 : kZeroingVoltage;
Comran Morshed71466fe2016-04-21 20:21:14 -0700640 if (unsafe_goal) {
Austin Schuh0c001a82016-05-01 12:30:09 -0700641 constexpr float kTriggerThreshold = 12.0 * 0.90 / 0.005;
Comran Morshed71466fe2016-04-21 20:21:14 -0700642
Alex Perrycb7da4b2019-08-28 19:35:56 -0700643 if (unsafe_goal->voltage_climber() > 1.0) {
Comran Morshed71466fe2016-04-21 20:21:14 -0700644 kill_shoulder_accumulator_ +=
Alex Perrycb7da4b2019-08-28 19:35:56 -0700645 ::std::min(kMaxClimberVoltage, unsafe_goal->voltage_climber());
Comran Morshed71466fe2016-04-21 20:21:14 -0700646 } else {
647 kill_shoulder_accumulator_ = 0.0;
648 }
649
650 if (kill_shoulder_accumulator_ > kTriggerThreshold) {
651 kill_shoulder_ = true;
652 }
653 }
Austin Schuh0c001a82016-05-01 12:30:09 -0700654 arm_.set_max_voltage(
Austin Schuh473a5652017-02-05 01:30:42 -0800655 {{kill_shoulder_ ? 0.0 : max_voltage,
656 kill_shoulder_ ? (arm_.X_hat(0, 0) < 0.05 ? kShooterHangingLowVoltage
657 : kShooterHangingVoltage)
658 : max_voltage}});
659 intake_.set_max_voltage({{max_voltage}});
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800660
Diana Vandenberg9cc9ab62016-04-20 21:27:47 -0700661 if (IsRunning() && !kill_shoulder_) {
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800662 // We don't want lots of negative voltage when we are near the bellypan on
663 // the shoulder...
664 // TODO(austin): Do I want to push negative power into the belly pan at this
665 // point? Maybe just put the goal slightly below the bellypan and call that
666 // good enough.
Austin Schuh6ca0f792016-03-12 14:06:14 -0800667 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 ||
668 arm_.X_hat(0, 0) <= kShoulderTransitionToLanded + 1e-4) {
Comran Morshed011f7d92016-02-16 23:03:06 +0000669 arm_.set_shoulder_asymetric_limits(kLandingShoulderDownVoltage,
670 max_voltage);
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800671 }
672 }
673
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800674 // Calculate the loops for a cycle.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700675 double intake_position_power;
676 double intake_velocity_power;
Austin Schuhbe041152016-02-28 22:01:52 -0800677 {
678 Eigen::Matrix<double, 3, 1> error = intake_.controller().error();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700679 intake_position_power =
Austin Schuh32501832017-02-25 18:32:56 -0800680 intake_.controller().controller().K(0, 0) * error(0, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700681 intake_velocity_power =
Austin Schuh32501832017-02-25 18:32:56 -0800682 intake_.controller().controller().K(0, 1) * error(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800683 }
684
Alex Perrycb7da4b2019-08-28 19:35:56 -0700685 double shoulder_position_power;
686 double shoulder_velocity_power;
687 double wrist_position_power;
688 double wrist_velocity_power;
Austin Schuhbe041152016-02-28 22:01:52 -0800689 {
690 Eigen::Matrix<double, 6, 1> error = arm_.controller().error();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700691 shoulder_position_power =
Austin Schuh32501832017-02-25 18:32:56 -0800692 arm_.controller().controller().K(0, 0) * error(0, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700693 shoulder_velocity_power =
Austin Schuh32501832017-02-25 18:32:56 -0800694 arm_.controller().controller().K(0, 1) * error(1, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700695 wrist_position_power = arm_.controller().controller().K(0, 2) * error(2, 0);
696 wrist_velocity_power = arm_.controller().controller().K(0, 3) * error(3, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800697 }
698
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800699 arm_.Update(disable);
700 intake_.Update(disable);
701
702 // Write out all the voltages.
703 if (output) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700704 OutputT output_struct;
705 output_struct.voltage_intake = intake_.voltage();
706 output_struct.voltage_shoulder = arm_.shoulder_voltage();
707 output_struct.voltage_wrist = arm_.wrist_voltage();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000708
Alex Perrycb7da4b2019-08-28 19:35:56 -0700709 output_struct.voltage_top_rollers = 0.0;
710 output_struct.voltage_bottom_rollers = 0.0;
Comran Morshed71466fe2016-04-21 20:21:14 -0700711
Alex Perrycb7da4b2019-08-28 19:35:56 -0700712 output_struct.voltage_climber = 0.0;
713 output_struct.unfold_climber = false;
Comran Morshedf4cd7642016-02-15 20:40:49 +0000714 if (unsafe_goal) {
Comran Morshed71466fe2016-04-21 20:21:14 -0700715 // Ball detector lights.
Austin Schuh4b652c92019-05-27 13:22:27 -0700716 ball_detector_fetcher_.Fetch();
Austin Schuh1defd262016-04-03 16:13:32 -0700717 bool ball_detected = false;
Austin Schuh4b652c92019-05-27 13:22:27 -0700718 if (ball_detector_fetcher_.get()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700719 ball_detected = ball_detector_fetcher_->voltage() > 2.5;
Austin Schuh1defd262016-04-03 16:13:32 -0700720 }
Comran Morshed71466fe2016-04-21 20:21:14 -0700721
722 // Climber.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700723 output_struct.voltage_climber = ::std::max(
Comran Morshed71466fe2016-04-21 20:21:14 -0700724 static_cast<float>(0.0),
Alex Perrycb7da4b2019-08-28 19:35:56 -0700725 ::std::min(unsafe_goal->voltage_climber(), kMaxClimberVoltage));
726 output_struct.unfold_climber = unsafe_goal->unfold_climber();
Comran Morshed71466fe2016-04-21 20:21:14 -0700727
728 // Intake.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700729 if (unsafe_goal->force_intake() || !ball_detected) {
730 output_struct.voltage_top_rollers = ::std::max(
Austin Schuh1defd262016-04-03 16:13:32 -0700731 -kMaxIntakeTopVoltage,
Alex Perrycb7da4b2019-08-28 19:35:56 -0700732 ::std::min(unsafe_goal->voltage_top_rollers(), kMaxIntakeTopVoltage));
733 output_struct.voltage_bottom_rollers =
Austin Schuh1defd262016-04-03 16:13:32 -0700734 ::std::max(-kMaxIntakeBottomVoltage,
Alex Perrycb7da4b2019-08-28 19:35:56 -0700735 ::std::min(unsafe_goal->voltage_bottom_rollers(),
Austin Schuh1defd262016-04-03 16:13:32 -0700736 kMaxIntakeBottomVoltage));
737 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700738 output_struct.voltage_top_rollers = 0.0;
739 output_struct.voltage_bottom_rollers = 0.0;
Austin Schuh1defd262016-04-03 16:13:32 -0700740 }
Comran Morshed71466fe2016-04-21 20:21:14 -0700741
742 // Traverse.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700743 output_struct.traverse_unlatched = unsafe_goal->traverse_unlatched();
744 output_struct.traverse_down = unsafe_goal->traverse_down();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000745 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700746
747 output->Send(Output::Pack(*output->fbb(), &output_struct));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800748 }
749
750 // Save debug/internal state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700751 flatbuffers::Offset<frc971::EstimatorState> shoulder_estimator_state_offset =
752 arm_.EstimatorState(status->fbb(), 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800753
Alex Perrycb7da4b2019-08-28 19:35:56 -0700754 JointState::Builder shoulder_builder = status->MakeBuilder<JointState>();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800755
Alex Perrycb7da4b2019-08-28 19:35:56 -0700756 shoulder_builder.add_angle(arm_.X_hat(0, 0));
757 shoulder_builder.add_angular_velocity(arm_.X_hat(1, 0));
758 shoulder_builder.add_goal_angle(arm_.goal(0, 0));
759 shoulder_builder.add_goal_angular_velocity(arm_.goal(1, 0));
760 shoulder_builder.add_unprofiled_goal_angle(arm_.unprofiled_goal(0, 0));
761 shoulder_builder.add_unprofiled_goal_angular_velocity(
762 arm_.unprofiled_goal(1, 0));
763 shoulder_builder.add_voltage_error(arm_.X_hat(4, 0));
764 shoulder_builder.add_calculated_velocity(
765 (arm_.shoulder_angle() - last_shoulder_angle_) / 0.005);
766 shoulder_builder.add_position_power(shoulder_position_power);
767 shoulder_builder.add_velocity_power(shoulder_velocity_power);
768 shoulder_builder.add_estimator_state(shoulder_estimator_state_offset);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800769
Alex Perrycb7da4b2019-08-28 19:35:56 -0700770 flatbuffers::Offset<JointState> shoulder_offset = shoulder_builder.Finish();
Austin Schuhbe041152016-02-28 22:01:52 -0800771
Alex Perrycb7da4b2019-08-28 19:35:56 -0700772 flatbuffers::Offset<frc971::EstimatorState> wrist_estimator_state_offset =
773 arm_.EstimatorState(status->fbb(), 1);
774
775 JointState::Builder wrist_builder = status->MakeBuilder<JointState>();
776
777 wrist_builder.add_angle(arm_.X_hat(2, 0));
778 wrist_builder.add_angular_velocity(arm_.X_hat(3, 0));
779 wrist_builder.add_goal_angle(arm_.goal(2, 0));
780 wrist_builder.add_goal_angular_velocity(arm_.goal(3, 0));
781 wrist_builder.add_unprofiled_goal_angle(arm_.unprofiled_goal(2, 0));
782 wrist_builder.add_unprofiled_goal_angular_velocity(
783 arm_.unprofiled_goal(3, 0));
784 wrist_builder.add_voltage_error(arm_.X_hat(5, 0));
785 wrist_builder.add_calculated_velocity(
786 (arm_.wrist_angle() - last_wrist_angle_) / 0.005);
787 wrist_builder.add_position_power(wrist_position_power);
788 wrist_builder.add_velocity_power(wrist_velocity_power);
789 wrist_builder.add_estimator_state(wrist_estimator_state_offset);
790
791 flatbuffers::Offset<JointState> wrist_offset = wrist_builder.Finish();
792
793 flatbuffers::Offset<frc971::EstimatorState> intake_estimator_state_offset =
794 intake_.EstimatorState(status->fbb(), 0);
795
796 JointState::Builder intake_builder = status->MakeBuilder<JointState>();
797 intake_builder.add_position_power(intake_position_power);
798 intake_builder.add_velocity_power(intake_velocity_power);
799 intake_builder.add_angle(intake_.X_hat(0, 0));
800 intake_builder.add_angular_velocity(intake_.X_hat(1, 0));
801 intake_builder.add_goal_angle(intake_.goal(0, 0));
802 intake_builder.add_goal_angular_velocity(intake_.goal(1, 0));
803 intake_builder.add_unprofiled_goal_angle(intake_.unprofiled_goal(0, 0));
804 intake_builder.add_unprofiled_goal_angular_velocity(
805 intake_.unprofiled_goal(1, 0));
806 intake_builder.add_calculated_velocity(
807 (intake_.position() - last_intake_angle_) / 0.005);
808 intake_builder.add_voltage_error(intake_.X_hat(2, 0));
809 intake_builder.add_estimator_state(intake_estimator_state_offset);
810 intake_builder.add_feedforwards_power(intake_.controller().ff_U(0, 0));
811
812 flatbuffers::Offset<JointState> intake_offset = intake_builder.Finish();
813
814 Status::Builder status_builder = status->MakeBuilder<Status>();
815
816 status_builder.add_shoulder(shoulder_offset);
817 status_builder.add_wrist(wrist_offset);
818 status_builder.add_intake(intake_offset);
819
820 status_builder.add_zeroed(arm_.zeroed() && intake_.zeroed());
821 status_builder.add_shoulder_controller_index(arm_.controller_index());
Austin Schuhf59b6bc2016-03-11 21:26:19 -0800822
Austin Schuhbe041152016-02-28 22:01:52 -0800823 last_shoulder_angle_ = arm_.shoulder_angle();
824 last_wrist_angle_ = arm_.wrist_angle();
Austin Schuh3634ed32017-02-05 16:28:49 -0800825 last_intake_angle_ = intake_.position();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800826
Alex Perrycb7da4b2019-08-28 19:35:56 -0700827 status_builder.add_estopped((state_ == ESTOP));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800828
Alex Perrycb7da4b2019-08-28 19:35:56 -0700829 status_builder.add_state(state_);
830 status_builder.add_is_collided(is_collided);
831
832 status->Send(status_builder.Finish());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800833
Adam Snaider06779722016-02-14 15:26:22 -0800834 last_state_ = state_before_switch;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800835}
836
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000837constexpr double Superstructure::kZeroingVoltage;
838constexpr double Superstructure::kOperatingVoltage;
Austin Schuh6ca0f792016-03-12 14:06:14 -0800839constexpr double Superstructure::kLandingShoulderDownVoltage;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000840constexpr double Superstructure::kShoulderMiddleAngle;
841constexpr double Superstructure::kLooseTolerance;
842constexpr double Superstructure::kIntakeUpperClear;
843constexpr double Superstructure::kIntakeLowerClear;
844constexpr double Superstructure::kShoulderUpAngle;
845constexpr double Superstructure::kShoulderLanded;
846constexpr double Superstructure::kTightTolerance;
847constexpr double Superstructure::kWristAlmostLevel;
848constexpr double Superstructure::kShoulderWristClearAngle;
Austin Schuhb1d682b2016-02-16 13:07:44 -0800849constexpr double Superstructure::kShoulderTransitionToLanded;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000850
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800851} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000852} // namespace control_loops
853} // namespace y2016