blob: d4818e2b4c40edbe06d3d0d3b3443cbaabe523d0 [file] [log] [blame]
Austin Schuhbfb04122019-05-22 21:16:51 -07001#include "y2014/actors/autonomous_actor.h"
2
3#include <stdio.h>
4
5#include <chrono>
6#include <memory>
7
8#include "aos/actions/actions.h"
9#include "aos/logging/logging.h"
10#include "aos/logging/queue_logging.h"
11#include "aos/time/time.h"
12#include "aos/util/phased_loop.h"
13#include "frc971/autonomous/auto.q.h"
14#include "frc971/control_loops/drivetrain/drivetrain.q.h"
15#include "y2014/actors/shoot_actor.h"
16#include "y2014/constants.h"
17#include "y2014/control_loops/claw/claw.q.h"
18#include "y2014/control_loops/drivetrain/drivetrain_base.h"
19#include "y2014/control_loops/shooter/shooter.q.h"
20#include "y2014/queues/auto_mode.q.h"
21#include "y2014/queues/hot_goal.q.h"
22
23namespace y2014 {
24namespace actors {
25
26namespace chrono = ::std::chrono;
27namespace this_thread = ::std::this_thread;
28using ::aos::monotonic_clock;
29using ::frc971::ProfileParameters;
30
31AutonomousActor::AutonomousActor(
32 ::aos::EventLoop *event_loop,
33 ::frc971::autonomous::AutonomousActionQueueGroup *s)
34 : frc971::autonomous::BaseAutonomousActor(
Austin Schuha3e576b2019-05-22 21:22:23 -070035 event_loop, s, control_loops::GetDrivetrainConfig()),
Austin Schuh7eed2de2019-05-25 14:34:40 -070036 auto_mode_fetcher_(event_loop->MakeFetcher<::y2014::sensors::AutoMode>(
37 ".y2014.sensors.auto_mode")),
Austin Schuha3e576b2019-05-22 21:22:23 -070038 hot_goal_fetcher_(
39 event_loop->MakeFetcher<::y2014::HotGoal>(".y2014.hot_goal")) {}
Austin Schuhbfb04122019-05-22 21:16:51 -070040
41void AutonomousActor::PositionClawVertically(double intake_power,
42 double centering_power) {
43 if (!control_loops::claw_queue.goal.MakeWithBuilder()
44 .bottom_angle(0.0)
45 .separation_angle(0.0)
46 .intake(intake_power)
47 .centering(centering_power)
48 .Send()) {
49 LOG(WARNING, "sending claw goal failed\n");
50 }
51}
52
53void AutonomousActor::PositionClawBackIntake() {
54 if (!control_loops::claw_queue.goal.MakeWithBuilder()
55 .bottom_angle(-2.273474)
56 .separation_angle(0.0)
57 .intake(12.0)
58 .centering(12.0)
59 .Send()) {
60 LOG(WARNING, "sending claw goal failed\n");
61 }
62}
63
64void AutonomousActor::PositionClawUpClosed() {
65 // Move the claw to where we're going to shoot from but keep it closed until
66 // it gets there.
67 if (!control_loops::claw_queue.goal.MakeWithBuilder()
68 .bottom_angle(0.86)
69 .separation_angle(0.0)
70 .intake(4.0)
71 .centering(1.0)
72 .Send()) {
73 LOG(WARNING, "sending claw goal failed\n");
74 }
75}
76
77void AutonomousActor::PositionClawForShot() {
78 if (!control_loops::claw_queue.goal.MakeWithBuilder()
79 .bottom_angle(0.86)
80 .separation_angle(0.10)
81 .intake(4.0)
82 .centering(1.0)
83 .Send()) {
84 LOG(WARNING, "sending claw goal failed\n");
85 }
86}
87
88void AutonomousActor::SetShotPower(double power) {
89 LOG(INFO, "Setting shot power to %f\n", power);
90 if (!control_loops::shooter_queue.goal.MakeWithBuilder()
91 .shot_power(power)
92 .shot_requested(false)
93 .unload_requested(false)
94 .load_requested(false)
95 .Send()) {
96 LOG(WARNING, "sending shooter goal failed\n");
97 }
98}
99
100const ProfileParameters kFastDrive = {3.0, 2.5};
101const ProfileParameters kSlowDrive = {2.5, 2.5};
102const ProfileParameters kFastWithBallDrive = {3.0, 2.0};
103const ProfileParameters kSlowWithBallDrive = {2.5, 2.0};
104const ProfileParameters kFastTurn = {3.0, 10.0};
105
106void AutonomousActor::Shoot() {
107 // Shoot.
108 auto shoot_action = actors::MakeShootAction();
109 shoot_action->Start();
110 WaitUntilDoneOrCanceled(::std::move(shoot_action));
111}
112
113bool AutonomousActor::WaitUntilClawDone() {
114 while (true) {
115 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(10),
116 ::std::chrono::milliseconds(10) / 2);
117 // Poll the running bit and auto done bits.
118 phased_loop.SleepUntilNext();
119 control_loops::claw_queue.status.FetchLatest();
120 control_loops::claw_queue.goal.FetchLatest();
121 if (ShouldCancel()) {
122 return false;
123 }
124 if (control_loops::claw_queue.status.get() == nullptr ||
125 control_loops::claw_queue.goal.get() == nullptr) {
126 continue;
127 }
128 bool ans =
129 control_loops::claw_queue.status->zeroed &&
130 (::std::abs(control_loops::claw_queue.status->bottom_velocity) < 1.0) &&
131 (::std::abs(control_loops::claw_queue.status->bottom -
132 control_loops::claw_queue.goal->bottom_angle) < 0.10) &&
133 (::std::abs(control_loops::claw_queue.status->separation -
134 control_loops::claw_queue.goal->separation_angle) < 0.4);
135 if (ans) {
136 return true;
137 }
138 }
139}
140
141class HotGoalDecoder {
142 public:
Austin Schuha3e576b2019-05-22 21:22:23 -0700143 HotGoalDecoder(::aos::Fetcher<::y2014::HotGoal> *hot_goal_fetcher)
144 : hot_goal_fetcher_(hot_goal_fetcher) {
145 ResetCounts();
146 }
Austin Schuhbfb04122019-05-22 21:16:51 -0700147
148 void ResetCounts() {
Austin Schuha3e576b2019-05-22 21:22:23 -0700149 hot_goal_fetcher_->Fetch();
150 if (hot_goal_fetcher_->get()) {
151 start_counts_ = *hot_goal_fetcher_->get();
Austin Schuhbfb04122019-05-22 21:16:51 -0700152 LOG_STRUCT(INFO, "counts reset to", start_counts_);
153 start_counts_valid_ = true;
154 } else {
155 LOG(WARNING, "no hot goal message. ignoring\n");
156 start_counts_valid_ = false;
157 }
158 }
159
Austin Schuha3e576b2019-05-22 21:22:23 -0700160 void Update() {
161 hot_goal_fetcher_->Fetch();
162 if (hot_goal_fetcher_->get())
163 LOG_STRUCT(INFO, "new counts", *hot_goal_fetcher_->get());
Austin Schuhbfb04122019-05-22 21:16:51 -0700164 }
165
166 bool left_triggered() const {
Austin Schuha3e576b2019-05-22 21:22:23 -0700167 if (!start_counts_valid_ || !hot_goal_fetcher_->get()) return false;
168 return (hot_goal_fetcher_->get()->left_count - start_counts_.left_count) >
169 kThreshold;
Austin Schuhbfb04122019-05-22 21:16:51 -0700170 }
171
172 bool right_triggered() const {
Austin Schuha3e576b2019-05-22 21:22:23 -0700173 if (!start_counts_valid_ || !hot_goal_fetcher_->get()) return false;
174 return (hot_goal_fetcher_->get()->right_count - start_counts_.right_count) >
175 kThreshold;
Austin Schuhbfb04122019-05-22 21:16:51 -0700176 }
177
178 bool is_left() const {
Austin Schuha3e576b2019-05-22 21:22:23 -0700179 if (!start_counts_valid_ || !hot_goal_fetcher_->get()) return false;
Austin Schuhbfb04122019-05-22 21:16:51 -0700180 const uint64_t left_difference =
Austin Schuha3e576b2019-05-22 21:22:23 -0700181 hot_goal_fetcher_->get()->left_count - start_counts_.left_count;
Austin Schuhbfb04122019-05-22 21:16:51 -0700182 const uint64_t right_difference =
Austin Schuha3e576b2019-05-22 21:22:23 -0700183 hot_goal_fetcher_->get()->right_count - start_counts_.right_count;
Austin Schuhbfb04122019-05-22 21:16:51 -0700184 if (left_difference > kThreshold) {
185 if (right_difference > kThreshold) {
186 // We've seen a lot of both, so pick the one we've seen the most of.
187 return left_difference > right_difference;
188 } else {
189 // We've seen enough left but not enough right, so go with it.
190 return true;
191 }
192 } else {
193 // We haven't seen enough left, so it's not left.
194 return false;
195 }
196 }
197
198 bool is_right() const {
Austin Schuha3e576b2019-05-22 21:22:23 -0700199 if (!start_counts_valid_ || !hot_goal_fetcher_->get()) return false;
Austin Schuhbfb04122019-05-22 21:16:51 -0700200 const uint64_t left_difference =
Austin Schuha3e576b2019-05-22 21:22:23 -0700201 hot_goal_fetcher_->get()->left_count - start_counts_.left_count;
Austin Schuhbfb04122019-05-22 21:16:51 -0700202 const uint64_t right_difference =
Austin Schuha3e576b2019-05-22 21:22:23 -0700203 hot_goal_fetcher_->get()->right_count - start_counts_.right_count;
Austin Schuhbfb04122019-05-22 21:16:51 -0700204 if (right_difference > kThreshold) {
205 if (left_difference > kThreshold) {
206 // We've seen a lot of both, so pick the one we've seen the most of.
207 return right_difference > left_difference;
208 } else {
209 // We've seen enough right but not enough left, so go with it.
210 return true;
211 }
212 } else {
213 // We haven't seen enough right, so it's not right.
214 return false;
215 }
216 }
217
218 private:
219 static const uint64_t kThreshold = 5;
220
221 ::y2014::HotGoal start_counts_;
222 bool start_counts_valid_;
Austin Schuha3e576b2019-05-22 21:22:23 -0700223
224 ::aos::Fetcher<::y2014::HotGoal> *hot_goal_fetcher_;
Austin Schuhbfb04122019-05-22 21:16:51 -0700225};
226
227bool AutonomousActor::RunAction(
228 const ::frc971::autonomous::AutonomousActionParams & /*params*/) {
229 enum class AutoVersion : uint8_t {
230 kStraight,
231 kDoubleHot,
232 kSingleHot,
233 };
234
235 // The front of the robot is 1.854 meters from the wall
236 static const double kShootDistance = 3.15;
237 static const double kPickupDistance = 0.5;
238 static const double kTurnAngle = 0.3;
239
240 monotonic_clock::time_point start_time = monotonic_clock::now();
241 LOG(INFO, "Handling auto mode\n");
242
243 AutoVersion auto_version;
Austin Schuh7eed2de2019-05-25 14:34:40 -0700244 auto_mode_fetcher_.Fetch();
245 if (!auto_mode_fetcher_.get()) {
Austin Schuhbfb04122019-05-22 21:16:51 -0700246 LOG(WARNING, "not sure which auto mode to use\n");
247 auto_version = AutoVersion::kStraight;
248 } else {
249 static const double kSelectorMin = 0.2, kSelectorMax = 4.4;
250
251 const double kSelectorStep = (kSelectorMax - kSelectorMin) / 3.0;
Austin Schuh7eed2de2019-05-25 14:34:40 -0700252 if (auto_mode_fetcher_->voltage < kSelectorStep + kSelectorMin) {
Austin Schuhbfb04122019-05-22 21:16:51 -0700253 auto_version = AutoVersion::kSingleHot;
Austin Schuh7eed2de2019-05-25 14:34:40 -0700254 } else if (auto_mode_fetcher_->voltage < 2 * kSelectorStep + kSelectorMin) {
Austin Schuhbfb04122019-05-22 21:16:51 -0700255 auto_version = AutoVersion::kStraight;
256 } else {
257 auto_version = AutoVersion::kDoubleHot;
258 }
259 }
260 LOG(INFO, "running auto %" PRIu8 "\n", static_cast<uint8_t>(auto_version));
261
262 const ProfileParameters &drive_params =
263 (auto_version == AutoVersion::kStraight) ? kFastDrive : kSlowDrive;
264 const ProfileParameters &drive_with_ball_params =
265 (auto_version == AutoVersion::kStraight) ? kFastWithBallDrive
266 : kSlowWithBallDrive;
267
Austin Schuha3e576b2019-05-22 21:22:23 -0700268 HotGoalDecoder hot_goal_decoder(&hot_goal_fetcher_);
Austin Schuhbfb04122019-05-22 21:16:51 -0700269 // True for left, false for right.
270 bool first_shot_left, second_shot_left_default, second_shot_left;
271
272 Reset();
273
274 // Turn the claw on, keep it straight up until the ball has been grabbed.
275 LOG(INFO, "Claw going up at %f\n",
276 ::aos::time::DurationInSeconds(monotonic_clock::now() - start_time));
277 PositionClawVertically(12.0, 4.0);
278 SetShotPower(115.0);
279
280 // Wait for the ball to enter the claw.
281 this_thread::sleep_for(chrono::milliseconds(250));
282 if (ShouldCancel()) return true;
283 LOG(INFO, "Readying claw for shot at %f\n",
284 ::aos::time::DurationInSeconds(monotonic_clock::now() - start_time));
285
286 if (ShouldCancel()) return true;
287 // Drive to the goal.
288 StartDrive(-kShootDistance, 0.0, drive_params, kFastTurn);
289 this_thread::sleep_for(chrono::milliseconds(750));
290 PositionClawForShot();
291 LOG(INFO, "Waiting until drivetrain is finished\n");
292 WaitForDriveProfileDone();
293 if (ShouldCancel()) return true;
294
295 hot_goal_decoder.Update();
296 if (hot_goal_decoder.is_left()) {
297 LOG(INFO, "first shot left\n");
298 first_shot_left = true;
299 second_shot_left_default = false;
300 } else if (hot_goal_decoder.is_right()) {
301 LOG(INFO, "first shot right\n");
302 first_shot_left = false;
303 second_shot_left_default = true;
304 } else {
305 LOG(INFO, "first shot defaulting left\n");
306 first_shot_left = true;
307 second_shot_left_default = true;
308 }
309 if (auto_version == AutoVersion::kDoubleHot) {
310 if (ShouldCancel()) return true;
311 StartDrive(0, first_shot_left ? kTurnAngle : -kTurnAngle,
312 drive_with_ball_params, kFastTurn);
313 WaitForDriveProfileDone();
314 if (ShouldCancel()) return true;
315 } else if (auto_version == AutoVersion::kSingleHot) {
316 do {
317 // TODO(brians): Wait for next message with timeout or something.
318 this_thread::sleep_for(chrono::milliseconds(3));
Austin Schuha3e576b2019-05-22 21:22:23 -0700319 hot_goal_decoder.Update();
Austin Schuhbfb04122019-05-22 21:16:51 -0700320 if (ShouldCancel()) return true;
321 } while (!hot_goal_decoder.left_triggered() &&
322 (monotonic_clock::now() - start_time) < chrono::seconds(9));
323 } else if (auto_version == AutoVersion::kStraight) {
324 this_thread::sleep_for(chrono::milliseconds(400));
325 }
326
327 // Shoot.
328 LOG(INFO, "Shooting at %f\n",
329 ::aos::time::DurationInSeconds(monotonic_clock::now() - start_time));
330 Shoot();
331 this_thread::sleep_for(chrono::milliseconds(50));
332
333 if (auto_version == AutoVersion::kDoubleHot) {
334 if (ShouldCancel()) return true;
335 StartDrive(0, first_shot_left ? -kTurnAngle : kTurnAngle,
336 drive_with_ball_params, kFastTurn);
337 WaitForDriveProfileDone();
338 if (ShouldCancel()) return true;
339 } else if (auto_version == AutoVersion::kSingleHot) {
340 LOG(INFO, "auto done at %f\n",
341 ::aos::time::DurationInSeconds(monotonic_clock::now() - start_time));
342 PositionClawVertically(0.0, 0.0);
343 return true;
344 }
345
346 {
347 if (ShouldCancel()) return true;
348 // Intake the new ball.
349 LOG(INFO, "Claw ready for intake at %f\n",
350 ::aos::time::DurationInSeconds(monotonic_clock::now() - start_time));
351 PositionClawBackIntake();
352 StartDrive(kShootDistance + kPickupDistance, 0.0, drive_params, kFastTurn);
353 LOG(INFO, "Waiting until drivetrain is finished\n");
354 WaitForDriveProfileDone();
355 if (ShouldCancel()) return true;
356 LOG(INFO, "Wait for the claw at %f\n",
357 ::aos::time::DurationInSeconds(monotonic_clock::now() - start_time));
358 if (!WaitUntilClawDone()) return true;
359 }
360
361 // Drive back.
362 {
363 LOG(INFO, "Driving back at %f\n",
364 ::aos::time::DurationInSeconds(monotonic_clock::now() - start_time));
365 StartDrive(-(kShootDistance + kPickupDistance), 0.0, drive_params,
366 kFastTurn);
367 this_thread::sleep_for(chrono::milliseconds(300));
368 hot_goal_decoder.ResetCounts();
369 if (ShouldCancel()) return true;
370 PositionClawUpClosed();
371 if (!WaitUntilClawDone()) return true;
372 PositionClawForShot();
373 LOG(INFO, "Waiting until drivetrain is finished\n");
374 WaitForDriveProfileDone();
375 if (ShouldCancel()) return true;
376 if (!WaitUntilClawDone()) return true;
377 }
378
379 hot_goal_decoder.Update();
380 if (hot_goal_decoder.is_left()) {
381 LOG(INFO, "second shot left\n");
382 second_shot_left = true;
383 } else if (hot_goal_decoder.is_right()) {
384 LOG(INFO, "second shot right\n");
385 second_shot_left = false;
386 } else {
387 LOG(INFO, "second shot defaulting %s\n",
388 second_shot_left_default ? "left" : "right");
389 second_shot_left = second_shot_left_default;
390 }
391 if (auto_version == AutoVersion::kDoubleHot) {
392 if (ShouldCancel()) return true;
393 StartDrive(0, second_shot_left ? kTurnAngle : -kTurnAngle, drive_params,
394 kFastTurn);
395 WaitForDriveProfileDone();
396 if (ShouldCancel()) return true;
397 } else if (auto_version == AutoVersion::kStraight) {
398 this_thread::sleep_for(chrono::milliseconds(400));
399 }
400
401 LOG(INFO, "Shooting at %f\n",
402 ::aos::time::DurationInSeconds(monotonic_clock::now() - start_time));
403 // Shoot
404 Shoot();
405 if (ShouldCancel()) return true;
406
407 // Get ready to zero when we come back up.
408 this_thread::sleep_for(chrono::milliseconds(50));
409 PositionClawVertically(0.0, 0.0);
410 return true;
411}
412
413} // namespace actors
414} // namespace y2014