blob: b5cb3f34e8954b5233555afb7a659f7e482ad280 [file] [log] [blame]
milind-u086d7262022-01-19 20:44:18 -08001#include "y2022/control_loops/superstructure/superstructure.h"
2
Austin Schuh99f7c6a2024-06-25 22:07:44 -07003#include "absl/flags/flag.h"
4
milind-u086d7262022-01-19 20:44:18 -08005#include "aos/events/event_loop.h"
Ravago Jones3283ce02022-03-09 19:31:29 -08006#include "aos/flatbuffer_merge.h"
Milind Upadhyay4b248da2022-09-21 21:20:12 -07007#include "aos/network/team_number.h"
James Kuszmaul40526952022-03-13 15:56:38 -07008#include "frc971/zeroing/wrap.h"
Milind Upadhyay225156b2022-02-25 22:42:12 -08009#include "y2022/control_loops/superstructure/collision_avoidance.h"
milind-u086d7262022-01-19 20:44:18 -080010
Austin Schuh99f7c6a2024-06-25 22:07:44 -070011ABSL_FLAG(bool, ignore_distance, false,
12 "If true, ignore distance when shooting and obay joystick_reader");
James Kuszmaulb9ba9a52022-03-31 22:16:01 -070013
Stephan Pleinesf63bde82024-01-13 15:59:33 -080014namespace y2022::control_loops::superstructure {
milind-u086d7262022-01-19 20:44:18 -080015
16using frc971::control_loops::AbsoluteEncoderProfiledJointStatus;
17using frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus;
Henry Speiser55aa3ba2022-02-21 23:21:12 -080018using frc971::control_loops::RelativeEncoderProfiledJointStatus;
milind-u086d7262022-01-19 20:44:18 -080019
20Superstructure::Superstructure(::aos::EventLoop *event_loop,
Henry Speiser55aa3ba2022-02-21 23:21:12 -080021 std::shared_ptr<const constants::Values> values,
milind-u086d7262022-01-19 20:44:18 -080022 const ::std::string &name)
23 : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080024 name),
Austin Schuh39f26f62022-02-24 21:34:46 -080025 values_(values),
26 climber_(values_->climber.subsystem_params),
27 intake_front_(values_->intake_front.subsystem_params),
28 intake_back_(values_->intake_back.subsystem_params),
29 turret_(values_->turret.subsystem_params),
Nathan Leongdd728002024-02-03 15:26:53 -080030 catapult_(values_->catapult.subsystem_params,
31 catapult::MakeCatapultPlant()),
Henry Speiser55aa3ba2022-02-21 23:21:12 -080032 drivetrain_status_fetcher_(
33 event_loop->MakeFetcher<frc971::control_loops::drivetrain::Status>(
Austin Schuh39f26f62022-02-24 21:34:46 -080034 "/drivetrain")),
Ravago Jones5da06352022-03-04 20:26:24 -080035 can_position_fetcher_(
James Kuszmaulb9ba9a52022-03-31 22:16:01 -070036 event_loop->MakeFetcher<CANPosition>("/superstructure")),
Ravago Jonesd51af7a2022-03-26 21:44:20 -070037 joystick_state_fetcher_(
38 event_loop->MakeFetcher<aos::JoystickState>("/aos")),
Milind Upadhyaye9075d12022-04-12 22:45:16 -070039 ball_color_fetcher_(
40 event_loop->MakeFetcher<y2022::vision::BallColor>("/superstructure")),
James Kuszmaulb9ba9a52022-03-31 22:16:01 -070041 aimer_(values) {
milind-u086d7262022-01-19 20:44:18 -080042 event_loop->SetRuntimeRealtimePriority(30);
43}
44
45void Superstructure::RunIteration(const Goal *unsafe_goal,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080046 const Position *position,
milind-u086d7262022-01-19 20:44:18 -080047 aos::Sender<Output>::Builder *output,
48 aos::Sender<Status>::Builder *status) {
49 if (WasReset()) {
50 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Henry Speiser55aa3ba2022-02-21 23:21:12 -080051 intake_front_.Reset();
52 intake_back_.Reset();
53 turret_.Reset();
54 climber_.Reset();
Austin Schuh39f26f62022-02-24 21:34:46 -080055 catapult_.Reset();
Henry Speiser55aa3ba2022-02-21 23:21:12 -080056 }
57
Ravago Jones5da06352022-03-04 20:26:24 -080058 OutputT output_struct;
Milind Upadhyay225156b2022-02-25 22:42:12 -080059
Ravago Jones5da06352022-03-04 20:26:24 -080060 aos::FlatbufferFixedAllocatorArray<
Austin Schuhfce2b602022-03-13 20:05:01 -070061 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 512>
James Kuszmaul40526952022-03-13 15:56:38 -070062 turret_loading_goal_buffer;
Nathan Leongdd728002024-02-03 15:26:53 -080063 aos::FlatbufferFixedAllocatorArray<
64 frc971::control_loops::catapult::CatapultGoal, 512>
65 catapult_goal_buffer;
66 aos::FlatbufferFixedAllocatorArray<
67 frc971::control_loops::catapult::CatapultGoal, 512>
Ravago Jonesd51af7a2022-03-26 21:44:20 -070068 catapult_discarding_goal_buffer;
Ravago Jones5da06352022-03-04 20:26:24 -080069
70 const aos::monotonic_clock::time_point timestamp =
71 event_loop()->context().monotonic_event_time;
Milind Upadhyay225156b2022-02-25 22:42:12 -080072
Ravago Jonesd51af7a2022-03-26 21:44:20 -070073 if (joystick_state_fetcher_.Fetch() &&
74 joystick_state_fetcher_->has_alliance()) {
75 alliance_ = joystick_state_fetcher_->alliance();
76 }
77
78 if (ball_color_fetcher_.Fetch() && ball_color_fetcher_->has_ball_color()) {
79 ball_color_ = ball_color_fetcher_->ball_color();
80 }
81
82 if (alliance_ != aos::Alliance::kInvalid &&
83 ball_color_ != aos::Alliance::kInvalid && alliance_ != ball_color_) {
84 switch (state_) {
85 case SuperstructureState::IDLE:
86 break;
87 case SuperstructureState::TRANSFERRING:
88 break;
89 case SuperstructureState::LOADING:
90 break;
91 case SuperstructureState::LOADED:
92 discarding_ball_ = true;
93 break;
94 case SuperstructureState::SHOOTING:
95 if (!fire_) {
96 // we can still tell it not to shoot into the hub
97 // and change the turret and catapult goals
98 discarding_ball_ = true;
99 }
100 break;
101 }
102 }
103
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800104 drivetrain_status_fetcher_.Fetch();
105 const float velocity = robot_velocity();
106
James Kuszmaul84083f42022-02-27 17:24:38 -0800107 const turret::Aimer::Goal *auto_aim_goal = nullptr;
108 if (drivetrain_status_fetcher_.get() != nullptr) {
109 aimer_.Update(drivetrain_status_fetcher_.get(),
110 turret::Aimer::ShotMode::kShootOnTheFly);
111 auto_aim_goal = aimer_.TurretGoal();
112 }
113
Ravago Jones5da06352022-03-04 20:26:24 -0800114 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
115 *turret_goal = nullptr;
Nathan Leongdd728002024-02-03 15:26:53 -0800116 const frc971::control_loops::catapult::CatapultGoal *catapult_goal = nullptr;
Ravago Jones5da06352022-03-04 20:26:24 -0800117 double roller_speed_compensated_front = 0.0;
118 double roller_speed_compensated_back = 0.0;
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700119 double transfer_roller_speed = 0.0;
Ravago Jones5da06352022-03-04 20:26:24 -0800120 double flipper_arms_voltage = 0.0;
James Kuszmaul40526952022-03-13 15:56:38 -0700121 bool have_active_intake_request = false;
milind-u6e7d8d42022-04-06 18:30:43 -0700122 bool climber_servo = false;
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800123
124 if (unsafe_goal != nullptr) {
125 roller_speed_compensated_front =
126 unsafe_goal->roller_speed_front() +
127 std::max(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
128
129 roller_speed_compensated_back =
130 unsafe_goal->roller_speed_back() -
131 std::min(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
132
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700133 transfer_roller_speed = unsafe_goal->transfer_roller_speed();
milind-u086d7262022-01-19 20:44:18 -0800134
milind-u6e7d8d42022-04-06 18:30:43 -0700135 climber_servo = unsafe_goal->climber_servo();
136
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700137 turret_goal = unsafe_goal->auto_aim() && !discarding_ball_
138 ? auto_aim_goal
139 : unsafe_goal->turret();
Ravago Jones3283ce02022-03-09 19:31:29 -0800140
141 catapult_goal = unsafe_goal->catapult();
142
143 constants::Values::ShotParams shot_params;
144 const double distance_to_goal = aimer_.DistanceToGoal();
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700145 if (!absl::GetFlag(FLAGS_ignore_distance) && unsafe_goal->auto_aim() &&
James Kuszmaulb9ba9a52022-03-31 22:16:01 -0700146 values_->shot_interpolation_table.GetInRange(distance_to_goal,
147 &shot_params)) {
Austin Schuhfce2b602022-03-13 20:05:01 -0700148 flatbuffers::FlatBufferBuilder *catapult_goal_fbb =
149 catapult_goal_buffer.fbb();
Ravago Jones3283ce02022-03-09 19:31:29 -0800150 std::optional<flatbuffers::Offset<
151 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal>>
152 return_position_offset;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700153 if (unsafe_goal->has_catapult() &&
Ravago Jones3283ce02022-03-09 19:31:29 -0800154 unsafe_goal->catapult()->has_return_position()) {
Austin Schuhfce2b602022-03-13 20:05:01 -0700155 return_position_offset = {aos::CopyFlatBuffer(
156 unsafe_goal->catapult()->return_position(), catapult_goal_fbb)};
Ravago Jones3283ce02022-03-09 19:31:29 -0800157 }
Nathan Leongdd728002024-02-03 15:26:53 -0800158 frc971::control_loops::catapult::CatapultGoal::Builder builder(
159 *catapult_goal_fbb);
Ravago Jones3283ce02022-03-09 19:31:29 -0800160 builder.add_shot_position(shot_params.shot_angle);
161 builder.add_shot_velocity(shot_params.shot_velocity);
162 if (return_position_offset.has_value()) {
163 builder.add_return_position(return_position_offset.value());
164 }
165 catapult_goal_buffer.Finish(builder.Finish());
166 catapult_goal = &catapult_goal_buffer.message();
167 }
James Kuszmaul40526952022-03-13 15:56:38 -0700168
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700169 if (discarding_ball_) {
170 flatbuffers::FlatBufferBuilder *catapult_goal_fbb =
171 catapult_discarding_goal_buffer.fbb();
172 std::optional<flatbuffers::Offset<
173 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal>>
174 return_position_offset;
175 if (unsafe_goal->has_catapult() &&
176 unsafe_goal->catapult()->has_return_position()) {
177 return_position_offset = {aos::CopyFlatBuffer(
178 unsafe_goal->catapult()->return_position(), catapult_goal_fbb)};
179 }
Nathan Leongdd728002024-02-03 15:26:53 -0800180 frc971::control_loops::catapult::CatapultGoal::Builder builder(
181 *catapult_goal_fbb);
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700182 builder.add_shot_position(kDiscardingPosition);
183 builder.add_shot_velocity(kDiscardingVelocity);
184 if (return_position_offset.has_value()) {
185 builder.add_return_position(return_position_offset.value());
186 }
187 catapult_discarding_goal_buffer.Finish(builder.Finish());
188 catapult_goal = &catapult_discarding_goal_buffer.message();
189 }
190
James Kuszmaul40526952022-03-13 15:56:38 -0700191 if (unsafe_goal->has_turret_intake()) {
192 have_active_intake_request = true;
193 }
James Kuszmaul84083f42022-02-27 17:24:38 -0800194 }
Austin Schuh39f26f62022-02-24 21:34:46 -0800195
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800196 // Superstructure state machine:
Ravago Jones5da06352022-03-04 20:26:24 -0800197 // 1. IDLE: Wait until an intake beambreak is triggerred, meaning that a ball
198 // is being intaked. This means that the transfer rollers have a ball. If
199 // we've been waiting here for too long without any beambreak triggered, the
200 // ball got lost, so reset.
201 // 2. TRANSFERRING: Until the turret reaches the loading position where the
202 // ball can be transferred into the catapult, wiggle the ball in place.
203 // Once the turret reaches the loading position, send the ball forward with
204 // the transfer rollers until the turret beambreak is triggered.
205 // If we have been in this state for too long, the ball probably got lost so
206 // reset back to IDLE.
207 // 3. LOADING: To load the ball into the catapult, put the flippers at the
208 // feeding speed. Wait for a timeout, and then wait until the ball has gone
209 // past the turret beambreak and the flippers have stopped moving, meaning
210 // that the ball is fully loaded in the catapult.
211 // 4. LOADED: Wait until the user asks us to fire to transition to the
212 // shooting stage. If asked to cancel the shot, reset back to the IDLE state.
213 // 5. SHOOTING: Open the flippers to get ready for the shot. If they don't
214 // open quickly enough, try reseating the ball and going back to the LOADING
215 // stage, which moves the flippers in the opposite direction first.
216 // Now, hold the flippers open and wait until the turret has reached its
217 // aiming goal. Once the turret is ready, tell the catapult to fire.
218 // If the flippers move back for some reason now, it could damage the
219 // catapult, so estop it. Otherwise, wait until the catapult shoots a ball and
220 // goes back to its return position. We have now finished the shot, so return
221 // to IDLE.
222
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800223 // If we started off preloaded, skip to the loaded state.
224 // Make sure we weren't already there just in case.
225 if (unsafe_goal != nullptr && unsafe_goal->preloaded()) {
226 switch (state_) {
227 case SuperstructureState::IDLE:
228 case SuperstructureState::TRANSFERRING:
229 case SuperstructureState::LOADING:
230 state_ = SuperstructureState::LOADED;
231 loading_timer_ = timestamp;
232 break;
233 case SuperstructureState::LOADED:
234 case SuperstructureState::SHOOTING:
235 break;
236 }
237 }
238
James Kuszmaul40526952022-03-13 15:56:38 -0700239 if (position->intake_beambreak_front()) {
240 front_intake_has_ball_ = true;
241 front_intake_beambreak_timer_ = timestamp;
Ravago Jones5da06352022-03-04 20:26:24 -0800242 }
243
James Kuszmaul40526952022-03-13 15:56:38 -0700244 if (position->intake_beambreak_back()) {
245 back_intake_has_ball_ = true;
246 back_intake_beambreak_timer_ = timestamp;
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800247 }
248
Ravago Jonesf70107e2022-04-12 15:05:56 -0700249 // Check if we're either spitting or have lost the ball.
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700250 if ((transfer_roller_speed < 0.0 && front_intake_has_ball_) ||
James Kuszmaul40526952022-03-13 15:56:38 -0700251 timestamp >
252 front_intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
253 front_intake_has_ball_ = false;
254 }
255
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700256 if ((transfer_roller_speed > 0.0 && back_intake_has_ball_) ||
James Kuszmaul40526952022-03-13 15:56:38 -0700257 timestamp >
258 back_intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
259 back_intake_has_ball_ = false;
260 }
261
262 // Wiggle transfer rollers: send the ball back and forth while waiting
263 // for the turret or waiting for another shot to be completed
264 const double wiggle_voltage =
265 constants::Values::kTransferRollerWiggleVoltage();
266 if (front_intake_has_ball_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800267 roller_speed_compensated_front = 0.0;
James Kuszmaul40526952022-03-13 15:56:38 -0700268 if (position->intake_beambreak_front()) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700269 transfer_roller_speed = -wiggle_voltage;
Ravago Jones5da06352022-03-04 20:26:24 -0800270 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700271 transfer_roller_speed = wiggle_voltage;
Ravago Jones5da06352022-03-04 20:26:24 -0800272 }
273 }
274
James Kuszmaul40526952022-03-13 15:56:38 -0700275 if (back_intake_has_ball_) {
276 roller_speed_compensated_back = 0.0;
277 if (position->intake_beambreak_back()) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700278 transfer_roller_speed = wiggle_voltage;
James Kuszmaul40526952022-03-13 15:56:38 -0700279 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700280 transfer_roller_speed = -wiggle_voltage;
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800281 }
282 }
283
James Kuszmaul40526952022-03-13 15:56:38 -0700284 // Create the goal message for putting the turret in its loading position.
285 // This will then get used in the state machine for the states (IDLE and
286 // TRANSFERRING) that use it.
287 double turret_loading_position =
288 (turret_intake_state_ == RequestedIntake::kFront
289 ? constants::Values::kTurretFrontIntakePos()
290 : constants::Values::kTurretBackIntakePos());
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700291 if (transitioning_second_ball_) {
292 // Turn to the loading position as close to the current position as
293 // possible since we just aimed.
294 turret_loading_position =
295 turret_.estimated_position() +
296 aos::math::NormalizeAngle(turret_loading_position -
297 turret_.estimated_position());
298 }
299
300 if (!transitioning_second_ball_ ||
301 (turret_loading_position > values_->turret_range.upper ||
302 turret_loading_position < values_->turret_range.lower)) {
303 // Turn to the loading position as close to the middle of the range as
304 // possible. Do the unwraping before we have a ball so we don't have to
305 // unwrap to shoot if we aren't transitioning a second ball. If we are doing
306 // the second ball, we need to reset back to the middle of the range
307 turret_loading_position =
308 frc971::zeroing::Wrap(values_->turret_range.middle_soft(),
309 turret_loading_position, 2.0 * M_PI);
310 }
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800311
James Kuszmaul40526952022-03-13 15:56:38 -0700312 turret_loading_goal_buffer.Finish(
313 frc971::control_loops::CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
314 *turret_loading_goal_buffer.fbb(), turret_loading_position));
315
milind-uc63d0942022-04-15 12:07:42 -0700316 const bool catapult_near_return_position =
317 (unsafe_goal != nullptr && unsafe_goal->has_catapult() &&
318 unsafe_goal->catapult()->has_return_position() &&
319 std::abs(unsafe_goal->catapult()->return_position()->unsafe_goal() -
320 catapult_.estimated_position()) < kCatapultGoalThreshold);
321
Henry Speiser77747b72022-03-06 17:18:29 -0800322 const bool turret_near_goal =
323 turret_goal != nullptr &&
324 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
325 kTurretGoalThreshold;
326 const bool collided = collision_avoidance_.IsCollided(
327 {.intake_front_position = intake_front_.estimated_position(),
328 .intake_back_position = intake_back_.estimated_position(),
329 .turret_position = turret_.estimated_position(),
330 .shooting = true});
331
Henry Speisera9c49a22022-04-01 15:22:35 -0700332 // Dont shoot if the robot is moving faster than this
James Kuszmaulb9ba9a52022-03-31 22:16:01 -0700333 constexpr double kMaxShootSpeed = 2.7;
Henry Speisera9c49a22022-04-01 15:22:35 -0700334 const bool moving_too_fast = std::abs(robot_velocity()) > kMaxShootSpeed;
335
Ravago Jones5da06352022-03-04 20:26:24 -0800336 switch (state_) {
337 case SuperstructureState::IDLE: {
James Kuszmaul40526952022-03-13 15:56:38 -0700338 // Only change the turret's goal loading position when idle, to prevent us
339 // spinning the turret around when TRANSFERRING...
Milind Upadhyay90648532022-03-13 20:37:02 -0700340 if (have_active_intake_request) {
341 turret_intake_state_ = unsafe_goal->turret_intake();
342 }
James Kuszmaul40526952022-03-13 15:56:38 -0700343 if (front_intake_has_ball_ != back_intake_has_ball_) {
James Kuszmaul40526952022-03-13 15:56:38 -0700344 turret_intake_state_ = front_intake_has_ball_ ? RequestedIntake::kFront
345 : RequestedIntake::kBack;
346 }
347 // When IDLE with no specific intake button pressed, allow the goal
348 // message to override the intaking stuff.
Austin Schuh92908812022-03-27 14:14:05 -0700349 if (have_active_intake_request || (turret_goal == nullptr)) {
James Kuszmaul40526952022-03-13 15:56:38 -0700350 turret_goal = &turret_loading_goal_buffer.message();
Ravago Jones5da06352022-03-04 20:26:24 -0800351 }
352
James Kuszmaul40526952022-03-13 15:56:38 -0700353 if (!front_intake_has_ball_ && !back_intake_has_ball_) {
Austin Schuh92908812022-03-27 14:14:05 -0700354 last_shot_angle_ = std::nullopt;
Ravago Jones5da06352022-03-04 20:26:24 -0800355 break;
356 }
357
358 state_ = SuperstructureState::TRANSFERRING;
Ravago Jones5da06352022-03-04 20:26:24 -0800359 // Save the side the ball is on for later
360
361 break;
362 }
363 case SuperstructureState::TRANSFERRING: {
James Kuszmaul40526952022-03-13 15:56:38 -0700364 // If we've been transferring for too long, the ball probably got lost.
365 if ((turret_intake_state_ == RequestedIntake::kFront &&
366 !front_intake_has_ball_) ||
367 (turret_intake_state_ == RequestedIntake::kBack &&
368 !back_intake_has_ball_)) {
Ravago Jones5da06352022-03-04 20:26:24 -0800369 state_ = SuperstructureState::IDLE;
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700370 transitioning_second_ball_ = false;
Ravago Jones5da06352022-03-04 20:26:24 -0800371 break;
372 }
373
James Kuszmaul40526952022-03-13 15:56:38 -0700374 turret_goal = &turret_loading_goal_buffer.message();
Milind Upadhyay27d16d32022-04-16 13:41:45 -0700375 aimer_.UpdateTurretGoal(turret_loading_position);
James Kuszmaul40526952022-03-13 15:56:38 -0700376
Ravago Jones5da06352022-03-04 20:26:24 -0800377 const bool turret_near_goal =
378 std::abs(turret_.estimated_position() - turret_loading_position) <
milind-uc63d0942022-04-15 12:07:42 -0700379 kTurretGoalLoadingThreshold;
380 if (!turret_near_goal || !catapult_near_return_position) {
Ravago Jones5da06352022-03-04 20:26:24 -0800381 break; // Wait for turret to reach the chosen intake
382 }
383
384 // Transfer rollers and flipper arm belt on
James Kuszmaul40526952022-03-13 15:56:38 -0700385 if (turret_intake_state_ == RequestedIntake::kFront) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700386 transfer_roller_speed = constants::Values::kTransferRollerVoltage();
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800387 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700388 transfer_roller_speed = -constants::Values::kTransferRollerVoltage();
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800389 }
Ravago Jones5da06352022-03-04 20:26:24 -0800390 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
391
392 // Ball is in catapult
393 if (position->turret_beambreak()) {
James Kuszmaul40526952022-03-13 15:56:38 -0700394 if (turret_intake_state_ == RequestedIntake::kFront) {
395 front_intake_has_ball_ = false;
396 } else {
397 back_intake_has_ball_ = false;
398 }
Ravago Jones5da06352022-03-04 20:26:24 -0800399 state_ = SuperstructureState::LOADING;
400 loading_timer_ = timestamp;
401 }
402 break;
403 }
404 case SuperstructureState::LOADING: {
405 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
406
407 // Keep feeding for kExtraLoadingTime
408
Ravago Jones5da06352022-03-04 20:26:24 -0800409 // The ball should go past the turret beambreak to be loaded.
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800410 // If we got a CAN reading not too long ago, the flippers should have
411 // also stopped.
Milind Upadhyay47e0d032022-03-05 23:06:25 -0800412 if (position->turret_beambreak()) {
413 loading_timer_ = timestamp;
414 } else if (timestamp >
415 loading_timer_ + constants::Values::kExtraLoadingTime()) {
Ravago Jones5da06352022-03-04 20:26:24 -0800416 state_ = SuperstructureState::LOADED;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700417 // reset color and wait for a new one once we know the ball is in place
418 ball_color_ = aos::Alliance::kInvalid;
Ravago Jones5da06352022-03-04 20:26:24 -0800419 reseating_in_catapult_ = false;
420 }
421 break;
422 }
423 case SuperstructureState::LOADED: {
424 if (unsafe_goal != nullptr) {
Austin Schuh92908812022-03-27 14:14:05 -0700425 if (turret_goal == nullptr) {
426 if (last_shot_angle_) {
427 turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
428 *last_shot_angle_);
429 }
430 turret_goal = &turret_loading_goal_buffer.message();
431 }
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700432
Ravago Jones5da06352022-03-04 20:26:24 -0800433 if (unsafe_goal->cancel_shot()) {
434 // Cancel the shot process
435 state_ = SuperstructureState::IDLE;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700436 } else if (unsafe_goal->fire() || discarding_ball_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800437 // Start if we were asked to and the turret is at goal
438 state_ = SuperstructureState::SHOOTING;
439 prev_shot_count_ = catapult_.shot_count();
440
441 // Reset opening timeout
442 flipper_opening_start_time_ = timestamp;
milind-uc63d0942022-04-15 12:07:42 -0700443 loading_timer_ = timestamp;
Ravago Jones5da06352022-03-04 20:26:24 -0800444 }
445 }
446 break;
447 }
448 case SuperstructureState::SHOOTING: {
Austin Schuh92908812022-03-27 14:14:05 -0700449 if (turret_goal == nullptr) {
450 if (last_shot_angle_) {
451 turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
452 *last_shot_angle_);
453 }
454 turret_goal = &turret_loading_goal_buffer.message();
455 last_shot_angle_ = turret_goal->unsafe_goal();
456 } else {
457 last_shot_angle_ = std::nullopt;
458 }
459 const bool turret_near_goal =
460 turret_goal != nullptr &&
461 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
462 kTurretGoalThreshold;
463
milind-uee3b0012022-03-14 21:19:57 -0700464 // Don't open the flippers until the turret's ready: give them as little
Henry Speisera9c49a22022-04-01 15:22:35 -0700465 // time to get bumped as possible. Or moving to fast.
466 if (!turret_near_goal || collided || moving_too_fast) {
milind-uee3b0012022-03-14 21:19:57 -0700467 break;
468 }
469
Ravago Jones5da06352022-03-04 20:26:24 -0800470 // Opening flipper arms could fail, wait until they are open using their
471 // potentiometers (the member below is just named encoder).
472 // Be a little more lenient if the flippers were already open in case of
473 // noise or collisions.
474 const double flipper_open_position =
475 (flippers_open_ ? constants::Values::kReseatFlipperPosition()
476 : constants::Values::kFlipperOpenPosition());
milind-u37dc40b2022-03-08 19:51:27 -0800477
478 // TODO(milind): add left arm back once it's fixed
Ravago Jones5da06352022-03-04 20:26:24 -0800479 flippers_open_ =
Ravago Jones5da06352022-03-04 20:26:24 -0800480 position->flipper_arm_right()->encoder() >= flipper_open_position;
481
482 if (flippers_open_) {
483 // Hold at kFlipperHoldVoltage
484 flipper_arms_voltage = constants::Values::kFlipperHoldVoltage();
485 } else {
486 // Open at kFlipperOpenVoltage
487 flipper_arms_voltage = constants::Values::kFlipperOpenVoltage();
488 }
489
milind-uee3b0012022-03-14 21:19:57 -0700490 // There are two possible failures for the flippers:
491 // 1. They never open on time
492 // 2. They opened and we started firing, but we got bumped or something
493 // and they went back.
494 // If the flippers didn't open in a reasonable amount of time, try
495 // reseating the ball and reversing them.
496 if (!fire_ && !flippers_open_ &&
Ravago Jones5da06352022-03-04 20:26:24 -0800497 timestamp >
498 loading_timer_ + constants::Values::kFlipperOpeningTimeout()) {
499 // Reseat the ball and try again
500 state_ = SuperstructureState::LOADING;
501 loading_timer_ = timestamp;
502 reseating_in_catapult_ = true;
503 break;
504 }
Ravago Jones5da06352022-03-04 20:26:24 -0800505
Henry Speiser28288cc2022-03-09 22:59:24 -0800506 // If the turret reached the aiming goal and the catapult is safe to move
507 // up, fire!
508 if (flippers_open_ && turret_near_goal && !collided) {
Ravago Jones5da06352022-03-04 20:26:24 -0800509 fire_ = true;
510 }
511
Ravago Jones5da06352022-03-04 20:26:24 -0800512 // Once the shot is complete and the catapult is back to its return
513 // position, go back to IDLE
Milind Upadhyay27d16d32022-04-16 13:41:45 -0700514 if (catapult_.shot_count() > prev_shot_count_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800515 prev_shot_count_ = catapult_.shot_count();
516 fire_ = false;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700517 discarding_ball_ = false;
Ravago Jones5da06352022-03-04 20:26:24 -0800518 state_ = SuperstructureState::IDLE;
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700519 transitioning_second_ball_ =
520 (front_intake_has_ball_ || back_intake_has_ball_);
Ravago Jones5da06352022-03-04 20:26:24 -0800521 }
522
523 break;
524 }
525 }
526
527 collision_avoidance_.UpdateGoal(
528 {.intake_front_position = intake_front_.estimated_position(),
529 .intake_back_position = intake_back_.estimated_position(),
Henry Speiser28288cc2022-03-09 22:59:24 -0800530 .turret_position = turret_.estimated_position(),
milind-uc63d0942022-04-15 12:07:42 -0700531 .shooting = (state_ == SuperstructureState::SHOOTING) ||
532 !catapult_near_return_position},
Ravago Jones5da06352022-03-04 20:26:24 -0800533 turret_goal);
534
535 turret_.set_min_position(collision_avoidance_.min_turret_goal());
536 turret_.set_max_position(collision_avoidance_.max_turret_goal());
537 intake_front_.set_min_position(collision_avoidance_.min_intake_front_goal());
538 intake_front_.set_max_position(collision_avoidance_.max_intake_front_goal());
539 intake_back_.set_min_position(collision_avoidance_.min_intake_back_goal());
540 intake_back_.set_max_position(collision_avoidance_.max_intake_back_goal());
541
James Kuszmaul84083f42022-02-27 17:24:38 -0800542 const flatbuffers::Offset<AimerStatus> aimer_offset =
543 aimer_.PopulateStatus(status->fbb());
544
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800545 // Disable the catapult if we want to restart to prevent damage with
546 // flippers
Austin Schuh39f26f62022-02-24 21:34:46 -0800547 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh97410d72022-03-12 15:37:23 -0800548 catapult_status_offset = catapult_.Iterate(
Nathan Leongdd728002024-02-03 15:26:53 -0800549 catapult_goal, position->catapult(), robot_state().voltage_battery(),
Austin Schuh97410d72022-03-12 15:37:23 -0800550 output != nullptr && !catapult_.estopped()
551 ? &(output_struct.catapult_voltage)
552 : nullptr,
553 fire_, status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800554
Ravago Jones5da06352022-03-04 20:26:24 -0800555 const flatbuffers::Offset<RelativeEncoderProfiledJointStatus>
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800556 climber_status_offset = climber_.Iterate(
557 unsafe_goal != nullptr ? unsafe_goal->climber() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800558 position->climber(),
559 output != nullptr ? &output_struct.climber_voltage : nullptr,
560 status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800561
Ravago Jones5da06352022-03-04 20:26:24 -0800562 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800563 intake_status_offset_front = intake_front_.Iterate(
564 unsafe_goal != nullptr ? unsafe_goal->intake_front() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800565 position->intake_front(),
566 output != nullptr ? &output_struct.intake_voltage_front : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800567 status->fbb());
568
Ravago Jones5da06352022-03-04 20:26:24 -0800569 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800570 intake_status_offset_back = intake_back_.Iterate(
571 unsafe_goal != nullptr ? unsafe_goal->intake_back() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800572 position->intake_back(),
573 output != nullptr ? &output_struct.intake_voltage_back : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800574 status->fbb());
575
Ravago Jones5da06352022-03-04 20:26:24 -0800576 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh7f120362022-03-05 17:10:11 -0800577 turret_status_offset = turret_.Iterate(
578 turret_goal, position->turret(),
579 output != nullptr ? &output_struct.turret_voltage : nullptr,
580 status->fbb());
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800581
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800582 if (output != nullptr) {
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800583 output_struct.roller_voltage_front = roller_speed_compensated_front;
584 output_struct.roller_voltage_back = roller_speed_compensated_back;
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700585 output_struct.transfer_roller_voltage = transfer_roller_speed;
Ravago Jones5da06352022-03-04 20:26:24 -0800586 output_struct.flipper_arms_voltage = flipper_arms_voltage;
milind-u6e7d8d42022-04-06 18:30:43 -0700587 if (climber_servo) {
Austin Schuh677af7e2022-04-13 19:45:39 -0700588 output_struct.climber_servo_left = 0.5;
Milind Upadhyay4b248da2022-09-21 21:20:12 -0700589 output_struct.climber_servo_right =
590 (aos::network::GetTeamNumber() == constants::kCompTeamNumber ? 1.0
591 : 0.5);
milind-u6e7d8d42022-04-06 18:30:43 -0700592 } else {
593 output_struct.climber_servo_left = 1.0;
594 output_struct.climber_servo_right = 0.0;
595 }
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800596
milind-u086d7262022-01-19 20:44:18 -0800597 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
598 }
599
600 Status::Builder status_builder = status->MakeBuilder<Status>();
601
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800602 const bool zeroed = intake_front_.zeroed() && intake_back_.zeroed() &&
Ravago Jones5da06352022-03-04 20:26:24 -0800603 turret_.zeroed() && climber_.zeroed() &&
604 catapult_.zeroed();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800605 const bool estopped = intake_front_.estopped() || intake_back_.estopped() ||
Ravago Jones5da06352022-03-04 20:26:24 -0800606 turret_.estopped() || climber_.estopped() ||
607 catapult_.estopped();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800608
609 status_builder.add_zeroed(zeroed);
610 status_builder.add_estopped(estopped);
611
612 status_builder.add_intake_front(intake_status_offset_front);
613 status_builder.add_intake_back(intake_status_offset_back);
614 status_builder.add_turret(turret_status_offset);
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800615 status_builder.add_climber(climber_status_offset);
Ravago Jones5da06352022-03-04 20:26:24 -0800616
Austin Schuh39f26f62022-02-24 21:34:46 -0800617 status_builder.add_catapult(catapult_status_offset);
618 status_builder.add_solve_time(catapult_.solve_time());
Austin Schuh80fc2752022-02-25 13:33:56 -0800619 status_builder.add_shot_count(catapult_.shot_count());
Austin Schuh41472552022-03-13 18:09:41 -0700620 status_builder.add_mpc_horizon(catapult_.mpc_horizon());
Ravago Jones3283ce02022-03-09 19:31:29 -0800621 if (catapult_goal != nullptr) {
622 status_builder.add_shot_position(catapult_goal->shot_position());
623 status_builder.add_shot_velocity(catapult_goal->shot_velocity());
624 }
Ravago Jones5da06352022-03-04 20:26:24 -0800625
626 status_builder.add_flippers_open(flippers_open_);
627 status_builder.add_reseating_in_catapult(reseating_in_catapult_);
628 status_builder.add_fire(fire_);
Henry Speisera9c49a22022-04-01 15:22:35 -0700629 status_builder.add_moving_too_fast(moving_too_fast);
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700630 status_builder.add_discarding_ball(discarding_ball_);
Milind Upadhyay9d68b132022-04-01 10:58:18 -0700631 status_builder.add_collided(collided);
Henry Speiser77747b72022-03-06 17:18:29 -0800632 status_builder.add_ready_to_fire(state_ == SuperstructureState::LOADED &&
633 turret_near_goal && !collided);
Ravago Jones5da06352022-03-04 20:26:24 -0800634 status_builder.add_state(state_);
James Kuszmaul40526952022-03-13 15:56:38 -0700635 if (!front_intake_has_ball_ && !back_intake_has_ball_) {
636 status_builder.add_intake_state(IntakeState::NO_BALL);
637 } else if (front_intake_has_ball_ && back_intake_has_ball_) {
638 status_builder.add_intake_state(turret_intake_state_ ==
639 RequestedIntake::kFront
640 ? IntakeState::INTAKE_FRONT_BALL
641 : IntakeState::INTAKE_BACK_BALL);
642 } else {
643 status_builder.add_intake_state(front_intake_has_ball_
644 ? IntakeState::INTAKE_FRONT_BALL
645 : IntakeState::INTAKE_BACK_BALL);
646 }
647 status_builder.add_front_intake_has_ball(front_intake_has_ball_);
648 status_builder.add_back_intake_has_ball(back_intake_has_ball_);
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700649 status_builder.add_transitioning_second_ball(transitioning_second_ball_);
milind-u086d7262022-01-19 20:44:18 -0800650
James Kuszmaul84083f42022-02-27 17:24:38 -0800651 status_builder.add_aimer(aimer_offset);
652
milind-u086d7262022-01-19 20:44:18 -0800653 (void)status->Send(status_builder.Finish());
654}
655
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800656double Superstructure::robot_velocity() const {
657 return (drivetrain_status_fetcher_.get() != nullptr
658 ? drivetrain_status_fetcher_->robot_speed()
659 : 0.0);
660}
661
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800662} // namespace y2022::control_loops::superstructure