blob: 7b69534b5b2a2d3925dcd7b79c980147400416cf [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"
James Kuszmaul61750662021-06-21 21:32:33 -07005#include "y2016/constants.h"
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
Stephan Pleinesf63bde82024-01-13 15:59:33 -080011namespace y2016::control_loops::superstructure {
Comran Morshed25f81a02016-01-23 13:40:10 +000012
Austin Schuh2fc10fa2016-02-08 00:44:34 -080013namespace {
Austin Schuh2d7820b2016-02-16 13:47:42 -080014// The maximum voltage the intake roller will be allowed to use.
Austin Schuhbe041152016-02-28 22:01:52 -080015constexpr float kMaxIntakeTopVoltage = 12.0;
16constexpr float kMaxIntakeBottomVoltage = 12.0;
Comran Morshed71466fe2016-04-21 20:21:14 -070017constexpr float kMaxClimberVoltage = 12.0;
Comran Morshedf4cd7642016-02-15 20:40:49 +000018
Austin Schuh2d7820b2016-02-16 13:47:42 -080019// Aliases to reduce typing.
Adam Snaider06779722016-02-14 15:26:22 -080020constexpr double kIntakeEncoderIndexDifference =
21 constants::Values::kIntakeEncoderIndexDifference;
22constexpr double kWristEncoderIndexDifference =
23 constants::Values::kWristEncoderIndexDifference;
24constexpr double kShoulderEncoderIndexDifference =
25 constants::Values::kShoulderEncoderIndexDifference;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080026} // namespace
27
Philipp Schrader0119eb12016-02-15 18:16:21 +000028// ///// CollisionAvoidance /////
29
30void CollisionAvoidance::UpdateGoal(double shoulder_angle_goal,
31 double wrist_angle_goal,
32 double intake_angle_goal) {
Austin Schuh6ca0f792016-03-12 14:06:14 -080033 const double original_shoulder_angle_goal = shoulder_angle_goal;
34 const double original_intake_angle_goal = intake_angle_goal;
Austin Schuh2c717862016-03-13 15:32:53 -070035 const double original_wrist_angle_goal = wrist_angle_goal;
Austin Schuh6ca0f792016-03-12 14:06:14 -080036
Philipp Schrader0119eb12016-02-15 18:16:21 +000037 double shoulder_angle = arm_->shoulder_angle();
38 double wrist_angle = arm_->wrist_angle();
Austin Schuh3634ed32017-02-05 16:28:49 -080039 double intake_angle = intake_->position();
Philipp Schrader0119eb12016-02-15 18:16:21 +000040
41 // TODO(phil): This may need tuning to account for bounciness in the limbs or
42 // some other thing that I haven't thought of. At the very least,
43 // incorporating a small safety margin makes writing test cases much easier
44 // since you can directly compare statuses against the constants in the
45 // CollisionAvoidance class.
Austin Schuhfef64ac2016-04-24 19:08:01 -070046 constexpr double kSafetyMargin = 0.03; // radians
Philipp Schrader0119eb12016-02-15 18:16:21 +000047
48 // Avoid colliding the shooter with the frame.
49 // If the shoulder is below a certain angle or we want to move it below
50 // that angle, then the shooter has to stay level to the ground. Otherwise,
51 // it will crash into the frame.
Austin Schuh2c717862016-03-13 15:32:53 -070052 if (intake_angle < kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
53 if (shoulder_angle < kMinShoulderAngleForHorizontalShooter ||
54 original_shoulder_angle_goal < kMinShoulderAngleForHorizontalShooter) {
55 wrist_angle_goal = 0.0;
56 } else if (shoulder_angle < kMinShoulderAngleForIntakeInterference ||
57 original_shoulder_angle_goal <
58 kMinShoulderAngleForIntakeInterference) {
59 wrist_angle_goal =
60 aos::Clip(original_wrist_angle_goal,
61 kMinWristAngleForMovingByIntake + kSafetyMargin,
62 kMaxWristAngleForMovingByIntake - kSafetyMargin);
63 }
64 } else {
65 if (shoulder_angle < kMinShoulderAngleForIntakeUpInterference ||
66 original_shoulder_angle_goal <
67 kMinShoulderAngleForIntakeUpInterference) {
68 wrist_angle_goal = 0.0;
69 }
70 }
71
Austin Schuh6ca0f792016-03-12 14:06:14 -080072 if (shoulder_angle < kMinShoulderAngleForIntakeUpInterference ||
73 original_shoulder_angle_goal < kMinShoulderAngleForIntakeUpInterference) {
Philipp Schrader0119eb12016-02-15 18:16:21 +000074 // Make sure that we don't move the shoulder below a certain angle until
75 // the wrist is level with the ground.
Austin Schuh6802a9d2016-03-12 21:34:53 -080076 if (intake_angle < kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
Austin Schuh2c717862016-03-13 15:32:53 -070077 if (wrist_angle > kMaxWristAngleForMovingByIntake ||
78 wrist_angle < kMinWristAngleForMovingByIntake) {
Austin Schuh6802a9d2016-03-12 21:34:53 -080079 shoulder_angle_goal =
Comran Morshed71466fe2016-04-21 20:21:14 -070080 ::std::max(original_shoulder_angle_goal,
81 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
Austin Schuh6802a9d2016-03-12 21:34:53 -080082 }
83 } else {
Austin Schuh2c717862016-03-13 15:32:53 -070084 if (wrist_angle > kMaxWristAngleForMovingByIntake ||
85 wrist_angle < kMinWristAngleForMovingByIntake) {
Comran Morshed71466fe2016-04-21 20:21:14 -070086 shoulder_angle_goal = ::std::max(
87 original_shoulder_angle_goal,
88 kMinShoulderAngleForIntakeUpInterference + kSafetyMargin);
Austin Schuh6802a9d2016-03-12 21:34:53 -080089 }
Austin Schuh6ca0f792016-03-12 14:06:14 -080090 }
Philipp Schrader0119eb12016-02-15 18:16:21 +000091 if (::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
92 shoulder_angle_goal =
Austin Schuh6802a9d2016-03-12 21:34:53 -080093 ::std::max(shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +000094 kMinShoulderAngleForHorizontalShooter + kSafetyMargin);
95 }
96 }
97
98 // Is the arm where it could interfere with the intake right now?
99 bool shoulder_is_in_danger =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800100 (shoulder_angle < kMinShoulderAngleForIntakeUpInterference &&
Philipp Schrader0119eb12016-02-15 18:16:21 +0000101 shoulder_angle > kMaxShoulderAngleUntilSafeIntakeStowing);
102
103 // Is the arm moving into collision zone from above?
104 bool shoulder_moving_into_danger_from_above =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800105 (shoulder_angle >= kMinShoulderAngleForIntakeUpInterference &&
106 original_shoulder_angle_goal <=
107 kMinShoulderAngleForIntakeUpInterference);
Philipp Schrader0119eb12016-02-15 18:16:21 +0000108
109 // Is the arm moving into collision zone from below?
110 bool shoulder_moving_into_danger_from_below =
111 (shoulder_angle <= kMaxShoulderAngleUntilSafeIntakeStowing &&
Austin Schuh6ca0f792016-03-12 14:06:14 -0800112 original_shoulder_angle_goal >= kMaxShoulderAngleUntilSafeIntakeStowing);
Philipp Schrader0119eb12016-02-15 18:16:21 +0000113
114 // Avoid colliding the arm with the intake.
115 if (shoulder_is_in_danger || shoulder_moving_into_danger_from_above ||
116 shoulder_moving_into_danger_from_below) {
117 // If the arm could collide with the intake, we make sure to move the
118 // intake out of the way. The arm has priority.
119 intake_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800120 ::std::min(original_intake_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000121 kMaxIntakeAngleBeforeArmInterference - kSafetyMargin);
122
123 // Don't let the shoulder move into the collision area until the intake is
124 // out of the way.
125 if (intake_angle > kMaxIntakeAngleBeforeArmInterference) {
126 const double kHalfwayPointBetweenSafeZones =
127 (kMinShoulderAngleForIntakeInterference +
128 kMaxShoulderAngleUntilSafeIntakeStowing) /
129 2.0;
130
131 if (shoulder_angle >= kHalfwayPointBetweenSafeZones) {
132 // The shoulder is closer to being above the collision area. Move it up
133 // there.
Austin Schuh2c717862016-03-13 15:32:53 -0700134 if (intake_angle <
135 kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
136 shoulder_angle_goal = ::std::max(
137 original_shoulder_angle_goal,
138 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
139 } else {
140 shoulder_angle_goal = ::std::max(
141 original_shoulder_angle_goal,
142 kMinShoulderAngleForIntakeUpInterference + kSafetyMargin);
143 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000144 } else {
145 // The shoulder is closer to being below the collision zone (i.e. in
146 // stowing/intake position), keep it there for now.
147 shoulder_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800148 ::std::min(original_shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000149 kMaxShoulderAngleUntilSafeIntakeStowing - kSafetyMargin);
150 }
151 }
152 }
153
154 // Send the possibly adjusted goals to the components.
155 arm_->set_unprofiled_goal(shoulder_angle_goal, wrist_angle_goal);
156 intake_->set_unprofiled_goal(intake_angle_goal);
157}
158
Philipp Schrader07147532016-02-16 01:23:07 +0000159bool CollisionAvoidance::collided() const {
160 return collided_with_given_angles(arm_->shoulder_angle(), arm_->wrist_angle(),
Austin Schuh3634ed32017-02-05 16:28:49 -0800161 intake_->position());
Philipp Schrader07147532016-02-16 01:23:07 +0000162}
163
164bool CollisionAvoidance::collided_with_given_angles(double shoulder_angle,
165 double wrist_angle,
166 double intake_angle) {
167 // The arm and the intake must not hit.
168 if (shoulder_angle >=
169 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
170 shoulder_angle <=
Austin Schuh6ca0f792016-03-12 14:06:14 -0800171 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference &&
Comran Morshed71466fe2016-04-21 20:21:14 -0700172 intake_angle > CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700173 AOS_LOG(DEBUG, "Collided: Intake %f > %f, and shoulder %f < %f < %f.\n",
174 intake_angle,
175 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
176 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
177 shoulder_angle,
178 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference);
Austin Schuh6ca0f792016-03-12 14:06:14 -0800179 return true;
180 }
181
182 if (shoulder_angle >=
183 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
184 shoulder_angle <=
185 CollisionAvoidance::kMinShoulderAngleForIntakeInterference &&
186 intake_angle < CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference &&
187 intake_angle > Superstructure::kIntakeLowerClear &&
Austin Schuh2c717862016-03-13 15:32:53 -0700188 (wrist_angle > CollisionAvoidance::kMaxWristAngleForMovingByIntake ||
189 wrist_angle < CollisionAvoidance::kMinWristAngleForMovingByIntake)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700190 AOS_LOG(
191 DEBUG,
Austin Schuh2c717862016-03-13 15:32:53 -0700192 "Collided: Intake %f < %f < %f, shoulder %f < %f < %f, and %f < %f < "
193 "%f.\n",
Austin Schuh6ca0f792016-03-12 14:06:14 -0800194 Superstructure::kIntakeLowerClear, intake_angle,
195 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
196 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
197 shoulder_angle,
Austin Schuh2c717862016-03-13 15:32:53 -0700198 CollisionAvoidance::kMinShoulderAngleForIntakeInterference,
199 CollisionAvoidance::kMinWristAngleForMovingByIntake, wrist_angle,
200 CollisionAvoidance::kMaxWristAngleForMovingByIntake);
Philipp Schrader07147532016-02-16 01:23:07 +0000201 return true;
202 }
203
204 // The wrist must go back to zero when the shoulder is moving the arm into
205 // a stowed/intaking position.
James Kuszmaul61750662021-06-21 21:32:33 -0700206 if (shoulder_angle<CollisionAvoidance::kMinShoulderAngleForHorizontalShooter
207 && ::std::abs(wrist_angle)>
208 kMaxWristAngleForSafeArmStowing) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700209 AOS_LOG(DEBUG, "Collided: Shoulder %f < %f and wrist |%f| > %f.\n",
210 shoulder_angle,
211 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter,
212 wrist_angle, kMaxWristAngleForSafeArmStowing);
Philipp Schrader07147532016-02-16 01:23:07 +0000213 return true;
214 }
215
216 return false;
217}
218
Philipp Schrader0119eb12016-02-15 18:16:21 +0000219constexpr double CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
220constexpr double CollisionAvoidance::kMinShoulderAngleForIntakeInterference;
221constexpr double CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
222constexpr double CollisionAvoidance::kMaxWristAngleForSafeArmStowing;
223constexpr double CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing;
224
Austin Schuh55a13dc2019-01-27 22:39:03 -0800225Superstructure::Superstructure(::aos::EventLoop *event_loop,
226 const ::std::string &name)
James Kuszmaul61750662021-06-21 21:32:33 -0700227 : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
228 name),
Austin Schuh4b652c92019-05-27 13:22:27 -0700229 ball_detector_fetcher_(
230 event_loop->MakeFetcher<::y2016::sensors::BallDetector>(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700231 "/superstructure")),
Philipp Schrader0119eb12016-02-15 18:16:21 +0000232 collision_avoidance_(&intake_, &arm_) {}
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800233
Adam Snaider06779722016-02-14 15:26:22 -0800234bool Superstructure::IsArmNear(double shoulder_tolerance,
235 double wrist_tolerance) {
236 return ((arm_.unprofiled_goal() - arm_.X_hat())
237 .block<2, 1>(0, 0)
238 .lpNorm<Eigen::Infinity>() < shoulder_tolerance) &&
239 ((arm_.unprofiled_goal() - arm_.X_hat())
240 .block<4, 1>(0, 0)
241 .lpNorm<Eigen::Infinity>() < wrist_tolerance) &&
242 ((arm_.unprofiled_goal() - arm_.goal())
243 .block<4, 1>(0, 0)
244 .lpNorm<Eigen::Infinity>() < 1e-6);
245}
246
247bool Superstructure::IsArmNear(double tolerance) {
248 return ((arm_.unprofiled_goal() - arm_.X_hat())
249 .block<4, 1>(0, 0)
250 .lpNorm<Eigen::Infinity>() < tolerance) &&
251 ((arm_.unprofiled_goal() - arm_.goal())
252 .block<4, 1>(0, 0)
253 .lpNorm<Eigen::Infinity>() < 1e-6);
254}
255
256bool Superstructure::IsIntakeNear(double tolerance) {
257 return ((intake_.unprofiled_goal() - intake_.X_hat())
258 .block<2, 1>(0, 0)
259 .lpNorm<Eigen::Infinity>() < tolerance);
260}
261
262double Superstructure::MoveButKeepAbove(double reference_angle,
263 double current_angle,
264 double move_distance) {
265 return -MoveButKeepBelow(-reference_angle, -current_angle, -move_distance);
266}
267
268double Superstructure::MoveButKeepBelow(double reference_angle,
269 double current_angle,
270 double move_distance) {
271 // There are 3 interesting places to move to.
272 const double small_negative_move = current_angle - move_distance;
273 const double small_positive_move = current_angle + move_distance;
274 // And the reference angle.
275
276 // Move the the highest one that is below reference_angle.
277 if (small_negative_move > reference_angle) {
278 return reference_angle;
279 } else if (small_positive_move > reference_angle) {
280 return small_negative_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800281 } else {
Adam Snaider06779722016-02-14 15:26:22 -0800282 return small_positive_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800283 }
284}
285
James Kuszmaul61750662021-06-21 21:32:33 -0700286void Superstructure::RunIteration(const Goal *unsafe_goal,
287 const Position *position,
288 aos::Sender<Output>::Builder *output,
289 aos::Sender<Status>::Builder *status) {
Adam Snaider06779722016-02-14 15:26:22 -0800290 const State state_before_switch = state_;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800291 if (WasReset()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700292 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800293 arm_.Reset();
294 intake_.Reset();
295 state_ = UNINITIALIZED;
296 }
297
298 // Bool to track if we should turn the motors on or not.
299 bool disable = output == nullptr;
300
Alex Perrycb7da4b2019-08-28 19:35:56 -0700301 arm_.Correct(position->shoulder(), position->wrist());
302 intake_.Correct(*position->intake());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800303
Adam Snaider06779722016-02-14 15:26:22 -0800304 // There are 2 main zeroing paths, HIGH_ARM_ZERO and LOW_ARM_ZERO.
305 //
306 // HIGH_ARM_ZERO works by lifting the arm all the way up so it is clear,
307 // moving the shooter to be horizontal, moving the intake out, and then moving
308 // the arm back down.
309 //
310 // LOW_ARM_ZERO works by moving the intake out of the way, lifting the arm up,
311 // leveling the shooter, and then moving back down.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800312
Diana Vandenberge2843c62016-02-13 17:44:20 -0800313 if (arm_.error() || intake_.error()) {
314 state_ = ESTOP;
315 }
316
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800317 const bool is_collided = collided();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800318 switch (state_) {
319 case UNINITIALIZED:
Adam Snaider06779722016-02-14 15:26:22 -0800320 // Wait in the uninitialized state until both the arm and intake are
321 // initialized.
Austin Schuhf257f3c2019-10-27 21:00:43 -0700322 AOS_LOG(DEBUG, "Uninitialized, waiting for intake and arm\n");
Adam Snaider06779722016-02-14 15:26:22 -0800323 if (arm_.initialized() && intake_.initialized()) {
324 state_ = DISABLED_INITIALIZED;
325 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800326 disable = true;
327 break;
328
Adam Snaider06779722016-02-14 15:26:22 -0800329 case DISABLED_INITIALIZED:
330 // Wait here until we are either fully zeroed while disabled, or we become
331 // enabled. At that point, figure out if we should HIGH_ARM_ZERO or
332 // LOW_ARM_ZERO.
333 if (disable) {
334 if (arm_.zeroed() && intake_.zeroed()) {
335 state_ = SLOW_RUNNING;
336 }
337 } else {
338 if (arm_.shoulder_angle() >= kShoulderMiddleAngle) {
339 state_ = HIGH_ARM_ZERO_LIFT_ARM;
340 } else {
341 state_ = LOW_ARM_ZERO_LOWER_INTAKE;
342 }
343 }
344
345 // Set the goals to where we are now so when we start back up, we don't
346 // jump.
Austin Schuh3634ed32017-02-05 16:28:49 -0800347 intake_.ForceGoal(intake_.position());
Adam Snaider06779722016-02-14 15:26:22 -0800348 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
349 // Set up the profile to be the zeroing profile.
350 intake_.AdjustProfile(0.5, 10);
351 arm_.AdjustProfile(0.5, 10, 0.5, 10);
352
353 // We are not ready to start doing anything yet.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800354 disable = true;
Adam Snaider06779722016-02-14 15:26:22 -0800355 break;
356
357 case HIGH_ARM_ZERO_LIFT_ARM:
358 if (disable) {
359 state_ = DISABLED_INITIALIZED;
360 } else {
361 // Raise the shoulder up out of the way.
362 arm_.set_unprofiled_goal(kShoulderUpAngle, arm_.wrist_angle());
363 if (IsArmNear(kLooseTolerance)) {
364 // Close enough, start the next move.
365 state_ = HIGH_ARM_ZERO_LEVEL_SHOOTER;
366 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800367 }
368 break;
369
Adam Snaider06779722016-02-14 15:26:22 -0800370 case HIGH_ARM_ZERO_LEVEL_SHOOTER:
371 if (disable) {
372 state_ = DISABLED_INITIALIZED;
373 } else {
374 // Move the shooter to be level.
375 arm_.set_unprofiled_goal(kShoulderUpAngle, 0.0);
376
377 if (IsArmNear(kLooseTolerance)) {
378 // Close enough, start the next move.
379 state_ = HIGH_ARM_ZERO_MOVE_INTAKE_OUT;
380 }
381 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800382 break;
383
Adam Snaider06779722016-02-14 15:26:22 -0800384 case HIGH_ARM_ZERO_MOVE_INTAKE_OUT:
385 if (disable) {
386 state_ = DISABLED_INITIALIZED;
387 } else {
388 // If we were just asked to move the intake, make sure it moves far
389 // enough.
390 if (last_state_ != HIGH_ARM_ZERO_MOVE_INTAKE_OUT) {
391 intake_.set_unprofiled_goal(
Austin Schuh3634ed32017-02-05 16:28:49 -0800392 MoveButKeepBelow(kIntakeUpperClear, intake_.position(),
Adam Snaider06779722016-02-14 15:26:22 -0800393 kIntakeEncoderIndexDifference * 2.5));
394 }
395
396 if (IsIntakeNear(kLooseTolerance)) {
397 // Close enough, start the next move.
398 state_ = HIGH_ARM_ZERO_LOWER_ARM;
399 }
400 }
401 break;
402
403 case HIGH_ARM_ZERO_LOWER_ARM:
404 if (disable) {
405 state_ = DISABLED_INITIALIZED;
406 } else {
407 // Land the shooter in the belly-pan. It should be zeroed by the time
408 // it gets there. If not, just estop.
409 arm_.set_unprofiled_goal(kShoulderLanded, 0.0);
410 if (arm_.zeroed() && intake_.zeroed()) {
411 state_ = RUNNING;
412 } else if (IsArmNear(kLooseTolerance)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700413 AOS_LOG(ERROR,
414 "Failed to zero while executing the HIGH_ARM_ZERO sequence. "
415 "Arm: %d Intake %d\n",
416 arm_.zeroed(), intake_.zeroed());
Adam Snaider06779722016-02-14 15:26:22 -0800417 state_ = ESTOP;
418 }
419 }
420 break;
421
422 case LOW_ARM_ZERO_LOWER_INTAKE:
423 if (disable) {
424 state_ = DISABLED_INITIALIZED;
425 } else {
426 // Move the intake down out of the way of the arm. Make sure to move it
427 // far enough to zero.
428 if (last_state_ != LOW_ARM_ZERO_LOWER_INTAKE) {
429 intake_.set_unprofiled_goal(
Austin Schuh3634ed32017-02-05 16:28:49 -0800430 MoveButKeepBelow(kIntakeLowerClear, intake_.position(),
Adam Snaider06779722016-02-14 15:26:22 -0800431 kIntakeEncoderIndexDifference * 2.5));
432 }
433 if (IsIntakeNear(kLooseTolerance)) {
434 if (::std::abs(arm_.wrist_angle()) < kWristAlmostLevel) {
435 state_ = LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER;
436 } else {
437 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
438 }
439 }
440 }
441 break;
442
443 case LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER:
444 if (disable) {
445 state_ = DISABLED_INITIALIZED;
446 } else {
447 // If we are supposed to level the shooter, set it to level, and wait
448 // until it is very close to level.
449 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
450 if (IsArmNear(kLooseTolerance, kTightTolerance)) {
451 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
452 }
453 }
454 break;
455
456 case LOW_ARM_ZERO_LIFT_SHOULDER:
457 if (disable) {
458 state_ = DISABLED_INITIALIZED;
459 } else {
460 // Decide where to move to. We need to move far enough to see an index
461 // pulse, but must also get high enough that we can safely level the
462 // shooter.
463 if (last_state_ != LOW_ARM_ZERO_LIFT_SHOULDER) {
464 arm_.set_unprofiled_goal(
465 MoveButKeepAbove(kShoulderWristClearAngle, arm_.shoulder_angle(),
466 ::std::max(kWristEncoderIndexDifference,
467 kShoulderEncoderIndexDifference) *
468 2.5),
469 arm_.unprofiled_goal(2, 0));
470 }
471
Brian Silverman741b27a2016-05-16 00:09:26 -0700472 // If we're about to ask the wrist to go past one of its limits, then
473 // move the goal so it will be just at the limit when we finish lifting
474 // the shoulder. If it wasn't intersecting something before, this can't
475 // cause it to crash into anything.
476 const double ungrounded_wrist = arm_.goal(2, 0) - arm_.goal(0, 0);
477 const double unprofiled_ungrounded_wrist =
478 arm_.unprofiled_goal(2, 0) - arm_.unprofiled_goal(0, 0);
479 if (unprofiled_ungrounded_wrist >
480 constants::Values::kWristRange.upper &&
481 ungrounded_wrist >
482 constants::Values::kWristRange.upper - kWristAlmostLevel) {
483 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0),
484 constants::Values::kWristRange.upper +
485 arm_.unprofiled_goal(0, 0));
486 } else if (unprofiled_ungrounded_wrist <
487 constants::Values::kWristRange.lower &&
488 ungrounded_wrist < constants::Values::kWristRange.lower +
489 kWristAlmostLevel) {
490 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0),
491 constants::Values::kWristRange.lower +
492 arm_.unprofiled_goal(0, 0));
493 }
494
Adam Snaider06779722016-02-14 15:26:22 -0800495 // Wait until we are level and then go for it.
496 if (IsArmNear(kLooseTolerance)) {
497 state_ = LOW_ARM_ZERO_LEVEL_SHOOTER;
498 }
499 }
500 break;
501
502 case LOW_ARM_ZERO_LEVEL_SHOOTER:
503 if (disable) {
504 state_ = DISABLED_INITIALIZED;
505 } else {
506 // Move the shooter level (and keep the same height). We don't want to
507 // got to RUNNING until we are completely level so that we don't
508 // give control back in a weird case where we might crash.
509 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
510 if (IsArmNear(kLooseTolerance)) {
511 if (arm_.zeroed() && intake_.zeroed()) {
512 state_ = RUNNING;
513 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700514 AOS_LOG(ERROR,
515 "Failed to zero while executing the LOW_ARM_ZERO sequence. "
516 "Arm: %d Intake %d\n",
517 arm_.zeroed(), intake_.zeroed());
Adam Snaider06779722016-02-14 15:26:22 -0800518 state_ = ESTOP;
519 }
520 }
521 }
522 break;
523
Austin Schuhb1d682b2016-02-16 13:07:44 -0800524 // These 4 cases are very similar.
Adam Snaider06779722016-02-14 15:26:22 -0800525 case SLOW_RUNNING:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800526 case RUNNING:
Austin Schuhb1d682b2016-02-16 13:07:44 -0800527 case LANDING_SLOW_RUNNING:
528 case LANDING_RUNNING: {
Austin Schuh829fe572016-02-14 21:41:16 -0800529 if (disable) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800530 // If we are disabled, go to slow running (or landing slow running) if
531 // we are collided.
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800532 if (is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800533 if (state_ == RUNNING) {
534 state_ = SLOW_RUNNING;
535 } else if (state_ == LANDING_RUNNING) {
536 state_ = LANDING_SLOW_RUNNING;
537 }
538 }
Austin Schuh829fe572016-02-14 21:41:16 -0800539
Austin Schuhb1d682b2016-02-16 13:07:44 -0800540 // Reset the profile to the current position so it moves well from here.
Austin Schuh3634ed32017-02-05 16:28:49 -0800541 intake_.ForceGoal(intake_.position());
Austin Schuh829fe572016-02-14 21:41:16 -0800542 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
543 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800544 // If we are in slow_running and are no longer collided, let 'er rip.
Austin Schuh829fe572016-02-14 21:41:16 -0800545 if (state_ == SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800546 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800547 state_ = RUNNING;
548 }
549 } else if (state_ == LANDING_SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800550 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800551 state_ = LANDING_RUNNING;
552 }
Austin Schuh829fe572016-02-14 21:41:16 -0800553 }
554 }
555
Austin Schuhb1d682b2016-02-16 13:07:44 -0800556 double requested_shoulder = constants::Values::kShoulderRange.lower;
557 double requested_wrist = 0.0;
558 double requested_intake = M_PI / 2.0;
559
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800560 if (unsafe_goal) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800561 // If we are in one of the landing states, limit the accelerations and
562 // velocities to land cleanly.
563 if (state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING) {
564 arm_.AdjustProfile(0.5, // Shoulder Velocity
565 4.0, // Shoulder acceleration,
566 4.0, // Wrist velocity
567 10.0); // Wrist acceleration.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700568 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake(),
569 unsafe_goal->max_angular_acceleration_intake());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800570
Austin Schuhb1d682b2016-02-16 13:07:44 -0800571 requested_shoulder =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700572 ::std::max(unsafe_goal->angle_shoulder(),
Austin Schuhb1d682b2016-02-16 13:07:44 -0800573 constants::Values::kShoulderRange.lower);
574 requested_wrist = 0.0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700575 requested_intake = unsafe_goal->angle_intake();
Austin Schuha1dd5272016-02-16 15:39:38 -0800576 // Transition to landing once the profile is close to finished for the
577 // shoulder.
578 if (arm_.goal(0, 0) > kShoulderTransitionToLanded + 1e-4 ||
579 arm_.unprofiled_goal(0, 0) > kShoulderTransitionToLanded + 1e-4) {
580 if (state_ == LANDING_RUNNING) {
581 state_ = RUNNING;
582 } else {
583 state_ = SLOW_RUNNING;
584 }
585 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000586 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800587 // Otherwise, give the user what he asked for.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700588 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder(),
589 unsafe_goal->max_angular_acceleration_shoulder(),
590 unsafe_goal->max_angular_velocity_wrist(),
591 unsafe_goal->max_angular_acceleration_wrist());
592 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake(),
593 unsafe_goal->max_angular_acceleration_intake());
Austin Schuhb1d682b2016-02-16 13:07:44 -0800594
595 // Except, don't let the shoulder go all the way down.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700596 requested_shoulder = ::std::max(unsafe_goal->angle_shoulder(),
Austin Schuhb1d682b2016-02-16 13:07:44 -0800597 kShoulderTransitionToLanded);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700598 requested_wrist = unsafe_goal->angle_wrist();
599 requested_intake = unsafe_goal->angle_intake();
Austin Schuhb1d682b2016-02-16 13:07:44 -0800600
601 // Transition to landing once the profile is close to finished for the
602 // shoulder.
Austin Schuha1dd5272016-02-16 15:39:38 -0800603 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 &&
604 arm_.unprofiled_goal(0, 0) <=
605 kShoulderTransitionToLanded + 1e-4) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800606 if (state_ == RUNNING) {
607 state_ = LANDING_RUNNING;
608 } else {
609 state_ = LANDING_SLOW_RUNNING;
610 }
611 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000612 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800613 }
614
Austin Schuhb1d682b2016-02-16 13:07:44 -0800615 // Push the request out to hardware!
616 collision_avoidance_.UpdateGoal(requested_shoulder, requested_wrist,
617 requested_intake);
618
619 // ESTOP if we hit the hard limits.
620 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
621 state_ = ESTOP;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800622 }
Austin Schuhb1d682b2016-02-16 13:07:44 -0800623 } break;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800624
625 case ESTOP:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700626 AOS_LOG(ERROR, "Estop\n");
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800627 disable = true;
628 break;
629 }
630
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800631 // Set the voltage limits.
Comran Morshed011f7d92016-02-16 23:03:06 +0000632 const double max_voltage = (state_ == RUNNING || state_ == LANDING_RUNNING)
633 ? kOperatingVoltage
634 : kZeroingVoltage;
Comran Morshed71466fe2016-04-21 20:21:14 -0700635 if (unsafe_goal) {
Austin Schuh0c001a82016-05-01 12:30:09 -0700636 constexpr float kTriggerThreshold = 12.0 * 0.90 / 0.005;
Comran Morshed71466fe2016-04-21 20:21:14 -0700637
Alex Perrycb7da4b2019-08-28 19:35:56 -0700638 if (unsafe_goal->voltage_climber() > 1.0) {
Comran Morshed71466fe2016-04-21 20:21:14 -0700639 kill_shoulder_accumulator_ +=
Alex Perrycb7da4b2019-08-28 19:35:56 -0700640 ::std::min(kMaxClimberVoltage, unsafe_goal->voltage_climber());
Comran Morshed71466fe2016-04-21 20:21:14 -0700641 } else {
642 kill_shoulder_accumulator_ = 0.0;
643 }
644
645 if (kill_shoulder_accumulator_ > kTriggerThreshold) {
646 kill_shoulder_ = true;
647 }
648 }
Austin Schuh0c001a82016-05-01 12:30:09 -0700649 arm_.set_max_voltage(
Austin Schuh473a5652017-02-05 01:30:42 -0800650 {{kill_shoulder_ ? 0.0 : max_voltage,
651 kill_shoulder_ ? (arm_.X_hat(0, 0) < 0.05 ? kShooterHangingLowVoltage
652 : kShooterHangingVoltage)
653 : max_voltage}});
654 intake_.set_max_voltage({{max_voltage}});
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800655
Diana Vandenberg9cc9ab62016-04-20 21:27:47 -0700656 if (IsRunning() && !kill_shoulder_) {
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800657 // We don't want lots of negative voltage when we are near the bellypan on
658 // the shoulder...
659 // TODO(austin): Do I want to push negative power into the belly pan at this
660 // point? Maybe just put the goal slightly below the bellypan and call that
661 // good enough.
Austin Schuh6ca0f792016-03-12 14:06:14 -0800662 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 ||
663 arm_.X_hat(0, 0) <= kShoulderTransitionToLanded + 1e-4) {
Comran Morshed011f7d92016-02-16 23:03:06 +0000664 arm_.set_shoulder_asymetric_limits(kLandingShoulderDownVoltage,
665 max_voltage);
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800666 }
667 }
668
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800669 // Calculate the loops for a cycle.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700670 double intake_position_power;
671 double intake_velocity_power;
Austin Schuhbe041152016-02-28 22:01:52 -0800672 {
673 Eigen::Matrix<double, 3, 1> error = intake_.controller().error();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700674 intake_position_power =
Austin Schuh32501832017-02-25 18:32:56 -0800675 intake_.controller().controller().K(0, 0) * error(0, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700676 intake_velocity_power =
Austin Schuh32501832017-02-25 18:32:56 -0800677 intake_.controller().controller().K(0, 1) * error(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800678 }
679
Alex Perrycb7da4b2019-08-28 19:35:56 -0700680 double shoulder_position_power;
681 double shoulder_velocity_power;
682 double wrist_position_power;
683 double wrist_velocity_power;
Austin Schuhbe041152016-02-28 22:01:52 -0800684 {
685 Eigen::Matrix<double, 6, 1> error = arm_.controller().error();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700686 shoulder_position_power =
Austin Schuh32501832017-02-25 18:32:56 -0800687 arm_.controller().controller().K(0, 0) * error(0, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700688 shoulder_velocity_power =
Austin Schuh32501832017-02-25 18:32:56 -0800689 arm_.controller().controller().K(0, 1) * error(1, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700690 wrist_position_power = arm_.controller().controller().K(0, 2) * error(2, 0);
691 wrist_velocity_power = 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) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700699 OutputT output_struct;
700 output_struct.voltage_intake = intake_.voltage();
701 output_struct.voltage_shoulder = arm_.shoulder_voltage();
702 output_struct.voltage_wrist = arm_.wrist_voltage();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000703
Alex Perrycb7da4b2019-08-28 19:35:56 -0700704 output_struct.voltage_top_rollers = 0.0;
705 output_struct.voltage_bottom_rollers = 0.0;
Comran Morshed71466fe2016-04-21 20:21:14 -0700706
Alex Perrycb7da4b2019-08-28 19:35:56 -0700707 output_struct.voltage_climber = 0.0;
708 output_struct.unfold_climber = false;
Comran Morshedf4cd7642016-02-15 20:40:49 +0000709 if (unsafe_goal) {
Comran Morshed71466fe2016-04-21 20:21:14 -0700710 // Ball detector lights.
Austin Schuh4b652c92019-05-27 13:22:27 -0700711 ball_detector_fetcher_.Fetch();
Austin Schuh1defd262016-04-03 16:13:32 -0700712 bool ball_detected = false;
Austin Schuh4b652c92019-05-27 13:22:27 -0700713 if (ball_detector_fetcher_.get()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700714 ball_detected = ball_detector_fetcher_->voltage() > 2.5;
Austin Schuh1defd262016-04-03 16:13:32 -0700715 }
Comran Morshed71466fe2016-04-21 20:21:14 -0700716
717 // Climber.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700718 output_struct.voltage_climber = ::std::max(
Comran Morshed71466fe2016-04-21 20:21:14 -0700719 static_cast<float>(0.0),
Alex Perrycb7da4b2019-08-28 19:35:56 -0700720 ::std::min(unsafe_goal->voltage_climber(), kMaxClimberVoltage));
721 output_struct.unfold_climber = unsafe_goal->unfold_climber();
Comran Morshed71466fe2016-04-21 20:21:14 -0700722
723 // Intake.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700724 if (unsafe_goal->force_intake() || !ball_detected) {
James Kuszmaul61750662021-06-21 21:32:33 -0700725 output_struct.voltage_top_rollers =
726 ::std::max(-kMaxIntakeTopVoltage,
727 ::std::min(unsafe_goal->voltage_top_rollers(),
728 kMaxIntakeTopVoltage));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700729 output_struct.voltage_bottom_rollers =
Austin Schuh1defd262016-04-03 16:13:32 -0700730 ::std::max(-kMaxIntakeBottomVoltage,
Alex Perrycb7da4b2019-08-28 19:35:56 -0700731 ::std::min(unsafe_goal->voltage_bottom_rollers(),
Austin Schuh1defd262016-04-03 16:13:32 -0700732 kMaxIntakeBottomVoltage));
733 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700734 output_struct.voltage_top_rollers = 0.0;
735 output_struct.voltage_bottom_rollers = 0.0;
Austin Schuh1defd262016-04-03 16:13:32 -0700736 }
Comran Morshed71466fe2016-04-21 20:21:14 -0700737
738 // Traverse.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700739 output_struct.traverse_unlatched = unsafe_goal->traverse_unlatched();
740 output_struct.traverse_down = unsafe_goal->traverse_down();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000741 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700742
milind1f1dca32021-07-03 13:50:07 -0700743 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800744 }
745
746 // Save debug/internal state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700747 flatbuffers::Offset<frc971::EstimatorState> shoulder_estimator_state_offset =
748 arm_.EstimatorState(status->fbb(), 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800749
Alex Perrycb7da4b2019-08-28 19:35:56 -0700750 JointState::Builder shoulder_builder = status->MakeBuilder<JointState>();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800751
Alex Perrycb7da4b2019-08-28 19:35:56 -0700752 shoulder_builder.add_angle(arm_.X_hat(0, 0));
753 shoulder_builder.add_angular_velocity(arm_.X_hat(1, 0));
754 shoulder_builder.add_goal_angle(arm_.goal(0, 0));
755 shoulder_builder.add_goal_angular_velocity(arm_.goal(1, 0));
756 shoulder_builder.add_unprofiled_goal_angle(arm_.unprofiled_goal(0, 0));
757 shoulder_builder.add_unprofiled_goal_angular_velocity(
758 arm_.unprofiled_goal(1, 0));
759 shoulder_builder.add_voltage_error(arm_.X_hat(4, 0));
760 shoulder_builder.add_calculated_velocity(
761 (arm_.shoulder_angle() - last_shoulder_angle_) / 0.005);
762 shoulder_builder.add_position_power(shoulder_position_power);
763 shoulder_builder.add_velocity_power(shoulder_velocity_power);
764 shoulder_builder.add_estimator_state(shoulder_estimator_state_offset);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800765
Alex Perrycb7da4b2019-08-28 19:35:56 -0700766 flatbuffers::Offset<JointState> shoulder_offset = shoulder_builder.Finish();
Austin Schuhbe041152016-02-28 22:01:52 -0800767
Alex Perrycb7da4b2019-08-28 19:35:56 -0700768 flatbuffers::Offset<frc971::EstimatorState> wrist_estimator_state_offset =
769 arm_.EstimatorState(status->fbb(), 1);
770
771 JointState::Builder wrist_builder = status->MakeBuilder<JointState>();
772
773 wrist_builder.add_angle(arm_.X_hat(2, 0));
774 wrist_builder.add_angular_velocity(arm_.X_hat(3, 0));
775 wrist_builder.add_goal_angle(arm_.goal(2, 0));
776 wrist_builder.add_goal_angular_velocity(arm_.goal(3, 0));
777 wrist_builder.add_unprofiled_goal_angle(arm_.unprofiled_goal(2, 0));
778 wrist_builder.add_unprofiled_goal_angular_velocity(
779 arm_.unprofiled_goal(3, 0));
780 wrist_builder.add_voltage_error(arm_.X_hat(5, 0));
781 wrist_builder.add_calculated_velocity(
782 (arm_.wrist_angle() - last_wrist_angle_) / 0.005);
783 wrist_builder.add_position_power(wrist_position_power);
784 wrist_builder.add_velocity_power(wrist_velocity_power);
785 wrist_builder.add_estimator_state(wrist_estimator_state_offset);
786
787 flatbuffers::Offset<JointState> wrist_offset = wrist_builder.Finish();
788
789 flatbuffers::Offset<frc971::EstimatorState> intake_estimator_state_offset =
790 intake_.EstimatorState(status->fbb(), 0);
791
792 JointState::Builder intake_builder = status->MakeBuilder<JointState>();
793 intake_builder.add_position_power(intake_position_power);
794 intake_builder.add_velocity_power(intake_velocity_power);
795 intake_builder.add_angle(intake_.X_hat(0, 0));
796 intake_builder.add_angular_velocity(intake_.X_hat(1, 0));
797 intake_builder.add_goal_angle(intake_.goal(0, 0));
798 intake_builder.add_goal_angular_velocity(intake_.goal(1, 0));
799 intake_builder.add_unprofiled_goal_angle(intake_.unprofiled_goal(0, 0));
800 intake_builder.add_unprofiled_goal_angular_velocity(
801 intake_.unprofiled_goal(1, 0));
802 intake_builder.add_calculated_velocity(
803 (intake_.position() - last_intake_angle_) / 0.005);
804 intake_builder.add_voltage_error(intake_.X_hat(2, 0));
805 intake_builder.add_estimator_state(intake_estimator_state_offset);
806 intake_builder.add_feedforwards_power(intake_.controller().ff_U(0, 0));
807
808 flatbuffers::Offset<JointState> intake_offset = intake_builder.Finish();
809
810 Status::Builder status_builder = status->MakeBuilder<Status>();
811
812 status_builder.add_shoulder(shoulder_offset);
813 status_builder.add_wrist(wrist_offset);
814 status_builder.add_intake(intake_offset);
815
816 status_builder.add_zeroed(arm_.zeroed() && intake_.zeroed());
817 status_builder.add_shoulder_controller_index(arm_.controller_index());
Austin Schuhf59b6bc2016-03-11 21:26:19 -0800818
Austin Schuhbe041152016-02-28 22:01:52 -0800819 last_shoulder_angle_ = arm_.shoulder_angle();
820 last_wrist_angle_ = arm_.wrist_angle();
Austin Schuh3634ed32017-02-05 16:28:49 -0800821 last_intake_angle_ = intake_.position();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800822
Alex Perrycb7da4b2019-08-28 19:35:56 -0700823 status_builder.add_estopped((state_ == ESTOP));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800824
Alex Perrycb7da4b2019-08-28 19:35:56 -0700825 status_builder.add_state(state_);
826 status_builder.add_is_collided(is_collided);
827
milind1f1dca32021-07-03 13:50:07 -0700828 (void)status->Send(status_builder.Finish());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800829
Adam Snaider06779722016-02-14 15:26:22 -0800830 last_state_ = state_before_switch;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800831}
832
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000833constexpr double Superstructure::kZeroingVoltage;
834constexpr double Superstructure::kOperatingVoltage;
Austin Schuh6ca0f792016-03-12 14:06:14 -0800835constexpr double Superstructure::kLandingShoulderDownVoltage;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000836constexpr double Superstructure::kShoulderMiddleAngle;
837constexpr double Superstructure::kLooseTolerance;
838constexpr double Superstructure::kIntakeUpperClear;
839constexpr double Superstructure::kIntakeLowerClear;
840constexpr double Superstructure::kShoulderUpAngle;
841constexpr double Superstructure::kShoulderLanded;
842constexpr double Superstructure::kTightTolerance;
843constexpr double Superstructure::kWristAlmostLevel;
844constexpr double Superstructure::kShoulderWristClearAngle;
Austin Schuhb1d682b2016-02-16 13:07:44 -0800845constexpr double Superstructure::kShoulderTransitionToLanded;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000846
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800847} // namespace y2016::control_loops::superstructure