blob: ab283232188d888d7a6261144fb16e4e4f618a74 [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#include <stdio.h>
2
3#include <memory>
4
5#include "aos/common/util/phased_loop.h"
6#include "aos/common/time.h"
7#include "aos/common/util/trapezoid_profile.h"
8#include "aos/common/logging/logging.h"
9#include "aos/common/logging/queue_logging.h"
Brian Silvermanbfa00602015-05-17 01:41:29 -040010#include "aos/common/actions/actions.h"
Brian Silverman17f503e2015-08-02 18:17:18 -070011
12#include "frc971/autonomous/auto.q.h"
13#include "y2014/constants.h"
14#include "y2014/control_loops/drivetrain/drivetrain.q.h"
15#include "y2014/control_loops/shooter/shooter.q.h"
16#include "y2014/control_loops/claw/claw.q.h"
Brian Silvermanbfa00602015-05-17 01:41:29 -040017#include "y2014/actors/shoot_actor.h"
18#include "y2014/actors/drivetrain_actor.h"
19#include "y2014/queues/auto_mode.q.h"
20
Brian Silverman17f503e2015-08-02 18:17:18 -070021#include "y2014/queues/hot_goal.q.h"
Brian Silvermanbfa00602015-05-17 01:41:29 -040022#include "y2014/queues/profile_params.q.h"
Brian Silverman17f503e2015-08-02 18:17:18 -070023
24using ::aos::time::Time;
25
26namespace frc971 {
27namespace autonomous {
28
29namespace time = ::aos::time;
30
31static double left_initial_position, right_initial_position;
32
33bool ShouldExitAuto() {
34 ::frc971::autonomous::autonomous.FetchLatest();
35 bool ans = !::frc971::autonomous::autonomous->run_auto;
36 if (ans) {
37 LOG(INFO, "Time to exit auto mode\n");
38 }
39 return ans;
40}
41
42void StopDrivetrain() {
43 LOG(INFO, "Stopping the drivetrain\n");
44 control_loops::drivetrain_queue.goal.MakeWithBuilder()
45 .control_loop_driving(true)
46 .left_goal(left_initial_position)
47 .left_velocity_goal(0)
48 .right_goal(right_initial_position)
49 .right_velocity_goal(0)
50 .quickturn(false)
51 .Send();
52}
53
54void ResetDrivetrain() {
55 LOG(INFO, "resetting the drivetrain\n");
56 control_loops::drivetrain_queue.goal.MakeWithBuilder()
57 .control_loop_driving(false)
58 .highgear(false)
59 .steering(0.0)
60 .throttle(0.0)
61 .left_goal(left_initial_position)
62 .left_velocity_goal(0)
63 .right_goal(right_initial_position)
64 .right_velocity_goal(0)
65 .Send();
66}
67
68void WaitUntilDoneOrCanceled(
69 ::std::unique_ptr<aos::common::actions::Action> action) {
70 if (!action) {
71 LOG(ERROR, "No action, not waiting\n");
72 return;
73 }
74 while (true) {
75 // Poll the running bit and auto done bits.
Brian Silvermanbfa00602015-05-17 01:41:29 -040076 ::aos::time::PhasedLoopXMS(10, 5000);
Brian Silverman17f503e2015-08-02 18:17:18 -070077 if (!action->Running() || ShouldExitAuto()) {
78 return;
79 }
80 }
81}
82
83void StepDrive(double distance, double theta) {
84 double left_goal = (left_initial_position + distance -
85 theta * constants::GetValues().turn_width / 2.0);
86 double right_goal = (right_initial_position + distance +
87 theta * constants::GetValues().turn_width / 2.0);
88 control_loops::drivetrain_queue.goal.MakeWithBuilder()
89 .control_loop_driving(true)
90 .left_goal(left_goal)
91 .right_goal(right_goal)
92 .left_velocity_goal(0.0)
93 .right_velocity_goal(0.0)
94 .Send();
95 left_initial_position = left_goal;
96 right_initial_position = right_goal;
97}
98
Brian Silvermanbfa00602015-05-17 01:41:29 -040099void PositionClawVertically(double intake_power = 0.0, double centering_power = 0.0) {
100 if (!control_loops::claw_queue.goal.MakeWithBuilder()
101 .bottom_angle(0.0)
102 .separation_angle(0.0)
103 .intake(intake_power)
104 .centering(centering_power)
105 .Send()) {
106 LOG(WARNING, "sending claw goal failed\n");
107 }
108}
109
110void PositionClawBackIntake() {
111 if (!control_loops::claw_queue.goal.MakeWithBuilder()
112 .bottom_angle(-2.273474)
113 .separation_angle(0.0)
114 .intake(12.0)
115 .centering(12.0)
116 .Send()) {
117 LOG(WARNING, "sending claw goal failed\n");
118 }
119}
120
121void PositionClawUpClosed() {
122 // Move the claw to where we're going to shoot from but keep it closed until
123 // it gets there.
124 if (!control_loops::claw_queue.goal.MakeWithBuilder()
125 .bottom_angle(0.86)
126 .separation_angle(0.0)
127 .intake(4.0)
128 .centering(1.0)
129 .Send()) {
130 LOG(WARNING, "sending claw goal failed\n");
131 }
132}
133
134void PositionClawForShot() {
135 if (!control_loops::claw_queue.goal.MakeWithBuilder()
136 .bottom_angle(0.86)
137 .separation_angle(0.10)
138 .intake(4.0)
139 .centering(1.0)
140 .Send()) {
141 LOG(WARNING, "sending claw goal failed\n");
142 }
143}
144
145void SetShotPower(double power) {
146 LOG(INFO, "Setting shot power to %f\n", power);
147 if (!control_loops::shooter_queue.goal.MakeWithBuilder()
148 .shot_power(power)
149 .shot_requested(false)
150 .unload_requested(false)
151 .load_requested(false)
152 .Send()) {
153 LOG(WARNING, "sending shooter goal failed\n");
154 }
155}
156
Brian Silverman17f503e2015-08-02 18:17:18 -0700157void WaitUntilNear(double distance) {
158 while (true) {
159 if (ShouldExitAuto()) return;
160 control_loops::drivetrain_queue.status.FetchAnother();
161 double left_error = ::std::abs(
162 left_initial_position -
163 control_loops::drivetrain_queue.status->filtered_left_position);
164 double right_error = ::std::abs(
165 right_initial_position -
166 control_loops::drivetrain_queue.status->filtered_right_position);
167 const double kPositionThreshold = 0.05 + distance;
168 if (right_error < kPositionThreshold && left_error < kPositionThreshold) {
169 LOG(INFO, "At the goal\n");
170 return;
171 }
172 }
173}
174
Brian Silvermanbfa00602015-05-17 01:41:29 -0400175const ProfileParams kFastDrive = {3.0, 2.5};
176const ProfileParams kSlowDrive = {2.5, 2.5};
177const ProfileParams kFastWithBallDrive = {3.0, 2.0};
178const ProfileParams kSlowWithBallDrive = {2.5, 2.0};
Brian Silverman17f503e2015-08-02 18:17:18 -0700179const ProfileParams kFastTurn = {3.0, 10.0};
Brian Silverman17f503e2015-08-02 18:17:18 -0700180
181::std::unique_ptr<::frc971::actors::DrivetrainAction> SetDriveGoal(
182 double distance, const ProfileParams drive_params, double theta = 0,
183 const ProfileParams &turn_params = kFastTurn) {
184 LOG(INFO, "Driving to %f\n", distance);
185
186 ::frc971::actors::DrivetrainActionParams params;
187 params.left_initial_position = left_initial_position;
188 params.right_initial_position = right_initial_position;
189 params.y_offset = distance;
190 params.theta_offset = theta;
191 params.maximum_turn_acceleration = turn_params.acceleration;
192 params.maximum_turn_velocity = turn_params.velocity;
193 params.maximum_velocity = drive_params.velocity;
194 params.maximum_acceleration = drive_params.acceleration;
195 auto drivetrain_action = actors::MakeDrivetrainAction(params);
196 drivetrain_action->Start();
197 left_initial_position +=
198 distance - theta * constants::GetValues().turn_width / 2.0;
199 right_initial_position +=
200 distance + theta * constants::GetValues().turn_width / 2.0;
201 return ::std::move(drivetrain_action);
202}
203
204void Shoot() {
205 // Shoot.
Brian Silvermanbfa00602015-05-17 01:41:29 -0400206 auto shoot_action = actors::MakeShootAction();
Brian Silverman17f503e2015-08-02 18:17:18 -0700207 shoot_action->Start();
Brian Silvermanbfa00602015-05-17 01:41:29 -0400208 WaitUntilDoneOrCanceled(::std::move(shoot_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700209}
210
211void InitializeEncoders() {
212 control_loops::drivetrain_queue.status.FetchAnother();
213 left_initial_position =
214 control_loops::drivetrain_queue.status->filtered_left_position;
215 right_initial_position =
216 control_loops::drivetrain_queue.status->filtered_right_position;
217}
218
219void WaitUntilClawDone() {
220 while (true) {
221 // Poll the running bit and auto done bits.
Brian Silvermanbfa00602015-05-17 01:41:29 -0400222 ::aos::time::PhasedLoopXMS(10, 5000);
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400223 control_loops::claw_queue.status.FetchLatest();
224 control_loops::claw_queue.goal.FetchLatest();
Brian Silverman17f503e2015-08-02 18:17:18 -0700225 if (ShouldExitAuto()) {
226 return;
227 }
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400228 if (control_loops::claw_queue.status.get() == nullptr ||
229 control_loops::claw_queue.goal.get() == nullptr) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700230 continue;
231 }
232 bool ans =
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400233 control_loops::claw_queue.status->zeroed &&
234 (::std::abs(control_loops::claw_queue.status->bottom_velocity) <
Brian Silverman17f503e2015-08-02 18:17:18 -0700235 1.0) &&
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400236 (::std::abs(control_loops::claw_queue.status->bottom -
237 control_loops::claw_queue.goal->bottom_angle) <
Brian Silverman17f503e2015-08-02 18:17:18 -0700238 0.10) &&
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400239 (::std::abs(control_loops::claw_queue.status->separation -
240 control_loops::claw_queue.goal->separation_angle) <
Brian Silverman17f503e2015-08-02 18:17:18 -0700241 0.4);
242 if (ans) {
243 return;
244 }
245 if (ShouldExitAuto()) return;
246 }
247}
248
249class HotGoalDecoder {
250 public:
251 HotGoalDecoder() {
252 ResetCounts();
253 }
254
255 void ResetCounts() {
256 hot_goal.FetchLatest();
257 if (hot_goal.get()) {
258 start_counts_ = *hot_goal;
259 LOG_STRUCT(INFO, "counts reset to", start_counts_);
260 start_counts_valid_ = true;
261 } else {
262 LOG(WARNING, "no hot goal message. ignoring\n");
263 start_counts_valid_ = false;
264 }
265 }
266
267 void Update(bool block = false) {
268 if (block) {
269 hot_goal.FetchAnother();
270 } else {
271 hot_goal.FetchLatest();
272 }
273 if (hot_goal.get()) LOG_STRUCT(INFO, "new counts", *hot_goal);
274 }
275
276 bool left_triggered() const {
277 if (!start_counts_valid_ || !hot_goal.get()) return false;
278 return (hot_goal->left_count - start_counts_.left_count) > kThreshold;
279 }
280
281 bool right_triggered() const {
282 if (!start_counts_valid_ || !hot_goal.get()) return false;
283 return (hot_goal->right_count - start_counts_.right_count) > kThreshold;
284 }
285
286 bool is_left() const {
287 if (!start_counts_valid_ || !hot_goal.get()) return false;
288 const uint64_t left_difference =
289 hot_goal->left_count - start_counts_.left_count;
290 const uint64_t right_difference =
291 hot_goal->right_count - start_counts_.right_count;
292 if (left_difference > kThreshold) {
293 if (right_difference > kThreshold) {
294 // We've seen a lot of both, so pick the one we've seen the most of.
295 return left_difference > right_difference;
296 } else {
297 // We've seen enough left but not enough right, so go with it.
298 return true;
299 }
300 } else {
301 // We haven't seen enough left, so it's not left.
302 return false;
303 }
304 }
305
306 bool is_right() const {
307 if (!start_counts_valid_ || !hot_goal.get()) return false;
308 const uint64_t left_difference =
309 hot_goal->left_count - start_counts_.left_count;
310 const uint64_t right_difference =
311 hot_goal->right_count - start_counts_.right_count;
312 if (right_difference > kThreshold) {
313 if (left_difference > kThreshold) {
314 // We've seen a lot of both, so pick the one we've seen the most of.
315 return right_difference > left_difference;
316 } else {
317 // We've seen enough right but not enough left, so go with it.
318 return true;
319 }
320 } else {
321 // We haven't seen enough right, so it's not right.
322 return false;
323 }
324 }
325
326 private:
327 static const uint64_t kThreshold = 5;
328
329 ::frc971::HotGoal start_counts_;
330 bool start_counts_valid_;
331};
332
333void HandleAuto() {
334 enum class AutoVersion : uint8_t {
335 kStraight,
336 kDoubleHot,
337 kSingleHot,
338 };
339
340 // The front of the robot is 1.854 meters from the wall
341 static const double kShootDistance = 3.15;
342 static const double kPickupDistance = 0.5;
343 static const double kTurnAngle = 0.3;
344
345 ::aos::time::Time start_time = ::aos::time::Time::Now();
346 LOG(INFO, "Handling auto mode\n");
347
348 AutoVersion auto_version;
349 ::frc971::sensors::auto_mode.FetchLatest();
350 if (!::frc971::sensors::auto_mode.get()) {
351 LOG(WARNING, "not sure which auto mode to use\n");
352 auto_version = AutoVersion::kStraight;
353 } else {
354 static const double kSelectorMin = 0.2, kSelectorMax = 4.4;
355
356 const double kSelectorStep = (kSelectorMax - kSelectorMin) / 3.0;
357 if (::frc971::sensors::auto_mode->voltage < kSelectorStep + kSelectorMin) {
358 auto_version = AutoVersion::kSingleHot;
359 } else if (::frc971::sensors::auto_mode->voltage <
360 2 * kSelectorStep + kSelectorMin) {
361 auto_version = AutoVersion::kStraight;
362 } else {
363 auto_version = AutoVersion::kDoubleHot;
364 }
365 }
366 LOG(INFO, "running auto %" PRIu8 "\n", static_cast<uint8_t>(auto_version));
367
Brian Silvermanbfa00602015-05-17 01:41:29 -0400368 const ProfileParams &drive_params =
369 (auto_version == AutoVersion::kStraight) ? kFastDrive : kSlowDrive;
370 const ProfileParams &drive_with_ball_params =
371 (auto_version == AutoVersion::kStraight) ? kFastWithBallDrive
372 : kSlowWithBallDrive;
Brian Silverman17f503e2015-08-02 18:17:18 -0700373
374 HotGoalDecoder hot_goal_decoder;
375 // True for left, false for right.
376 bool first_shot_left, second_shot_left_default, second_shot_left;
377
378 ResetDrivetrain();
379
380 time::SleepFor(time::Time::InSeconds(0.1));
381 if (ShouldExitAuto()) return;
382 InitializeEncoders();
383
384 // Turn the claw on, keep it straight up until the ball has been grabbed.
385 LOG(INFO, "Claw going up at %f\n",
386 (::aos::time::Time::Now() - start_time).ToSeconds());
387 PositionClawVertically(12.0, 4.0);
388 SetShotPower(115.0);
389
390 // Wait for the ball to enter the claw.
391 time::SleepFor(time::Time::InSeconds(0.25));
392 if (ShouldExitAuto()) return;
393 LOG(INFO, "Readying claw for shot at %f\n",
394 (::aos::time::Time::Now() - start_time).ToSeconds());
395
396 {
397 if (ShouldExitAuto()) return;
398 // Drive to the goal.
Brian Silvermanbfa00602015-05-17 01:41:29 -0400399 auto drivetrain_action = SetDriveGoal(-kShootDistance, drive_params);
Brian Silverman17f503e2015-08-02 18:17:18 -0700400 time::SleepFor(time::Time::InSeconds(0.75));
401 PositionClawForShot();
402 LOG(INFO, "Waiting until drivetrain is finished\n");
Brian Silvermanbfa00602015-05-17 01:41:29 -0400403 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700404 if (ShouldExitAuto()) return;
405 }
406
407 hot_goal_decoder.Update();
408 if (hot_goal_decoder.is_left()) {
409 LOG(INFO, "first shot left\n");
410 first_shot_left = true;
411 second_shot_left_default = false;
412 } else if (hot_goal_decoder.is_right()) {
413 LOG(INFO, "first shot right\n");
414 first_shot_left = false;
415 second_shot_left_default = true;
416 } else {
417 LOG(INFO, "first shot defaulting left\n");
418 first_shot_left = true;
419 second_shot_left_default = true;
420 }
421 if (auto_version == AutoVersion::kDoubleHot) {
422 if (ShouldExitAuto()) return;
Brian Silvermanbfa00602015-05-17 01:41:29 -0400423 auto drivetrain_action = SetDriveGoal(
424 0, drive_with_ball_params, first_shot_left ? kTurnAngle : -kTurnAngle);
425 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700426 if (ShouldExitAuto()) return;
427 } else if (auto_version == AutoVersion::kSingleHot) {
428 do {
429 // TODO(brians): Wait for next message with timeout or something.
430 ::aos::time::SleepFor(::aos::time::Time::InSeconds(0.003));
431 hot_goal_decoder.Update(false);
432 if (ShouldExitAuto()) return;
433 } while (!hot_goal_decoder.left_triggered() &&
434 (::aos::time::Time::Now() - start_time) <
435 ::aos::time::Time::InSeconds(9));
436 } else if (auto_version == AutoVersion::kStraight) {
437 time::SleepFor(time::Time::InSeconds(0.4));
438 }
439
440 // Shoot.
441 LOG(INFO, "Shooting at %f\n",
442 (::aos::time::Time::Now() - start_time).ToSeconds());
443 Shoot();
444 time::SleepFor(time::Time::InSeconds(0.05));
445
446 if (auto_version == AutoVersion::kDoubleHot) {
447 if (ShouldExitAuto()) return;
Brian Silvermanbfa00602015-05-17 01:41:29 -0400448 auto drivetrain_action = SetDriveGoal(
449 0, drive_with_ball_params, first_shot_left ? -kTurnAngle : kTurnAngle);
450 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700451 if (ShouldExitAuto()) return;
452 } else if (auto_version == AutoVersion::kSingleHot) {
453 LOG(INFO, "auto done at %f\n",
454 (::aos::time::Time::Now() - start_time).ToSeconds());
455 PositionClawVertically(0.0, 0.0);
456 return;
457 }
458
459 {
460 if (ShouldExitAuto()) return;
461 // Intake the new ball.
462 LOG(INFO, "Claw ready for intake at %f\n",
463 (::aos::time::Time::Now() - start_time).ToSeconds());
464 PositionClawBackIntake();
465 auto drivetrain_action =
Brian Silvermanbfa00602015-05-17 01:41:29 -0400466 SetDriveGoal(kShootDistance + kPickupDistance, drive_params);
Brian Silverman17f503e2015-08-02 18:17:18 -0700467 LOG(INFO, "Waiting until drivetrain is finished\n");
Brian Silvermanbfa00602015-05-17 01:41:29 -0400468 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700469 if (ShouldExitAuto()) return;
470 LOG(INFO, "Wait for the claw at %f\n",
471 (::aos::time::Time::Now() - start_time).ToSeconds());
472 WaitUntilClawDone();
473 if (ShouldExitAuto()) return;
474 }
475
476 // Drive back.
477 {
478 LOG(INFO, "Driving back at %f\n",
479 (::aos::time::Time::Now() - start_time).ToSeconds());
480 auto drivetrain_action =
Brian Silvermanbfa00602015-05-17 01:41:29 -0400481 SetDriveGoal(-(kShootDistance + kPickupDistance), drive_params);
Brian Silverman17f503e2015-08-02 18:17:18 -0700482 time::SleepFor(time::Time::InSeconds(0.3));
483 hot_goal_decoder.ResetCounts();
484 if (ShouldExitAuto()) return;
485 PositionClawUpClosed();
486 WaitUntilClawDone();
487 if (ShouldExitAuto()) return;
488 PositionClawForShot();
489 LOG(INFO, "Waiting until drivetrain is finished\n");
Brian Silvermanbfa00602015-05-17 01:41:29 -0400490 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700491 if (ShouldExitAuto()) return;
492 WaitUntilClawDone();
493 if (ShouldExitAuto()) return;
494 }
495
496 hot_goal_decoder.Update();
497 if (hot_goal_decoder.is_left()) {
498 LOG(INFO, "second shot left\n");
499 second_shot_left = true;
500 } else if (hot_goal_decoder.is_right()) {
501 LOG(INFO, "second shot right\n");
502 second_shot_left = false;
503 } else {
504 LOG(INFO, "second shot defaulting %s\n",
505 second_shot_left_default ? "left" : "right");
506 second_shot_left = second_shot_left_default;
507 }
508 if (auto_version == AutoVersion::kDoubleHot) {
509 if (ShouldExitAuto()) return;
Brian Silvermanbfa00602015-05-17 01:41:29 -0400510 auto drivetrain_action = SetDriveGoal(
511 0, drive_params, second_shot_left ? kTurnAngle : -kTurnAngle);
512 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700513 if (ShouldExitAuto()) return;
514 } else if (auto_version == AutoVersion::kStraight) {
515 time::SleepFor(time::Time::InSeconds(0.4));
516 }
517
518 LOG(INFO, "Shooting at %f\n",
519 (::aos::time::Time::Now() - start_time).ToSeconds());
520 // Shoot
521 Shoot();
522 if (ShouldExitAuto()) return;
523
524 // Get ready to zero when we come back up.
525 time::SleepFor(time::Time::InSeconds(0.05));
526 PositionClawVertically(0.0, 0.0);
527}
528
529} // namespace autonomous
530} // namespace frc971