blob: 7d7e12ee78e1265f29734431b39eee27a0a33daf [file] [log] [blame]
Philipp Schrader4bd29b12017-02-22 04:42:27 +00001#include "frc971/autonomous/base_autonomous_actor.h"
2
3#include <inttypes.h>
4
5#include <chrono>
6#include <cmath>
7
John Park33858a32018-09-28 23:05:48 -07008#include "aos/util/phased_loop.h"
9#include "aos/logging/logging.h"
Philipp Schrader4bd29b12017-02-22 04:42:27 +000010
11#include "frc971/control_loops/drivetrain/drivetrain.q.h"
James Kuszmaul8bb5df22019-05-01 21:40:08 -050012#include "frc971/control_loops/drivetrain/localizer.q.h"
13#include "y2019/control_loops/drivetrain/target_selector.q.h"
Philipp Schrader4bd29b12017-02-22 04:42:27 +000014
Philipp Schrader4bd29b12017-02-22 04:42:27 +000015using ::frc971::control_loops::drivetrain_queue;
16using ::aos::monotonic_clock;
17namespace chrono = ::std::chrono;
18namespace this_thread = ::std::this_thread;
19
Brian Silvermanf83678c2017-03-11 14:02:26 -080020namespace frc971 {
21namespace autonomous {
Philipp Schrader4bd29b12017-02-22 04:42:27 +000022
23BaseAutonomousActor::BaseAutonomousActor(
Austin Schuheb99d072019-05-12 21:03:38 -070024 ::aos::EventLoop *event_loop, AutonomousActionQueueGroup *s,
Austin Schuhbcce26a2018-03-26 23:41:24 -070025 const control_loops::drivetrain::DrivetrainConfig<double> &dt_config)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000026 : aos::common::actions::ActorBase<AutonomousActionQueueGroup>(s),
27 dt_config_(dt_config),
Austin Schuhc087b102019-05-12 15:33:12 -070028 initial_drivetrain_({0.0, 0.0}),
29 target_selector_hint_sender_(
Austin Schuheb99d072019-05-12 21:03:38 -070030 event_loop->MakeSender<
Austin Schuhc087b102019-05-12 15:33:12 -070031 ::y2019::control_loops::drivetrain::TargetSelectorHint>(
32 ".y2019.control_loops.drivetrain.target_selector_hint")) {}
Philipp Schrader4bd29b12017-02-22 04:42:27 +000033
34void BaseAutonomousActor::ResetDrivetrain() {
35 LOG(INFO, "resetting the drivetrain\n");
Austin Schuh0aae9242018-03-14 19:49:44 -070036 max_drivetrain_voltage_ = 12.0;
James Kuszmaulc73bb222019-04-07 12:15:35 -070037 goal_spline_handle_ = 0;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000038 drivetrain_queue.goal.MakeWithBuilder()
Austin Schuh78379ea2019-01-04 20:39:45 -080039 .controller_type(0)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000040 .highgear(true)
Austin Schuh2b1fce02018-03-02 20:05:20 -080041 .wheel(0.0)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000042 .throttle(0.0)
43 .left_goal(initial_drivetrain_.left)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000044 .right_goal(initial_drivetrain_.right)
Austin Schuh0aae9242018-03-14 19:49:44 -070045 .max_ss_voltage(max_drivetrain_voltage_)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000046 .Send();
47}
48
49void BaseAutonomousActor::InitializeEncoders() {
50 drivetrain_queue.status.FetchAnother();
51 initial_drivetrain_.left = drivetrain_queue.status->estimated_left_position;
52 initial_drivetrain_.right = drivetrain_queue.status->estimated_right_position;
53}
54
55void BaseAutonomousActor::StartDrive(double distance, double angle,
56 ProfileParameters linear,
57 ProfileParameters angular) {
58 LOG(INFO, "Driving distance %f, angle %f\n", distance, angle);
59 {
60 const double dangle = angle * dt_config_.robot_radius;
61 initial_drivetrain_.left += distance - dangle;
62 initial_drivetrain_.right += distance + dangle;
63 }
64
65 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
Austin Schuh78379ea2019-01-04 20:39:45 -080066 drivetrain_message->controller_type = 1;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000067 drivetrain_message->highgear = true;
Austin Schuh2b1fce02018-03-02 20:05:20 -080068 drivetrain_message->wheel = 0.0;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000069 drivetrain_message->throttle = 0.0;
70 drivetrain_message->left_goal = initial_drivetrain_.left;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000071 drivetrain_message->right_goal = initial_drivetrain_.right;
Austin Schuh0aae9242018-03-14 19:49:44 -070072 drivetrain_message->max_ss_voltage = max_drivetrain_voltage_;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000073 drivetrain_message->linear = linear;
74 drivetrain_message->angular = angular;
75
Austin Schuh6bcc2302019-03-23 22:28:06 -070076 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
Philipp Schrader4bd29b12017-02-22 04:42:27 +000077
78 drivetrain_message.Send();
79}
80
81void BaseAutonomousActor::WaitUntilDoneOrCanceled(
82 ::std::unique_ptr<aos::common::actions::Action> action) {
83 if (!action) {
84 LOG(ERROR, "No action, not waiting\n");
85 return;
86 }
87
88 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
89 ::std::chrono::milliseconds(5) / 2);
90 while (true) {
91 // Poll the running bit and see if we should cancel.
92 phased_loop.SleepUntilNext();
93 if (!action->Running() || ShouldCancel()) {
94 return;
95 }
96 }
97}
98
99bool BaseAutonomousActor::WaitForDriveDone() {
100 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
101 ::std::chrono::milliseconds(5) / 2);
102
103 while (true) {
104 if (ShouldCancel()) {
105 return false;
106 }
107 phased_loop.SleepUntilNext();
108 drivetrain_queue.status.FetchLatest();
109 if (IsDriveDone()) {
110 return true;
111 }
112 }
113}
114
115bool BaseAutonomousActor::IsDriveDone() {
Brian Silvermanf83678c2017-03-11 14:02:26 -0800116 static constexpr double kPositionTolerance = 0.02;
117 static constexpr double kVelocityTolerance = 0.10;
118 static constexpr double kProfileTolerance = 0.001;
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000119
120 if (drivetrain_queue.status.get()) {
121 if (::std::abs(drivetrain_queue.status->profiled_left_position_goal -
122 initial_drivetrain_.left) < kProfileTolerance &&
123 ::std::abs(drivetrain_queue.status->profiled_right_position_goal -
124 initial_drivetrain_.right) < kProfileTolerance &&
125 ::std::abs(drivetrain_queue.status->estimated_left_position -
126 initial_drivetrain_.left) < kPositionTolerance &&
127 ::std::abs(drivetrain_queue.status->estimated_right_position -
128 initial_drivetrain_.right) < kPositionTolerance &&
129 ::std::abs(drivetrain_queue.status->estimated_left_velocity) <
130 kVelocityTolerance &&
131 ::std::abs(drivetrain_queue.status->estimated_right_velocity) <
132 kVelocityTolerance) {
133 LOG(INFO, "Finished drive\n");
134 return true;
135 }
136 }
137 return false;
138}
139
140bool BaseAutonomousActor::WaitForAboveAngle(double angle) {
141 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
142 ::std::chrono::milliseconds(5) / 2);
143 while (true) {
144 if (ShouldCancel()) {
145 return false;
146 }
147 phased_loop.SleepUntilNext();
148 drivetrain_queue.status.FetchLatest();
149 if (IsDriveDone()) {
150 return true;
151 }
152 if (drivetrain_queue.status.get()) {
153 if (drivetrain_queue.status->ground_angle > angle) {
154 return true;
155 }
156 }
157 }
158}
159
160bool BaseAutonomousActor::WaitForBelowAngle(double angle) {
161 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
162 ::std::chrono::milliseconds(5) / 2);
163 while (true) {
164 if (ShouldCancel()) {
165 return false;
166 }
167 phased_loop.SleepUntilNext();
168 drivetrain_queue.status.FetchLatest();
169 if (IsDriveDone()) {
170 return true;
171 }
172 if (drivetrain_queue.status.get()) {
173 if (drivetrain_queue.status->ground_angle < angle) {
174 return true;
175 }
176 }
177 }
178}
179
180bool BaseAutonomousActor::WaitForMaxBy(double angle) {
181 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
182 ::std::chrono::milliseconds(5) / 2);
183 double max_angle = -M_PI;
184 while (true) {
185 if (ShouldCancel()) {
186 return false;
187 }
188 phased_loop.SleepUntilNext();
189 drivetrain_queue.status.FetchLatest();
190 if (IsDriveDone()) {
191 return true;
192 }
193 if (drivetrain_queue.status.get()) {
194 if (drivetrain_queue.status->ground_angle > max_angle) {
195 max_angle = drivetrain_queue.status->ground_angle;
196 }
197 if (drivetrain_queue.status->ground_angle < max_angle - angle) {
198 return true;
199 }
200 }
201 }
202}
203
204bool BaseAutonomousActor::WaitForDriveNear(double distance, double angle) {
205 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
206 ::std::chrono::milliseconds(5) / 2);
207 constexpr double kPositionTolerance = 0.02;
208 constexpr double kProfileTolerance = 0.001;
209
Austin Schuhe6af9992018-07-08 15:59:14 -0700210 bool drive_has_been_close = false;
211 bool turn_has_been_close = false;
212 bool printed_first = false;
213
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000214 while (true) {
215 if (ShouldCancel()) {
216 return false;
217 }
218 phased_loop.SleepUntilNext();
219 drivetrain_queue.status.FetchLatest();
220 if (drivetrain_queue.status.get()) {
221 const double left_profile_error =
222 (initial_drivetrain_.left -
223 drivetrain_queue.status->profiled_left_position_goal);
224 const double right_profile_error =
225 (initial_drivetrain_.right -
226 drivetrain_queue.status->profiled_right_position_goal);
227
228 const double left_error =
229 (initial_drivetrain_.left -
230 drivetrain_queue.status->estimated_left_position);
231 const double right_error =
232 (initial_drivetrain_.right -
233 drivetrain_queue.status->estimated_right_position);
234
235 const double profile_distance_to_go =
236 (left_profile_error + right_profile_error) / 2.0;
237 const double profile_angle_to_go =
238 (right_profile_error - left_profile_error) /
239 (dt_config_.robot_radius * 2.0);
240
241 const double distance_to_go = (left_error + right_error) / 2.0;
242 const double angle_to_go =
243 (right_error - left_error) / (dt_config_.robot_radius * 2.0);
244
Austin Schuhe6af9992018-07-08 15:59:14 -0700245 const bool drive_close =
246 ::std::abs(profile_distance_to_go) < distance + kProfileTolerance &&
247 ::std::abs(distance_to_go) < distance + kPositionTolerance;
248 const bool turn_close =
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000249 ::std::abs(profile_angle_to_go) < angle + kProfileTolerance &&
Austin Schuhe6af9992018-07-08 15:59:14 -0700250 ::std::abs(angle_to_go) < angle + kPositionTolerance;
251
252 drive_has_been_close |= drive_close;
253 turn_has_been_close |= turn_close;
254 if (drive_has_been_close && !turn_has_been_close && !printed_first) {
255 LOG(INFO, "Drive finished first\n");
256 printed_first = true;
257 } else if (!drive_has_been_close && turn_has_been_close &&
258 !printed_first) {
259 LOG(INFO, "Turn finished first\n");
260 printed_first = true;
261 }
262
263 if (drive_close && turn_close) {
264 LOG(INFO, "Closer than %f < %f distance, %f < %f angle\n",
265 distance_to_go, distance, angle_to_go, angle);
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000266 return true;
267 }
268 }
269 }
270}
271
Austin Schuh0aae9242018-03-14 19:49:44 -0700272bool BaseAutonomousActor::WaitForDriveProfileNear(double tolerance) {
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000273 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
274 ::std::chrono::milliseconds(5) / 2);
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000275 while (true) {
276 if (ShouldCancel()) {
277 return false;
278 }
279 phased_loop.SleepUntilNext();
280 drivetrain_queue.status.FetchLatest();
Austin Schuh0aae9242018-03-14 19:49:44 -0700281
282 const Eigen::Matrix<double, 7, 1> current_error =
283 (Eigen::Matrix<double, 7, 1>()
284 << initial_drivetrain_.left -
285 drivetrain_queue.status->profiled_left_position_goal,
286 0.0, initial_drivetrain_.right -
287 drivetrain_queue.status->profiled_right_position_goal,
288 0.0, 0.0, 0.0, 0.0)
289 .finished();
290 const Eigen::Matrix<double, 2, 1> linear_error =
291 dt_config_.LeftRightToLinear(current_error);
292
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000293 if (drivetrain_queue.status.get()) {
Austin Schuh0aae9242018-03-14 19:49:44 -0700294 if (::std::abs(linear_error(0)) < tolerance) {
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000295 LOG(INFO, "Finished drive\n");
296 return true;
297 }
298 }
299 }
300}
301
Austin Schuh0aae9242018-03-14 19:49:44 -0700302bool BaseAutonomousActor::WaitForDriveProfileDone() {
303 constexpr double kProfileTolerance = 0.001;
304 return WaitForDriveProfileNear(kProfileTolerance);
305}
306
307bool BaseAutonomousActor::WaitForTurnProfileNear(double tolerance) {
Austin Schuh546a0382017-04-16 19:10:18 -0700308 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
309 ::std::chrono::milliseconds(5) / 2);
Austin Schuh546a0382017-04-16 19:10:18 -0700310 while (true) {
311 if (ShouldCancel()) {
312 return false;
313 }
314 phased_loop.SleepUntilNext();
315 drivetrain_queue.status.FetchLatest();
Austin Schuh0aae9242018-03-14 19:49:44 -0700316
317 const Eigen::Matrix<double, 7, 1> current_error =
318 (Eigen::Matrix<double, 7, 1>()
319 << initial_drivetrain_.left -
320 drivetrain_queue.status->profiled_left_position_goal,
321 0.0, initial_drivetrain_.right -
322 drivetrain_queue.status->profiled_right_position_goal,
323 0.0, 0.0, 0.0, 0.0)
324 .finished();
325 const Eigen::Matrix<double, 2, 1> angular_error =
326 dt_config_.LeftRightToAngular(current_error);
327
Austin Schuh546a0382017-04-16 19:10:18 -0700328 if (drivetrain_queue.status.get()) {
Austin Schuh0aae9242018-03-14 19:49:44 -0700329 if (::std::abs(angular_error(0)) < tolerance) {
330 LOG(INFO, "Finished turn\n");
Austin Schuh546a0382017-04-16 19:10:18 -0700331 return true;
332 }
333 }
334 }
335}
336
Austin Schuh0aae9242018-03-14 19:49:44 -0700337bool BaseAutonomousActor::WaitForTurnProfileDone() {
338 constexpr double kProfileTolerance = 0.001;
339 return WaitForTurnProfileNear(kProfileTolerance);
340}
341
Austin Schuh546a0382017-04-16 19:10:18 -0700342double BaseAutonomousActor::DriveDistanceLeft() {
343 using ::frc971::control_loops::drivetrain_queue;
344 drivetrain_queue.status.FetchLatest();
345 if (drivetrain_queue.status.get()) {
346 const double left_error =
347 (initial_drivetrain_.left -
348 drivetrain_queue.status->estimated_left_position);
349 const double right_error =
350 (initial_drivetrain_.right -
351 drivetrain_queue.status->estimated_right_position);
352
353 return (left_error + right_error) / 2.0;
354 } else {
355 return 0;
356 }
357}
358
James Kuszmaul838040b2019-04-13 15:51:02 -0700359bool BaseAutonomousActor::SplineHandle::SplineDistanceRemaining(
360 double distance) {
361 drivetrain_queue.status.FetchLatest();
362 if (drivetrain_queue.status.get()) {
363 return drivetrain_queue.status->trajectory_logging.is_executing &&
364 drivetrain_queue.status->trajectory_logging.distance_remaining <
365 distance;
366 }
367 return false;
368}
369bool BaseAutonomousActor::SplineHandle::WaitForSplineDistanceRemaining(
370 double distance) {
371 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
372 ::std::chrono::milliseconds(5) / 2);
373 while (true) {
374 if (base_autonomous_actor_->ShouldCancel()) {
375 return false;
376 }
377 phased_loop.SleepUntilNext();
378 if (SplineDistanceRemaining(distance)) {
379 return true;
380 }
381 }
382}
383
James Kuszmaul8bb5df22019-05-01 21:40:08 -0500384void BaseAutonomousActor::LineFollowAtVelocity(double velocity, int hint) {
James Kuszmaul838040b2019-04-13 15:51:02 -0700385 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
386 drivetrain_message->controller_type = 3;
387 // TODO(james): Currently the 4.0 is copied from the
388 // line_follow_drivetrain.cc, but it is somewhat year-specific, so we should
389 // factor it out in some way.
390 drivetrain_message->throttle = velocity / 4.0;
391 drivetrain_message.Send();
Austin Schuhc087b102019-05-12 15:33:12 -0700392 auto target_hint = target_selector_hint_sender_.MakeMessage();
James Kuszmaul8bb5df22019-05-01 21:40:08 -0500393 target_hint->suggested_target = hint;
394 target_hint.Send();
James Kuszmaul838040b2019-04-13 15:51:02 -0700395}
396
Austin Schuh6bcc2302019-03-23 22:28:06 -0700397BaseAutonomousActor::SplineHandle BaseAutonomousActor::PlanSpline(
James Kuszmaulc73bb222019-04-07 12:15:35 -0700398 const ::frc971::MultiSpline &spline, SplineDirection direction) {
Austin Schuh6bcc2302019-03-23 22:28:06 -0700399 LOG(INFO, "Planning spline\n");
400
401 int32_t spline_handle = (++spline_handle_) | ((getpid() & 0xFFFF) << 15);
402
403 drivetrain_queue.goal.FetchLatest();
404
405 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
James Kuszmaulc864bcf2019-05-01 20:17:34 -0700406
407 int controller_type = 2;
408 if (drivetrain_queue.goal.get()) {
409 controller_type = drivetrain_queue.goal->controller_type;
410 drivetrain_message->throttle = drivetrain_queue.goal->throttle;
411 }
412 drivetrain_message->controller_type = controller_type;
Austin Schuh6bcc2302019-03-23 22:28:06 -0700413
414 drivetrain_message->spline = spline;
415 drivetrain_message->spline.spline_idx = spline_handle;
416 drivetrain_message->spline_handle = goal_spline_handle_;
James Kuszmaul29e417d2019-04-13 10:03:35 -0700417 drivetrain_message->spline.drive_spline_backwards =
James Kuszmaulc73bb222019-04-07 12:15:35 -0700418 direction == SplineDirection::kBackward;
Austin Schuh6bcc2302019-03-23 22:28:06 -0700419
420 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
421
422 drivetrain_message.Send();
423
424 return BaseAutonomousActor::SplineHandle(spline_handle, this);
425}
426
427bool BaseAutonomousActor::SplineHandle::IsPlanned() {
428 drivetrain_queue.status.FetchLatest();
Austin Schuhb5b79a52019-05-08 20:32:07 -0700429 LOG_STRUCT(DEBUG, "dts", *drivetrain_queue.status.get());
Austin Schuh6bcc2302019-03-23 22:28:06 -0700430 if (drivetrain_queue.status.get() &&
431 ((drivetrain_queue.status->trajectory_logging.planning_spline_idx ==
432 spline_handle_ &&
433 drivetrain_queue.status->trajectory_logging.planning_state == 3) ||
434 drivetrain_queue.status->trajectory_logging.current_spline_idx ==
435 spline_handle_)) {
436 return true;
437 }
438 return false;
439}
440
441bool BaseAutonomousActor::SplineHandle::WaitForPlan() {
442 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
443 ::std::chrono::milliseconds(5) / 2);
444 while (true) {
445 if (base_autonomous_actor_->ShouldCancel()) {
446 return false;
447 }
448 phased_loop.SleepUntilNext();
449 if (IsPlanned()) {
450 return true;
451 }
452 }
453}
454
455void BaseAutonomousActor::SplineHandle::Start() {
456 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
457 drivetrain_message->controller_type = 2;
458
459 LOG(INFO, "Starting spline\n");
460
461 drivetrain_message->spline_handle = spline_handle_;
462 base_autonomous_actor_->goal_spline_handle_ = spline_handle_;
463
464 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
465
466 drivetrain_message.Send();
467}
468
469bool BaseAutonomousActor::SplineHandle::IsDone() {
470 drivetrain_queue.status.FetchLatest();
471 LOG_STRUCT(INFO, "dts", *drivetrain_queue.status.get());
472
James Kuszmaulc73bb222019-04-07 12:15:35 -0700473 // We check that the spline we are waiting on is neither currently planning
James Kuszmaul29e417d2019-04-13 10:03:35 -0700474 // nor executing (we check is_executed because it is possible to receive
475 // status messages with is_executing false before the execution has started).
476 // We check for planning so that the user can go straight from starting the
477 // planner to executing without a WaitForPlan in between.
Austin Schuh6bcc2302019-03-23 22:28:06 -0700478 if (drivetrain_queue.status.get() &&
James Kuszmaul29e417d2019-04-13 10:03:35 -0700479 ((!drivetrain_queue.status->trajectory_logging.is_executed &&
480 drivetrain_queue.status->trajectory_logging.current_spline_idx ==
481 spline_handle_) ||
James Kuszmaulc73bb222019-04-07 12:15:35 -0700482 drivetrain_queue.status->trajectory_logging.planning_spline_idx ==
483 spline_handle_)) {
Austin Schuh6bcc2302019-03-23 22:28:06 -0700484 return false;
485 }
486 return true;
487}
488
489bool BaseAutonomousActor::SplineHandle::WaitForDone() {
490 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
491 ::std::chrono::milliseconds(5) / 2);
492 while (true) {
493 if (base_autonomous_actor_->ShouldCancel()) {
494 return false;
495 }
496 phased_loop.SleepUntilNext();
497 if (IsDone()) {
498 return true;
499 }
500 }
501}
502
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000503::std::unique_ptr<AutonomousAction> MakeAutonomousAction(
504 const AutonomousActionParams &params) {
505 return ::std::unique_ptr<AutonomousAction>(
506 new AutonomousAction(&autonomous_action, params));
507}
508
509} // namespace autonomous
510} // namespace frc971