blob: f3f3beed4d8393e8a0e6864d0c97993a867ed27f [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"
10
11#include "frc971/autonomous/auto.q.h"
12#include "y2014/constants.h"
13#include "y2014/control_loops/drivetrain/drivetrain.q.h"
14#include "y2014/control_loops/shooter/shooter.q.h"
15#include "y2014/control_loops/claw/claw.q.h"
16#include "frc971/actions/action_client.h"
17#include "frc971/actions/shoot_action.h"
18#include "frc971/actions/drivetrain_action.h"
19#include "frc971/queues/other_sensors.q.h"
20#include "y2014/queues/hot_goal.q.h"
21
22using ::aos::time::Time;
23
24namespace frc971 {
25namespace autonomous {
26
27namespace time = ::aos::time;
28
29static double left_initial_position, right_initial_position;
30
31bool ShouldExitAuto() {
32 ::frc971::autonomous::autonomous.FetchLatest();
33 bool ans = !::frc971::autonomous::autonomous->run_auto;
34 if (ans) {
35 LOG(INFO, "Time to exit auto mode\n");
36 }
37 return ans;
38}
39
40void StopDrivetrain() {
41 LOG(INFO, "Stopping the drivetrain\n");
42 control_loops::drivetrain_queue.goal.MakeWithBuilder()
43 .control_loop_driving(true)
44 .left_goal(left_initial_position)
45 .left_velocity_goal(0)
46 .right_goal(right_initial_position)
47 .right_velocity_goal(0)
48 .quickturn(false)
49 .Send();
50}
51
52void ResetDrivetrain() {
53 LOG(INFO, "resetting the drivetrain\n");
54 control_loops::drivetrain_queue.goal.MakeWithBuilder()
55 .control_loop_driving(false)
56 .highgear(false)
57 .steering(0.0)
58 .throttle(0.0)
59 .left_goal(left_initial_position)
60 .left_velocity_goal(0)
61 .right_goal(right_initial_position)
62 .right_velocity_goal(0)
63 .Send();
64}
65
66void WaitUntilDoneOrCanceled(
67 ::std::unique_ptr<aos::common::actions::Action> action) {
68 if (!action) {
69 LOG(ERROR, "No action, not waiting\n");
70 return;
71 }
72 while (true) {
73 // Poll the running bit and auto done bits.
74 ::aos::time::PhasedLoopXMS(5, 2500);
75 if (!action->Running() || ShouldExitAuto()) {
76 return;
77 }
78 }
79}
80
81void StepDrive(double distance, double theta) {
82 double left_goal = (left_initial_position + distance -
83 theta * constants::GetValues().turn_width / 2.0);
84 double right_goal = (right_initial_position + distance +
85 theta * constants::GetValues().turn_width / 2.0);
86 control_loops::drivetrain_queue.goal.MakeWithBuilder()
87 .control_loop_driving(true)
88 .left_goal(left_goal)
89 .right_goal(right_goal)
90 .left_velocity_goal(0.0)
91 .right_velocity_goal(0.0)
92 .Send();
93 left_initial_position = left_goal;
94 right_initial_position = right_goal;
95}
96
97void WaitUntilNear(double distance) {
98 while (true) {
99 if (ShouldExitAuto()) return;
100 control_loops::drivetrain_queue.status.FetchAnother();
101 double left_error = ::std::abs(
102 left_initial_position -
103 control_loops::drivetrain_queue.status->filtered_left_position);
104 double right_error = ::std::abs(
105 right_initial_position -
106 control_loops::drivetrain_queue.status->filtered_right_position);
107 const double kPositionThreshold = 0.05 + distance;
108 if (right_error < kPositionThreshold && left_error < kPositionThreshold) {
109 LOG(INFO, "At the goal\n");
110 return;
111 }
112 }
113}
114
115const ProfileParams kFastDrive = {2.0, 3.5};
116const ProfileParams kFastKnockDrive = {2.0, 3.0};
117const ProfileParams kStackingSecondDrive = {2.0, 1.5};
118const ProfileParams kFastTurn = {3.0, 10.0};
119const ProfileParams kStackingFirstTurn = {1.0, 1.0};
120const ProfileParams kStackingSecondTurn = {2.0, 6.0};
121const ProfileParams kComboTurn = {1.2, 8.0};
122const ProfileParams kRaceTurn = {4.0, 10.0};
123const ProfileParams kRaceDrive = {2.0, 2.0};
124const ProfileParams kRaceBackupDrive = {2.0, 5.0};
125
126::std::unique_ptr<::frc971::actors::DrivetrainAction> SetDriveGoal(
127 double distance, const ProfileParams drive_params, double theta = 0,
128 const ProfileParams &turn_params = kFastTurn) {
129 LOG(INFO, "Driving to %f\n", distance);
130
131 ::frc971::actors::DrivetrainActionParams params;
132 params.left_initial_position = left_initial_position;
133 params.right_initial_position = right_initial_position;
134 params.y_offset = distance;
135 params.theta_offset = theta;
136 params.maximum_turn_acceleration = turn_params.acceleration;
137 params.maximum_turn_velocity = turn_params.velocity;
138 params.maximum_velocity = drive_params.velocity;
139 params.maximum_acceleration = drive_params.acceleration;
140 auto drivetrain_action = actors::MakeDrivetrainAction(params);
141 drivetrain_action->Start();
142 left_initial_position +=
143 distance - theta * constants::GetValues().turn_width / 2.0;
144 right_initial_position +=
145 distance + theta * constants::GetValues().turn_width / 2.0;
146 return ::std::move(drivetrain_action);
147}
148
149void Shoot() {
150 // Shoot.
151 auto shoot_action = actions::MakeShootAction();
152 shoot_action->Start();
153 WaitUntilDoneOrCanceled(shoot_action.get());
154}
155
156void InitializeEncoders() {
157 control_loops::drivetrain_queue.status.FetchAnother();
158 left_initial_position =
159 control_loops::drivetrain_queue.status->filtered_left_position;
160 right_initial_position =
161 control_loops::drivetrain_queue.status->filtered_right_position;
162}
163
164void WaitUntilClawDone() {
165 while (true) {
166 // Poll the running bit and auto done bits.
167 // TODO(sensors): Fix this time.
168 ::aos::time::PhasedLoop10MS(5000);
169 control_loops::claw_queue_group.status.FetchLatest();
170 control_loops::claw_queue_group.goal.FetchLatest();
171 if (ShouldExitAuto()) {
172 return;
173 }
174 if (control_loops::claw_queue_group.status.get() == nullptr ||
175 control_loops::claw_queue_group.goal.get() == nullptr) {
176 continue;
177 }
178 bool ans =
179 control_loops::claw_queue_group.status->zeroed &&
180 (::std::abs(control_loops::claw_queue_group.status->bottom_velocity) <
181 1.0) &&
182 (::std::abs(control_loops::claw_queue_group.status->bottom -
183 control_loops::claw_queue_group.goal->bottom_angle) <
184 0.10) &&
185 (::std::abs(control_loops::claw_queue_group.status->separation -
186 control_loops::claw_queue_group.goal->separation_angle) <
187 0.4);
188 if (ans) {
189 return;
190 }
191 if (ShouldExitAuto()) return;
192 }
193}
194
195class HotGoalDecoder {
196 public:
197 HotGoalDecoder() {
198 ResetCounts();
199 }
200
201 void ResetCounts() {
202 hot_goal.FetchLatest();
203 if (hot_goal.get()) {
204 start_counts_ = *hot_goal;
205 LOG_STRUCT(INFO, "counts reset to", start_counts_);
206 start_counts_valid_ = true;
207 } else {
208 LOG(WARNING, "no hot goal message. ignoring\n");
209 start_counts_valid_ = false;
210 }
211 }
212
213 void Update(bool block = false) {
214 if (block) {
215 hot_goal.FetchAnother();
216 } else {
217 hot_goal.FetchLatest();
218 }
219 if (hot_goal.get()) LOG_STRUCT(INFO, "new counts", *hot_goal);
220 }
221
222 bool left_triggered() const {
223 if (!start_counts_valid_ || !hot_goal.get()) return false;
224 return (hot_goal->left_count - start_counts_.left_count) > kThreshold;
225 }
226
227 bool right_triggered() const {
228 if (!start_counts_valid_ || !hot_goal.get()) return false;
229 return (hot_goal->right_count - start_counts_.right_count) > kThreshold;
230 }
231
232 bool is_left() const {
233 if (!start_counts_valid_ || !hot_goal.get()) return false;
234 const uint64_t left_difference =
235 hot_goal->left_count - start_counts_.left_count;
236 const uint64_t right_difference =
237 hot_goal->right_count - start_counts_.right_count;
238 if (left_difference > kThreshold) {
239 if (right_difference > kThreshold) {
240 // We've seen a lot of both, so pick the one we've seen the most of.
241 return left_difference > right_difference;
242 } else {
243 // We've seen enough left but not enough right, so go with it.
244 return true;
245 }
246 } else {
247 // We haven't seen enough left, so it's not left.
248 return false;
249 }
250 }
251
252 bool is_right() const {
253 if (!start_counts_valid_ || !hot_goal.get()) return false;
254 const uint64_t left_difference =
255 hot_goal->left_count - start_counts_.left_count;
256 const uint64_t right_difference =
257 hot_goal->right_count - start_counts_.right_count;
258 if (right_difference > kThreshold) {
259 if (left_difference > kThreshold) {
260 // We've seen a lot of both, so pick the one we've seen the most of.
261 return right_difference > left_difference;
262 } else {
263 // We've seen enough right but not enough left, so go with it.
264 return true;
265 }
266 } else {
267 // We haven't seen enough right, so it's not right.
268 return false;
269 }
270 }
271
272 private:
273 static const uint64_t kThreshold = 5;
274
275 ::frc971::HotGoal start_counts_;
276 bool start_counts_valid_;
277};
278
279void HandleAuto() {
280 enum class AutoVersion : uint8_t {
281 kStraight,
282 kDoubleHot,
283 kSingleHot,
284 };
285
286 // The front of the robot is 1.854 meters from the wall
287 static const double kShootDistance = 3.15;
288 static const double kPickupDistance = 0.5;
289 static const double kTurnAngle = 0.3;
290
291 ::aos::time::Time start_time = ::aos::time::Time::Now();
292 LOG(INFO, "Handling auto mode\n");
293
294 AutoVersion auto_version;
295 ::frc971::sensors::auto_mode.FetchLatest();
296 if (!::frc971::sensors::auto_mode.get()) {
297 LOG(WARNING, "not sure which auto mode to use\n");
298 auto_version = AutoVersion::kStraight;
299 } else {
300 static const double kSelectorMin = 0.2, kSelectorMax = 4.4;
301
302 const double kSelectorStep = (kSelectorMax - kSelectorMin) / 3.0;
303 if (::frc971::sensors::auto_mode->voltage < kSelectorStep + kSelectorMin) {
304 auto_version = AutoVersion::kSingleHot;
305 } else if (::frc971::sensors::auto_mode->voltage <
306 2 * kSelectorStep + kSelectorMin) {
307 auto_version = AutoVersion::kStraight;
308 } else {
309 auto_version = AutoVersion::kDoubleHot;
310 }
311 }
312 LOG(INFO, "running auto %" PRIu8 "\n", static_cast<uint8_t>(auto_version));
313
314 const bool drive_slow_acceleration = auto_version == AutoVersion::kStraight;
315
316 HotGoalDecoder hot_goal_decoder;
317 // True for left, false for right.
318 bool first_shot_left, second_shot_left_default, second_shot_left;
319
320 ResetDrivetrain();
321
322 time::SleepFor(time::Time::InSeconds(0.1));
323 if (ShouldExitAuto()) return;
324 InitializeEncoders();
325
326 // Turn the claw on, keep it straight up until the ball has been grabbed.
327 LOG(INFO, "Claw going up at %f\n",
328 (::aos::time::Time::Now() - start_time).ToSeconds());
329 PositionClawVertically(12.0, 4.0);
330 SetShotPower(115.0);
331
332 // Wait for the ball to enter the claw.
333 time::SleepFor(time::Time::InSeconds(0.25));
334 if (ShouldExitAuto()) return;
335 LOG(INFO, "Readying claw for shot at %f\n",
336 (::aos::time::Time::Now() - start_time).ToSeconds());
337
338 {
339 if (ShouldExitAuto()) return;
340 // Drive to the goal.
341 auto drivetrain_action = SetDriveGoal(-kShootDistance,
342 drive_slow_acceleration, 2.5);
343 time::SleepFor(time::Time::InSeconds(0.75));
344 PositionClawForShot();
345 LOG(INFO, "Waiting until drivetrain is finished\n");
346 WaitUntilDoneOrCanceled(drivetrain_action.get());
347 if (ShouldExitAuto()) return;
348 }
349
350 hot_goal_decoder.Update();
351 if (hot_goal_decoder.is_left()) {
352 LOG(INFO, "first shot left\n");
353 first_shot_left = true;
354 second_shot_left_default = false;
355 } else if (hot_goal_decoder.is_right()) {
356 LOG(INFO, "first shot right\n");
357 first_shot_left = false;
358 second_shot_left_default = true;
359 } else {
360 LOG(INFO, "first shot defaulting left\n");
361 first_shot_left = true;
362 second_shot_left_default = true;
363 }
364 if (auto_version == AutoVersion::kDoubleHot) {
365 if (ShouldExitAuto()) return;
366 auto drivetrain_action =
367 SetDriveGoal(0, drive_slow_acceleration, 2,
368 first_shot_left ? kTurnAngle : -kTurnAngle);
369 WaitUntilDoneOrCanceled(drivetrain_action.get());
370 if (ShouldExitAuto()) return;
371 } else if (auto_version == AutoVersion::kSingleHot) {
372 do {
373 // TODO(brians): Wait for next message with timeout or something.
374 ::aos::time::SleepFor(::aos::time::Time::InSeconds(0.003));
375 hot_goal_decoder.Update(false);
376 if (ShouldExitAuto()) return;
377 } while (!hot_goal_decoder.left_triggered() &&
378 (::aos::time::Time::Now() - start_time) <
379 ::aos::time::Time::InSeconds(9));
380 } else if (auto_version == AutoVersion::kStraight) {
381 time::SleepFor(time::Time::InSeconds(0.4));
382 }
383
384 // Shoot.
385 LOG(INFO, "Shooting at %f\n",
386 (::aos::time::Time::Now() - start_time).ToSeconds());
387 Shoot();
388 time::SleepFor(time::Time::InSeconds(0.05));
389
390 if (auto_version == AutoVersion::kDoubleHot) {
391 if (ShouldExitAuto()) return;
392 auto drivetrain_action =
393 SetDriveGoal(0, drive_slow_acceleration, 2,
394 first_shot_left ? -kTurnAngle : kTurnAngle);
395 WaitUntilDoneOrCanceled(drivetrain_action.get());
396 if (ShouldExitAuto()) return;
397 } else if (auto_version == AutoVersion::kSingleHot) {
398 LOG(INFO, "auto done at %f\n",
399 (::aos::time::Time::Now() - start_time).ToSeconds());
400 PositionClawVertically(0.0, 0.0);
401 return;
402 }
403
404 {
405 if (ShouldExitAuto()) return;
406 // Intake the new ball.
407 LOG(INFO, "Claw ready for intake at %f\n",
408 (::aos::time::Time::Now() - start_time).ToSeconds());
409 PositionClawBackIntake();
410 auto drivetrain_action =
411 SetDriveGoal(kShootDistance + kPickupDistance,
412 drive_slow_acceleration, 2.5);
413 LOG(INFO, "Waiting until drivetrain is finished\n");
414 WaitUntilDoneOrCanceled(drivetrain_action.get());
415 if (ShouldExitAuto()) return;
416 LOG(INFO, "Wait for the claw at %f\n",
417 (::aos::time::Time::Now() - start_time).ToSeconds());
418 WaitUntilClawDone();
419 if (ShouldExitAuto()) return;
420 }
421
422 // Drive back.
423 {
424 LOG(INFO, "Driving back at %f\n",
425 (::aos::time::Time::Now() - start_time).ToSeconds());
426 auto drivetrain_action =
427 SetDriveGoal(-(kShootDistance + kPickupDistance),
428 drive_slow_acceleration, 2.5);
429 time::SleepFor(time::Time::InSeconds(0.3));
430 hot_goal_decoder.ResetCounts();
431 if (ShouldExitAuto()) return;
432 PositionClawUpClosed();
433 WaitUntilClawDone();
434 if (ShouldExitAuto()) return;
435 PositionClawForShot();
436 LOG(INFO, "Waiting until drivetrain is finished\n");
437 WaitUntilDoneOrCanceled(drivetrain_action.get());
438 if (ShouldExitAuto()) return;
439 WaitUntilClawDone();
440 if (ShouldExitAuto()) return;
441 }
442
443 hot_goal_decoder.Update();
444 if (hot_goal_decoder.is_left()) {
445 LOG(INFO, "second shot left\n");
446 second_shot_left = true;
447 } else if (hot_goal_decoder.is_right()) {
448 LOG(INFO, "second shot right\n");
449 second_shot_left = false;
450 } else {
451 LOG(INFO, "second shot defaulting %s\n",
452 second_shot_left_default ? "left" : "right");
453 second_shot_left = second_shot_left_default;
454 }
455 if (auto_version == AutoVersion::kDoubleHot) {
456 if (ShouldExitAuto()) return;
457 auto drivetrain_action =
458 SetDriveGoal(0, drive_slow_acceleration, 2,
459 second_shot_left ? kTurnAngle : -kTurnAngle);
460 WaitUntilDoneOrCanceled(drivetrain_action.get());
461 if (ShouldExitAuto()) return;
462 } else if (auto_version == AutoVersion::kStraight) {
463 time::SleepFor(time::Time::InSeconds(0.4));
464 }
465
466 LOG(INFO, "Shooting at %f\n",
467 (::aos::time::Time::Now() - start_time).ToSeconds());
468 // Shoot
469 Shoot();
470 if (ShouldExitAuto()) return;
471
472 // Get ready to zero when we come back up.
473 time::SleepFor(time::Time::InSeconds(0.05));
474 PositionClawVertically(0.0, 0.0);
475}
476
477} // namespace autonomous
478} // namespace frc971