blob: 5abe995adda854d78645f9a071771f52d0f0de8e [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
milind-u086d7262022-01-19 20:44:18 -080012namespace y2022 {
13namespace control_loops {
14namespace superstructure {
15
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),
Ravago Jones5da06352022-03-04 20:26:24 -080030 catapult_(*values_),
Henry Speiser55aa3ba2022-02-21 23:21:12 -080031 drivetrain_status_fetcher_(
32 event_loop->MakeFetcher<frc971::control_loops::drivetrain::Status>(
Austin Schuh39f26f62022-02-24 21:34:46 -080033 "/drivetrain")),
Ravago Jones5da06352022-03-04 20:26:24 -080034 can_position_fetcher_(
James Kuszmaulb9ba9a52022-03-31 22:16:01 -070035 event_loop->MakeFetcher<CANPosition>("/superstructure")),
Ravago Jonesd51af7a2022-03-26 21:44:20 -070036 joystick_state_fetcher_(
37 event_loop->MakeFetcher<aos::JoystickState>("/aos")),
Milind Upadhyaye9075d12022-04-12 22:45:16 -070038 ball_color_fetcher_(
39 event_loop->MakeFetcher<y2022::vision::BallColor>("/superstructure")),
James Kuszmaulb9ba9a52022-03-31 22:16:01 -070040 aimer_(values) {
milind-u086d7262022-01-19 20:44:18 -080041 event_loop->SetRuntimeRealtimePriority(30);
42}
43
44void Superstructure::RunIteration(const Goal *unsafe_goal,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080045 const Position *position,
milind-u086d7262022-01-19 20:44:18 -080046 aos::Sender<Output>::Builder *output,
47 aos::Sender<Status>::Builder *status) {
48 if (WasReset()) {
49 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Henry Speiser55aa3ba2022-02-21 23:21:12 -080050 intake_front_.Reset();
51 intake_back_.Reset();
52 turret_.Reset();
53 climber_.Reset();
Austin Schuh39f26f62022-02-24 21:34:46 -080054 catapult_.Reset();
Henry Speiser55aa3ba2022-02-21 23:21:12 -080055 }
56
Ravago Jones5da06352022-03-04 20:26:24 -080057 OutputT output_struct;
Milind Upadhyay225156b2022-02-25 22:42:12 -080058
Ravago Jones5da06352022-03-04 20:26:24 -080059 aos::FlatbufferFixedAllocatorArray<
Austin Schuhfce2b602022-03-13 20:05:01 -070060 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 512>
James Kuszmaul40526952022-03-13 15:56:38 -070061 turret_loading_goal_buffer;
Austin Schuhfce2b602022-03-13 20:05:01 -070062 aos::FlatbufferFixedAllocatorArray<CatapultGoal, 512> catapult_goal_buffer;
Ravago Jonesd51af7a2022-03-26 21:44:20 -070063 aos::FlatbufferFixedAllocatorArray<CatapultGoal, 512>
64 catapult_discarding_goal_buffer;
Ravago Jones5da06352022-03-04 20:26:24 -080065
66 const aos::monotonic_clock::time_point timestamp =
67 event_loop()->context().monotonic_event_time;
Milind Upadhyay225156b2022-02-25 22:42:12 -080068
Ravago Jonesd51af7a2022-03-26 21:44:20 -070069 if (joystick_state_fetcher_.Fetch() &&
70 joystick_state_fetcher_->has_alliance()) {
71 alliance_ = joystick_state_fetcher_->alliance();
72 }
73
74 if (ball_color_fetcher_.Fetch() && ball_color_fetcher_->has_ball_color()) {
75 ball_color_ = ball_color_fetcher_->ball_color();
76 }
77
78 if (alliance_ != aos::Alliance::kInvalid &&
79 ball_color_ != aos::Alliance::kInvalid && alliance_ != ball_color_) {
80 switch (state_) {
81 case SuperstructureState::IDLE:
82 break;
83 case SuperstructureState::TRANSFERRING:
84 break;
85 case SuperstructureState::LOADING:
86 break;
87 case SuperstructureState::LOADED:
88 discarding_ball_ = true;
89 break;
90 case SuperstructureState::SHOOTING:
91 if (!fire_) {
92 // we can still tell it not to shoot into the hub
93 // and change the turret and catapult goals
94 discarding_ball_ = true;
95 }
96 break;
97 }
98 }
99
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800100 drivetrain_status_fetcher_.Fetch();
101 const float velocity = robot_velocity();
102
James Kuszmaul84083f42022-02-27 17:24:38 -0800103 const turret::Aimer::Goal *auto_aim_goal = nullptr;
104 if (drivetrain_status_fetcher_.get() != nullptr) {
105 aimer_.Update(drivetrain_status_fetcher_.get(),
106 turret::Aimer::ShotMode::kShootOnTheFly);
107 auto_aim_goal = aimer_.TurretGoal();
108 }
109
Ravago Jones5da06352022-03-04 20:26:24 -0800110 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
111 *turret_goal = nullptr;
Ravago Jones3283ce02022-03-09 19:31:29 -0800112 const CatapultGoal *catapult_goal = nullptr;
Ravago Jones5da06352022-03-04 20:26:24 -0800113 double roller_speed_compensated_front = 0.0;
114 double roller_speed_compensated_back = 0.0;
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700115 double transfer_roller_speed = 0.0;
Ravago Jones5da06352022-03-04 20:26:24 -0800116 double flipper_arms_voltage = 0.0;
James Kuszmaul40526952022-03-13 15:56:38 -0700117 bool have_active_intake_request = false;
milind-u6e7d8d42022-04-06 18:30:43 -0700118 bool climber_servo = false;
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800119
120 if (unsafe_goal != nullptr) {
121 roller_speed_compensated_front =
122 unsafe_goal->roller_speed_front() +
123 std::max(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
124
125 roller_speed_compensated_back =
126 unsafe_goal->roller_speed_back() -
127 std::min(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
128
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700129 transfer_roller_speed = unsafe_goal->transfer_roller_speed();
milind-u086d7262022-01-19 20:44:18 -0800130
milind-u6e7d8d42022-04-06 18:30:43 -0700131 climber_servo = unsafe_goal->climber_servo();
132
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700133 turret_goal = unsafe_goal->auto_aim() && !discarding_ball_
134 ? auto_aim_goal
135 : unsafe_goal->turret();
Ravago Jones3283ce02022-03-09 19:31:29 -0800136
137 catapult_goal = unsafe_goal->catapult();
138
139 constants::Values::ShotParams shot_params;
140 const double distance_to_goal = aimer_.DistanceToGoal();
James Kuszmaulb9ba9a52022-03-31 22:16:01 -0700141 if (!FLAGS_ignore_distance && unsafe_goal->auto_aim() &&
142 values_->shot_interpolation_table.GetInRange(distance_to_goal,
143 &shot_params)) {
Austin Schuhfce2b602022-03-13 20:05:01 -0700144 flatbuffers::FlatBufferBuilder *catapult_goal_fbb =
145 catapult_goal_buffer.fbb();
Ravago Jones3283ce02022-03-09 19:31:29 -0800146 std::optional<flatbuffers::Offset<
147 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal>>
148 return_position_offset;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700149 if (unsafe_goal->has_catapult() &&
Ravago Jones3283ce02022-03-09 19:31:29 -0800150 unsafe_goal->catapult()->has_return_position()) {
Austin Schuhfce2b602022-03-13 20:05:01 -0700151 return_position_offset = {aos::CopyFlatBuffer(
152 unsafe_goal->catapult()->return_position(), catapult_goal_fbb)};
Ravago Jones3283ce02022-03-09 19:31:29 -0800153 }
Austin Schuhfce2b602022-03-13 20:05:01 -0700154 CatapultGoal::Builder builder(*catapult_goal_fbb);
Ravago Jones3283ce02022-03-09 19:31:29 -0800155 builder.add_shot_position(shot_params.shot_angle);
156 builder.add_shot_velocity(shot_params.shot_velocity);
157 if (return_position_offset.has_value()) {
158 builder.add_return_position(return_position_offset.value());
159 }
160 catapult_goal_buffer.Finish(builder.Finish());
161 catapult_goal = &catapult_goal_buffer.message();
162 }
James Kuszmaul40526952022-03-13 15:56:38 -0700163
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700164 if (discarding_ball_) {
165 flatbuffers::FlatBufferBuilder *catapult_goal_fbb =
166 catapult_discarding_goal_buffer.fbb();
167 std::optional<flatbuffers::Offset<
168 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal>>
169 return_position_offset;
170 if (unsafe_goal->has_catapult() &&
171 unsafe_goal->catapult()->has_return_position()) {
172 return_position_offset = {aos::CopyFlatBuffer(
173 unsafe_goal->catapult()->return_position(), catapult_goal_fbb)};
174 }
175 CatapultGoal::Builder builder(*catapult_goal_fbb);
176 builder.add_shot_position(kDiscardingPosition);
177 builder.add_shot_velocity(kDiscardingVelocity);
178 if (return_position_offset.has_value()) {
179 builder.add_return_position(return_position_offset.value());
180 }
181 catapult_discarding_goal_buffer.Finish(builder.Finish());
182 catapult_goal = &catapult_discarding_goal_buffer.message();
183 }
184
James Kuszmaul40526952022-03-13 15:56:38 -0700185 if (unsafe_goal->has_turret_intake()) {
186 have_active_intake_request = true;
187 }
James Kuszmaul84083f42022-02-27 17:24:38 -0800188 }
Austin Schuh39f26f62022-02-24 21:34:46 -0800189
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800190 // Superstructure state machine:
Ravago Jones5da06352022-03-04 20:26:24 -0800191 // 1. IDLE: Wait until an intake beambreak is triggerred, meaning that a ball
192 // is being intaked. This means that the transfer rollers have a ball. If
193 // we've been waiting here for too long without any beambreak triggered, the
194 // ball got lost, so reset.
195 // 2. TRANSFERRING: Until the turret reaches the loading position where the
196 // ball can be transferred into the catapult, wiggle the ball in place.
197 // Once the turret reaches the loading position, send the ball forward with
198 // the transfer rollers until the turret beambreak is triggered.
199 // If we have been in this state for too long, the ball probably got lost so
200 // reset back to IDLE.
201 // 3. LOADING: To load the ball into the catapult, put the flippers at the
202 // feeding speed. Wait for a timeout, and then wait until the ball has gone
203 // past the turret beambreak and the flippers have stopped moving, meaning
204 // that the ball is fully loaded in the catapult.
205 // 4. LOADED: Wait until the user asks us to fire to transition to the
206 // shooting stage. If asked to cancel the shot, reset back to the IDLE state.
207 // 5. SHOOTING: Open the flippers to get ready for the shot. If they don't
208 // open quickly enough, try reseating the ball and going back to the LOADING
209 // stage, which moves the flippers in the opposite direction first.
210 // Now, hold the flippers open and wait until the turret has reached its
211 // aiming goal. Once the turret is ready, tell the catapult to fire.
212 // If the flippers move back for some reason now, it could damage the
213 // catapult, so estop it. Otherwise, wait until the catapult shoots a ball and
214 // goes back to its return position. We have now finished the shot, so return
215 // to IDLE.
216
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800217 // If we started off preloaded, skip to the loaded state.
218 // Make sure we weren't already there just in case.
219 if (unsafe_goal != nullptr && unsafe_goal->preloaded()) {
220 switch (state_) {
221 case SuperstructureState::IDLE:
222 case SuperstructureState::TRANSFERRING:
223 case SuperstructureState::LOADING:
224 state_ = SuperstructureState::LOADED;
225 loading_timer_ = timestamp;
226 break;
227 case SuperstructureState::LOADED:
228 case SuperstructureState::SHOOTING:
229 break;
230 }
231 }
232
James Kuszmaul40526952022-03-13 15:56:38 -0700233 if (position->intake_beambreak_front()) {
234 front_intake_has_ball_ = true;
235 front_intake_beambreak_timer_ = timestamp;
Ravago Jones5da06352022-03-04 20:26:24 -0800236 }
237
James Kuszmaul40526952022-03-13 15:56:38 -0700238 if (position->intake_beambreak_back()) {
239 back_intake_has_ball_ = true;
240 back_intake_beambreak_timer_ = timestamp;
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800241 }
242
Ravago Jonesf70107e2022-04-12 15:05:56 -0700243 // Check if we're either spitting or have lost the ball.
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700244 if ((transfer_roller_speed < 0.0 && front_intake_has_ball_) ||
James Kuszmaul40526952022-03-13 15:56:38 -0700245 timestamp >
246 front_intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
247 front_intake_has_ball_ = false;
248 }
249
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700250 if ((transfer_roller_speed > 0.0 && back_intake_has_ball_) ||
James Kuszmaul40526952022-03-13 15:56:38 -0700251 timestamp >
252 back_intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
253 back_intake_has_ball_ = false;
254 }
255
256 // Wiggle transfer rollers: send the ball back and forth while waiting
257 // for the turret or waiting for another shot to be completed
258 const double wiggle_voltage =
259 constants::Values::kTransferRollerWiggleVoltage();
260 if (front_intake_has_ball_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800261 roller_speed_compensated_front = 0.0;
James Kuszmaul40526952022-03-13 15:56:38 -0700262 if (position->intake_beambreak_front()) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700263 transfer_roller_speed = -wiggle_voltage;
Ravago Jones5da06352022-03-04 20:26:24 -0800264 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700265 transfer_roller_speed = wiggle_voltage;
Ravago Jones5da06352022-03-04 20:26:24 -0800266 }
267 }
268
James Kuszmaul40526952022-03-13 15:56:38 -0700269 if (back_intake_has_ball_) {
270 roller_speed_compensated_back = 0.0;
271 if (position->intake_beambreak_back()) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700272 transfer_roller_speed = wiggle_voltage;
James Kuszmaul40526952022-03-13 15:56:38 -0700273 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700274 transfer_roller_speed = -wiggle_voltage;
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800275 }
276 }
277
James Kuszmaul40526952022-03-13 15:56:38 -0700278 // Create the goal message for putting the turret in its loading position.
279 // This will then get used in the state machine for the states (IDLE and
280 // TRANSFERRING) that use it.
281 double turret_loading_position =
282 (turret_intake_state_ == RequestedIntake::kFront
283 ? constants::Values::kTurretFrontIntakePos()
284 : constants::Values::kTurretBackIntakePos());
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700285 if (transitioning_second_ball_) {
286 // Turn to the loading position as close to the current position as
287 // possible since we just aimed.
288 turret_loading_position =
289 turret_.estimated_position() +
290 aos::math::NormalizeAngle(turret_loading_position -
291 turret_.estimated_position());
292 }
293
294 if (!transitioning_second_ball_ ||
295 (turret_loading_position > values_->turret_range.upper ||
296 turret_loading_position < values_->turret_range.lower)) {
297 // Turn to the loading position as close to the middle of the range as
298 // possible. Do the unwraping before we have a ball so we don't have to
299 // unwrap to shoot if we aren't transitioning a second ball. If we are doing
300 // the second ball, we need to reset back to the middle of the range
301 turret_loading_position =
302 frc971::zeroing::Wrap(values_->turret_range.middle_soft(),
303 turret_loading_position, 2.0 * M_PI);
304 }
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800305
James Kuszmaul40526952022-03-13 15:56:38 -0700306 turret_loading_goal_buffer.Finish(
307 frc971::control_loops::CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
308 *turret_loading_goal_buffer.fbb(), turret_loading_position));
309
milind-uc63d0942022-04-15 12:07:42 -0700310 const bool catapult_near_return_position =
311 (unsafe_goal != nullptr && unsafe_goal->has_catapult() &&
312 unsafe_goal->catapult()->has_return_position() &&
313 std::abs(unsafe_goal->catapult()->return_position()->unsafe_goal() -
314 catapult_.estimated_position()) < kCatapultGoalThreshold);
315
Henry Speiser77747b72022-03-06 17:18:29 -0800316 const bool turret_near_goal =
317 turret_goal != nullptr &&
318 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
319 kTurretGoalThreshold;
320 const bool collided = collision_avoidance_.IsCollided(
321 {.intake_front_position = intake_front_.estimated_position(),
322 .intake_back_position = intake_back_.estimated_position(),
323 .turret_position = turret_.estimated_position(),
324 .shooting = true});
325
Henry Speisera9c49a22022-04-01 15:22:35 -0700326 // Dont shoot if the robot is moving faster than this
James Kuszmaulb9ba9a52022-03-31 22:16:01 -0700327 constexpr double kMaxShootSpeed = 2.7;
Henry Speisera9c49a22022-04-01 15:22:35 -0700328 const bool moving_too_fast = std::abs(robot_velocity()) > kMaxShootSpeed;
329
Ravago Jones5da06352022-03-04 20:26:24 -0800330 switch (state_) {
331 case SuperstructureState::IDLE: {
James Kuszmaul40526952022-03-13 15:56:38 -0700332 // Only change the turret's goal loading position when idle, to prevent us
333 // spinning the turret around when TRANSFERRING...
Milind Upadhyay90648532022-03-13 20:37:02 -0700334 if (have_active_intake_request) {
335 turret_intake_state_ = unsafe_goal->turret_intake();
336 }
James Kuszmaul40526952022-03-13 15:56:38 -0700337 if (front_intake_has_ball_ != back_intake_has_ball_) {
James Kuszmaul40526952022-03-13 15:56:38 -0700338 turret_intake_state_ = front_intake_has_ball_ ? RequestedIntake::kFront
339 : RequestedIntake::kBack;
340 }
341 // When IDLE with no specific intake button pressed, allow the goal
342 // message to override the intaking stuff.
Austin Schuh92908812022-03-27 14:14:05 -0700343 if (have_active_intake_request || (turret_goal == nullptr)) {
James Kuszmaul40526952022-03-13 15:56:38 -0700344 turret_goal = &turret_loading_goal_buffer.message();
Ravago Jones5da06352022-03-04 20:26:24 -0800345 }
346
James Kuszmaul40526952022-03-13 15:56:38 -0700347 if (!front_intake_has_ball_ && !back_intake_has_ball_) {
Austin Schuh92908812022-03-27 14:14:05 -0700348 last_shot_angle_ = std::nullopt;
Ravago Jones5da06352022-03-04 20:26:24 -0800349 break;
350 }
351
352 state_ = SuperstructureState::TRANSFERRING;
Ravago Jones5da06352022-03-04 20:26:24 -0800353 // Save the side the ball is on for later
354
355 break;
356 }
357 case SuperstructureState::TRANSFERRING: {
James Kuszmaul40526952022-03-13 15:56:38 -0700358 // If we've been transferring for too long, the ball probably got lost.
359 if ((turret_intake_state_ == RequestedIntake::kFront &&
360 !front_intake_has_ball_) ||
361 (turret_intake_state_ == RequestedIntake::kBack &&
362 !back_intake_has_ball_)) {
Ravago Jones5da06352022-03-04 20:26:24 -0800363 state_ = SuperstructureState::IDLE;
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700364 transitioning_second_ball_ = false;
Ravago Jones5da06352022-03-04 20:26:24 -0800365 break;
366 }
367
James Kuszmaul40526952022-03-13 15:56:38 -0700368 turret_goal = &turret_loading_goal_buffer.message();
Milind Upadhyay27d16d32022-04-16 13:41:45 -0700369 aimer_.UpdateTurretGoal(turret_loading_position);
James Kuszmaul40526952022-03-13 15:56:38 -0700370
Ravago Jones5da06352022-03-04 20:26:24 -0800371 const bool turret_near_goal =
372 std::abs(turret_.estimated_position() - turret_loading_position) <
milind-uc63d0942022-04-15 12:07:42 -0700373 kTurretGoalLoadingThreshold;
374 if (!turret_near_goal || !catapult_near_return_position) {
Ravago Jones5da06352022-03-04 20:26:24 -0800375 break; // Wait for turret to reach the chosen intake
376 }
377
378 // Transfer rollers and flipper arm belt on
James Kuszmaul40526952022-03-13 15:56:38 -0700379 if (turret_intake_state_ == RequestedIntake::kFront) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700380 transfer_roller_speed = constants::Values::kTransferRollerVoltage();
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800381 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700382 transfer_roller_speed = -constants::Values::kTransferRollerVoltage();
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800383 }
Ravago Jones5da06352022-03-04 20:26:24 -0800384 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
385
386 // Ball is in catapult
387 if (position->turret_beambreak()) {
James Kuszmaul40526952022-03-13 15:56:38 -0700388 if (turret_intake_state_ == RequestedIntake::kFront) {
389 front_intake_has_ball_ = false;
390 } else {
391 back_intake_has_ball_ = false;
392 }
Ravago Jones5da06352022-03-04 20:26:24 -0800393 state_ = SuperstructureState::LOADING;
394 loading_timer_ = timestamp;
395 }
396 break;
397 }
398 case SuperstructureState::LOADING: {
399 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
400
401 // Keep feeding for kExtraLoadingTime
402
Ravago Jones5da06352022-03-04 20:26:24 -0800403 // The ball should go past the turret beambreak to be loaded.
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800404 // If we got a CAN reading not too long ago, the flippers should have
405 // also stopped.
Milind Upadhyay47e0d032022-03-05 23:06:25 -0800406 if (position->turret_beambreak()) {
407 loading_timer_ = timestamp;
408 } else if (timestamp >
409 loading_timer_ + constants::Values::kExtraLoadingTime()) {
Ravago Jones5da06352022-03-04 20:26:24 -0800410 state_ = SuperstructureState::LOADED;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700411 // reset color and wait for a new one once we know the ball is in place
412 ball_color_ = aos::Alliance::kInvalid;
Ravago Jones5da06352022-03-04 20:26:24 -0800413 reseating_in_catapult_ = false;
414 }
415 break;
416 }
417 case SuperstructureState::LOADED: {
418 if (unsafe_goal != nullptr) {
Austin Schuh92908812022-03-27 14:14:05 -0700419 if (turret_goal == nullptr) {
420 if (last_shot_angle_) {
421 turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
422 *last_shot_angle_);
423 }
424 turret_goal = &turret_loading_goal_buffer.message();
425 }
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700426
Ravago Jones5da06352022-03-04 20:26:24 -0800427 if (unsafe_goal->cancel_shot()) {
428 // Cancel the shot process
429 state_ = SuperstructureState::IDLE;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700430 } else if (unsafe_goal->fire() || discarding_ball_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800431 // Start if we were asked to and the turret is at goal
432 state_ = SuperstructureState::SHOOTING;
433 prev_shot_count_ = catapult_.shot_count();
434
435 // Reset opening timeout
436 flipper_opening_start_time_ = timestamp;
milind-uc63d0942022-04-15 12:07:42 -0700437 loading_timer_ = timestamp;
Ravago Jones5da06352022-03-04 20:26:24 -0800438 }
439 }
440 break;
441 }
442 case SuperstructureState::SHOOTING: {
Austin Schuh92908812022-03-27 14:14:05 -0700443 if (turret_goal == nullptr) {
444 if (last_shot_angle_) {
445 turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
446 *last_shot_angle_);
447 }
448 turret_goal = &turret_loading_goal_buffer.message();
449 last_shot_angle_ = turret_goal->unsafe_goal();
450 } else {
451 last_shot_angle_ = std::nullopt;
452 }
453 const bool turret_near_goal =
454 turret_goal != nullptr &&
455 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
456 kTurretGoalThreshold;
457
milind-uee3b0012022-03-14 21:19:57 -0700458 // Don't open the flippers until the turret's ready: give them as little
Henry Speisera9c49a22022-04-01 15:22:35 -0700459 // time to get bumped as possible. Or moving to fast.
460 if (!turret_near_goal || collided || moving_too_fast) {
milind-uee3b0012022-03-14 21:19:57 -0700461 break;
462 }
463
Ravago Jones5da06352022-03-04 20:26:24 -0800464 // Opening flipper arms could fail, wait until they are open using their
465 // potentiometers (the member below is just named encoder).
466 // Be a little more lenient if the flippers were already open in case of
467 // noise or collisions.
468 const double flipper_open_position =
469 (flippers_open_ ? constants::Values::kReseatFlipperPosition()
470 : constants::Values::kFlipperOpenPosition());
milind-u37dc40b2022-03-08 19:51:27 -0800471
472 // TODO(milind): add left arm back once it's fixed
Ravago Jones5da06352022-03-04 20:26:24 -0800473 flippers_open_ =
Ravago Jones5da06352022-03-04 20:26:24 -0800474 position->flipper_arm_right()->encoder() >= flipper_open_position;
475
476 if (flippers_open_) {
477 // Hold at kFlipperHoldVoltage
478 flipper_arms_voltage = constants::Values::kFlipperHoldVoltage();
479 } else {
480 // Open at kFlipperOpenVoltage
481 flipper_arms_voltage = constants::Values::kFlipperOpenVoltage();
482 }
483
milind-uee3b0012022-03-14 21:19:57 -0700484 // There are two possible failures for the flippers:
485 // 1. They never open on time
486 // 2. They opened and we started firing, but we got bumped or something
487 // and they went back.
488 // If the flippers didn't open in a reasonable amount of time, try
489 // reseating the ball and reversing them.
490 if (!fire_ && !flippers_open_ &&
Ravago Jones5da06352022-03-04 20:26:24 -0800491 timestamp >
492 loading_timer_ + constants::Values::kFlipperOpeningTimeout()) {
493 // Reseat the ball and try again
494 state_ = SuperstructureState::LOADING;
495 loading_timer_ = timestamp;
496 reseating_in_catapult_ = true;
497 break;
498 }
Ravago Jones5da06352022-03-04 20:26:24 -0800499
Henry Speiser28288cc2022-03-09 22:59:24 -0800500 // If the turret reached the aiming goal and the catapult is safe to move
501 // up, fire!
502 if (flippers_open_ && turret_near_goal && !collided) {
Ravago Jones5da06352022-03-04 20:26:24 -0800503 fire_ = true;
504 }
505
Ravago Jones5da06352022-03-04 20:26:24 -0800506 // Once the shot is complete and the catapult is back to its return
507 // position, go back to IDLE
Milind Upadhyay27d16d32022-04-16 13:41:45 -0700508 if (catapult_.shot_count() > prev_shot_count_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800509 prev_shot_count_ = catapult_.shot_count();
510 fire_ = false;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700511 discarding_ball_ = false;
Ravago Jones5da06352022-03-04 20:26:24 -0800512 state_ = SuperstructureState::IDLE;
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700513 transitioning_second_ball_ =
514 (front_intake_has_ball_ || back_intake_has_ball_);
Ravago Jones5da06352022-03-04 20:26:24 -0800515 }
516
517 break;
518 }
519 }
520
521 collision_avoidance_.UpdateGoal(
522 {.intake_front_position = intake_front_.estimated_position(),
523 .intake_back_position = intake_back_.estimated_position(),
Henry Speiser28288cc2022-03-09 22:59:24 -0800524 .turret_position = turret_.estimated_position(),
milind-uc63d0942022-04-15 12:07:42 -0700525 .shooting = (state_ == SuperstructureState::SHOOTING) ||
526 !catapult_near_return_position},
Ravago Jones5da06352022-03-04 20:26:24 -0800527 turret_goal);
528
529 turret_.set_min_position(collision_avoidance_.min_turret_goal());
530 turret_.set_max_position(collision_avoidance_.max_turret_goal());
531 intake_front_.set_min_position(collision_avoidance_.min_intake_front_goal());
532 intake_front_.set_max_position(collision_avoidance_.max_intake_front_goal());
533 intake_back_.set_min_position(collision_avoidance_.min_intake_back_goal());
534 intake_back_.set_max_position(collision_avoidance_.max_intake_back_goal());
535
James Kuszmaul84083f42022-02-27 17:24:38 -0800536 const flatbuffers::Offset<AimerStatus> aimer_offset =
537 aimer_.PopulateStatus(status->fbb());
538
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800539 // Disable the catapult if we want to restart to prevent damage with
540 // flippers
Austin Schuh39f26f62022-02-24 21:34:46 -0800541 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh97410d72022-03-12 15:37:23 -0800542 catapult_status_offset = catapult_.Iterate(
Ravago Jones3283ce02022-03-09 19:31:29 -0800543 catapult_goal, position, robot_state().voltage_battery(),
Austin Schuh97410d72022-03-12 15:37:23 -0800544 output != nullptr && !catapult_.estopped()
545 ? &(output_struct.catapult_voltage)
546 : nullptr,
547 fire_, status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800548
Ravago Jones5da06352022-03-04 20:26:24 -0800549 const flatbuffers::Offset<RelativeEncoderProfiledJointStatus>
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800550 climber_status_offset = climber_.Iterate(
551 unsafe_goal != nullptr ? unsafe_goal->climber() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800552 position->climber(),
553 output != nullptr ? &output_struct.climber_voltage : nullptr,
554 status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800555
Ravago Jones5da06352022-03-04 20:26:24 -0800556 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800557 intake_status_offset_front = intake_front_.Iterate(
558 unsafe_goal != nullptr ? unsafe_goal->intake_front() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800559 position->intake_front(),
560 output != nullptr ? &output_struct.intake_voltage_front : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800561 status->fbb());
562
Ravago Jones5da06352022-03-04 20:26:24 -0800563 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800564 intake_status_offset_back = intake_back_.Iterate(
565 unsafe_goal != nullptr ? unsafe_goal->intake_back() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800566 position->intake_back(),
567 output != nullptr ? &output_struct.intake_voltage_back : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800568 status->fbb());
569
Ravago Jones5da06352022-03-04 20:26:24 -0800570 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh7f120362022-03-05 17:10:11 -0800571 turret_status_offset = turret_.Iterate(
572 turret_goal, position->turret(),
573 output != nullptr ? &output_struct.turret_voltage : nullptr,
574 status->fbb());
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800575
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800576 if (output != nullptr) {
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800577 output_struct.roller_voltage_front = roller_speed_compensated_front;
578 output_struct.roller_voltage_back = roller_speed_compensated_back;
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700579 output_struct.transfer_roller_voltage = transfer_roller_speed;
Ravago Jones5da06352022-03-04 20:26:24 -0800580 output_struct.flipper_arms_voltage = flipper_arms_voltage;
milind-u6e7d8d42022-04-06 18:30:43 -0700581 if (climber_servo) {
Austin Schuh677af7e2022-04-13 19:45:39 -0700582 output_struct.climber_servo_left = 0.5;
Milind Upadhyay4b248da2022-09-21 21:20:12 -0700583 output_struct.climber_servo_right =
584 (aos::network::GetTeamNumber() == constants::kCompTeamNumber ? 1.0
585 : 0.5);
milind-u6e7d8d42022-04-06 18:30:43 -0700586 } else {
587 output_struct.climber_servo_left = 1.0;
588 output_struct.climber_servo_right = 0.0;
589 }
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800590
milind-u086d7262022-01-19 20:44:18 -0800591 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
592 }
593
594 Status::Builder status_builder = status->MakeBuilder<Status>();
595
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800596 const bool zeroed = intake_front_.zeroed() && intake_back_.zeroed() &&
Ravago Jones5da06352022-03-04 20:26:24 -0800597 turret_.zeroed() && climber_.zeroed() &&
598 catapult_.zeroed();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800599 const bool estopped = intake_front_.estopped() || intake_back_.estopped() ||
Ravago Jones5da06352022-03-04 20:26:24 -0800600 turret_.estopped() || climber_.estopped() ||
601 catapult_.estopped();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800602
603 status_builder.add_zeroed(zeroed);
604 status_builder.add_estopped(estopped);
605
606 status_builder.add_intake_front(intake_status_offset_front);
607 status_builder.add_intake_back(intake_status_offset_back);
608 status_builder.add_turret(turret_status_offset);
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800609 status_builder.add_climber(climber_status_offset);
Ravago Jones5da06352022-03-04 20:26:24 -0800610
Austin Schuh39f26f62022-02-24 21:34:46 -0800611 status_builder.add_catapult(catapult_status_offset);
612 status_builder.add_solve_time(catapult_.solve_time());
Austin Schuh80fc2752022-02-25 13:33:56 -0800613 status_builder.add_shot_count(catapult_.shot_count());
Austin Schuh41472552022-03-13 18:09:41 -0700614 status_builder.add_mpc_horizon(catapult_.mpc_horizon());
Ravago Jones3283ce02022-03-09 19:31:29 -0800615 if (catapult_goal != nullptr) {
616 status_builder.add_shot_position(catapult_goal->shot_position());
617 status_builder.add_shot_velocity(catapult_goal->shot_velocity());
618 }
Ravago Jones5da06352022-03-04 20:26:24 -0800619
620 status_builder.add_flippers_open(flippers_open_);
621 status_builder.add_reseating_in_catapult(reseating_in_catapult_);
622 status_builder.add_fire(fire_);
Henry Speisera9c49a22022-04-01 15:22:35 -0700623 status_builder.add_moving_too_fast(moving_too_fast);
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700624 status_builder.add_discarding_ball(discarding_ball_);
Milind Upadhyay9d68b132022-04-01 10:58:18 -0700625 status_builder.add_collided(collided);
Henry Speiser77747b72022-03-06 17:18:29 -0800626 status_builder.add_ready_to_fire(state_ == SuperstructureState::LOADED &&
627 turret_near_goal && !collided);
Ravago Jones5da06352022-03-04 20:26:24 -0800628 status_builder.add_state(state_);
James Kuszmaul40526952022-03-13 15:56:38 -0700629 if (!front_intake_has_ball_ && !back_intake_has_ball_) {
630 status_builder.add_intake_state(IntakeState::NO_BALL);
631 } else if (front_intake_has_ball_ && back_intake_has_ball_) {
632 status_builder.add_intake_state(turret_intake_state_ ==
633 RequestedIntake::kFront
634 ? IntakeState::INTAKE_FRONT_BALL
635 : IntakeState::INTAKE_BACK_BALL);
636 } else {
637 status_builder.add_intake_state(front_intake_has_ball_
638 ? IntakeState::INTAKE_FRONT_BALL
639 : IntakeState::INTAKE_BACK_BALL);
640 }
641 status_builder.add_front_intake_has_ball(front_intake_has_ball_);
642 status_builder.add_back_intake_has_ball(back_intake_has_ball_);
Milind Upadhyay1612bbb2022-04-16 17:10:32 -0700643 status_builder.add_transitioning_second_ball(transitioning_second_ball_);
milind-u086d7262022-01-19 20:44:18 -0800644
James Kuszmaul84083f42022-02-27 17:24:38 -0800645 status_builder.add_aimer(aimer_offset);
646
milind-u086d7262022-01-19 20:44:18 -0800647 (void)status->Send(status_builder.Finish());
648}
649
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800650double Superstructure::robot_velocity() const {
651 return (drivetrain_status_fetcher_.get() != nullptr
652 ? drivetrain_status_fetcher_->robot_speed()
653 : 0.0);
654}
655
milind-u086d7262022-01-19 20:44:18 -0800656} // namespace superstructure
657} // namespace control_loops
658} // namespace y2022