blob: fe4099a937cca7c4b89578ea53dd6c051783d80d [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)
Austin Schuh86f895e2015-11-08 13:40:51 -080046 .highgear(true)
Brian Silverman17f503e2015-08-02 18:17:18 -070047 .left_goal(left_initial_position)
48 .left_velocity_goal(0)
49 .right_goal(right_initial_position)
50 .right_velocity_goal(0)
51 .quickturn(false)
52 .Send();
53}
54
55void ResetDrivetrain() {
56 LOG(INFO, "resetting the drivetrain\n");
57 control_loops::drivetrain_queue.goal.MakeWithBuilder()
58 .control_loop_driving(false)
Austin Schuh86f895e2015-11-08 13:40:51 -080059 .highgear(true)
Brian Silverman17f503e2015-08-02 18:17:18 -070060 .steering(0.0)
61 .throttle(0.0)
62 .left_goal(left_initial_position)
63 .left_velocity_goal(0)
64 .right_goal(right_initial_position)
65 .right_velocity_goal(0)
66 .Send();
67}
68
69void WaitUntilDoneOrCanceled(
70 ::std::unique_ptr<aos::common::actions::Action> action) {
71 if (!action) {
72 LOG(ERROR, "No action, not waiting\n");
73 return;
74 }
75 while (true) {
76 // Poll the running bit and auto done bits.
Brian Silvermanbfa00602015-05-17 01:41:29 -040077 ::aos::time::PhasedLoopXMS(10, 5000);
Brian Silverman17f503e2015-08-02 18:17:18 -070078 if (!action->Running() || ShouldExitAuto()) {
79 return;
80 }
81 }
82}
83
84void StepDrive(double distance, double theta) {
85 double left_goal = (left_initial_position + distance -
86 theta * constants::GetValues().turn_width / 2.0);
87 double right_goal = (right_initial_position + distance +
88 theta * constants::GetValues().turn_width / 2.0);
89 control_loops::drivetrain_queue.goal.MakeWithBuilder()
90 .control_loop_driving(true)
Austin Schuh86f895e2015-11-08 13:40:51 -080091 .highgear(true)
Brian Silverman17f503e2015-08-02 18:17:18 -070092 .left_goal(left_goal)
93 .right_goal(right_goal)
94 .left_velocity_goal(0.0)
95 .right_velocity_goal(0.0)
96 .Send();
97 left_initial_position = left_goal;
98 right_initial_position = right_goal;
99}
100
Brian Silvermanbfa00602015-05-17 01:41:29 -0400101void PositionClawVertically(double intake_power = 0.0, double centering_power = 0.0) {
102 if (!control_loops::claw_queue.goal.MakeWithBuilder()
103 .bottom_angle(0.0)
104 .separation_angle(0.0)
105 .intake(intake_power)
106 .centering(centering_power)
107 .Send()) {
108 LOG(WARNING, "sending claw goal failed\n");
109 }
110}
111
112void PositionClawBackIntake() {
113 if (!control_loops::claw_queue.goal.MakeWithBuilder()
114 .bottom_angle(-2.273474)
115 .separation_angle(0.0)
116 .intake(12.0)
117 .centering(12.0)
118 .Send()) {
119 LOG(WARNING, "sending claw goal failed\n");
120 }
121}
122
123void PositionClawUpClosed() {
124 // Move the claw to where we're going to shoot from but keep it closed until
125 // it gets there.
126 if (!control_loops::claw_queue.goal.MakeWithBuilder()
127 .bottom_angle(0.86)
128 .separation_angle(0.0)
129 .intake(4.0)
130 .centering(1.0)
131 .Send()) {
132 LOG(WARNING, "sending claw goal failed\n");
133 }
134}
135
136void PositionClawForShot() {
137 if (!control_loops::claw_queue.goal.MakeWithBuilder()
138 .bottom_angle(0.86)
139 .separation_angle(0.10)
140 .intake(4.0)
141 .centering(1.0)
142 .Send()) {
143 LOG(WARNING, "sending claw goal failed\n");
144 }
145}
146
147void SetShotPower(double power) {
148 LOG(INFO, "Setting shot power to %f\n", power);
149 if (!control_loops::shooter_queue.goal.MakeWithBuilder()
150 .shot_power(power)
151 .shot_requested(false)
152 .unload_requested(false)
153 .load_requested(false)
154 .Send()) {
155 LOG(WARNING, "sending shooter goal failed\n");
156 }
157}
158
Brian Silverman17f503e2015-08-02 18:17:18 -0700159void WaitUntilNear(double distance) {
160 while (true) {
161 if (ShouldExitAuto()) return;
162 control_loops::drivetrain_queue.status.FetchAnother();
163 double left_error = ::std::abs(
164 left_initial_position -
165 control_loops::drivetrain_queue.status->filtered_left_position);
166 double right_error = ::std::abs(
167 right_initial_position -
168 control_loops::drivetrain_queue.status->filtered_right_position);
169 const double kPositionThreshold = 0.05 + distance;
170 if (right_error < kPositionThreshold && left_error < kPositionThreshold) {
171 LOG(INFO, "At the goal\n");
172 return;
173 }
174 }
175}
176
Brian Silvermanbfa00602015-05-17 01:41:29 -0400177const ProfileParams kFastDrive = {3.0, 2.5};
178const ProfileParams kSlowDrive = {2.5, 2.5};
179const ProfileParams kFastWithBallDrive = {3.0, 2.0};
180const ProfileParams kSlowWithBallDrive = {2.5, 2.0};
Brian Silverman17f503e2015-08-02 18:17:18 -0700181const ProfileParams kFastTurn = {3.0, 10.0};
Brian Silverman17f503e2015-08-02 18:17:18 -0700182
183::std::unique_ptr<::frc971::actors::DrivetrainAction> SetDriveGoal(
184 double distance, const ProfileParams drive_params, double theta = 0,
185 const ProfileParams &turn_params = kFastTurn) {
186 LOG(INFO, "Driving to %f\n", distance);
187
188 ::frc971::actors::DrivetrainActionParams params;
189 params.left_initial_position = left_initial_position;
190 params.right_initial_position = right_initial_position;
191 params.y_offset = distance;
192 params.theta_offset = theta;
193 params.maximum_turn_acceleration = turn_params.acceleration;
194 params.maximum_turn_velocity = turn_params.velocity;
195 params.maximum_velocity = drive_params.velocity;
196 params.maximum_acceleration = drive_params.acceleration;
197 auto drivetrain_action = actors::MakeDrivetrainAction(params);
198 drivetrain_action->Start();
199 left_initial_position +=
200 distance - theta * constants::GetValues().turn_width / 2.0;
201 right_initial_position +=
202 distance + theta * constants::GetValues().turn_width / 2.0;
203 return ::std::move(drivetrain_action);
204}
205
206void Shoot() {
207 // Shoot.
Brian Silvermanbfa00602015-05-17 01:41:29 -0400208 auto shoot_action = actors::MakeShootAction();
Brian Silverman17f503e2015-08-02 18:17:18 -0700209 shoot_action->Start();
Brian Silvermanbfa00602015-05-17 01:41:29 -0400210 WaitUntilDoneOrCanceled(::std::move(shoot_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700211}
212
213void InitializeEncoders() {
214 control_loops::drivetrain_queue.status.FetchAnother();
215 left_initial_position =
216 control_loops::drivetrain_queue.status->filtered_left_position;
217 right_initial_position =
218 control_loops::drivetrain_queue.status->filtered_right_position;
219}
220
221void WaitUntilClawDone() {
222 while (true) {
223 // Poll the running bit and auto done bits.
Brian Silvermanbfa00602015-05-17 01:41:29 -0400224 ::aos::time::PhasedLoopXMS(10, 5000);
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400225 control_loops::claw_queue.status.FetchLatest();
226 control_loops::claw_queue.goal.FetchLatest();
Brian Silverman17f503e2015-08-02 18:17:18 -0700227 if (ShouldExitAuto()) {
228 return;
229 }
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400230 if (control_loops::claw_queue.status.get() == nullptr ||
231 control_loops::claw_queue.goal.get() == nullptr) {
Brian Silverman17f503e2015-08-02 18:17:18 -0700232 continue;
233 }
234 bool ans =
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400235 control_loops::claw_queue.status->zeroed &&
236 (::std::abs(control_loops::claw_queue.status->bottom_velocity) <
Brian Silverman17f503e2015-08-02 18:17:18 -0700237 1.0) &&
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400238 (::std::abs(control_loops::claw_queue.status->bottom -
239 control_loops::claw_queue.goal->bottom_angle) <
Brian Silverman17f503e2015-08-02 18:17:18 -0700240 0.10) &&
Brian Silvermanbe5ded62015-05-14 00:23:49 -0400241 (::std::abs(control_loops::claw_queue.status->separation -
242 control_loops::claw_queue.goal->separation_angle) <
Brian Silverman17f503e2015-08-02 18:17:18 -0700243 0.4);
244 if (ans) {
245 return;
246 }
247 if (ShouldExitAuto()) return;
248 }
249}
250
251class HotGoalDecoder {
252 public:
253 HotGoalDecoder() {
254 ResetCounts();
255 }
256
257 void ResetCounts() {
258 hot_goal.FetchLatest();
259 if (hot_goal.get()) {
260 start_counts_ = *hot_goal;
261 LOG_STRUCT(INFO, "counts reset to", start_counts_);
262 start_counts_valid_ = true;
263 } else {
264 LOG(WARNING, "no hot goal message. ignoring\n");
265 start_counts_valid_ = false;
266 }
267 }
268
269 void Update(bool block = false) {
270 if (block) {
271 hot_goal.FetchAnother();
272 } else {
273 hot_goal.FetchLatest();
274 }
275 if (hot_goal.get()) LOG_STRUCT(INFO, "new counts", *hot_goal);
276 }
277
278 bool left_triggered() const {
279 if (!start_counts_valid_ || !hot_goal.get()) return false;
280 return (hot_goal->left_count - start_counts_.left_count) > kThreshold;
281 }
282
283 bool right_triggered() const {
284 if (!start_counts_valid_ || !hot_goal.get()) return false;
285 return (hot_goal->right_count - start_counts_.right_count) > kThreshold;
286 }
287
288 bool is_left() const {
289 if (!start_counts_valid_ || !hot_goal.get()) return false;
290 const uint64_t left_difference =
291 hot_goal->left_count - start_counts_.left_count;
292 const uint64_t right_difference =
293 hot_goal->right_count - start_counts_.right_count;
294 if (left_difference > kThreshold) {
295 if (right_difference > kThreshold) {
296 // We've seen a lot of both, so pick the one we've seen the most of.
297 return left_difference > right_difference;
298 } else {
299 // We've seen enough left but not enough right, so go with it.
300 return true;
301 }
302 } else {
303 // We haven't seen enough left, so it's not left.
304 return false;
305 }
306 }
307
308 bool is_right() const {
309 if (!start_counts_valid_ || !hot_goal.get()) return false;
310 const uint64_t left_difference =
311 hot_goal->left_count - start_counts_.left_count;
312 const uint64_t right_difference =
313 hot_goal->right_count - start_counts_.right_count;
314 if (right_difference > kThreshold) {
315 if (left_difference > kThreshold) {
316 // We've seen a lot of both, so pick the one we've seen the most of.
317 return right_difference > left_difference;
318 } else {
319 // We've seen enough right but not enough left, so go with it.
320 return true;
321 }
322 } else {
323 // We haven't seen enough right, so it's not right.
324 return false;
325 }
326 }
327
328 private:
329 static const uint64_t kThreshold = 5;
330
331 ::frc971::HotGoal start_counts_;
332 bool start_counts_valid_;
333};
334
335void HandleAuto() {
336 enum class AutoVersion : uint8_t {
337 kStraight,
338 kDoubleHot,
339 kSingleHot,
340 };
341
342 // The front of the robot is 1.854 meters from the wall
343 static const double kShootDistance = 3.15;
344 static const double kPickupDistance = 0.5;
345 static const double kTurnAngle = 0.3;
346
347 ::aos::time::Time start_time = ::aos::time::Time::Now();
348 LOG(INFO, "Handling auto mode\n");
349
350 AutoVersion auto_version;
351 ::frc971::sensors::auto_mode.FetchLatest();
352 if (!::frc971::sensors::auto_mode.get()) {
353 LOG(WARNING, "not sure which auto mode to use\n");
354 auto_version = AutoVersion::kStraight;
355 } else {
356 static const double kSelectorMin = 0.2, kSelectorMax = 4.4;
357
358 const double kSelectorStep = (kSelectorMax - kSelectorMin) / 3.0;
359 if (::frc971::sensors::auto_mode->voltage < kSelectorStep + kSelectorMin) {
360 auto_version = AutoVersion::kSingleHot;
361 } else if (::frc971::sensors::auto_mode->voltage <
362 2 * kSelectorStep + kSelectorMin) {
363 auto_version = AutoVersion::kStraight;
364 } else {
365 auto_version = AutoVersion::kDoubleHot;
366 }
367 }
368 LOG(INFO, "running auto %" PRIu8 "\n", static_cast<uint8_t>(auto_version));
369
Brian Silvermanbfa00602015-05-17 01:41:29 -0400370 const ProfileParams &drive_params =
371 (auto_version == AutoVersion::kStraight) ? kFastDrive : kSlowDrive;
372 const ProfileParams &drive_with_ball_params =
373 (auto_version == AutoVersion::kStraight) ? kFastWithBallDrive
374 : kSlowWithBallDrive;
Brian Silverman17f503e2015-08-02 18:17:18 -0700375
376 HotGoalDecoder hot_goal_decoder;
377 // True for left, false for right.
378 bool first_shot_left, second_shot_left_default, second_shot_left;
379
380 ResetDrivetrain();
381
382 time::SleepFor(time::Time::InSeconds(0.1));
383 if (ShouldExitAuto()) return;
384 InitializeEncoders();
385
386 // Turn the claw on, keep it straight up until the ball has been grabbed.
387 LOG(INFO, "Claw going up at %f\n",
388 (::aos::time::Time::Now() - start_time).ToSeconds());
389 PositionClawVertically(12.0, 4.0);
390 SetShotPower(115.0);
391
392 // Wait for the ball to enter the claw.
393 time::SleepFor(time::Time::InSeconds(0.25));
394 if (ShouldExitAuto()) return;
395 LOG(INFO, "Readying claw for shot at %f\n",
396 (::aos::time::Time::Now() - start_time).ToSeconds());
397
398 {
399 if (ShouldExitAuto()) return;
400 // Drive to the goal.
Brian Silvermanbfa00602015-05-17 01:41:29 -0400401 auto drivetrain_action = SetDriveGoal(-kShootDistance, drive_params);
Brian Silverman17f503e2015-08-02 18:17:18 -0700402 time::SleepFor(time::Time::InSeconds(0.75));
403 PositionClawForShot();
404 LOG(INFO, "Waiting until drivetrain is finished\n");
Brian Silvermanbfa00602015-05-17 01:41:29 -0400405 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700406 if (ShouldExitAuto()) return;
407 }
408
409 hot_goal_decoder.Update();
410 if (hot_goal_decoder.is_left()) {
411 LOG(INFO, "first shot left\n");
412 first_shot_left = true;
413 second_shot_left_default = false;
414 } else if (hot_goal_decoder.is_right()) {
415 LOG(INFO, "first shot right\n");
416 first_shot_left = false;
417 second_shot_left_default = true;
418 } else {
419 LOG(INFO, "first shot defaulting left\n");
420 first_shot_left = true;
421 second_shot_left_default = true;
422 }
423 if (auto_version == AutoVersion::kDoubleHot) {
424 if (ShouldExitAuto()) return;
Brian Silvermanbfa00602015-05-17 01:41:29 -0400425 auto drivetrain_action = SetDriveGoal(
426 0, drive_with_ball_params, first_shot_left ? kTurnAngle : -kTurnAngle);
427 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700428 if (ShouldExitAuto()) return;
429 } else if (auto_version == AutoVersion::kSingleHot) {
430 do {
431 // TODO(brians): Wait for next message with timeout or something.
432 ::aos::time::SleepFor(::aos::time::Time::InSeconds(0.003));
433 hot_goal_decoder.Update(false);
434 if (ShouldExitAuto()) return;
435 } while (!hot_goal_decoder.left_triggered() &&
436 (::aos::time::Time::Now() - start_time) <
437 ::aos::time::Time::InSeconds(9));
438 } else if (auto_version == AutoVersion::kStraight) {
439 time::SleepFor(time::Time::InSeconds(0.4));
440 }
441
442 // Shoot.
443 LOG(INFO, "Shooting at %f\n",
444 (::aos::time::Time::Now() - start_time).ToSeconds());
445 Shoot();
446 time::SleepFor(time::Time::InSeconds(0.05));
447
448 if (auto_version == AutoVersion::kDoubleHot) {
449 if (ShouldExitAuto()) return;
Brian Silvermanbfa00602015-05-17 01:41:29 -0400450 auto drivetrain_action = SetDriveGoal(
451 0, drive_with_ball_params, first_shot_left ? -kTurnAngle : kTurnAngle);
452 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700453 if (ShouldExitAuto()) return;
454 } else if (auto_version == AutoVersion::kSingleHot) {
455 LOG(INFO, "auto done at %f\n",
456 (::aos::time::Time::Now() - start_time).ToSeconds());
457 PositionClawVertically(0.0, 0.0);
458 return;
459 }
460
461 {
462 if (ShouldExitAuto()) return;
463 // Intake the new ball.
464 LOG(INFO, "Claw ready for intake at %f\n",
465 (::aos::time::Time::Now() - start_time).ToSeconds());
466 PositionClawBackIntake();
467 auto drivetrain_action =
Brian Silvermanbfa00602015-05-17 01:41:29 -0400468 SetDriveGoal(kShootDistance + kPickupDistance, drive_params);
Brian Silverman17f503e2015-08-02 18:17:18 -0700469 LOG(INFO, "Waiting until drivetrain is finished\n");
Brian Silvermanbfa00602015-05-17 01:41:29 -0400470 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700471 if (ShouldExitAuto()) return;
472 LOG(INFO, "Wait for the claw at %f\n",
473 (::aos::time::Time::Now() - start_time).ToSeconds());
474 WaitUntilClawDone();
475 if (ShouldExitAuto()) return;
476 }
477
478 // Drive back.
479 {
480 LOG(INFO, "Driving back at %f\n",
481 (::aos::time::Time::Now() - start_time).ToSeconds());
482 auto drivetrain_action =
Brian Silvermanbfa00602015-05-17 01:41:29 -0400483 SetDriveGoal(-(kShootDistance + kPickupDistance), drive_params);
Brian Silverman17f503e2015-08-02 18:17:18 -0700484 time::SleepFor(time::Time::InSeconds(0.3));
485 hot_goal_decoder.ResetCounts();
486 if (ShouldExitAuto()) return;
487 PositionClawUpClosed();
488 WaitUntilClawDone();
489 if (ShouldExitAuto()) return;
490 PositionClawForShot();
491 LOG(INFO, "Waiting until drivetrain is finished\n");
Brian Silvermanbfa00602015-05-17 01:41:29 -0400492 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700493 if (ShouldExitAuto()) return;
494 WaitUntilClawDone();
495 if (ShouldExitAuto()) return;
496 }
497
498 hot_goal_decoder.Update();
499 if (hot_goal_decoder.is_left()) {
500 LOG(INFO, "second shot left\n");
501 second_shot_left = true;
502 } else if (hot_goal_decoder.is_right()) {
503 LOG(INFO, "second shot right\n");
504 second_shot_left = false;
505 } else {
506 LOG(INFO, "second shot defaulting %s\n",
507 second_shot_left_default ? "left" : "right");
508 second_shot_left = second_shot_left_default;
509 }
510 if (auto_version == AutoVersion::kDoubleHot) {
511 if (ShouldExitAuto()) return;
Brian Silvermanbfa00602015-05-17 01:41:29 -0400512 auto drivetrain_action = SetDriveGoal(
513 0, drive_params, second_shot_left ? kTurnAngle : -kTurnAngle);
514 WaitUntilDoneOrCanceled(::std::move(drivetrain_action));
Brian Silverman17f503e2015-08-02 18:17:18 -0700515 if (ShouldExitAuto()) return;
516 } else if (auto_version == AutoVersion::kStraight) {
517 time::SleepFor(time::Time::InSeconds(0.4));
518 }
519
520 LOG(INFO, "Shooting at %f\n",
521 (::aos::time::Time::Now() - start_time).ToSeconds());
522 // Shoot
523 Shoot();
524 if (ShouldExitAuto()) return;
525
526 // Get ready to zero when we come back up.
527 time::SleepFor(time::Time::InSeconds(0.05));
528 PositionClawVertically(0.0, 0.0);
529}
530
531} // namespace autonomous
532} // namespace frc971