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