blob: cee13bf596655643525df1d219f4f8de178ecc60 [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")),
Ravago Jonesd51af7a2022-03-26 21:44:20 -070035 joystick_state_fetcher_(
36 event_loop->MakeFetcher<aos::JoystickState>("/aos")),
37 ball_color_fetcher_(event_loop->MakeFetcher<y2022::vision::BallColor>(
38 "/superstructure")),
James Kuszmaulb9ba9a52022-03-31 22:16:01 -070039 aimer_(values) {
milind-u086d7262022-01-19 20:44:18 -080040 event_loop->SetRuntimeRealtimePriority(30);
41}
42
43void Superstructure::RunIteration(const Goal *unsafe_goal,
Siddhant Kanwar0e37f592022-02-21 19:26:50 -080044 const Position *position,
milind-u086d7262022-01-19 20:44:18 -080045 aos::Sender<Output>::Builder *output,
46 aos::Sender<Status>::Builder *status) {
47 if (WasReset()) {
48 AOS_LOG(ERROR, "WPILib reset, restarting\n");
Henry Speiser55aa3ba2022-02-21 23:21:12 -080049 intake_front_.Reset();
50 intake_back_.Reset();
51 turret_.Reset();
52 climber_.Reset();
Austin Schuh39f26f62022-02-24 21:34:46 -080053 catapult_.Reset();
Henry Speiser55aa3ba2022-02-21 23:21:12 -080054 }
55
Ravago Jones5da06352022-03-04 20:26:24 -080056 OutputT output_struct;
Milind Upadhyay225156b2022-02-25 22:42:12 -080057
Ravago Jones5da06352022-03-04 20:26:24 -080058 aos::FlatbufferFixedAllocatorArray<
Austin Schuhfce2b602022-03-13 20:05:01 -070059 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal, 512>
James Kuszmaul40526952022-03-13 15:56:38 -070060 turret_loading_goal_buffer;
Austin Schuhfce2b602022-03-13 20:05:01 -070061 aos::FlatbufferFixedAllocatorArray<CatapultGoal, 512> catapult_goal_buffer;
Ravago Jonesd51af7a2022-03-26 21:44:20 -070062 aos::FlatbufferFixedAllocatorArray<CatapultGoal, 512>
63 catapult_discarding_goal_buffer;
Ravago Jones5da06352022-03-04 20:26:24 -080064
65 const aos::monotonic_clock::time_point timestamp =
66 event_loop()->context().monotonic_event_time;
Milind Upadhyay225156b2022-02-25 22:42:12 -080067
Ravago Jonesd51af7a2022-03-26 21:44:20 -070068 if (joystick_state_fetcher_.Fetch() &&
69 joystick_state_fetcher_->has_alliance()) {
70 alliance_ = joystick_state_fetcher_->alliance();
71 }
72
73 if (ball_color_fetcher_.Fetch() && ball_color_fetcher_->has_ball_color()) {
74 ball_color_ = ball_color_fetcher_->ball_color();
75 }
76
77 if (alliance_ != aos::Alliance::kInvalid &&
78 ball_color_ != aos::Alliance::kInvalid && alliance_ != ball_color_) {
79 switch (state_) {
80 case SuperstructureState::IDLE:
81 break;
82 case SuperstructureState::TRANSFERRING:
83 break;
84 case SuperstructureState::LOADING:
85 break;
86 case SuperstructureState::LOADED:
87 discarding_ball_ = true;
88 break;
89 case SuperstructureState::SHOOTING:
90 if (!fire_) {
91 // we can still tell it not to shoot into the hub
92 // and change the turret and catapult goals
93 discarding_ball_ = true;
94 }
95 break;
96 }
97 }
98
Henry Speiser55aa3ba2022-02-21 23:21:12 -080099 drivetrain_status_fetcher_.Fetch();
100 const float velocity = robot_velocity();
101
James Kuszmaul84083f42022-02-27 17:24:38 -0800102 const turret::Aimer::Goal *auto_aim_goal = nullptr;
103 if (drivetrain_status_fetcher_.get() != nullptr) {
104 aimer_.Update(drivetrain_status_fetcher_.get(),
105 turret::Aimer::ShotMode::kShootOnTheFly);
106 auto_aim_goal = aimer_.TurretGoal();
107 }
108
Ravago Jones5da06352022-03-04 20:26:24 -0800109 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
110 *turret_goal = nullptr;
Ravago Jones3283ce02022-03-09 19:31:29 -0800111 const CatapultGoal *catapult_goal = nullptr;
Ravago Jones5da06352022-03-04 20:26:24 -0800112 double roller_speed_compensated_front = 0.0;
113 double roller_speed_compensated_back = 0.0;
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700114 double transfer_roller_speed = 0.0;
Ravago Jones5da06352022-03-04 20:26:24 -0800115 double flipper_arms_voltage = 0.0;
James Kuszmaul40526952022-03-13 15:56:38 -0700116 bool have_active_intake_request = false;
milind-u6e7d8d42022-04-06 18:30:43 -0700117 bool climber_servo = false;
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800118
119 if (unsafe_goal != nullptr) {
120 roller_speed_compensated_front =
121 unsafe_goal->roller_speed_front() +
122 std::max(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
123
124 roller_speed_compensated_back =
125 unsafe_goal->roller_speed_back() -
126 std::min(velocity * unsafe_goal->roller_speed_compensation(), 0.0);
127
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700128 transfer_roller_speed = unsafe_goal->transfer_roller_speed();
milind-u086d7262022-01-19 20:44:18 -0800129
milind-u6e7d8d42022-04-06 18:30:43 -0700130 climber_servo = unsafe_goal->climber_servo();
131
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700132 turret_goal = unsafe_goal->auto_aim() && !discarding_ball_
133 ? auto_aim_goal
134 : unsafe_goal->turret();
Ravago Jones3283ce02022-03-09 19:31:29 -0800135
136 catapult_goal = unsafe_goal->catapult();
137
138 constants::Values::ShotParams shot_params;
139 const double distance_to_goal = aimer_.DistanceToGoal();
James Kuszmaulb9ba9a52022-03-31 22:16:01 -0700140 if (!FLAGS_ignore_distance && unsafe_goal->auto_aim() &&
141 values_->shot_interpolation_table.GetInRange(distance_to_goal,
142 &shot_params)) {
Austin Schuhfce2b602022-03-13 20:05:01 -0700143 flatbuffers::FlatBufferBuilder *catapult_goal_fbb =
144 catapult_goal_buffer.fbb();
Ravago Jones3283ce02022-03-09 19:31:29 -0800145 std::optional<flatbuffers::Offset<
146 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal>>
147 return_position_offset;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700148 if (unsafe_goal->has_catapult() &&
Ravago Jones3283ce02022-03-09 19:31:29 -0800149 unsafe_goal->catapult()->has_return_position()) {
Austin Schuhfce2b602022-03-13 20:05:01 -0700150 return_position_offset = {aos::CopyFlatBuffer(
151 unsafe_goal->catapult()->return_position(), catapult_goal_fbb)};
Ravago Jones3283ce02022-03-09 19:31:29 -0800152 }
Austin Schuhfce2b602022-03-13 20:05:01 -0700153 CatapultGoal::Builder builder(*catapult_goal_fbb);
Ravago Jones3283ce02022-03-09 19:31:29 -0800154 builder.add_shot_position(shot_params.shot_angle);
155 builder.add_shot_velocity(shot_params.shot_velocity);
156 if (return_position_offset.has_value()) {
157 builder.add_return_position(return_position_offset.value());
158 }
159 catapult_goal_buffer.Finish(builder.Finish());
160 catapult_goal = &catapult_goal_buffer.message();
161 }
James Kuszmaul40526952022-03-13 15:56:38 -0700162
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700163 if (discarding_ball_) {
164 flatbuffers::FlatBufferBuilder *catapult_goal_fbb =
165 catapult_discarding_goal_buffer.fbb();
166 std::optional<flatbuffers::Offset<
167 frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal>>
168 return_position_offset;
169 if (unsafe_goal->has_catapult() &&
170 unsafe_goal->catapult()->has_return_position()) {
171 return_position_offset = {aos::CopyFlatBuffer(
172 unsafe_goal->catapult()->return_position(), catapult_goal_fbb)};
173 }
174 CatapultGoal::Builder builder(*catapult_goal_fbb);
175 builder.add_shot_position(kDiscardingPosition);
176 builder.add_shot_velocity(kDiscardingVelocity);
177 if (return_position_offset.has_value()) {
178 builder.add_return_position(return_position_offset.value());
179 }
180 catapult_discarding_goal_buffer.Finish(builder.Finish());
181 catapult_goal = &catapult_discarding_goal_buffer.message();
182 }
183
James Kuszmaul40526952022-03-13 15:56:38 -0700184 if (unsafe_goal->has_turret_intake()) {
185 have_active_intake_request = true;
186 }
James Kuszmaul84083f42022-02-27 17:24:38 -0800187 }
Austin Schuh39f26f62022-02-24 21:34:46 -0800188
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800189 // Superstructure state machine:
Ravago Jones5da06352022-03-04 20:26:24 -0800190 // 1. IDLE: Wait until an intake beambreak is triggerred, meaning that a ball
191 // is being intaked. This means that the transfer rollers have a ball. If
192 // we've been waiting here for too long without any beambreak triggered, the
193 // ball got lost, so reset.
194 // 2. TRANSFERRING: Until the turret reaches the loading position where the
195 // ball can be transferred into the catapult, wiggle the ball in place.
196 // Once the turret reaches the loading position, send the ball forward with
197 // the transfer rollers until the turret beambreak is triggered.
198 // If we have been in this state for too long, the ball probably got lost so
199 // reset back to IDLE.
200 // 3. LOADING: To load the ball into the catapult, put the flippers at the
201 // feeding speed. Wait for a timeout, and then wait until the ball has gone
202 // past the turret beambreak and the flippers have stopped moving, meaning
203 // that the ball is fully loaded in the catapult.
204 // 4. LOADED: Wait until the user asks us to fire to transition to the
205 // shooting stage. If asked to cancel the shot, reset back to the IDLE state.
206 // 5. SHOOTING: Open the flippers to get ready for the shot. If they don't
207 // open quickly enough, try reseating the ball and going back to the LOADING
208 // stage, which moves the flippers in the opposite direction first.
209 // Now, hold the flippers open and wait until the turret has reached its
210 // aiming goal. Once the turret is ready, tell the catapult to fire.
211 // If the flippers move back for some reason now, it could damage the
212 // catapult, so estop it. Otherwise, wait until the catapult shoots a ball and
213 // goes back to its return position. We have now finished the shot, so return
214 // to IDLE.
215
Milind Upadhyay803bbf02022-03-11 17:56:26 -0800216 // If we started off preloaded, skip to the loaded state.
217 // Make sure we weren't already there just in case.
218 if (unsafe_goal != nullptr && unsafe_goal->preloaded()) {
219 switch (state_) {
220 case SuperstructureState::IDLE:
221 case SuperstructureState::TRANSFERRING:
222 case SuperstructureState::LOADING:
223 state_ = SuperstructureState::LOADED;
224 loading_timer_ = timestamp;
225 break;
226 case SuperstructureState::LOADED:
227 case SuperstructureState::SHOOTING:
228 break;
229 }
230 }
231
James Kuszmaul40526952022-03-13 15:56:38 -0700232 if (position->intake_beambreak_front()) {
233 front_intake_has_ball_ = true;
234 front_intake_beambreak_timer_ = timestamp;
Ravago Jones5da06352022-03-04 20:26:24 -0800235 }
236
James Kuszmaul40526952022-03-13 15:56:38 -0700237 if (position->intake_beambreak_back()) {
238 back_intake_has_ball_ = true;
239 back_intake_beambreak_timer_ = timestamp;
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800240 }
241
James Kuszmaul40526952022-03-13 15:56:38 -0700242 // Check if we're either spitting of have lost the ball.
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700243 if ((transfer_roller_speed < 0.0 && front_intake_has_ball_) ||
James Kuszmaul40526952022-03-13 15:56:38 -0700244 timestamp >
245 front_intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
246 front_intake_has_ball_ = false;
247 }
248
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700249 if ((transfer_roller_speed > 0.0 && back_intake_has_ball_) ||
James Kuszmaul40526952022-03-13 15:56:38 -0700250 timestamp >
251 back_intake_beambreak_timer_ + constants::Values::kBallLostTime()) {
252 back_intake_has_ball_ = false;
253 }
254
255 // Wiggle transfer rollers: send the ball back and forth while waiting
256 // for the turret or waiting for another shot to be completed
257 const double wiggle_voltage =
258 constants::Values::kTransferRollerWiggleVoltage();
259 if (front_intake_has_ball_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800260 roller_speed_compensated_front = 0.0;
James Kuszmaul40526952022-03-13 15:56:38 -0700261 if (position->intake_beambreak_front()) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700262 transfer_roller_speed = -wiggle_voltage;
Ravago Jones5da06352022-03-04 20:26:24 -0800263 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700264 transfer_roller_speed = wiggle_voltage;
Ravago Jones5da06352022-03-04 20:26:24 -0800265 }
266 }
267
James Kuszmaul40526952022-03-13 15:56:38 -0700268 if (back_intake_has_ball_) {
269 roller_speed_compensated_back = 0.0;
270 if (position->intake_beambreak_back()) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700271 transfer_roller_speed = wiggle_voltage;
James Kuszmaul40526952022-03-13 15:56:38 -0700272 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700273 transfer_roller_speed = -wiggle_voltage;
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800274 }
275 }
276
James Kuszmaul40526952022-03-13 15:56:38 -0700277 // Create the goal message for putting the turret in its loading position.
278 // This will then get used in the state machine for the states (IDLE and
279 // TRANSFERRING) that use it.
280 double turret_loading_position =
281 (turret_intake_state_ == RequestedIntake::kFront
282 ? constants::Values::kTurretFrontIntakePos()
283 : constants::Values::kTurretBackIntakePos());
284 // Turn to the loading position as close to the current position as
285 // possible.
286 turret_loading_position =
287 turret_.estimated_position() +
288 aos::math::NormalizeAngle(turret_loading_position -
289 turret_.estimated_position());
290 // if out of range, reset back to within +/- pi of zero.
291 if (turret_loading_position > constants::Values::kTurretRange().upper ||
292 turret_loading_position < constants::Values::kTurretRange().lower) {
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800293 turret_loading_position =
James Kuszmaul2a59cf02022-03-17 11:02:02 -0700294 frc971::zeroing::Wrap(constants::Values::kTurretRange().middle_soft(),
James Kuszmaul40526952022-03-13 15:56:38 -0700295 turret_loading_position, 2.0 * M_PI);
Milind Upadhyay918ecf02022-03-12 15:55:46 -0800296 }
297
James Kuszmaul40526952022-03-13 15:56:38 -0700298 turret_loading_goal_buffer.Finish(
299 frc971::control_loops::CreateStaticZeroingSingleDOFProfiledSubsystemGoal(
300 *turret_loading_goal_buffer.fbb(), turret_loading_position));
301
Henry Speiser77747b72022-03-06 17:18:29 -0800302 const bool turret_near_goal =
303 turret_goal != nullptr &&
304 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
305 kTurretGoalThreshold;
306 const bool collided = collision_avoidance_.IsCollided(
307 {.intake_front_position = intake_front_.estimated_position(),
308 .intake_back_position = intake_back_.estimated_position(),
309 .turret_position = turret_.estimated_position(),
310 .shooting = true});
311
Henry Speisera9c49a22022-04-01 15:22:35 -0700312 // Dont shoot if the robot is moving faster than this
James Kuszmaulb9ba9a52022-03-31 22:16:01 -0700313 constexpr double kMaxShootSpeed = 2.7;
Henry Speisera9c49a22022-04-01 15:22:35 -0700314 const bool moving_too_fast = std::abs(robot_velocity()) > kMaxShootSpeed;
315
Ravago Jones5da06352022-03-04 20:26:24 -0800316 switch (state_) {
317 case SuperstructureState::IDLE: {
James Kuszmaul40526952022-03-13 15:56:38 -0700318 // Only change the turret's goal loading position when idle, to prevent us
319 // spinning the turret around when TRANSFERRING...
Milind Upadhyay90648532022-03-13 20:37:02 -0700320 if (have_active_intake_request) {
321 turret_intake_state_ = unsafe_goal->turret_intake();
322 }
James Kuszmaul40526952022-03-13 15:56:38 -0700323 if (front_intake_has_ball_ != back_intake_has_ball_) {
James Kuszmaul40526952022-03-13 15:56:38 -0700324 turret_intake_state_ = front_intake_has_ball_ ? RequestedIntake::kFront
325 : RequestedIntake::kBack;
326 }
327 // When IDLE with no specific intake button pressed, allow the goal
328 // message to override the intaking stuff.
Austin Schuh92908812022-03-27 14:14:05 -0700329 if (have_active_intake_request || (turret_goal == nullptr)) {
James Kuszmaul40526952022-03-13 15:56:38 -0700330 turret_goal = &turret_loading_goal_buffer.message();
Ravago Jones5da06352022-03-04 20:26:24 -0800331 }
332
James Kuszmaul40526952022-03-13 15:56:38 -0700333 if (!front_intake_has_ball_ && !back_intake_has_ball_) {
Austin Schuh92908812022-03-27 14:14:05 -0700334 last_shot_angle_ = std::nullopt;
Ravago Jones5da06352022-03-04 20:26:24 -0800335 break;
336 }
337
338 state_ = SuperstructureState::TRANSFERRING;
Ravago Jones5da06352022-03-04 20:26:24 -0800339 // Save the side the ball is on for later
340
341 break;
342 }
343 case SuperstructureState::TRANSFERRING: {
James Kuszmaul40526952022-03-13 15:56:38 -0700344 // If we've been transferring for too long, the ball probably got lost.
345 if ((turret_intake_state_ == RequestedIntake::kFront &&
346 !front_intake_has_ball_) ||
347 (turret_intake_state_ == RequestedIntake::kBack &&
348 !back_intake_has_ball_)) {
Ravago Jones5da06352022-03-04 20:26:24 -0800349 state_ = SuperstructureState::IDLE;
350 break;
351 }
352
James Kuszmaul40526952022-03-13 15:56:38 -0700353 turret_goal = &turret_loading_goal_buffer.message();
354
Ravago Jones5da06352022-03-04 20:26:24 -0800355 const bool turret_near_goal =
356 std::abs(turret_.estimated_position() - turret_loading_position) <
357 kTurretGoalThreshold;
358 if (!turret_near_goal) {
359 break; // Wait for turret to reach the chosen intake
360 }
361
362 // Transfer rollers and flipper arm belt on
James Kuszmaul40526952022-03-13 15:56:38 -0700363 if (turret_intake_state_ == RequestedIntake::kFront) {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700364 transfer_roller_speed = constants::Values::kTransferRollerVoltage();
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800365 } else {
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700366 transfer_roller_speed = -constants::Values::kTransferRollerVoltage();
Milind Upadhyayb1a74ea2022-03-09 20:34:35 -0800367 }
Ravago Jones5da06352022-03-04 20:26:24 -0800368 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
369
370 // Ball is in catapult
371 if (position->turret_beambreak()) {
James Kuszmaul40526952022-03-13 15:56:38 -0700372 if (turret_intake_state_ == RequestedIntake::kFront) {
373 front_intake_has_ball_ = false;
374 } else {
375 back_intake_has_ball_ = false;
376 }
Ravago Jones5da06352022-03-04 20:26:24 -0800377 state_ = SuperstructureState::LOADING;
378 loading_timer_ = timestamp;
379 }
380 break;
381 }
382 case SuperstructureState::LOADING: {
383 flipper_arms_voltage = constants::Values::kFlipperFeedVoltage();
384
385 // Keep feeding for kExtraLoadingTime
386
Ravago Jones5da06352022-03-04 20:26:24 -0800387 // The ball should go past the turret beambreak to be loaded.
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800388 // If we got a CAN reading not too long ago, the flippers should have
389 // also stopped.
Milind Upadhyay47e0d032022-03-05 23:06:25 -0800390 if (position->turret_beambreak()) {
391 loading_timer_ = timestamp;
392 } else if (timestamp >
393 loading_timer_ + constants::Values::kExtraLoadingTime()) {
Ravago Jones5da06352022-03-04 20:26:24 -0800394 state_ = SuperstructureState::LOADED;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700395 // reset color and wait for a new one once we know the ball is in place
396 ball_color_ = aos::Alliance::kInvalid;
Ravago Jones5da06352022-03-04 20:26:24 -0800397 reseating_in_catapult_ = false;
398 }
399 break;
400 }
401 case SuperstructureState::LOADED: {
402 if (unsafe_goal != nullptr) {
Austin Schuh92908812022-03-27 14:14:05 -0700403 if (turret_goal == nullptr) {
404 if (last_shot_angle_) {
405 turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
406 *last_shot_angle_);
407 }
408 turret_goal = &turret_loading_goal_buffer.message();
409 }
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700410
Ravago Jones5da06352022-03-04 20:26:24 -0800411 if (unsafe_goal->cancel_shot()) {
412 // Cancel the shot process
413 state_ = SuperstructureState::IDLE;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700414 } else if (unsafe_goal->fire() || discarding_ball_) {
Ravago Jones5da06352022-03-04 20:26:24 -0800415 // Start if we were asked to and the turret is at goal
416 state_ = SuperstructureState::SHOOTING;
417 prev_shot_count_ = catapult_.shot_count();
418
419 // Reset opening timeout
420 flipper_opening_start_time_ = timestamp;
421 }
422 }
423 break;
424 }
425 case SuperstructureState::SHOOTING: {
Austin Schuh92908812022-03-27 14:14:05 -0700426 if (turret_goal == nullptr) {
427 if (last_shot_angle_) {
428 turret_loading_goal_buffer.mutable_message()->mutate_unsafe_goal(
429 *last_shot_angle_);
430 }
431 turret_goal = &turret_loading_goal_buffer.message();
432 last_shot_angle_ = turret_goal->unsafe_goal();
433 } else {
434 last_shot_angle_ = std::nullopt;
435 }
436 const bool turret_near_goal =
437 turret_goal != nullptr &&
438 std::abs(turret_goal->unsafe_goal() - turret_.position()) <
439 kTurretGoalThreshold;
440
milind-uee3b0012022-03-14 21:19:57 -0700441 // Don't open the flippers until the turret's ready: give them as little
Henry Speisera9c49a22022-04-01 15:22:35 -0700442 // time to get bumped as possible. Or moving to fast.
443 if (!turret_near_goal || collided || moving_too_fast) {
milind-uee3b0012022-03-14 21:19:57 -0700444 break;
445 }
446
Ravago Jones5da06352022-03-04 20:26:24 -0800447 // Opening flipper arms could fail, wait until they are open using their
448 // potentiometers (the member below is just named encoder).
449 // Be a little more lenient if the flippers were already open in case of
450 // noise or collisions.
451 const double flipper_open_position =
452 (flippers_open_ ? constants::Values::kReseatFlipperPosition()
453 : constants::Values::kFlipperOpenPosition());
milind-u37dc40b2022-03-08 19:51:27 -0800454
455 // TODO(milind): add left arm back once it's fixed
Ravago Jones5da06352022-03-04 20:26:24 -0800456 flippers_open_ =
Ravago Jones5da06352022-03-04 20:26:24 -0800457 position->flipper_arm_right()->encoder() >= flipper_open_position;
458
459 if (flippers_open_) {
460 // Hold at kFlipperHoldVoltage
461 flipper_arms_voltage = constants::Values::kFlipperHoldVoltage();
462 } else {
463 // Open at kFlipperOpenVoltage
464 flipper_arms_voltage = constants::Values::kFlipperOpenVoltage();
465 }
466
milind-uee3b0012022-03-14 21:19:57 -0700467 // There are two possible failures for the flippers:
468 // 1. They never open on time
469 // 2. They opened and we started firing, but we got bumped or something
470 // and they went back.
471 // If the flippers didn't open in a reasonable amount of time, try
472 // reseating the ball and reversing them.
473 if (!fire_ && !flippers_open_ &&
Ravago Jones5da06352022-03-04 20:26:24 -0800474 timestamp >
475 loading_timer_ + constants::Values::kFlipperOpeningTimeout()) {
476 // Reseat the ball and try again
477 state_ = SuperstructureState::LOADING;
478 loading_timer_ = timestamp;
479 reseating_in_catapult_ = true;
480 break;
481 }
Ravago Jones5da06352022-03-04 20:26:24 -0800482
Henry Speiser28288cc2022-03-09 22:59:24 -0800483 // If the turret reached the aiming goal and the catapult is safe to move
484 // up, fire!
485 if (flippers_open_ && turret_near_goal && !collided) {
Ravago Jones5da06352022-03-04 20:26:24 -0800486 fire_ = true;
487 }
488
Ravago Jones5da06352022-03-04 20:26:24 -0800489 const bool near_return_position =
490 (unsafe_goal != nullptr && unsafe_goal->has_catapult() &&
491 unsafe_goal->catapult()->has_return_position() &&
492 std::abs(unsafe_goal->catapult()->return_position()->unsafe_goal() -
493 catapult_.estimated_position()) < kCatapultGoalThreshold);
494
495 // Once the shot is complete and the catapult is back to its return
496 // position, go back to IDLE
497 if (catapult_.shot_count() > prev_shot_count_ && near_return_position) {
498 prev_shot_count_ = catapult_.shot_count();
499 fire_ = false;
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700500 discarding_ball_ = false;
Ravago Jones5da06352022-03-04 20:26:24 -0800501 state_ = SuperstructureState::IDLE;
502 }
503
504 break;
505 }
506 }
507
508 collision_avoidance_.UpdateGoal(
509 {.intake_front_position = intake_front_.estimated_position(),
510 .intake_back_position = intake_back_.estimated_position(),
Henry Speiser28288cc2022-03-09 22:59:24 -0800511 .turret_position = turret_.estimated_position(),
512 .shooting = state_ == SuperstructureState::SHOOTING},
Ravago Jones5da06352022-03-04 20:26:24 -0800513 turret_goal);
514
515 turret_.set_min_position(collision_avoidance_.min_turret_goal());
516 turret_.set_max_position(collision_avoidance_.max_turret_goal());
517 intake_front_.set_min_position(collision_avoidance_.min_intake_front_goal());
518 intake_front_.set_max_position(collision_avoidance_.max_intake_front_goal());
519 intake_back_.set_min_position(collision_avoidance_.min_intake_back_goal());
520 intake_back_.set_max_position(collision_avoidance_.max_intake_back_goal());
521
James Kuszmaul84083f42022-02-27 17:24:38 -0800522 const flatbuffers::Offset<AimerStatus> aimer_offset =
523 aimer_.PopulateStatus(status->fbb());
524
Milind Upadhyay5dfabef2022-03-06 14:08:15 -0800525 // Disable the catapult if we want to restart to prevent damage with
526 // flippers
Austin Schuh39f26f62022-02-24 21:34:46 -0800527 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh97410d72022-03-12 15:37:23 -0800528 catapult_status_offset = catapult_.Iterate(
Ravago Jones3283ce02022-03-09 19:31:29 -0800529 catapult_goal, position, robot_state().voltage_battery(),
Austin Schuh97410d72022-03-12 15:37:23 -0800530 output != nullptr && !catapult_.estopped()
531 ? &(output_struct.catapult_voltage)
532 : nullptr,
533 fire_, status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800534
Ravago Jones5da06352022-03-04 20:26:24 -0800535 const flatbuffers::Offset<RelativeEncoderProfiledJointStatus>
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800536 climber_status_offset = climber_.Iterate(
537 unsafe_goal != nullptr ? unsafe_goal->climber() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800538 position->climber(),
539 output != nullptr ? &output_struct.climber_voltage : nullptr,
540 status->fbb());
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800541
Ravago Jones5da06352022-03-04 20:26:24 -0800542 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800543 intake_status_offset_front = intake_front_.Iterate(
544 unsafe_goal != nullptr ? unsafe_goal->intake_front() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800545 position->intake_front(),
546 output != nullptr ? &output_struct.intake_voltage_front : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800547 status->fbb());
548
Ravago Jones5da06352022-03-04 20:26:24 -0800549 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800550 intake_status_offset_back = intake_back_.Iterate(
551 unsafe_goal != nullptr ? unsafe_goal->intake_back() : nullptr,
Austin Schuh7f120362022-03-05 17:10:11 -0800552 position->intake_back(),
553 output != nullptr ? &output_struct.intake_voltage_back : nullptr,
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800554 status->fbb());
555
Ravago Jones5da06352022-03-04 20:26:24 -0800556 const flatbuffers::Offset<PotAndAbsoluteEncoderProfiledJointStatus>
Austin Schuh7f120362022-03-05 17:10:11 -0800557 turret_status_offset = turret_.Iterate(
558 turret_goal, position->turret(),
559 output != nullptr ? &output_struct.turret_voltage : nullptr,
560 status->fbb());
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800561
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800562 if (output != nullptr) {
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800563 output_struct.roller_voltage_front = roller_speed_compensated_front;
564 output_struct.roller_voltage_back = roller_speed_compensated_back;
Milind Upadhyay29dcc172022-04-02 19:21:30 -0700565 output_struct.transfer_roller_voltage = transfer_roller_speed;
Ravago Jones5da06352022-03-04 20:26:24 -0800566 output_struct.flipper_arms_voltage = flipper_arms_voltage;
milind-u6e7d8d42022-04-06 18:30:43 -0700567 if (climber_servo) {
Austin Schuh677af7e2022-04-13 19:45:39 -0700568 output_struct.climber_servo_left = 0.5;
milind-u6e7d8d42022-04-06 18:30:43 -0700569 output_struct.climber_servo_right = 1.0;
570 } else {
571 output_struct.climber_servo_left = 1.0;
572 output_struct.climber_servo_right = 0.0;
573 }
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800574
milind-u086d7262022-01-19 20:44:18 -0800575 output->CheckOk(output->Send(Output::Pack(*output->fbb(), &output_struct)));
576 }
577
578 Status::Builder status_builder = status->MakeBuilder<Status>();
579
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800580 const bool zeroed = intake_front_.zeroed() && intake_back_.zeroed() &&
Ravago Jones5da06352022-03-04 20:26:24 -0800581 turret_.zeroed() && climber_.zeroed() &&
582 catapult_.zeroed();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800583 const bool estopped = intake_front_.estopped() || intake_back_.estopped() ||
Ravago Jones5da06352022-03-04 20:26:24 -0800584 turret_.estopped() || climber_.estopped() ||
585 catapult_.estopped();
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800586
587 status_builder.add_zeroed(zeroed);
588 status_builder.add_estopped(estopped);
589
590 status_builder.add_intake_front(intake_status_offset_front);
591 status_builder.add_intake_back(intake_status_offset_back);
592 status_builder.add_turret(turret_status_offset);
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800593 status_builder.add_climber(climber_status_offset);
Ravago Jones5da06352022-03-04 20:26:24 -0800594
Austin Schuh39f26f62022-02-24 21:34:46 -0800595 status_builder.add_catapult(catapult_status_offset);
596 status_builder.add_solve_time(catapult_.solve_time());
Austin Schuh80fc2752022-02-25 13:33:56 -0800597 status_builder.add_shot_count(catapult_.shot_count());
Austin Schuh41472552022-03-13 18:09:41 -0700598 status_builder.add_mpc_horizon(catapult_.mpc_horizon());
Ravago Jones3283ce02022-03-09 19:31:29 -0800599 if (catapult_goal != nullptr) {
600 status_builder.add_shot_position(catapult_goal->shot_position());
601 status_builder.add_shot_velocity(catapult_goal->shot_velocity());
602 }
Ravago Jones5da06352022-03-04 20:26:24 -0800603
604 status_builder.add_flippers_open(flippers_open_);
605 status_builder.add_reseating_in_catapult(reseating_in_catapult_);
606 status_builder.add_fire(fire_);
Henry Speisera9c49a22022-04-01 15:22:35 -0700607 status_builder.add_moving_too_fast(moving_too_fast);
Ravago Jonesd51af7a2022-03-26 21:44:20 -0700608 status_builder.add_discarding_ball(discarding_ball_);
Henry Speiser77747b72022-03-06 17:18:29 -0800609 status_builder.add_ready_to_fire(state_ == SuperstructureState::LOADED &&
610 turret_near_goal && !collided);
Ravago Jones5da06352022-03-04 20:26:24 -0800611 status_builder.add_state(state_);
James Kuszmaul40526952022-03-13 15:56:38 -0700612 if (!front_intake_has_ball_ && !back_intake_has_ball_) {
613 status_builder.add_intake_state(IntakeState::NO_BALL);
614 } else if (front_intake_has_ball_ && back_intake_has_ball_) {
615 status_builder.add_intake_state(turret_intake_state_ ==
616 RequestedIntake::kFront
617 ? IntakeState::INTAKE_FRONT_BALL
618 : IntakeState::INTAKE_BACK_BALL);
619 } else {
620 status_builder.add_intake_state(front_intake_has_ball_
621 ? IntakeState::INTAKE_FRONT_BALL
622 : IntakeState::INTAKE_BACK_BALL);
623 }
624 status_builder.add_front_intake_has_ball(front_intake_has_ball_);
625 status_builder.add_back_intake_has_ball(back_intake_has_ball_);
milind-u086d7262022-01-19 20:44:18 -0800626
James Kuszmaul84083f42022-02-27 17:24:38 -0800627 status_builder.add_aimer(aimer_offset);
628
milind-u086d7262022-01-19 20:44:18 -0800629 (void)status->Send(status_builder.Finish());
630}
631
Henry Speiser55aa3ba2022-02-21 23:21:12 -0800632double Superstructure::robot_velocity() const {
633 return (drivetrain_status_fetcher_.get() != nullptr
634 ? drivetrain_status_fetcher_->robot_speed()
635 : 0.0);
636}
637
milind-u086d7262022-01-19 20:44:18 -0800638} // namespace superstructure
639} // namespace control_loops
640} // namespace y2022