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