blob: fc6aeeee5fcb8bd0ea3cc7a775cb3d49ac6483aa [file] [log] [blame]
milind-u086d7262022-01-19 20:44:18 -08001#include "y2022/control_loops/superstructure/superstructure.h"
2
3#include "aos/events/event_loop.h"
Ravago Jones3283ce02022-03-09 19:31:29 -08004#include "aos/flatbuffer_merge.h"
Milind Upadhyay4b248da2022-09-21 21:20:12 -07005#include "aos/network/team_number.h"
James Kuszmaul40526952022-03-13 15:56:38 -07006#include "frc971/zeroing/wrap.h"
Milind Upadhyay225156b2022-02-25 22:42:12 -08007#include "y2022/control_loops/superstructure/collision_avoidance.h"
milind-u086d7262022-01-19 20:44:18 -08008
James Kuszmaulb9ba9a52022-03-31 22:16:01 -07009DEFINE_bool(ignore_distance, false,
10 "If true, ignore distance when shooting and obay joystick_reader");
11
Stephan Pleinesf63bde82024-01-13 15:59:33 -080012namespace y2022::control_loops::superstructure {
milind-u086d7262022-01-19 20:44:18 -080013
14using frc971::control_loops::AbsoluteEncoderProfiledJointStatus;
15using frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus;
Henry Speiser55aa3ba2022-02-21 23:21:12 -080016using frc971::control_loops::RelativeEncoderProfiledJointStatus;
milind-u086d7262022-01-19 20:44:18 -080017
18Superstructure::Superstructure(::aos::EventLoop *event_loop,
Henry Speiser55aa3ba2022-02-21 23:21:12 -080019 std::shared_ptr<const constants::Values> values,
milind-u086d7262022-01-19 20:44:18 -080020 const ::std::string &name)
21 : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080022 name),
Austin Schuh39f26f62022-02-24 21:34:46 -080023 values_(values),
24 climber_(values_->climber.subsystem_params),
25 intake_front_(values_->intake_front.subsystem_params),
26 intake_back_(values_->intake_back.subsystem_params),
27 turret_(values_->turret.subsystem_params),
Ravago Jones5da06352022-03-04 20:26:24 -080028 catapult_(*values_),
Henry Speiser55aa3ba2022-02-21 23:21:12 -080029 drivetrain_status_fetcher_(
30 event_loop->MakeFetcher<frc971::control_loops::drivetrain::Status>(
Austin Schuh39f26f62022-02-24 21:34:46 -080031 "/drivetrain")),
Ravago Jones5da06352022-03-04 20:26:24 -080032 can_position_fetcher_(
James Kuszmaulb9ba9a52022-03-31 22:16:01 -070033 event_loop->MakeFetcher<CANPosition>("/superstructure")),
Ravago Jonesd51af7a2022-03-26 21:44:20 -070034 joystick_state_fetcher_(
35 event_loop->MakeFetcher<aos::JoystickState>("/aos")),
Milind Upadhyaye9075d12022-04-12 22:45:16 -070036 ball_color_fetcher_(
37 event_loop->MakeFetcher<y2022::vision::BallColor>("/superstructure")),
James Kuszmaulb9ba9a52022-03-31 22:16:01 -070038 aimer_(values) {
milind-u086d7262022-01-19 20:44:18 -080039 event_loop->SetRuntimeRealtimePriority(30);
40}
41
42void Superstructure::RunIteration(const Goal *unsafe_goal,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080043 const Position *position,
milind-u086d7262022-01-19 20:44:18 -080044 aos::Sender<Output>::Builder *output,
45 aos::Sender<Status>::Builder *status) {
46 if (WasReset()) {
47 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Henry Speiser55aa3ba2022-02-21 23:21:12 -080048 intake_front_.Reset();
49 intake_back_.Reset();
50 turret_.Reset();
51 climber_.Reset();
Austin Schuh39f26f62022-02-24 21:34:46 -080052 catapult_.Reset();
Henry Speiser55aa3ba2022-02-21 23:21:12 -080053 }
54
Ravago Jones5da06352022-03-04 20:26:24 -080055 OutputT output_struct;
Milind Upadhyay225156b2022-02-25 22:42:12 -080056
Ravago Jones5da06352022-03-04 20:26:24 -080057 aos::FlatbufferFixedAllocatorArray<
Austin Schuhfce2b602022-03-13 20:05:01 -070058 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 512>
James Kuszmaul40526952022-03-13 15:56:38 -070059 turret_loading_goal_buffer;
Austin Schuhfce2b602022-03-13 20:05:01 -070060 aos::FlatbufferFixedAllocatorArray<CatapultGoal, 512> catapult_goal_buffer;
Ravago Jonesd51af7a2022-03-26 21:44:20 -070061 aos::FlatbufferFixedAllocatorArray<CatapultGoal, 512>
62 catapult_discarding_goal_buffer;
Ravago Jones5da06352022-03-04 20:26:24 -080063
64 const aos::monotonic_clock::time_point timestamp =
65 event_loop()->context().monotonic_event_time;
Milind Upadhyay225156b2022-02-25 22:42:12 -080066
Ravago Jonesd51af7a2022-03-26 21:44:20 -070067 if (joystick_state_fetcher_.Fetch() &&
68 joystick_state_fetcher_->has_alliance()) {
69 alliance_ = joystick_state_fetcher_->alliance();
70 }
71
72 if (ball_color_fetcher_.Fetch() && ball_color_fetcher_->has_ball_color()) {
73 ball_color_ = ball_color_fetcher_->ball_color();
74 }
75
76 if (alliance_ != aos::Alliance::kInvalid &&
77 ball_color_ != aos::Alliance::kInvalid && alliance_ != ball_color_) {
78 switch (state_) {
79 case SuperstructureState::IDLE:
80 break;
81 case SuperstructureState::TRANSFERRING:
82 break;
83 case SuperstructureState::LOADING:
84 break;
85 case SuperstructureState::LOADED:
86 discarding_ball_ = true;
87 break;
88 case SuperstructureState::SHOOTING:
89 if (!fire_) {
90 // we can still tell it not to shoot into the hub
91 // and change the turret and catapult goals
92 discarding_ball_ = true;
93 }
94 break;
95 }
96 }
97
Henry Speiser55aa3ba2022-02-21 23:21:12 -080098 drivetrain_status_fetcher_.Fetch();
99 const float velocity = robot_velocity();
100
James Kuszmaul84083f42022-02-27 17:24:38 -0800101 const turret::Aimer::Goal *auto_aim_goal = nullptr;
102 if (drivetrain_status_fetcher_.get() != nullptr) {
103 aimer_.Update(drivetrain_status_fetcher_.get(),
104 turret::Aimer::ShotMode::kShootOnTheFly);
105 auto_aim_goal = aimer_.TurretGoal();
106 }
107
Ravago Jones5da06352022-03-04 20:26:24 -0800108 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
109 *turret_goal = nullptr;
Ravago Jones3283ce02022-03-09 19:31:29 -0800110 const CatapultGoal *catapult_goal = nullptr;
Ravago Jones5da06352022-03-04 20:26:24 -0800111 double roller_speed_compensated_front = 0.0;
112 double roller_speed_compensated_back = 0.0;
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700113 double transfer_roller_speed = 0.0;
Ravago Jones5da06352022-03-04 20:26:24 -0800114 double flipper_arms_voltage = 0.0;
James Kuszmaul40526952022-03-13 15:56:38 -0700115 bool have_active_intake_request = false;
milind-u6e7d8d42022-04-06 18:30:43 -0700116 bool climber_servo = false;
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800117
118 if (unsafe_goal != nullptr) {
119 roller_speed_compensated_front =
120 unsafe_goal->roller_speed_front() +
121 std::max(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
122
123 roller_speed_compensated_back =
124 unsafe_goal->roller_speed_back() -
125 std::min(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
126
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700127 transfer_roller_speed = unsafe_goal->transfer_roller_speed();
milind-u086d7262022-01-19 20:44:18 -0800128
milind-u6e7d8d42022-04-06 18:30:43 -0700129 climber_servo = unsafe_goal->climber_servo();
130
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700131 turret_goal = unsafe_goal->auto_aim() && !discarding_ball_
132 ? auto_aim_goal
133 : unsafe_goal->turret();
Ravago Jones3283ce02022-03-09 19:31:29 -0800134
135 catapult_goal = unsafe_goal->catapult();
136
137 constants::Values::ShotParams shot_params;
138 const double distance_to_goal = aimer_.DistanceToGoal();
James Kuszmaulb9ba9a52022-03-31 22:16:01 -0700139 if (!FLAGS_ignore_distance && unsafe_goal->auto_aim() &&
140 values_->shot_interpolation_table.GetInRange(distance_to_goal,
141 &shot_params)) {
Austin Schuhfce2b602022-03-13 20:05:01 -0700142 flatbuffers::FlatBufferBuilder *catapult_goal_fbb =
143 catapult_goal_buffer.fbb();
Ravago Jones3283ce02022-03-09 19:31:29 -0800144 std::optional<flatbuffers::Offset<
145 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal>>
146 return_position_offset;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700147 if (unsafe_goal->has_catapult() &&
Ravago Jones3283ce02022-03-09 19:31:29 -0800148 unsafe_goal->catapult()->has_return_position()) {
Austin Schuhfce2b602022-03-13 20:05:01 -0700149 return_position_offset = {aos::CopyFlatBuffer(
150 unsafe_goal->catapult()->return_position(), catapult_goal_fbb)};
Ravago Jones3283ce02022-03-09 19:31:29 -0800151 }
Austin Schuhfce2b602022-03-13 20:05:01 -0700152 CatapultGoal::Builder builder(*catapult_goal_fbb);
Ravago Jones3283ce02022-03-09 19:31:29 -0800153 builder.add_shot_position(shot_params.shot_angle);
154 builder.add_shot_velocity(shot_params.shot_velocity);
155 if (return_position_offset.has_value()) {
156 builder.add_return_position(return_position_offset.value());
157 }
158 catapult_goal_buffer.Finish(builder.Finish());
159 catapult_goal = &catapult_goal_buffer.message();
160 }
James Kuszmaul40526952022-03-13 15:56:38 -0700161
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700162 if (discarding_ball_) {
163 flatbuffers::FlatBufferBuilder *catapult_goal_fbb =
164 catapult_discarding_goal_buffer.fbb();
165 std::optional<flatbuffers::Offset<
166 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal>>
167 return_position_offset;
168 if (unsafe_goal->has_catapult() &&
169 unsafe_goal->catapult()->has_return_position()) {
170 return_position_offset = {aos::CopyFlatBuffer(
171 unsafe_goal->catapult()->return_position(), catapult_goal_fbb)};
172 }
173 CatapultGoal::Builder builder(*catapult_goal_fbb);
174 builder.add_shot_position(kDiscardingPosition);
175 builder.add_shot_velocity(kDiscardingVelocity);
176 if (return_position_offset.has_value()) {
177 builder.add_return_position(return_position_offset.value());
178 }
179 catapult_discarding_goal_buffer.Finish(builder.Finish());
180 catapult_goal = &catapult_discarding_goal_buffer.message();
181 }
182
James Kuszmaul40526952022-03-13 15:56:38 -0700183 if (unsafe_goal->has_turret_intake()) {
184 have_active_intake_request = true;
185 }
James Kuszmaul84083f42022-02-27 17:24:38 -0800186 }
Austin Schuh39f26f62022-02-24 21:34:46 -0800187
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800188 // Superstructure state machine:
Ravago Jones5da06352022-03-04 20:26:24 -0800189 // 1. IDLE: Wait until an intake beambreak is triggerred, meaning that a ball
190 // is being intaked. This means that the transfer rollers have a ball. If
191 // we've been waiting here for too long without any beambreak triggered, the
192 // ball got lost, so reset.
193 // 2. TRANSFERRING: Until the turret reaches the loading position where the
194 // ball can be transferred into the catapult, wiggle the ball in place.
195 // Once the turret reaches the loading position, send the ball forward with
196 // the transfer rollers until the turret beambreak is triggered.
197 // If we have been in this state for too long, the ball probably got lost so
198 // reset back to IDLE.
199 // 3. LOADING: To load the ball into the catapult, put the flippers at the
200 // feeding speed. Wait for a timeout, and then wait until the ball has gone
201 // past the turret beambreak and the flippers have stopped moving, meaning
202 // that the ball is fully loaded in the catapult.
203 // 4. LOADED: Wait until the user asks us to fire to transition to the
204 // shooting stage. If asked to cancel the shot, reset back to the IDLE state.
205 // 5. SHOOTING: Open the flippers to get ready for the shot. If they don't
206 // open quickly enough, try reseating the ball and going back to the LOADING
207 // stage, which moves the flippers in the opposite direction first.
208 // Now, hold the flippers open and wait until the turret has reached its
209 // aiming goal. Once the turret is ready, tell the catapult to fire.
210 // If the flippers move back for some reason now, it could damage the
211 // catapult, so estop it. Otherwise, wait until the catapult shoots a ball and
212 // goes back to its return position. We have now finished the shot, so return
213 // to IDLE.
214
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800215 // If we started off preloaded, skip to the loaded state.
216 // Make sure we weren't already there just in case.
217 if (unsafe_goal != nullptr && unsafe_goal->preloaded()) {
218 switch (state_) {
219 case SuperstructureState::IDLE:
220 case SuperstructureState::TRANSFERRING:
221 case SuperstructureState::LOADING:
222 state_ = SuperstructureState::LOADED;
223 loading_timer_ = timestamp;
224 break;
225 case SuperstructureState::LOADED:
226 case SuperstructureState::SHOOTING:
227 break;
228 }
229 }
230
James Kuszmaul40526952022-03-13 15:56:38 -0700231 if (position->intake_beambreak_front()) {
232 front_intake_has_ball_ = true;
233 front_intake_beambreak_timer_ = timestamp;
Ravago Jones5da06352022-03-04 20:26:24 -0800234 }
235
James Kuszmaul40526952022-03-13 15:56:38 -0700236 if (position->intake_beambreak_back()) {
237 back_intake_has_ball_ = true;
238 back_intake_beambreak_timer_ = timestamp;
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800239 }
240
Ravago Jonesf70107e2022-04-12 15:05:56 -0700241 // Check if we're either spitting or have lost the ball.
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700242 if ((transfer_roller_speed < 0.0 && front_intake_has_ball_) ||
James Kuszmaul40526952022-03-13 15:56:38 -0700243 timestamp >
244 front_intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
245 front_intake_has_ball_ = false;
246 }
247
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700248 if ((transfer_roller_speed > 0.0 && back_intake_has_ball_) ||
James Kuszmaul40526952022-03-13 15:56:38 -0700249 timestamp >
250 back_intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
251 back_intake_has_ball_ = false;
252 }
253
254 // Wiggle transfer rollers: send the ball back and forth while waiting
255 // for the turret or waiting for another shot to be completed
256 const double wiggle_voltage =
257 constants::Values::kTransferRollerWiggleVoltage();
258 if (front_intake_has_ball_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800259 roller_speed_compensated_front = 0.0;
James Kuszmaul40526952022-03-13 15:56:38 -0700260 if (position->intake_beambreak_front()) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700261 transfer_roller_speed = -wiggle_voltage;
Ravago Jones5da06352022-03-04 20:26:24 -0800262 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700263 transfer_roller_speed = wiggle_voltage;
Ravago Jones5da06352022-03-04 20:26:24 -0800264 }
265 }
266
James Kuszmaul40526952022-03-13 15:56:38 -0700267 if (back_intake_has_ball_) {
268 roller_speed_compensated_back = 0.0;
269 if (position->intake_beambreak_back()) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700270 transfer_roller_speed = wiggle_voltage;
James Kuszmaul40526952022-03-13 15:56:38 -0700271 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700272 transfer_roller_speed = -wiggle_voltage;
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800273 }
274 }
275
James Kuszmaul40526952022-03-13 15:56:38 -0700276 // Create the goal message for putting the turret in its loading position.
277 // This will then get used in the state machine for the states (IDLE and
278 // TRANSFERRING) that use it.
279 double turret_loading_position =
280 (turret_intake_state_ == RequestedIntake::kFront
281 ? constants::Values::kTurretFrontIntakePos()
282 : constants::Values::kTurretBackIntakePos());
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700283 if (transitioning_second_ball_) {
284 // Turn to the loading position as close to the current position as
285 // possible since we just aimed.
286 turret_loading_position =
287 turret_.estimated_position() +
288 aos::math::NormalizeAngle(turret_loading_position -
289 turret_.estimated_position());
290 }
291
292 if (!transitioning_second_ball_ ||
293 (turret_loading_position > values_->turret_range.upper ||
294 turret_loading_position < values_->turret_range.lower)) {
295 // Turn to the loading position as close to the middle of the range as
296 // possible. Do the unwraping before we have a ball so we don't have to
297 // unwrap to shoot if we aren't transitioning a second ball. If we are doing
298 // the second ball, we need to reset back to the middle of the range
299 turret_loading_position =
300 frc971::zeroing::Wrap(values_->turret_range.middle_soft(),
301 turret_loading_position, 2.0 * M_PI);
302 }
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800303
James Kuszmaul40526952022-03-13 15:56:38 -0700304 turret_loading_goal_buffer.Finish(
305 frc971::control_loops::CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
306 *turret_loading_goal_buffer.fbb(), turret_loading_position));
307
milind-uc63d0942022-04-15 12:07:42 -0700308 const bool catapult_near_return_position =
309 (unsafe_goal != nullptr && unsafe_goal->has_catapult() &&
310 unsafe_goal->catapult()->has_return_position() &&
311 std::abs(unsafe_goal->catapult()->return_position()->unsafe_goal() -
312 catapult_.estimated_position()) < kCatapultGoalThreshold);
313
Henry Speiser77747b72022-03-06 17:18:29 -0800314 const bool turret_near_goal =
315 turret_goal != nullptr &&
316 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
317 kTurretGoalThreshold;
318 const bool collided = collision_avoidance_.IsCollided(
319 {.intake_front_position = intake_front_.estimated_position(),
320 .intake_back_position = intake_back_.estimated_position(),
321 .turret_position = turret_.estimated_position(),
322 .shooting = true});
323
Henry Speisera9c49a22022-04-01 15:22:35 -0700324 // Dont shoot if the robot is moving faster than this
James Kuszmaulb9ba9a52022-03-31 22:16:01 -0700325 constexpr double kMaxShootSpeed = 2.7;
Henry Speisera9c49a22022-04-01 15:22:35 -0700326 const bool moving_too_fast = std::abs(robot_velocity()) > kMaxShootSpeed;
327
Ravago Jones5da06352022-03-04 20:26:24 -0800328 switch (state_) {
329 case SuperstructureState::IDLE: {
James Kuszmaul40526952022-03-13 15:56:38 -0700330 // Only change the turret's goal loading position when idle, to prevent us
331 // spinning the turret around when TRANSFERRING...
Milind Upadhyay90648532022-03-13 20:37:02 -0700332 if (have_active_intake_request) {
333 turret_intake_state_ = unsafe_goal->turret_intake();
334 }
James Kuszmaul40526952022-03-13 15:56:38 -0700335 if (front_intake_has_ball_ != back_intake_has_ball_) {
James Kuszmaul40526952022-03-13 15:56:38 -0700336 turret_intake_state_ = front_intake_has_ball_ ? RequestedIntake::kFront
337 : RequestedIntake::kBack;
338 }
339 // When IDLE with no specific intake button pressed, allow the goal
340 // message to override the intaking stuff.
Austin Schuh92908812022-03-27 14:14:05 -0700341 if (have_active_intake_request || (turret_goal == nullptr)) {
James Kuszmaul40526952022-03-13 15:56:38 -0700342 turret_goal = &turret_loading_goal_buffer.message();
Ravago Jones5da06352022-03-04 20:26:24 -0800343 }
344
James Kuszmaul40526952022-03-13 15:56:38 -0700345 if (!front_intake_has_ball_ && !back_intake_has_ball_) {
Austin Schuh92908812022-03-27 14:14:05 -0700346 last_shot_angle_ = std::nullopt;
Ravago Jones5da06352022-03-04 20:26:24 -0800347 break;
348 }
349
350 state_ = SuperstructureState::TRANSFERRING;
Ravago Jones5da06352022-03-04 20:26:24 -0800351 // Save the side the ball is on for later
352
353 break;
354 }
355 case SuperstructureState::TRANSFERRING: {
James Kuszmaul40526952022-03-13 15:56:38 -0700356 // If we've been transferring for too long, the ball probably got lost.
357 if ((turret_intake_state_ == RequestedIntake::kFront &&
358 !front_intake_has_ball_) ||
359 (turret_intake_state_ == RequestedIntake::kBack &&
360 !back_intake_has_ball_)) {
Ravago Jones5da06352022-03-04 20:26:24 -0800361 state_ = SuperstructureState::IDLE;
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700362 transitioning_second_ball_ = false;
Ravago Jones5da06352022-03-04 20:26:24 -0800363 break;
364 }
365
James Kuszmaul40526952022-03-13 15:56:38 -0700366 turret_goal = &turret_loading_goal_buffer.message();
Milind Upadhyay27d16d32022-04-16 13:41:45 -0700367 aimer_.UpdateTurretGoal(turret_loading_position);
James Kuszmaul40526952022-03-13 15:56:38 -0700368
Ravago Jones5da06352022-03-04 20:26:24 -0800369 const bool turret_near_goal =
370 std::abs(turret_.estimated_position() - turret_loading_position) <
milind-uc63d0942022-04-15 12:07:42 -0700371 kTurretGoalLoadingThreshold;
372 if (!turret_near_goal || !catapult_near_return_position) {
Ravago Jones5da06352022-03-04 20:26:24 -0800373 break; // Wait for turret to reach the chosen intake
374 }
375
376 // Transfer rollers and flipper arm belt on
James Kuszmaul40526952022-03-13 15:56:38 -0700377 if (turret_intake_state_ == RequestedIntake::kFront) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700378 transfer_roller_speed = constants::Values::kTransferRollerVoltage();
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800379 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700380 transfer_roller_speed = -constants::Values::kTransferRollerVoltage();
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800381 }
Ravago Jones5da06352022-03-04 20:26:24 -0800382 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
383
384 // Ball is in catapult
385 if (position->turret_beambreak()) {
James Kuszmaul40526952022-03-13 15:56:38 -0700386 if (turret_intake_state_ == RequestedIntake::kFront) {
387 front_intake_has_ball_ = false;
388 } else {
389 back_intake_has_ball_ = false;
390 }
Ravago Jones5da06352022-03-04 20:26:24 -0800391 state_ = SuperstructureState::LOADING;
392 loading_timer_ = timestamp;
393 }
394 break;
395 }
396 case SuperstructureState::LOADING: {
397 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
398
399 // Keep feeding for kExtraLoadingTime
400
Ravago Jones5da06352022-03-04 20:26:24 -0800401 // The ball should go past the turret beambreak to be loaded.
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800402 // If we got a CAN reading not too long ago, the flippers should have
403 // also stopped.
Milind Upadhyay47e0d032022-03-05 23:06:25 -0800404 if (position->turret_beambreak()) {
405 loading_timer_ = timestamp;
406 } else if (timestamp >
407 loading_timer_ + constants::Values::kExtraLoadingTime()) {
Ravago Jones5da06352022-03-04 20:26:24 -0800408 state_ = SuperstructureState::LOADED;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700409 // reset color and wait for a new one once we know the ball is in place
410 ball_color_ = aos::Alliance::kInvalid;
Ravago Jones5da06352022-03-04 20:26:24 -0800411 reseating_in_catapult_ = false;
412 }
413 break;
414 }
415 case SuperstructureState::LOADED: {
416 if (unsafe_goal != nullptr) {
Austin Schuh92908812022-03-27 14:14:05 -0700417 if (turret_goal == nullptr) {
418 if (last_shot_angle_) {
419 turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
420 *last_shot_angle_);
421 }
422 turret_goal = &turret_loading_goal_buffer.message();
423 }
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700424
Ravago Jones5da06352022-03-04 20:26:24 -0800425 if (unsafe_goal->cancel_shot()) {
426 // Cancel the shot process
427 state_ = SuperstructureState::IDLE;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700428 } else if (unsafe_goal->fire() || discarding_ball_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800429 // Start if we were asked to and the turret is at goal
430 state_ = SuperstructureState::SHOOTING;
431 prev_shot_count_ = catapult_.shot_count();
432
433 // Reset opening timeout
434 flipper_opening_start_time_ = timestamp;
milind-uc63d0942022-04-15 12:07:42 -0700435 loading_timer_ = timestamp;
Ravago Jones5da06352022-03-04 20:26:24 -0800436 }
437 }
438 break;
439 }
440 case SuperstructureState::SHOOTING: {
Austin Schuh92908812022-03-27 14:14:05 -0700441 if (turret_goal == nullptr) {
442 if (last_shot_angle_) {
443 turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
444 *last_shot_angle_);
445 }
446 turret_goal = &turret_loading_goal_buffer.message();
447 last_shot_angle_ = turret_goal->unsafe_goal();
448 } else {
449 last_shot_angle_ = std::nullopt;
450 }
451 const bool turret_near_goal =
452 turret_goal != nullptr &&
453 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
454 kTurretGoalThreshold;
455
milind-uee3b0012022-03-14 21:19:57 -0700456 // Don't open the flippers until the turret's ready: give them as little
Henry Speisera9c49a22022-04-01 15:22:35 -0700457 // time to get bumped as possible. Or moving to fast.
458 if (!turret_near_goal || collided || moving_too_fast) {
milind-uee3b0012022-03-14 21:19:57 -0700459 break;
460 }
461
Ravago Jones5da06352022-03-04 20:26:24 -0800462 // Opening flipper arms could fail, wait until they are open using their
463 // potentiometers (the member below is just named encoder).
464 // Be a little more lenient if the flippers were already open in case of
465 // noise or collisions.
466 const double flipper_open_position =
467 (flippers_open_ ? constants::Values::kReseatFlipperPosition()
468 : constants::Values::kFlipperOpenPosition());
milind-u37dc40b2022-03-08 19:51:27 -0800469
470 // TODO(milind): add left arm back once it's fixed
Ravago Jones5da06352022-03-04 20:26:24 -0800471 flippers_open_ =
Ravago Jones5da06352022-03-04 20:26:24 -0800472 position->flipper_arm_right()->encoder() >= flipper_open_position;
473
474 if (flippers_open_) {
475 // Hold at kFlipperHoldVoltage
476 flipper_arms_voltage = constants::Values::kFlipperHoldVoltage();
477 } else {
478 // Open at kFlipperOpenVoltage
479 flipper_arms_voltage = constants::Values::kFlipperOpenVoltage();
480 }
481
milind-uee3b0012022-03-14 21:19:57 -0700482 // There are two possible failures for the flippers:
483 // 1. They never open on time
484 // 2. They opened and we started firing, but we got bumped or something
485 // and they went back.
486 // If the flippers didn't open in a reasonable amount of time, try
487 // reseating the ball and reversing them.
488 if (!fire_ && !flippers_open_ &&
Ravago Jones5da06352022-03-04 20:26:24 -0800489 timestamp >
490 loading_timer_ + constants::Values::kFlipperOpeningTimeout()) {
491 // Reseat the ball and try again
492 state_ = SuperstructureState::LOADING;
493 loading_timer_ = timestamp;
494 reseating_in_catapult_ = true;
495 break;
496 }
Ravago Jones5da06352022-03-04 20:26:24 -0800497
Henry Speiser28288cc2022-03-09 22:59:24 -0800498 // If the turret reached the aiming goal and the catapult is safe to move
499 // up, fire!
500 if (flippers_open_ && turret_near_goal && !collided) {
Ravago Jones5da06352022-03-04 20:26:24 -0800501 fire_ = true;
502 }
503
Ravago Jones5da06352022-03-04 20:26:24 -0800504 // Once the shot is complete and the catapult is back to its return
505 // position, go back to IDLE
Milind Upadhyay27d16d32022-04-16 13:41:45 -0700506 if (catapult_.shot_count() > prev_shot_count_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800507 prev_shot_count_ = catapult_.shot_count();
508 fire_ = false;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700509 discarding_ball_ = false;
Ravago Jones5da06352022-03-04 20:26:24 -0800510 state_ = SuperstructureState::IDLE;
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700511 transitioning_second_ball_ =
512 (front_intake_has_ball_ || back_intake_has_ball_);
Ravago Jones5da06352022-03-04 20:26:24 -0800513 }
514
515 break;
516 }
517 }
518
519 collision_avoidance_.UpdateGoal(
520 {.intake_front_position = intake_front_.estimated_position(),
521 .intake_back_position = intake_back_.estimated_position(),
Henry Speiser28288cc2022-03-09 22:59:24 -0800522 .turret_position = turret_.estimated_position(),
milind-uc63d0942022-04-15 12:07:42 -0700523 .shooting = (state_ == SuperstructureState::SHOOTING) ||
524 !catapult_near_return_position},
Ravago Jones5da06352022-03-04 20:26:24 -0800525 turret_goal);
526
527 turret_.set_min_position(collision_avoidance_.min_turret_goal());
528 turret_.set_max_position(collision_avoidance_.max_turret_goal());
529 intake_front_.set_min_position(collision_avoidance_.min_intake_front_goal());
530 intake_front_.set_max_position(collision_avoidance_.max_intake_front_goal());
531 intake_back_.set_min_position(collision_avoidance_.min_intake_back_goal());
532 intake_back_.set_max_position(collision_avoidance_.max_intake_back_goal());
533
James Kuszmaul84083f42022-02-27 17:24:38 -0800534 const flatbuffers::Offset<AimerStatus> aimer_offset =
535 aimer_.PopulateStatus(status->fbb());
536
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800537 // Disable the catapult if we want to restart to prevent damage with
538 // flippers
Austin Schuh39f26f62022-02-24 21:34:46 -0800539 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh97410d72022-03-12 15:37:23 -0800540 catapult_status_offset = catapult_.Iterate(
Ravago Jones3283ce02022-03-09 19:31:29 -0800541 catapult_goal, position, robot_state().voltage_battery(),
Austin Schuh97410d72022-03-12 15:37:23 -0800542 output != nullptr && !catapult_.estopped()
543 ? &(output_struct.catapult_voltage)
544 : nullptr,
545 fire_, status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800546
Ravago Jones5da06352022-03-04 20:26:24 -0800547 const flatbuffers::Offset<RelativeEncoderProfiledJointStatus>
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800548 climber_status_offset = climber_.Iterate(
549 unsafe_goal != nullptr ? unsafe_goal->climber() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800550 position->climber(),
551 output != nullptr ? &output_struct.climber_voltage : nullptr,
552 status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800553
Ravago Jones5da06352022-03-04 20:26:24 -0800554 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800555 intake_status_offset_front = intake_front_.Iterate(
556 unsafe_goal != nullptr ? unsafe_goal->intake_front() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800557 position->intake_front(),
558 output != nullptr ? &output_struct.intake_voltage_front : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800559 status->fbb());
560
Ravago Jones5da06352022-03-04 20:26:24 -0800561 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800562 intake_status_offset_back = intake_back_.Iterate(
563 unsafe_goal != nullptr ? unsafe_goal->intake_back() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800564 position->intake_back(),
565 output != nullptr ? &output_struct.intake_voltage_back : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800566 status->fbb());
567
Ravago Jones5da06352022-03-04 20:26:24 -0800568 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh7f120362022-03-05 17:10:11 -0800569 turret_status_offset = turret_.Iterate(
570 turret_goal, position->turret(),
571 output != nullptr ? &output_struct.turret_voltage : nullptr,
572 status->fbb());
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800573
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800574 if (output != nullptr) {
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800575 output_struct.roller_voltage_front = roller_speed_compensated_front;
576 output_struct.roller_voltage_back = roller_speed_compensated_back;
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700577 output_struct.transfer_roller_voltage = transfer_roller_speed;
Ravago Jones5da06352022-03-04 20:26:24 -0800578 output_struct.flipper_arms_voltage = flipper_arms_voltage;
milind-u6e7d8d42022-04-06 18:30:43 -0700579 if (climber_servo) {
Austin Schuh677af7e2022-04-13 19:45:39 -0700580 output_struct.climber_servo_left = 0.5;
Milind Upadhyay4b248da2022-09-21 21:20:12 -0700581 output_struct.climber_servo_right =
582 (aos::network::GetTeamNumber() == constants::kCompTeamNumber ? 1.0
583 : 0.5);
milind-u6e7d8d42022-04-06 18:30:43 -0700584 } else {
585 output_struct.climber_servo_left = 1.0;
586 output_struct.climber_servo_right = 0.0;
587 }
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800588
milind-u086d7262022-01-19 20:44:18 -0800589 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
590 }
591
592 Status::Builder status_builder = status->MakeBuilder<Status>();
593
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800594 const bool zeroed = intake_front_.zeroed() && intake_back_.zeroed() &&
Ravago Jones5da06352022-03-04 20:26:24 -0800595 turret_.zeroed() && climber_.zeroed() &&
596 catapult_.zeroed();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800597 const bool estopped = intake_front_.estopped() || intake_back_.estopped() ||
Ravago Jones5da06352022-03-04 20:26:24 -0800598 turret_.estopped() || climber_.estopped() ||
599 catapult_.estopped();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800600
601 status_builder.add_zeroed(zeroed);
602 status_builder.add_estopped(estopped);
603
604 status_builder.add_intake_front(intake_status_offset_front);
605 status_builder.add_intake_back(intake_status_offset_back);
606 status_builder.add_turret(turret_status_offset);
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800607 status_builder.add_climber(climber_status_offset);
Ravago Jones5da06352022-03-04 20:26:24 -0800608
Austin Schuh39f26f62022-02-24 21:34:46 -0800609 status_builder.add_catapult(catapult_status_offset);
610 status_builder.add_solve_time(catapult_.solve_time());
Austin Schuh80fc2752022-02-25 13:33:56 -0800611 status_builder.add_shot_count(catapult_.shot_count());
Austin Schuh41472552022-03-13 18:09:41 -0700612 status_builder.add_mpc_horizon(catapult_.mpc_horizon());
Ravago Jones3283ce02022-03-09 19:31:29 -0800613 if (catapult_goal != nullptr) {
614 status_builder.add_shot_position(catapult_goal->shot_position());
615 status_builder.add_shot_velocity(catapult_goal->shot_velocity());
616 }
Ravago Jones5da06352022-03-04 20:26:24 -0800617
618 status_builder.add_flippers_open(flippers_open_);
619 status_builder.add_reseating_in_catapult(reseating_in_catapult_);
620 status_builder.add_fire(fire_);
Henry Speisera9c49a22022-04-01 15:22:35 -0700621 status_builder.add_moving_too_fast(moving_too_fast);
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700622 status_builder.add_discarding_ball(discarding_ball_);
Milind Upadhyay9d68b132022-04-01 10:58:18 -0700623 status_builder.add_collided(collided);
Henry Speiser77747b72022-03-06 17:18:29 -0800624 status_builder.add_ready_to_fire(state_ == SuperstructureState::LOADED &&
625 turret_near_goal && !collided);
Ravago Jones5da06352022-03-04 20:26:24 -0800626 status_builder.add_state(state_);
James Kuszmaul40526952022-03-13 15:56:38 -0700627 if (!front_intake_has_ball_ && !back_intake_has_ball_) {
628 status_builder.add_intake_state(IntakeState::NO_BALL);
629 } else if (front_intake_has_ball_ && back_intake_has_ball_) {
630 status_builder.add_intake_state(turret_intake_state_ ==
631 RequestedIntake::kFront
632 ? IntakeState::INTAKE_FRONT_BALL
633 : IntakeState::INTAKE_BACK_BALL);
634 } else {
635 status_builder.add_intake_state(front_intake_has_ball_
636 ? IntakeState::INTAKE_FRONT_BALL
637 : IntakeState::INTAKE_BACK_BALL);
638 }
639 status_builder.add_front_intake_has_ball(front_intake_has_ball_);
640 status_builder.add_back_intake_has_ball(back_intake_has_ball_);
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700641 status_builder.add_transitioning_second_ball(transitioning_second_ball_);
milind-u086d7262022-01-19 20:44:18 -0800642
James Kuszmaul84083f42022-02-27 17:24:38 -0800643 status_builder.add_aimer(aimer_offset);
644
milind-u086d7262022-01-19 20:44:18 -0800645 (void)status->Send(status_builder.Finish());
646}
647
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800648double Superstructure::robot_velocity() const {
649 return (drivetrain_status_fetcher_.get() != nullptr
650 ? drivetrain_status_fetcher_->robot_speed()
651 : 0.0);
652}
653
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800654} // namespace y2022::control_loops::superstructure