blob: 88dfd93a287bde019f578e4e25e2f7ebe15e1d01 [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
Comran Morshed25f81a02016-01-23 13:40:10 +000011namespace y2016 {
12namespace control_loops {
Austin Schuh2fc10fa2016-02-08 00:44:34 -080013namespace superstructure {
Comran Morshed25f81a02016-01-23 13:40:10 +000014
Austin Schuh2fc10fa2016-02-08 00:44:34 -080015namespace {
Austin Schuh2d7820b2016-02-16 13:47:42 -080016// The maximum voltage the intake roller will be allowed to use.
Austin Schuhbe041152016-02-28 22:01:52 -080017constexpr float kMaxIntakeTopVoltage = 12.0;
18constexpr float kMaxIntakeBottomVoltage = 12.0;
Comran Morshed71466fe2016-04-21 20:21:14 -070019constexpr float kMaxClimberVoltage = 12.0;
Comran Morshedf4cd7642016-02-15 20:40:49 +000020
Austin Schuh2d7820b2016-02-16 13:47:42 -080021// Aliases to reduce typing.
Adam Snaider06779722016-02-14 15:26:22 -080022constexpr double kIntakeEncoderIndexDifference =
23 constants::Values::kIntakeEncoderIndexDifference;
24constexpr double kWristEncoderIndexDifference =
25 constants::Values::kWristEncoderIndexDifference;
26constexpr double kShoulderEncoderIndexDifference =
27 constants::Values::kShoulderEncoderIndexDifference;
Austin Schuh2fc10fa2016-02-08 00:44:34 -080028} // namespace
29
Philipp Schrader0119eb12016-02-15 18:16:21 +000030// ///// CollisionAvoidance /////
31
32void CollisionAvoidance::UpdateGoal(double shoulder_angle_goal,
33 double wrist_angle_goal,
34 double intake_angle_goal) {
Austin Schuh6ca0f792016-03-12 14:06:14 -080035 const double original_shoulder_angle_goal = shoulder_angle_goal;
36 const double original_intake_angle_goal = intake_angle_goal;
Austin Schuh2c717862016-03-13 15:32:53 -070037 const double original_wrist_angle_goal = wrist_angle_goal;
Austin Schuh6ca0f792016-03-12 14:06:14 -080038
Philipp Schrader0119eb12016-02-15 18:16:21 +000039 double shoulder_angle = arm_->shoulder_angle();
40 double wrist_angle = arm_->wrist_angle();
Austin Schuh3634ed32017-02-05 16:28:49 -080041 double intake_angle = intake_->position();
Philipp Schrader0119eb12016-02-15 18:16:21 +000042
43 // TODO(phil): This may need tuning to account for bounciness in the limbs or
44 // some other thing that I haven't thought of. At the very least,
45 // incorporating a small safety margin makes writing test cases much easier
46 // since you can directly compare statuses against the constants in the
47 // CollisionAvoidance class.
Austin Schuhfef64ac2016-04-24 19:08:01 -070048 constexpr double kSafetyMargin = 0.03; // radians
Philipp Schrader0119eb12016-02-15 18:16:21 +000049
50 // Avoid colliding the shooter with the frame.
51 // If the shoulder is below a certain angle or we want to move it below
52 // that angle, then the shooter has to stay level to the ground. Otherwise,
53 // it will crash into the frame.
Austin Schuh2c717862016-03-13 15:32:53 -070054 if (intake_angle < kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
55 if (shoulder_angle < kMinShoulderAngleForHorizontalShooter ||
56 original_shoulder_angle_goal < kMinShoulderAngleForHorizontalShooter) {
57 wrist_angle_goal = 0.0;
58 } else if (shoulder_angle < kMinShoulderAngleForIntakeInterference ||
59 original_shoulder_angle_goal <
60 kMinShoulderAngleForIntakeInterference) {
61 wrist_angle_goal =
62 aos::Clip(original_wrist_angle_goal,
63 kMinWristAngleForMovingByIntake + kSafetyMargin,
64 kMaxWristAngleForMovingByIntake - kSafetyMargin);
65 }
66 } else {
67 if (shoulder_angle < kMinShoulderAngleForIntakeUpInterference ||
68 original_shoulder_angle_goal <
69 kMinShoulderAngleForIntakeUpInterference) {
70 wrist_angle_goal = 0.0;
71 }
72 }
73
Austin Schuh6ca0f792016-03-12 14:06:14 -080074 if (shoulder_angle < kMinShoulderAngleForIntakeUpInterference ||
75 original_shoulder_angle_goal < kMinShoulderAngleForIntakeUpInterference) {
Philipp Schrader0119eb12016-02-15 18:16:21 +000076 // Make sure that we don't move the shoulder below a certain angle until
77 // the wrist is level with the ground.
Austin Schuh6802a9d2016-03-12 21:34:53 -080078 if (intake_angle < kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
Austin Schuh2c717862016-03-13 15:32:53 -070079 if (wrist_angle > kMaxWristAngleForMovingByIntake ||
80 wrist_angle < kMinWristAngleForMovingByIntake) {
Austin Schuh6802a9d2016-03-12 21:34:53 -080081 shoulder_angle_goal =
Comran Morshed71466fe2016-04-21 20:21:14 -070082 ::std::max(original_shoulder_angle_goal,
83 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
Austin Schuh6802a9d2016-03-12 21:34:53 -080084 }
85 } else {
Austin Schuh2c717862016-03-13 15:32:53 -070086 if (wrist_angle > kMaxWristAngleForMovingByIntake ||
87 wrist_angle < kMinWristAngleForMovingByIntake) {
Comran Morshed71466fe2016-04-21 20:21:14 -070088 shoulder_angle_goal = ::std::max(
89 original_shoulder_angle_goal,
90 kMinShoulderAngleForIntakeUpInterference + kSafetyMargin);
Austin Schuh6802a9d2016-03-12 21:34:53 -080091 }
Austin Schuh6ca0f792016-03-12 14:06:14 -080092 }
Philipp Schrader0119eb12016-02-15 18:16:21 +000093 if (::std::abs(wrist_angle) > kMaxWristAngleForSafeArmStowing) {
94 shoulder_angle_goal =
Austin Schuh6802a9d2016-03-12 21:34:53 -080095 ::std::max(shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +000096 kMinShoulderAngleForHorizontalShooter + kSafetyMargin);
97 }
98 }
99
100 // Is the arm where it could interfere with the intake right now?
101 bool shoulder_is_in_danger =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800102 (shoulder_angle < kMinShoulderAngleForIntakeUpInterference &&
Philipp Schrader0119eb12016-02-15 18:16:21 +0000103 shoulder_angle > kMaxShoulderAngleUntilSafeIntakeStowing);
104
105 // Is the arm moving into collision zone from above?
106 bool shoulder_moving_into_danger_from_above =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800107 (shoulder_angle >= kMinShoulderAngleForIntakeUpInterference &&
108 original_shoulder_angle_goal <=
109 kMinShoulderAngleForIntakeUpInterference);
Philipp Schrader0119eb12016-02-15 18:16:21 +0000110
111 // Is the arm moving into collision zone from below?
112 bool shoulder_moving_into_danger_from_below =
113 (shoulder_angle <= kMaxShoulderAngleUntilSafeIntakeStowing &&
Austin Schuh6ca0f792016-03-12 14:06:14 -0800114 original_shoulder_angle_goal >= kMaxShoulderAngleUntilSafeIntakeStowing);
Philipp Schrader0119eb12016-02-15 18:16:21 +0000115
116 // Avoid colliding the arm with the intake.
117 if (shoulder_is_in_danger || shoulder_moving_into_danger_from_above ||
118 shoulder_moving_into_danger_from_below) {
119 // If the arm could collide with the intake, we make sure to move the
120 // intake out of the way. The arm has priority.
121 intake_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800122 ::std::min(original_intake_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000123 kMaxIntakeAngleBeforeArmInterference - kSafetyMargin);
124
125 // Don't let the shoulder move into the collision area until the intake is
126 // out of the way.
127 if (intake_angle > kMaxIntakeAngleBeforeArmInterference) {
128 const double kHalfwayPointBetweenSafeZones =
129 (kMinShoulderAngleForIntakeInterference +
130 kMaxShoulderAngleUntilSafeIntakeStowing) /
131 2.0;
132
133 if (shoulder_angle >= kHalfwayPointBetweenSafeZones) {
134 // The shoulder is closer to being above the collision area. Move it up
135 // there.
Austin Schuh2c717862016-03-13 15:32:53 -0700136 if (intake_angle <
137 kMaxIntakeAngleBeforeArmInterference + kSafetyMargin) {
138 shoulder_angle_goal = ::std::max(
139 original_shoulder_angle_goal,
140 kMinShoulderAngleForIntakeInterference + kSafetyMargin);
141 } else {
142 shoulder_angle_goal = ::std::max(
143 original_shoulder_angle_goal,
144 kMinShoulderAngleForIntakeUpInterference + kSafetyMargin);
145 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000146 } else {
147 // The shoulder is closer to being below the collision zone (i.e. in
148 // stowing/intake position), keep it there for now.
149 shoulder_angle_goal =
Austin Schuh6ca0f792016-03-12 14:06:14 -0800150 ::std::min(original_shoulder_angle_goal,
Philipp Schrader0119eb12016-02-15 18:16:21 +0000151 kMaxShoulderAngleUntilSafeIntakeStowing - kSafetyMargin);
152 }
153 }
154 }
155
156 // Send the possibly adjusted goals to the components.
157 arm_->set_unprofiled_goal(shoulder_angle_goal, wrist_angle_goal);
158 intake_->set_unprofiled_goal(intake_angle_goal);
159}
160
Philipp Schrader07147532016-02-16 01:23:07 +0000161bool CollisionAvoidance::collided() const {
162 return collided_with_given_angles(arm_->shoulder_angle(), arm_->wrist_angle(),
Austin Schuh3634ed32017-02-05 16:28:49 -0800163 intake_->position());
Philipp Schrader07147532016-02-16 01:23:07 +0000164}
165
166bool CollisionAvoidance::collided_with_given_angles(double shoulder_angle,
167 double wrist_angle,
168 double intake_angle) {
169 // The arm and the intake must not hit.
170 if (shoulder_angle >=
171 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
172 shoulder_angle <=
Austin Schuh6ca0f792016-03-12 14:06:14 -0800173 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference &&
Comran Morshed71466fe2016-04-21 20:21:14 -0700174 intake_angle > CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700175 AOS_LOG(DEBUG, "Collided: Intake %f > %f, and shoulder %f < %f < %f.\n",
176 intake_angle,
177 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
178 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
179 shoulder_angle,
180 CollisionAvoidance::kMinShoulderAngleForIntakeUpInterference);
Austin Schuh6ca0f792016-03-12 14:06:14 -0800181 return true;
182 }
183
184 if (shoulder_angle >=
185 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing &&
186 shoulder_angle <=
187 CollisionAvoidance::kMinShoulderAngleForIntakeInterference &&
188 intake_angle < CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference &&
189 intake_angle > Superstructure::kIntakeLowerClear &&
Austin Schuh2c717862016-03-13 15:32:53 -0700190 (wrist_angle > CollisionAvoidance::kMaxWristAngleForMovingByIntake ||
191 wrist_angle < CollisionAvoidance::kMinWristAngleForMovingByIntake)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700192 AOS_LOG(
193 DEBUG,
Austin Schuh2c717862016-03-13 15:32:53 -0700194 "Collided: Intake %f < %f < %f, shoulder %f < %f < %f, and %f < %f < "
195 "%f.\n",
Austin Schuh6ca0f792016-03-12 14:06:14 -0800196 Superstructure::kIntakeLowerClear, intake_angle,
197 CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference,
198 CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing,
199 shoulder_angle,
Austin Schuh2c717862016-03-13 15:32:53 -0700200 CollisionAvoidance::kMinShoulderAngleForIntakeInterference,
201 CollisionAvoidance::kMinWristAngleForMovingByIntake, wrist_angle,
202 CollisionAvoidance::kMaxWristAngleForMovingByIntake);
Philipp Schrader07147532016-02-16 01:23:07 +0000203 return true;
204 }
205
206 // The wrist must go back to zero when the shoulder is moving the arm into
207 // a stowed/intaking position.
James Kuszmaul61750662021-06-21 21:32:33 -0700208 if (shoulder_angle<CollisionAvoidance::kMinShoulderAngleForHorizontalShooter
209 && ::std::abs(wrist_angle)>
210 kMaxWristAngleForSafeArmStowing) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700211 AOS_LOG(DEBUG, "Collided: Shoulder %f < %f and wrist |%f| > %f.\n",
212 shoulder_angle,
213 CollisionAvoidance::kMinShoulderAngleForHorizontalShooter,
214 wrist_angle, kMaxWristAngleForSafeArmStowing);
Philipp Schrader07147532016-02-16 01:23:07 +0000215 return true;
216 }
217
218 return false;
219}
220
Philipp Schrader0119eb12016-02-15 18:16:21 +0000221constexpr double CollisionAvoidance::kMinShoulderAngleForHorizontalShooter;
222constexpr double CollisionAvoidance::kMinShoulderAngleForIntakeInterference;
223constexpr double CollisionAvoidance::kMaxIntakeAngleBeforeArmInterference;
224constexpr double CollisionAvoidance::kMaxWristAngleForSafeArmStowing;
225constexpr double CollisionAvoidance::kMaxShoulderAngleUntilSafeIntakeStowing;
226
Austin Schuh55a13dc2019-01-27 22:39:03 -0800227Superstructure::Superstructure(::aos::EventLoop *event_loop,
228 const ::std::string &name)
James Kuszmaul61750662021-06-21 21:32:33 -0700229 : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
230 name),
Austin Schuh4b652c92019-05-27 13:22:27 -0700231 ball_detector_fetcher_(
232 event_loop->MakeFetcher<::y2016::sensors::BallDetector>(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700233 "/superstructure")),
Philipp Schrader0119eb12016-02-15 18:16:21 +0000234 collision_avoidance_(&intake_, &arm_) {}
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800235
Adam Snaider06779722016-02-14 15:26:22 -0800236bool Superstructure::IsArmNear(double shoulder_tolerance,
237 double wrist_tolerance) {
238 return ((arm_.unprofiled_goal() - arm_.X_hat())
239 .block<2, 1>(0, 0)
240 .lpNorm<Eigen::Infinity>() < shoulder_tolerance) &&
241 ((arm_.unprofiled_goal() - arm_.X_hat())
242 .block<4, 1>(0, 0)
243 .lpNorm<Eigen::Infinity>() < wrist_tolerance) &&
244 ((arm_.unprofiled_goal() - arm_.goal())
245 .block<4, 1>(0, 0)
246 .lpNorm<Eigen::Infinity>() < 1e-6);
247}
248
249bool Superstructure::IsArmNear(double tolerance) {
250 return ((arm_.unprofiled_goal() - arm_.X_hat())
251 .block<4, 1>(0, 0)
252 .lpNorm<Eigen::Infinity>() < tolerance) &&
253 ((arm_.unprofiled_goal() - arm_.goal())
254 .block<4, 1>(0, 0)
255 .lpNorm<Eigen::Infinity>() < 1e-6);
256}
257
258bool Superstructure::IsIntakeNear(double tolerance) {
259 return ((intake_.unprofiled_goal() - intake_.X_hat())
260 .block<2, 1>(0, 0)
261 .lpNorm<Eigen::Infinity>() < tolerance);
262}
263
264double Superstructure::MoveButKeepAbove(double reference_angle,
265 double current_angle,
266 double move_distance) {
267 return -MoveButKeepBelow(-reference_angle, -current_angle, -move_distance);
268}
269
270double Superstructure::MoveButKeepBelow(double reference_angle,
271 double current_angle,
272 double move_distance) {
273 // There are 3 interesting places to move to.
274 const double small_negative_move = current_angle - move_distance;
275 const double small_positive_move = current_angle + move_distance;
276 // And the reference angle.
277
278 // Move the the highest one that is below reference_angle.
279 if (small_negative_move > reference_angle) {
280 return reference_angle;
281 } else if (small_positive_move > reference_angle) {
282 return small_negative_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800283 } else {
Adam Snaider06779722016-02-14 15:26:22 -0800284 return small_positive_move;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800285 }
286}
287
James Kuszmaul61750662021-06-21 21:32:33 -0700288void Superstructure::RunIteration(const Goal *unsafe_goal,
289 const Position *position,
290 aos::Sender<Output>::Builder *output,
291 aos::Sender<Status>::Builder *status) {
Adam Snaider06779722016-02-14 15:26:22 -0800292 const State state_before_switch = state_;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800293 if (WasReset()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700294 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800295 arm_.Reset();
296 intake_.Reset();
297 state_ = UNINITIALIZED;
298 }
299
300 // Bool to track if we should turn the motors on or not.
301 bool disable = output == nullptr;
302
Alex Perrycb7da4b2019-08-28 19:35:56 -0700303 arm_.Correct(position->shoulder(), position->wrist());
304 intake_.Correct(*position->intake());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800305
Adam Snaider06779722016-02-14 15:26:22 -0800306 // There are 2 main zeroing paths, HIGH_ARM_ZERO and LOW_ARM_ZERO.
307 //
308 // HIGH_ARM_ZERO works by lifting the arm all the way up so it is clear,
309 // moving the shooter to be horizontal, moving the intake out, and then moving
310 // the arm back down.
311 //
312 // LOW_ARM_ZERO works by moving the intake out of the way, lifting the arm up,
313 // leveling the shooter, and then moving back down.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800314
Diana Vandenberge2843c62016-02-13 17:44:20 -0800315 if (arm_.error() || intake_.error()) {
316 state_ = ESTOP;
317 }
318
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800319 const bool is_collided = collided();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800320 switch (state_) {
321 case UNINITIALIZED:
Adam Snaider06779722016-02-14 15:26:22 -0800322 // Wait in the uninitialized state until both the arm and intake are
323 // initialized.
Austin Schuhf257f3c2019-10-27 21:00:43 -0700324 AOS_LOG(DEBUG, "Uninitialized, waiting for intake and arm\n");
Adam Snaider06779722016-02-14 15:26:22 -0800325 if (arm_.initialized() && intake_.initialized()) {
326 state_ = DISABLED_INITIALIZED;
327 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800328 disable = true;
329 break;
330
Adam Snaider06779722016-02-14 15:26:22 -0800331 case DISABLED_INITIALIZED:
332 // Wait here until we are either fully zeroed while disabled, or we become
333 // enabled. At that point, figure out if we should HIGH_ARM_ZERO or
334 // LOW_ARM_ZERO.
335 if (disable) {
336 if (arm_.zeroed() && intake_.zeroed()) {
337 state_ = SLOW_RUNNING;
338 }
339 } else {
340 if (arm_.shoulder_angle() >= kShoulderMiddleAngle) {
341 state_ = HIGH_ARM_ZERO_LIFT_ARM;
342 } else {
343 state_ = LOW_ARM_ZERO_LOWER_INTAKE;
344 }
345 }
346
347 // Set the goals to where we are now so when we start back up, we don't
348 // jump.
Austin Schuh3634ed32017-02-05 16:28:49 -0800349 intake_.ForceGoal(intake_.position());
Adam Snaider06779722016-02-14 15:26:22 -0800350 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
351 // Set up the profile to be the zeroing profile.
352 intake_.AdjustProfile(0.5, 10);
353 arm_.AdjustProfile(0.5, 10, 0.5, 10);
354
355 // We are not ready to start doing anything yet.
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800356 disable = true;
Adam Snaider06779722016-02-14 15:26:22 -0800357 break;
358
359 case HIGH_ARM_ZERO_LIFT_ARM:
360 if (disable) {
361 state_ = DISABLED_INITIALIZED;
362 } else {
363 // Raise the shoulder up out of the way.
364 arm_.set_unprofiled_goal(kShoulderUpAngle, arm_.wrist_angle());
365 if (IsArmNear(kLooseTolerance)) {
366 // Close enough, start the next move.
367 state_ = HIGH_ARM_ZERO_LEVEL_SHOOTER;
368 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800369 }
370 break;
371
Adam Snaider06779722016-02-14 15:26:22 -0800372 case HIGH_ARM_ZERO_LEVEL_SHOOTER:
373 if (disable) {
374 state_ = DISABLED_INITIALIZED;
375 } else {
376 // Move the shooter to be level.
377 arm_.set_unprofiled_goal(kShoulderUpAngle, 0.0);
378
379 if (IsArmNear(kLooseTolerance)) {
380 // Close enough, start the next move.
381 state_ = HIGH_ARM_ZERO_MOVE_INTAKE_OUT;
382 }
383 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800384 break;
385
Adam Snaider06779722016-02-14 15:26:22 -0800386 case HIGH_ARM_ZERO_MOVE_INTAKE_OUT:
387 if (disable) {
388 state_ = DISABLED_INITIALIZED;
389 } else {
390 // If we were just asked to move the intake, make sure it moves far
391 // enough.
392 if (last_state_ != HIGH_ARM_ZERO_MOVE_INTAKE_OUT) {
393 intake_.set_unprofiled_goal(
Austin Schuh3634ed32017-02-05 16:28:49 -0800394 MoveButKeepBelow(kIntakeUpperClear, intake_.position(),
Adam Snaider06779722016-02-14 15:26:22 -0800395 kIntakeEncoderIndexDifference * 2.5));
396 }
397
398 if (IsIntakeNear(kLooseTolerance)) {
399 // Close enough, start the next move.
400 state_ = HIGH_ARM_ZERO_LOWER_ARM;
401 }
402 }
403 break;
404
405 case HIGH_ARM_ZERO_LOWER_ARM:
406 if (disable) {
407 state_ = DISABLED_INITIALIZED;
408 } else {
409 // Land the shooter in the belly-pan. It should be zeroed by the time
410 // it gets there. If not, just estop.
411 arm_.set_unprofiled_goal(kShoulderLanded, 0.0);
412 if (arm_.zeroed() && intake_.zeroed()) {
413 state_ = RUNNING;
414 } else if (IsArmNear(kLooseTolerance)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700415 AOS_LOG(ERROR,
416 "Failed to zero while executing the HIGH_ARM_ZERO sequence. "
417 "Arm: %d Intake %d\n",
418 arm_.zeroed(), intake_.zeroed());
Adam Snaider06779722016-02-14 15:26:22 -0800419 state_ = ESTOP;
420 }
421 }
422 break;
423
424 case LOW_ARM_ZERO_LOWER_INTAKE:
425 if (disable) {
426 state_ = DISABLED_INITIALIZED;
427 } else {
428 // Move the intake down out of the way of the arm. Make sure to move it
429 // far enough to zero.
430 if (last_state_ != LOW_ARM_ZERO_LOWER_INTAKE) {
431 intake_.set_unprofiled_goal(
Austin Schuh3634ed32017-02-05 16:28:49 -0800432 MoveButKeepBelow(kIntakeLowerClear, intake_.position(),
Adam Snaider06779722016-02-14 15:26:22 -0800433 kIntakeEncoderIndexDifference * 2.5));
434 }
435 if (IsIntakeNear(kLooseTolerance)) {
436 if (::std::abs(arm_.wrist_angle()) < kWristAlmostLevel) {
437 state_ = LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER;
438 } else {
439 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
440 }
441 }
442 }
443 break;
444
445 case LOW_ARM_ZERO_MAYBE_LEVEL_SHOOTER:
446 if (disable) {
447 state_ = DISABLED_INITIALIZED;
448 } else {
449 // If we are supposed to level the shooter, set it to level, and wait
450 // until it is very close to level.
451 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
452 if (IsArmNear(kLooseTolerance, kTightTolerance)) {
453 state_ = LOW_ARM_ZERO_LIFT_SHOULDER;
454 }
455 }
456 break;
457
458 case LOW_ARM_ZERO_LIFT_SHOULDER:
459 if (disable) {
460 state_ = DISABLED_INITIALIZED;
461 } else {
462 // Decide where to move to. We need to move far enough to see an index
463 // pulse, but must also get high enough that we can safely level the
464 // shooter.
465 if (last_state_ != LOW_ARM_ZERO_LIFT_SHOULDER) {
466 arm_.set_unprofiled_goal(
467 MoveButKeepAbove(kShoulderWristClearAngle, arm_.shoulder_angle(),
468 ::std::max(kWristEncoderIndexDifference,
469 kShoulderEncoderIndexDifference) *
470 2.5),
471 arm_.unprofiled_goal(2, 0));
472 }
473
Brian Silverman741b27a2016-05-16 00:09:26 -0700474 // If we're about to ask the wrist to go past one of its limits, then
475 // move the goal so it will be just at the limit when we finish lifting
476 // the shoulder. If it wasn't intersecting something before, this can't
477 // cause it to crash into anything.
478 const double ungrounded_wrist = arm_.goal(2, 0) - arm_.goal(0, 0);
479 const double unprofiled_ungrounded_wrist =
480 arm_.unprofiled_goal(2, 0) - arm_.unprofiled_goal(0, 0);
481 if (unprofiled_ungrounded_wrist >
482 constants::Values::kWristRange.upper &&
483 ungrounded_wrist >
484 constants::Values::kWristRange.upper - kWristAlmostLevel) {
485 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0),
486 constants::Values::kWristRange.upper +
487 arm_.unprofiled_goal(0, 0));
488 } else if (unprofiled_ungrounded_wrist <
489 constants::Values::kWristRange.lower &&
490 ungrounded_wrist < constants::Values::kWristRange.lower +
491 kWristAlmostLevel) {
492 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0),
493 constants::Values::kWristRange.lower +
494 arm_.unprofiled_goal(0, 0));
495 }
496
Adam Snaider06779722016-02-14 15:26:22 -0800497 // Wait until we are level and then go for it.
498 if (IsArmNear(kLooseTolerance)) {
499 state_ = LOW_ARM_ZERO_LEVEL_SHOOTER;
500 }
501 }
502 break;
503
504 case LOW_ARM_ZERO_LEVEL_SHOOTER:
505 if (disable) {
506 state_ = DISABLED_INITIALIZED;
507 } else {
508 // Move the shooter level (and keep the same height). We don't want to
509 // got to RUNNING until we are completely level so that we don't
510 // give control back in a weird case where we might crash.
511 arm_.set_unprofiled_goal(arm_.unprofiled_goal(0, 0), 0.0);
512 if (IsArmNear(kLooseTolerance)) {
513 if (arm_.zeroed() && intake_.zeroed()) {
514 state_ = RUNNING;
515 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700516 AOS_LOG(ERROR,
517 "Failed to zero while executing the LOW_ARM_ZERO sequence. "
518 "Arm: %d Intake %d\n",
519 arm_.zeroed(), intake_.zeroed());
Adam Snaider06779722016-02-14 15:26:22 -0800520 state_ = ESTOP;
521 }
522 }
523 }
524 break;
525
Austin Schuhb1d682b2016-02-16 13:07:44 -0800526 // These 4 cases are very similar.
Adam Snaider06779722016-02-14 15:26:22 -0800527 case SLOW_RUNNING:
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800528 case RUNNING:
Austin Schuhb1d682b2016-02-16 13:07:44 -0800529 case LANDING_SLOW_RUNNING:
530 case LANDING_RUNNING: {
Austin Schuh829fe572016-02-14 21:41:16 -0800531 if (disable) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800532 // If we are disabled, go to slow running (or landing slow running) if
533 // we are collided.
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800534 if (is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800535 if (state_ == RUNNING) {
536 state_ = SLOW_RUNNING;
537 } else if (state_ == LANDING_RUNNING) {
538 state_ = LANDING_SLOW_RUNNING;
539 }
540 }
Austin Schuh829fe572016-02-14 21:41:16 -0800541
Austin Schuhb1d682b2016-02-16 13:07:44 -0800542 // Reset the profile to the current position so it moves well from here.
Austin Schuh3634ed32017-02-05 16:28:49 -0800543 intake_.ForceGoal(intake_.position());
Austin Schuh829fe572016-02-14 21:41:16 -0800544 arm_.ForceGoal(arm_.shoulder_angle(), arm_.wrist_angle());
545 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800546 // If we are in slow_running and are no longer collided, let 'er rip.
Austin Schuh829fe572016-02-14 21:41:16 -0800547 if (state_ == SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800548 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800549 state_ = RUNNING;
550 }
551 } else if (state_ == LANDING_SLOW_RUNNING) {
Austin Schuh5b1a4cd2016-03-11 21:28:38 -0800552 if (!is_collided) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800553 state_ = LANDING_RUNNING;
554 }
Austin Schuh829fe572016-02-14 21:41:16 -0800555 }
556 }
557
Austin Schuhb1d682b2016-02-16 13:07:44 -0800558 double requested_shoulder = constants::Values::kShoulderRange.lower;
559 double requested_wrist = 0.0;
560 double requested_intake = M_PI / 2.0;
561
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800562 if (unsafe_goal) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800563 // If we are in one of the landing states, limit the accelerations and
564 // velocities to land cleanly.
565 if (state_ == LANDING_SLOW_RUNNING || state_ == LANDING_RUNNING) {
566 arm_.AdjustProfile(0.5, // Shoulder Velocity
567 4.0, // Shoulder acceleration,
568 4.0, // Wrist velocity
569 10.0); // Wrist acceleration.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700570 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake(),
571 unsafe_goal->max_angular_acceleration_intake());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800572
Austin Schuhb1d682b2016-02-16 13:07:44 -0800573 requested_shoulder =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700574 ::std::max(unsafe_goal->angle_shoulder(),
Austin Schuhb1d682b2016-02-16 13:07:44 -0800575 constants::Values::kShoulderRange.lower);
576 requested_wrist = 0.0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700577 requested_intake = unsafe_goal->angle_intake();
Austin Schuha1dd5272016-02-16 15:39:38 -0800578 // Transition to landing once the profile is close to finished for the
579 // shoulder.
580 if (arm_.goal(0, 0) > kShoulderTransitionToLanded + 1e-4 ||
581 arm_.unprofiled_goal(0, 0) > kShoulderTransitionToLanded + 1e-4) {
582 if (state_ == LANDING_RUNNING) {
583 state_ = RUNNING;
584 } else {
585 state_ = SLOW_RUNNING;
586 }
587 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000588 } else {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800589 // Otherwise, give the user what he asked for.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700590 arm_.AdjustProfile(unsafe_goal->max_angular_velocity_shoulder(),
591 unsafe_goal->max_angular_acceleration_shoulder(),
592 unsafe_goal->max_angular_velocity_wrist(),
593 unsafe_goal->max_angular_acceleration_wrist());
594 intake_.AdjustProfile(unsafe_goal->max_angular_velocity_intake(),
595 unsafe_goal->max_angular_acceleration_intake());
Austin Schuhb1d682b2016-02-16 13:07:44 -0800596
597 // Except, don't let the shoulder go all the way down.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700598 requested_shoulder = ::std::max(unsafe_goal->angle_shoulder(),
Austin Schuhb1d682b2016-02-16 13:07:44 -0800599 kShoulderTransitionToLanded);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700600 requested_wrist = unsafe_goal->angle_wrist();
601 requested_intake = unsafe_goal->angle_intake();
Austin Schuhb1d682b2016-02-16 13:07:44 -0800602
603 // Transition to landing once the profile is close to finished for the
604 // shoulder.
Austin Schuha1dd5272016-02-16 15:39:38 -0800605 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 &&
606 arm_.unprofiled_goal(0, 0) <=
607 kShoulderTransitionToLanded + 1e-4) {
Austin Schuhb1d682b2016-02-16 13:07:44 -0800608 if (state_ == RUNNING) {
609 state_ = LANDING_RUNNING;
610 } else {
611 state_ = LANDING_SLOW_RUNNING;
612 }
613 }
Philipp Schrader0119eb12016-02-15 18:16:21 +0000614 }
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800615 }
616
Austin Schuhb1d682b2016-02-16 13:07:44 -0800617 // Push the request out to hardware!
618 collision_avoidance_.UpdateGoal(requested_shoulder, requested_wrist,
619 requested_intake);
620
621 // ESTOP if we hit the hard limits.
622 if ((arm_.CheckHardLimits() || intake_.CheckHardLimits()) && output) {
623 state_ = ESTOP;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800624 }
Austin Schuhb1d682b2016-02-16 13:07:44 -0800625 } break;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800626
627 case ESTOP:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700628 AOS_LOG(ERROR, "Estop\n");
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800629 disable = true;
630 break;
631 }
632
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800633 // Set the voltage limits.
Comran Morshed011f7d92016-02-16 23:03:06 +0000634 const double max_voltage = (state_ == RUNNING || state_ == LANDING_RUNNING)
635 ? kOperatingVoltage
636 : kZeroingVoltage;
Comran Morshed71466fe2016-04-21 20:21:14 -0700637 if (unsafe_goal) {
Austin Schuh0c001a82016-05-01 12:30:09 -0700638 constexpr float kTriggerThreshold = 12.0 * 0.90 / 0.005;
Comran Morshed71466fe2016-04-21 20:21:14 -0700639
Alex Perrycb7da4b2019-08-28 19:35:56 -0700640 if (unsafe_goal->voltage_climber() > 1.0) {
Comran Morshed71466fe2016-04-21 20:21:14 -0700641 kill_shoulder_accumulator_ +=
Alex Perrycb7da4b2019-08-28 19:35:56 -0700642 ::std::min(kMaxClimberVoltage, unsafe_goal->voltage_climber());
Comran Morshed71466fe2016-04-21 20:21:14 -0700643 } else {
644 kill_shoulder_accumulator_ = 0.0;
645 }
646
647 if (kill_shoulder_accumulator_ > kTriggerThreshold) {
648 kill_shoulder_ = true;
649 }
650 }
Austin Schuh0c001a82016-05-01 12:30:09 -0700651 arm_.set_max_voltage(
Austin Schuh473a5652017-02-05 01:30:42 -0800652 {{kill_shoulder_ ? 0.0 : max_voltage,
653 kill_shoulder_ ? (arm_.X_hat(0, 0) < 0.05 ? kShooterHangingLowVoltage
654 : kShooterHangingVoltage)
655 : max_voltage}});
656 intake_.set_max_voltage({{max_voltage}});
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800657
Diana Vandenberg9cc9ab62016-04-20 21:27:47 -0700658 if (IsRunning() && !kill_shoulder_) {
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800659 // We don't want lots of negative voltage when we are near the bellypan on
660 // the shoulder...
661 // TODO(austin): Do I want to push negative power into the belly pan at this
662 // point? Maybe just put the goal slightly below the bellypan and call that
663 // good enough.
Austin Schuh6ca0f792016-03-12 14:06:14 -0800664 if (arm_.goal(0, 0) <= kShoulderTransitionToLanded + 1e-4 ||
665 arm_.X_hat(0, 0) <= kShoulderTransitionToLanded + 1e-4) {
Comran Morshed011f7d92016-02-16 23:03:06 +0000666 arm_.set_shoulder_asymetric_limits(kLandingShoulderDownVoltage,
667 max_voltage);
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800668 }
669 }
670
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800671 // Calculate the loops for a cycle.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700672 double intake_position_power;
673 double intake_velocity_power;
Austin Schuhbe041152016-02-28 22:01:52 -0800674 {
675 Eigen::Matrix<double, 3, 1> error = intake_.controller().error();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700676 intake_position_power =
Austin Schuh32501832017-02-25 18:32:56 -0800677 intake_.controller().controller().K(0, 0) * error(0, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700678 intake_velocity_power =
Austin Schuh32501832017-02-25 18:32:56 -0800679 intake_.controller().controller().K(0, 1) * error(1, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800680 }
681
Alex Perrycb7da4b2019-08-28 19:35:56 -0700682 double shoulder_position_power;
683 double shoulder_velocity_power;
684 double wrist_position_power;
685 double wrist_velocity_power;
Austin Schuhbe041152016-02-28 22:01:52 -0800686 {
687 Eigen::Matrix<double, 6, 1> error = arm_.controller().error();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700688 shoulder_position_power =
Austin Schuh32501832017-02-25 18:32:56 -0800689 arm_.controller().controller().K(0, 0) * error(0, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700690 shoulder_velocity_power =
Austin Schuh32501832017-02-25 18:32:56 -0800691 arm_.controller().controller().K(0, 1) * error(1, 0);
Alex Perrycb7da4b2019-08-28 19:35:56 -0700692 wrist_position_power = arm_.controller().controller().K(0, 2) * error(2, 0);
693 wrist_velocity_power = arm_.controller().controller().K(0, 3) * error(3, 0);
Austin Schuhbe041152016-02-28 22:01:52 -0800694 }
695
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800696 arm_.Update(disable);
697 intake_.Update(disable);
698
699 // Write out all the voltages.
700 if (output) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700701 OutputT output_struct;
702 output_struct.voltage_intake = intake_.voltage();
703 output_struct.voltage_shoulder = arm_.shoulder_voltage();
704 output_struct.voltage_wrist = arm_.wrist_voltage();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000705
Alex Perrycb7da4b2019-08-28 19:35:56 -0700706 output_struct.voltage_top_rollers = 0.0;
707 output_struct.voltage_bottom_rollers = 0.0;
Comran Morshed71466fe2016-04-21 20:21:14 -0700708
Alex Perrycb7da4b2019-08-28 19:35:56 -0700709 output_struct.voltage_climber = 0.0;
710 output_struct.unfold_climber = false;
Comran Morshedf4cd7642016-02-15 20:40:49 +0000711 if (unsafe_goal) {
Comran Morshed71466fe2016-04-21 20:21:14 -0700712 // Ball detector lights.
Austin Schuh4b652c92019-05-27 13:22:27 -0700713 ball_detector_fetcher_.Fetch();
Austin Schuh1defd262016-04-03 16:13:32 -0700714 bool ball_detected = false;
Austin Schuh4b652c92019-05-27 13:22:27 -0700715 if (ball_detector_fetcher_.get()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700716 ball_detected = ball_detector_fetcher_->voltage() > 2.5;
Austin Schuh1defd262016-04-03 16:13:32 -0700717 }
Comran Morshed71466fe2016-04-21 20:21:14 -0700718
719 // Climber.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700720 output_struct.voltage_climber = ::std::max(
Comran Morshed71466fe2016-04-21 20:21:14 -0700721 static_cast<float>(0.0),
Alex Perrycb7da4b2019-08-28 19:35:56 -0700722 ::std::min(unsafe_goal->voltage_climber(), kMaxClimberVoltage));
723 output_struct.unfold_climber = unsafe_goal->unfold_climber();
Comran Morshed71466fe2016-04-21 20:21:14 -0700724
725 // Intake.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700726 if (unsafe_goal->force_intake() || !ball_detected) {
James Kuszmaul61750662021-06-21 21:32:33 -0700727 output_struct.voltage_top_rollers =
728 ::std::max(-kMaxIntakeTopVoltage,
729 ::std::min(unsafe_goal->voltage_top_rollers(),
730 kMaxIntakeTopVoltage));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700731 output_struct.voltage_bottom_rollers =
Austin Schuh1defd262016-04-03 16:13:32 -0700732 ::std::max(-kMaxIntakeBottomVoltage,
Alex Perrycb7da4b2019-08-28 19:35:56 -0700733 ::std::min(unsafe_goal->voltage_bottom_rollers(),
Austin Schuh1defd262016-04-03 16:13:32 -0700734 kMaxIntakeBottomVoltage));
735 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700736 output_struct.voltage_top_rollers = 0.0;
737 output_struct.voltage_bottom_rollers = 0.0;
Austin Schuh1defd262016-04-03 16:13:32 -0700738 }
Comran Morshed71466fe2016-04-21 20:21:14 -0700739
740 // Traverse.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700741 output_struct.traverse_unlatched = unsafe_goal->traverse_unlatched();
742 output_struct.traverse_down = unsafe_goal->traverse_down();
Comran Morshedf4cd7642016-02-15 20:40:49 +0000743 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700744
milind1f1dca32021-07-03 13:50:07 -0700745 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800746 }
747
748 // Save debug/internal state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700749 flatbuffers::Offset<frc971::EstimatorState> shoulder_estimator_state_offset =
750 arm_.EstimatorState(status->fbb(), 0);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800751
Alex Perrycb7da4b2019-08-28 19:35:56 -0700752 JointState::Builder shoulder_builder = status->MakeBuilder<JointState>();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800753
Alex Perrycb7da4b2019-08-28 19:35:56 -0700754 shoulder_builder.add_angle(arm_.X_hat(0, 0));
755 shoulder_builder.add_angular_velocity(arm_.X_hat(1, 0));
756 shoulder_builder.add_goal_angle(arm_.goal(0, 0));
757 shoulder_builder.add_goal_angular_velocity(arm_.goal(1, 0));
758 shoulder_builder.add_unprofiled_goal_angle(arm_.unprofiled_goal(0, 0));
759 shoulder_builder.add_unprofiled_goal_angular_velocity(
760 arm_.unprofiled_goal(1, 0));
761 shoulder_builder.add_voltage_error(arm_.X_hat(4, 0));
762 shoulder_builder.add_calculated_velocity(
763 (arm_.shoulder_angle() - last_shoulder_angle_) / 0.005);
764 shoulder_builder.add_position_power(shoulder_position_power);
765 shoulder_builder.add_velocity_power(shoulder_velocity_power);
766 shoulder_builder.add_estimator_state(shoulder_estimator_state_offset);
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800767
Alex Perrycb7da4b2019-08-28 19:35:56 -0700768 flatbuffers::Offset<JointState> shoulder_offset = shoulder_builder.Finish();
Austin Schuhbe041152016-02-28 22:01:52 -0800769
Alex Perrycb7da4b2019-08-28 19:35:56 -0700770 flatbuffers::Offset<frc971::EstimatorState> wrist_estimator_state_offset =
771 arm_.EstimatorState(status->fbb(), 1);
772
773 JointState::Builder wrist_builder = status->MakeBuilder<JointState>();
774
775 wrist_builder.add_angle(arm_.X_hat(2, 0));
776 wrist_builder.add_angular_velocity(arm_.X_hat(3, 0));
777 wrist_builder.add_goal_angle(arm_.goal(2, 0));
778 wrist_builder.add_goal_angular_velocity(arm_.goal(3, 0));
779 wrist_builder.add_unprofiled_goal_angle(arm_.unprofiled_goal(2, 0));
780 wrist_builder.add_unprofiled_goal_angular_velocity(
781 arm_.unprofiled_goal(3, 0));
782 wrist_builder.add_voltage_error(arm_.X_hat(5, 0));
783 wrist_builder.add_calculated_velocity(
784 (arm_.wrist_angle() - last_wrist_angle_) / 0.005);
785 wrist_builder.add_position_power(wrist_position_power);
786 wrist_builder.add_velocity_power(wrist_velocity_power);
787 wrist_builder.add_estimator_state(wrist_estimator_state_offset);
788
789 flatbuffers::Offset<JointState> wrist_offset = wrist_builder.Finish();
790
791 flatbuffers::Offset<frc971::EstimatorState> intake_estimator_state_offset =
792 intake_.EstimatorState(status->fbb(), 0);
793
794 JointState::Builder intake_builder = status->MakeBuilder<JointState>();
795 intake_builder.add_position_power(intake_position_power);
796 intake_builder.add_velocity_power(intake_velocity_power);
797 intake_builder.add_angle(intake_.X_hat(0, 0));
798 intake_builder.add_angular_velocity(intake_.X_hat(1, 0));
799 intake_builder.add_goal_angle(intake_.goal(0, 0));
800 intake_builder.add_goal_angular_velocity(intake_.goal(1, 0));
801 intake_builder.add_unprofiled_goal_angle(intake_.unprofiled_goal(0, 0));
802 intake_builder.add_unprofiled_goal_angular_velocity(
803 intake_.unprofiled_goal(1, 0));
804 intake_builder.add_calculated_velocity(
805 (intake_.position() - last_intake_angle_) / 0.005);
806 intake_builder.add_voltage_error(intake_.X_hat(2, 0));
807 intake_builder.add_estimator_state(intake_estimator_state_offset);
808 intake_builder.add_feedforwards_power(intake_.controller().ff_U(0, 0));
809
810 flatbuffers::Offset<JointState> intake_offset = intake_builder.Finish();
811
812 Status::Builder status_builder = status->MakeBuilder<Status>();
813
814 status_builder.add_shoulder(shoulder_offset);
815 status_builder.add_wrist(wrist_offset);
816 status_builder.add_intake(intake_offset);
817
818 status_builder.add_zeroed(arm_.zeroed() && intake_.zeroed());
819 status_builder.add_shoulder_controller_index(arm_.controller_index());
Austin Schuhf59b6bc2016-03-11 21:26:19 -0800820
Austin Schuhbe041152016-02-28 22:01:52 -0800821 last_shoulder_angle_ = arm_.shoulder_angle();
822 last_wrist_angle_ = arm_.wrist_angle();
Austin Schuh3634ed32017-02-05 16:28:49 -0800823 last_intake_angle_ = intake_.position();
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800824
Alex Perrycb7da4b2019-08-28 19:35:56 -0700825 status_builder.add_estopped((state_ == ESTOP));
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800826
Alex Perrycb7da4b2019-08-28 19:35:56 -0700827 status_builder.add_state(state_);
828 status_builder.add_is_collided(is_collided);
829
milind1f1dca32021-07-03 13:50:07 -0700830 (void)status->Send(status_builder.Finish());
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800831
Adam Snaider06779722016-02-14 15:26:22 -0800832 last_state_ = state_before_switch;
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800833}
834
Philipp Schrader3da48ac2016-03-06 23:03:44 +0000835constexpr double Superstructure::kZeroingVoltage;
836constexpr double Superstructure::kOperatingVoltage;
Austin Schuh6ca0f792016-03-12 14:06:14 -0800837constexpr double Superstructure::kLandingShoulderDownVoltage;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000838constexpr double Superstructure::kShoulderMiddleAngle;
839constexpr double Superstructure::kLooseTolerance;
840constexpr double Superstructure::kIntakeUpperClear;
841constexpr double Superstructure::kIntakeLowerClear;
842constexpr double Superstructure::kShoulderUpAngle;
843constexpr double Superstructure::kShoulderLanded;
844constexpr double Superstructure::kTightTolerance;
845constexpr double Superstructure::kWristAlmostLevel;
846constexpr double Superstructure::kShoulderWristClearAngle;
Austin Schuhb1d682b2016-02-16 13:07:44 -0800847constexpr double Superstructure::kShoulderTransitionToLanded;
Philipp Schrader0119eb12016-02-15 18:16:21 +0000848
Austin Schuh2fc10fa2016-02-08 00:44:34 -0800849} // namespace superstructure
Comran Morshed25f81a02016-01-23 13:40:10 +0000850} // namespace control_loops
851} // namespace y2016