blob: 15f9c2a6d66ec128b652ecc111a73936388f4fa8 [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"
James Kuszmaul40526952022-03-13 15:56:38 -07005#include "frc971/zeroing/wrap.h"
Milind Upadhyay225156b2022-02-25 22:42:12 -08006#include "y2022/control_loops/superstructure/collision_avoidance.h"
milind-u086d7262022-01-19 20:44:18 -08007
James Kuszmaulb9ba9a52022-03-31 22:16:01 -07008DEFINE_bool(ignore_distance, false,
9 "If true, ignore distance when shooting and obay joystick_reader");
10
milind-u086d7262022-01-19 20:44:18 -080011namespace y2022 {
12namespace control_loops {
13namespace superstructure {
14
15using frc971::control_loops::AbsoluteEncoderProfiledJointStatus;
16using frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus;
Henry Speiser55aa3ba2022-02-21 23:21:12 -080017using frc971::control_loops::RelativeEncoderProfiledJointStatus;
milind-u086d7262022-01-19 20:44:18 -080018
19Superstructure::Superstructure(::aos::EventLoop *event_loop,
Henry Speiser55aa3ba2022-02-21 23:21:12 -080020 std::shared_ptr<const constants::Values> values,
milind-u086d7262022-01-19 20:44:18 -080021 const ::std::string &name)
22 : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080023 name),
Austin Schuh39f26f62022-02-24 21:34:46 -080024 values_(values),
25 climber_(values_->climber.subsystem_params),
26 intake_front_(values_->intake_front.subsystem_params),
27 intake_back_(values_->intake_back.subsystem_params),
28 turret_(values_->turret.subsystem_params),
Ravago Jones5da06352022-03-04 20:26:24 -080029 catapult_(*values_),
Henry Speiser55aa3ba2022-02-21 23:21:12 -080030 drivetrain_status_fetcher_(
31 event_loop->MakeFetcher<frc971::control_loops::drivetrain::Status>(
Austin Schuh39f26f62022-02-24 21:34:46 -080032 "/drivetrain")),
Ravago Jones5da06352022-03-04 20:26:24 -080033 can_position_fetcher_(
James Kuszmaulb9ba9a52022-03-31 22:16:01 -070034 event_loop->MakeFetcher<CANPosition>("/superstructure")),
35 aimer_(values) {
milind-u086d7262022-01-19 20:44:18 -080036 event_loop->SetRuntimeRealtimePriority(30);
37}
38
39void Superstructure::RunIteration(const Goal *unsafe_goal,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080040 const Position *position,
milind-u086d7262022-01-19 20:44:18 -080041 aos::Sender<Output>::Builder *output,
42 aos::Sender<Status>::Builder *status) {
43 if (WasReset()) {
44 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Henry Speiser55aa3ba2022-02-21 23:21:12 -080045 intake_front_.Reset();
46 intake_back_.Reset();
47 turret_.Reset();
48 climber_.Reset();
Austin Schuh39f26f62022-02-24 21:34:46 -080049 catapult_.Reset();
Henry Speiser55aa3ba2022-02-21 23:21:12 -080050 }
51
Ravago Jones5da06352022-03-04 20:26:24 -080052 OutputT output_struct;
Milind Upadhyay225156b2022-02-25 22:42:12 -080053
Ravago Jones5da06352022-03-04 20:26:24 -080054 aos::FlatbufferFixedAllocatorArray<
Austin Schuhfce2b602022-03-13 20:05:01 -070055 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 512>
James Kuszmaul40526952022-03-13 15:56:38 -070056 turret_loading_goal_buffer;
Austin Schuhfce2b602022-03-13 20:05:01 -070057 aos::FlatbufferFixedAllocatorArray<CatapultGoal, 512> catapult_goal_buffer;
Ravago Jones5da06352022-03-04 20:26:24 -080058
59 const aos::monotonic_clock::time_point timestamp =
60 event_loop()->context().monotonic_event_time;
Milind Upadhyay225156b2022-02-25 22:42:12 -080061
Henry Speiser55aa3ba2022-02-21 23:21:12 -080062 drivetrain_status_fetcher_.Fetch();
63 const float velocity = robot_velocity();
64
James Kuszmaul84083f42022-02-27 17:24:38 -080065 const turret::Aimer::Goal *auto_aim_goal = nullptr;
66 if (drivetrain_status_fetcher_.get() != nullptr) {
67 aimer_.Update(drivetrain_status_fetcher_.get(),
68 turret::Aimer::ShotMode::kShootOnTheFly);
69 auto_aim_goal = aimer_.TurretGoal();
70 }
71
Ravago Jones5da06352022-03-04 20:26:24 -080072 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
73 *turret_goal = nullptr;
Ravago Jones3283ce02022-03-09 19:31:29 -080074 const CatapultGoal *catapult_goal = nullptr;
Ravago Jones5da06352022-03-04 20:26:24 -080075 double roller_speed_compensated_front = 0.0;
76 double roller_speed_compensated_back = 0.0;
Milind Upadhyay29dcc172022-04-02 19:21:30 -070077 double transfer_roller_speed = 0.0;
Ravago Jones5da06352022-03-04 20:26:24 -080078 double flipper_arms_voltage = 0.0;
James Kuszmaul40526952022-03-13 15:56:38 -070079 bool have_active_intake_request = false;
milind-u6e7d8d42022-04-06 18:30:43 -070080 bool climber_servo = false;
Henry Speiser55aa3ba2022-02-21 23:21:12 -080081
82 if (unsafe_goal != nullptr) {
83 roller_speed_compensated_front =
84 unsafe_goal->roller_speed_front() +
85 std::max(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
86
87 roller_speed_compensated_back =
88 unsafe_goal->roller_speed_back() -
89 std::min(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
90
Milind Upadhyay29dcc172022-04-02 19:21:30 -070091 transfer_roller_speed = unsafe_goal->transfer_roller_speed();
milind-u086d7262022-01-19 20:44:18 -080092
milind-u6e7d8d42022-04-06 18:30:43 -070093 climber_servo = unsafe_goal->climber_servo();
94
James Kuszmaul84083f42022-02-27 17:24:38 -080095 turret_goal =
96 unsafe_goal->auto_aim() ? auto_aim_goal : unsafe_goal->turret();
Ravago Jones3283ce02022-03-09 19:31:29 -080097
98 catapult_goal = unsafe_goal->catapult();
99
100 constants::Values::ShotParams shot_params;
101 const double distance_to_goal = aimer_.DistanceToGoal();
James Kuszmaulb9ba9a52022-03-31 22:16:01 -0700102 if (!FLAGS_ignore_distance && unsafe_goal->auto_aim() &&
103 values_->shot_interpolation_table.GetInRange(distance_to_goal,
104 &shot_params)) {
Austin Schuhfce2b602022-03-13 20:05:01 -0700105 flatbuffers::FlatBufferBuilder *catapult_goal_fbb =
106 catapult_goal_buffer.fbb();
Ravago Jones3283ce02022-03-09 19:31:29 -0800107 std::optional<flatbuffers::Offset<
108 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal>>
109 return_position_offset;
110 if (unsafe_goal != nullptr && unsafe_goal->has_catapult() &&
111 unsafe_goal->catapult()->has_return_position()) {
Austin Schuhfce2b602022-03-13 20:05:01 -0700112 return_position_offset = {aos::CopyFlatBuffer(
113 unsafe_goal->catapult()->return_position(), catapult_goal_fbb)};
Ravago Jones3283ce02022-03-09 19:31:29 -0800114 }
Austin Schuhfce2b602022-03-13 20:05:01 -0700115 CatapultGoal::Builder builder(*catapult_goal_fbb);
Ravago Jones3283ce02022-03-09 19:31:29 -0800116 builder.add_shot_position(shot_params.shot_angle);
117 builder.add_shot_velocity(shot_params.shot_velocity);
118 if (return_position_offset.has_value()) {
119 builder.add_return_position(return_position_offset.value());
120 }
121 catapult_goal_buffer.Finish(builder.Finish());
122 catapult_goal = &catapult_goal_buffer.message();
123 }
James Kuszmaul40526952022-03-13 15:56:38 -0700124
125 if (unsafe_goal->has_turret_intake()) {
126 have_active_intake_request = true;
127 }
James Kuszmaul84083f42022-02-27 17:24:38 -0800128 }
Austin Schuh39f26f62022-02-24 21:34:46 -0800129
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800130 // Superstructure state machine:
Ravago Jones5da06352022-03-04 20:26:24 -0800131 // 1. IDLE: Wait until an intake beambreak is triggerred, meaning that a ball
132 // is being intaked. This means that the transfer rollers have a ball. If
133 // we've been waiting here for too long without any beambreak triggered, the
134 // ball got lost, so reset.
135 // 2. TRANSFERRING: Until the turret reaches the loading position where the
136 // ball can be transferred into the catapult, wiggle the ball in place.
137 // Once the turret reaches the loading position, send the ball forward with
138 // the transfer rollers until the turret beambreak is triggered.
139 // If we have been in this state for too long, the ball probably got lost so
140 // reset back to IDLE.
141 // 3. LOADING: To load the ball into the catapult, put the flippers at the
142 // feeding speed. Wait for a timeout, and then wait until the ball has gone
143 // past the turret beambreak and the flippers have stopped moving, meaning
144 // that the ball is fully loaded in the catapult.
145 // 4. LOADED: Wait until the user asks us to fire to transition to the
146 // shooting stage. If asked to cancel the shot, reset back to the IDLE state.
147 // 5. SHOOTING: Open the flippers to get ready for the shot. If they don't
148 // open quickly enough, try reseating the ball and going back to the LOADING
149 // stage, which moves the flippers in the opposite direction first.
150 // Now, hold the flippers open and wait until the turret has reached its
151 // aiming goal. Once the turret is ready, tell the catapult to fire.
152 // If the flippers move back for some reason now, it could damage the
153 // catapult, so estop it. Otherwise, wait until the catapult shoots a ball and
154 // goes back to its return position. We have now finished the shot, so return
155 // to IDLE.
156
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800157 // If we started off preloaded, skip to the loaded state.
158 // Make sure we weren't already there just in case.
159 if (unsafe_goal != nullptr && unsafe_goal->preloaded()) {
160 switch (state_) {
161 case SuperstructureState::IDLE:
162 case SuperstructureState::TRANSFERRING:
163 case SuperstructureState::LOADING:
164 state_ = SuperstructureState::LOADED;
165 loading_timer_ = timestamp;
166 break;
167 case SuperstructureState::LOADED:
168 case SuperstructureState::SHOOTING:
169 break;
170 }
171 }
172
James Kuszmaul40526952022-03-13 15:56:38 -0700173 if (position->intake_beambreak_front()) {
174 front_intake_has_ball_ = true;
175 front_intake_beambreak_timer_ = timestamp;
Ravago Jones5da06352022-03-04 20:26:24 -0800176 }
177
James Kuszmaul40526952022-03-13 15:56:38 -0700178 if (position->intake_beambreak_back()) {
179 back_intake_has_ball_ = true;
180 back_intake_beambreak_timer_ = timestamp;
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800181 }
182
James Kuszmaul40526952022-03-13 15:56:38 -0700183 // Check if we're either spitting of have lost the ball.
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700184 if ((transfer_roller_speed < 0.0 && front_intake_has_ball_) ||
James Kuszmaul40526952022-03-13 15:56:38 -0700185 timestamp >
186 front_intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
187 front_intake_has_ball_ = false;
188 }
189
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700190 if ((transfer_roller_speed > 0.0 && back_intake_has_ball_) ||
James Kuszmaul40526952022-03-13 15:56:38 -0700191 timestamp >
192 back_intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
193 back_intake_has_ball_ = false;
194 }
195
196 // Wiggle transfer rollers: send the ball back and forth while waiting
197 // for the turret or waiting for another shot to be completed
198 const double wiggle_voltage =
199 constants::Values::kTransferRollerWiggleVoltage();
200 if (front_intake_has_ball_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800201 roller_speed_compensated_front = 0.0;
James Kuszmaul40526952022-03-13 15:56:38 -0700202 if (position->intake_beambreak_front()) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700203 transfer_roller_speed = -wiggle_voltage;
Ravago Jones5da06352022-03-04 20:26:24 -0800204 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700205 transfer_roller_speed = wiggle_voltage;
Ravago Jones5da06352022-03-04 20:26:24 -0800206 }
207 }
208
James Kuszmaul40526952022-03-13 15:56:38 -0700209 if (back_intake_has_ball_) {
210 roller_speed_compensated_back = 0.0;
211 if (position->intake_beambreak_back()) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700212 transfer_roller_speed = wiggle_voltage;
James Kuszmaul40526952022-03-13 15:56:38 -0700213 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700214 transfer_roller_speed = -wiggle_voltage;
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800215 }
216 }
217
James Kuszmaul40526952022-03-13 15:56:38 -0700218 // Create the goal message for putting the turret in its loading position.
219 // This will then get used in the state machine for the states (IDLE and
220 // TRANSFERRING) that use it.
221 double turret_loading_position =
222 (turret_intake_state_ == RequestedIntake::kFront
223 ? constants::Values::kTurretFrontIntakePos()
224 : constants::Values::kTurretBackIntakePos());
225 // Turn to the loading position as close to the current position as
226 // possible.
227 turret_loading_position =
228 turret_.estimated_position() +
229 aos::math::NormalizeAngle(turret_loading_position -
230 turret_.estimated_position());
231 // if out of range, reset back to within +/- pi of zero.
232 if (turret_loading_position > constants::Values::kTurretRange().upper ||
233 turret_loading_position < constants::Values::kTurretRange().lower) {
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800234 turret_loading_position =
James Kuszmaul2a59cf02022-03-17 11:02:02 -0700235 frc971::zeroing::Wrap(constants::Values::kTurretRange().middle_soft(),
James Kuszmaul40526952022-03-13 15:56:38 -0700236 turret_loading_position, 2.0 * M_PI);
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800237 }
238
James Kuszmaul40526952022-03-13 15:56:38 -0700239 turret_loading_goal_buffer.Finish(
240 frc971::control_loops::CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
241 *turret_loading_goal_buffer.fbb(), turret_loading_position));
242
Henry Speiser77747b72022-03-06 17:18:29 -0800243 const bool turret_near_goal =
244 turret_goal != nullptr &&
245 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
246 kTurretGoalThreshold;
247 const bool collided = collision_avoidance_.IsCollided(
248 {.intake_front_position = intake_front_.estimated_position(),
249 .intake_back_position = intake_back_.estimated_position(),
250 .turret_position = turret_.estimated_position(),
251 .shooting = true});
252
Henry Speisera9c49a22022-04-01 15:22:35 -0700253 // Dont shoot if the robot is moving faster than this
James Kuszmaulb9ba9a52022-03-31 22:16:01 -0700254 constexpr double kMaxShootSpeed = 2.7;
Henry Speisera9c49a22022-04-01 15:22:35 -0700255 const bool moving_too_fast = std::abs(robot_velocity()) > kMaxShootSpeed;
256
Ravago Jones5da06352022-03-04 20:26:24 -0800257 switch (state_) {
258 case SuperstructureState::IDLE: {
James Kuszmaul40526952022-03-13 15:56:38 -0700259 // Only change the turret's goal loading position when idle, to prevent us
260 // spinning the turret around when TRANSFERRING...
Milind Upadhyay90648532022-03-13 20:37:02 -0700261 if (have_active_intake_request) {
262 turret_intake_state_ = unsafe_goal->turret_intake();
263 }
James Kuszmaul40526952022-03-13 15:56:38 -0700264 if (front_intake_has_ball_ != back_intake_has_ball_) {
James Kuszmaul40526952022-03-13 15:56:38 -0700265 turret_intake_state_ = front_intake_has_ball_ ? RequestedIntake::kFront
266 : RequestedIntake::kBack;
267 }
268 // When IDLE with no specific intake button pressed, allow the goal
269 // message to override the intaking stuff.
Austin Schuh92908812022-03-27 14:14:05 -0700270 if (have_active_intake_request || (turret_goal == nullptr)) {
James Kuszmaul40526952022-03-13 15:56:38 -0700271 turret_goal = &turret_loading_goal_buffer.message();
Ravago Jones5da06352022-03-04 20:26:24 -0800272 }
273
James Kuszmaul40526952022-03-13 15:56:38 -0700274 if (!front_intake_has_ball_ && !back_intake_has_ball_) {
Austin Schuh92908812022-03-27 14:14:05 -0700275 last_shot_angle_ = std::nullopt;
Ravago Jones5da06352022-03-04 20:26:24 -0800276 break;
277 }
278
279 state_ = SuperstructureState::TRANSFERRING;
Ravago Jones5da06352022-03-04 20:26:24 -0800280 // Save the side the ball is on for later
281
282 break;
283 }
284 case SuperstructureState::TRANSFERRING: {
James Kuszmaul40526952022-03-13 15:56:38 -0700285 // If we've been transferring for too long, the ball probably got lost.
286 if ((turret_intake_state_ == RequestedIntake::kFront &&
287 !front_intake_has_ball_) ||
288 (turret_intake_state_ == RequestedIntake::kBack &&
289 !back_intake_has_ball_)) {
Ravago Jones5da06352022-03-04 20:26:24 -0800290 state_ = SuperstructureState::IDLE;
291 break;
292 }
293
James Kuszmaul40526952022-03-13 15:56:38 -0700294 turret_goal = &turret_loading_goal_buffer.message();
295
Ravago Jones5da06352022-03-04 20:26:24 -0800296 const bool turret_near_goal =
297 std::abs(turret_.estimated_position() - turret_loading_position) <
298 kTurretGoalThreshold;
299 if (!turret_near_goal) {
300 break; // Wait for turret to reach the chosen intake
301 }
302
303 // Transfer rollers and flipper arm belt on
James Kuszmaul40526952022-03-13 15:56:38 -0700304 if (turret_intake_state_ == RequestedIntake::kFront) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700305 transfer_roller_speed = constants::Values::kTransferRollerVoltage();
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800306 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700307 transfer_roller_speed = -constants::Values::kTransferRollerVoltage();
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800308 }
Ravago Jones5da06352022-03-04 20:26:24 -0800309 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
310
311 // Ball is in catapult
312 if (position->turret_beambreak()) {
James Kuszmaul40526952022-03-13 15:56:38 -0700313 if (turret_intake_state_ == RequestedIntake::kFront) {
314 front_intake_has_ball_ = false;
315 } else {
316 back_intake_has_ball_ = false;
317 }
Ravago Jones5da06352022-03-04 20:26:24 -0800318 state_ = SuperstructureState::LOADING;
319 loading_timer_ = timestamp;
320 }
321 break;
322 }
323 case SuperstructureState::LOADING: {
324 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
325
326 // Keep feeding for kExtraLoadingTime
327
Ravago Jones5da06352022-03-04 20:26:24 -0800328 // The ball should go past the turret beambreak to be loaded.
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800329 // If we got a CAN reading not too long ago, the flippers should have
330 // also stopped.
Milind Upadhyay47e0d032022-03-05 23:06:25 -0800331 if (position->turret_beambreak()) {
332 loading_timer_ = timestamp;
333 } else if (timestamp >
334 loading_timer_ + constants::Values::kExtraLoadingTime()) {
Ravago Jones5da06352022-03-04 20:26:24 -0800335 state_ = SuperstructureState::LOADED;
336 reseating_in_catapult_ = false;
337 }
338 break;
339 }
340 case SuperstructureState::LOADED: {
341 if (unsafe_goal != nullptr) {
Austin Schuh92908812022-03-27 14:14:05 -0700342 if (turret_goal == nullptr) {
343 if (last_shot_angle_) {
344 turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
345 *last_shot_angle_);
346 }
347 turret_goal = &turret_loading_goal_buffer.message();
348 }
Ravago Jones5da06352022-03-04 20:26:24 -0800349 if (unsafe_goal->cancel_shot()) {
350 // Cancel the shot process
351 state_ = SuperstructureState::IDLE;
352 } else if (unsafe_goal->fire()) {
353 // Start if we were asked to and the turret is at goal
354 state_ = SuperstructureState::SHOOTING;
355 prev_shot_count_ = catapult_.shot_count();
356
357 // Reset opening timeout
358 flipper_opening_start_time_ = timestamp;
359 }
360 }
361 break;
362 }
363 case SuperstructureState::SHOOTING: {
Austin Schuh92908812022-03-27 14:14:05 -0700364 if (turret_goal == nullptr) {
365 if (last_shot_angle_) {
366 turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
367 *last_shot_angle_);
368 }
369 turret_goal = &turret_loading_goal_buffer.message();
370 last_shot_angle_ = turret_goal->unsafe_goal();
371 } else {
372 last_shot_angle_ = std::nullopt;
373 }
374 const bool turret_near_goal =
375 turret_goal != nullptr &&
376 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
377 kTurretGoalThreshold;
378
milind-uee3b0012022-03-14 21:19:57 -0700379 // Don't open the flippers until the turret's ready: give them as little
Henry Speisera9c49a22022-04-01 15:22:35 -0700380 // time to get bumped as possible. Or moving to fast.
381 if (!turret_near_goal || collided || moving_too_fast) {
milind-uee3b0012022-03-14 21:19:57 -0700382 break;
383 }
384
Ravago Jones5da06352022-03-04 20:26:24 -0800385 // Opening flipper arms could fail, wait until they are open using their
386 // potentiometers (the member below is just named encoder).
387 // Be a little more lenient if the flippers were already open in case of
388 // noise or collisions.
389 const double flipper_open_position =
390 (flippers_open_ ? constants::Values::kReseatFlipperPosition()
391 : constants::Values::kFlipperOpenPosition());
milind-u37dc40b2022-03-08 19:51:27 -0800392
393 // TODO(milind): add left arm back once it's fixed
Ravago Jones5da06352022-03-04 20:26:24 -0800394 flippers_open_ =
Ravago Jones5da06352022-03-04 20:26:24 -0800395 position->flipper_arm_right()->encoder() >= flipper_open_position;
396
397 if (flippers_open_) {
398 // Hold at kFlipperHoldVoltage
399 flipper_arms_voltage = constants::Values::kFlipperHoldVoltage();
400 } else {
401 // Open at kFlipperOpenVoltage
402 flipper_arms_voltage = constants::Values::kFlipperOpenVoltage();
403 }
404
milind-uee3b0012022-03-14 21:19:57 -0700405 // There are two possible failures for the flippers:
406 // 1. They never open on time
407 // 2. They opened and we started firing, but we got bumped or something
408 // and they went back.
409 // If the flippers didn't open in a reasonable amount of time, try
410 // reseating the ball and reversing them.
411 if (!fire_ && !flippers_open_ &&
Ravago Jones5da06352022-03-04 20:26:24 -0800412 timestamp >
413 loading_timer_ + constants::Values::kFlipperOpeningTimeout()) {
414 // Reseat the ball and try again
415 state_ = SuperstructureState::LOADING;
416 loading_timer_ = timestamp;
417 reseating_in_catapult_ = true;
418 break;
419 }
Ravago Jones5da06352022-03-04 20:26:24 -0800420
Henry Speiser28288cc2022-03-09 22:59:24 -0800421 // If the turret reached the aiming goal and the catapult is safe to move
422 // up, fire!
423 if (flippers_open_ && turret_near_goal && !collided) {
Ravago Jones5da06352022-03-04 20:26:24 -0800424 fire_ = true;
425 }
426
Ravago Jones5da06352022-03-04 20:26:24 -0800427 const bool near_return_position =
428 (unsafe_goal != nullptr && unsafe_goal->has_catapult() &&
429 unsafe_goal->catapult()->has_return_position() &&
430 std::abs(unsafe_goal->catapult()->return_position()->unsafe_goal() -
431 catapult_.estimated_position()) < kCatapultGoalThreshold);
432
433 // Once the shot is complete and the catapult is back to its return
434 // position, go back to IDLE
435 if (catapult_.shot_count() > prev_shot_count_ && near_return_position) {
436 prev_shot_count_ = catapult_.shot_count();
437 fire_ = false;
438 state_ = SuperstructureState::IDLE;
439 }
440
441 break;
442 }
443 }
444
445 collision_avoidance_.UpdateGoal(
446 {.intake_front_position = intake_front_.estimated_position(),
447 .intake_back_position = intake_back_.estimated_position(),
Henry Speiser28288cc2022-03-09 22:59:24 -0800448 .turret_position = turret_.estimated_position(),
449 .shooting = state_ == SuperstructureState::SHOOTING},
Ravago Jones5da06352022-03-04 20:26:24 -0800450 turret_goal);
451
452 turret_.set_min_position(collision_avoidance_.min_turret_goal());
453 turret_.set_max_position(collision_avoidance_.max_turret_goal());
454 intake_front_.set_min_position(collision_avoidance_.min_intake_front_goal());
455 intake_front_.set_max_position(collision_avoidance_.max_intake_front_goal());
456 intake_back_.set_min_position(collision_avoidance_.min_intake_back_goal());
457 intake_back_.set_max_position(collision_avoidance_.max_intake_back_goal());
458
James Kuszmaul84083f42022-02-27 17:24:38 -0800459 const flatbuffers::Offset<AimerStatus> aimer_offset =
460 aimer_.PopulateStatus(status->fbb());
461
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800462 // Disable the catapult if we want to restart to prevent damage with
463 // flippers
Austin Schuh39f26f62022-02-24 21:34:46 -0800464 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh97410d72022-03-12 15:37:23 -0800465 catapult_status_offset = catapult_.Iterate(
Ravago Jones3283ce02022-03-09 19:31:29 -0800466 catapult_goal, position, robot_state().voltage_battery(),
Austin Schuh97410d72022-03-12 15:37:23 -0800467 output != nullptr && !catapult_.estopped()
468 ? &(output_struct.catapult_voltage)
469 : nullptr,
470 fire_, status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800471
Ravago Jones5da06352022-03-04 20:26:24 -0800472 const flatbuffers::Offset<RelativeEncoderProfiledJointStatus>
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800473 climber_status_offset = climber_.Iterate(
474 unsafe_goal != nullptr ? unsafe_goal->climber() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800475 position->climber(),
476 output != nullptr ? &output_struct.climber_voltage : nullptr,
477 status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800478
Ravago Jones5da06352022-03-04 20:26:24 -0800479 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800480 intake_status_offset_front = intake_front_.Iterate(
481 unsafe_goal != nullptr ? unsafe_goal->intake_front() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800482 position->intake_front(),
483 output != nullptr ? &output_struct.intake_voltage_front : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800484 status->fbb());
485
Ravago Jones5da06352022-03-04 20:26:24 -0800486 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800487 intake_status_offset_back = intake_back_.Iterate(
488 unsafe_goal != nullptr ? unsafe_goal->intake_back() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800489 position->intake_back(),
490 output != nullptr ? &output_struct.intake_voltage_back : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800491 status->fbb());
492
Ravago Jones5da06352022-03-04 20:26:24 -0800493 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh7f120362022-03-05 17:10:11 -0800494 turret_status_offset = turret_.Iterate(
495 turret_goal, position->turret(),
496 output != nullptr ? &output_struct.turret_voltage : nullptr,
497 status->fbb());
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800498
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800499 if (output != nullptr) {
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800500 output_struct.roller_voltage_front = roller_speed_compensated_front;
501 output_struct.roller_voltage_back = roller_speed_compensated_back;
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700502 output_struct.transfer_roller_voltage = transfer_roller_speed;
Ravago Jones5da06352022-03-04 20:26:24 -0800503 output_struct.flipper_arms_voltage = flipper_arms_voltage;
milind-u6e7d8d42022-04-06 18:30:43 -0700504 if (climber_servo) {
505 output_struct.climber_servo_left = 0.0;
506 output_struct.climber_servo_right = 1.0;
507 } else {
508 output_struct.climber_servo_left = 1.0;
509 output_struct.climber_servo_right = 0.0;
510 }
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800511
milind-u086d7262022-01-19 20:44:18 -0800512 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
513 }
514
515 Status::Builder status_builder = status->MakeBuilder<Status>();
516
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800517 const bool zeroed = intake_front_.zeroed() && intake_back_.zeroed() &&
Ravago Jones5da06352022-03-04 20:26:24 -0800518 turret_.zeroed() && climber_.zeroed() &&
519 catapult_.zeroed();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800520 const bool estopped = intake_front_.estopped() || intake_back_.estopped() ||
Ravago Jones5da06352022-03-04 20:26:24 -0800521 turret_.estopped() || climber_.estopped() ||
522 catapult_.estopped();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800523
524 status_builder.add_zeroed(zeroed);
525 status_builder.add_estopped(estopped);
526
527 status_builder.add_intake_front(intake_status_offset_front);
528 status_builder.add_intake_back(intake_status_offset_back);
529 status_builder.add_turret(turret_status_offset);
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800530 status_builder.add_climber(climber_status_offset);
Ravago Jones5da06352022-03-04 20:26:24 -0800531
Austin Schuh39f26f62022-02-24 21:34:46 -0800532 status_builder.add_catapult(catapult_status_offset);
533 status_builder.add_solve_time(catapult_.solve_time());
Austin Schuh80fc2752022-02-25 13:33:56 -0800534 status_builder.add_shot_count(catapult_.shot_count());
Austin Schuh41472552022-03-13 18:09:41 -0700535 status_builder.add_mpc_horizon(catapult_.mpc_horizon());
Ravago Jones3283ce02022-03-09 19:31:29 -0800536 if (catapult_goal != nullptr) {
537 status_builder.add_shot_position(catapult_goal->shot_position());
538 status_builder.add_shot_velocity(catapult_goal->shot_velocity());
539 }
Ravago Jones5da06352022-03-04 20:26:24 -0800540
541 status_builder.add_flippers_open(flippers_open_);
542 status_builder.add_reseating_in_catapult(reseating_in_catapult_);
543 status_builder.add_fire(fire_);
Henry Speisera9c49a22022-04-01 15:22:35 -0700544 status_builder.add_moving_too_fast(moving_too_fast);
Henry Speiser77747b72022-03-06 17:18:29 -0800545 status_builder.add_ready_to_fire(state_ == SuperstructureState::LOADED &&
546 turret_near_goal && !collided);
Ravago Jones5da06352022-03-04 20:26:24 -0800547 status_builder.add_state(state_);
James Kuszmaul40526952022-03-13 15:56:38 -0700548 if (!front_intake_has_ball_ && !back_intake_has_ball_) {
549 status_builder.add_intake_state(IntakeState::NO_BALL);
550 } else if (front_intake_has_ball_ && back_intake_has_ball_) {
551 status_builder.add_intake_state(turret_intake_state_ ==
552 RequestedIntake::kFront
553 ? IntakeState::INTAKE_FRONT_BALL
554 : IntakeState::INTAKE_BACK_BALL);
555 } else {
556 status_builder.add_intake_state(front_intake_has_ball_
557 ? IntakeState::INTAKE_FRONT_BALL
558 : IntakeState::INTAKE_BACK_BALL);
559 }
560 status_builder.add_front_intake_has_ball(front_intake_has_ball_);
561 status_builder.add_back_intake_has_ball(back_intake_has_ball_);
milind-u086d7262022-01-19 20:44:18 -0800562
James Kuszmaul84083f42022-02-27 17:24:38 -0800563 status_builder.add_aimer(aimer_offset);
564
milind-u086d7262022-01-19 20:44:18 -0800565 (void)status->Send(status_builder.Finish());
566}
567
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800568double Superstructure::robot_velocity() const {
569 return (drivetrain_status_fetcher_.get() != nullptr
570 ? drivetrain_status_fetcher_->robot_speed()
571 : 0.0);
572}
573
milind-u086d7262022-01-19 20:44:18 -0800574} // namespace superstructure
575} // namespace control_loops
576} // namespace y2022