blob: 40d65c375e15fb4e29aae32bf7fed47c5d6b54e5 [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 Schuh1bf8a212019-05-26 22:13:14 -070024 ::aos::EventLoop *event_loop,
Austin Schuhbcce26a2018-03-26 23:41:24 -070025 const control_loops::drivetrain::DrivetrainConfig<double> &dt_config)
Austin Schuh1bf8a212019-05-26 22:13:14 -070026 : aos::common::actions::ActorBase<AutonomousActionQueueGroup>(
27 event_loop, ".frc971.autonomous.autonomous_action"),
Philipp Schrader4bd29b12017-02-22 04:42:27 +000028 dt_config_(dt_config),
Austin Schuhc087b102019-05-12 15:33:12 -070029 initial_drivetrain_({0.0, 0.0}),
30 target_selector_hint_sender_(
Austin Schuheb99d072019-05-12 21:03:38 -070031 event_loop->MakeSender<
Austin Schuhc087b102019-05-12 15:33:12 -070032 ::y2019::control_loops::drivetrain::TargetSelectorHint>(
33 ".y2019.control_loops.drivetrain.target_selector_hint")) {}
Philipp Schrader4bd29b12017-02-22 04:42:27 +000034
35void BaseAutonomousActor::ResetDrivetrain() {
36 LOG(INFO, "resetting the drivetrain\n");
Austin Schuh0aae9242018-03-14 19:49:44 -070037 max_drivetrain_voltage_ = 12.0;
James Kuszmaulc73bb222019-04-07 12:15:35 -070038 goal_spline_handle_ = 0;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000039 drivetrain_queue.goal.MakeWithBuilder()
Austin Schuh78379ea2019-01-04 20:39:45 -080040 .controller_type(0)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000041 .highgear(true)
Austin Schuh2b1fce02018-03-02 20:05:20 -080042 .wheel(0.0)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000043 .throttle(0.0)
44 .left_goal(initial_drivetrain_.left)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000045 .right_goal(initial_drivetrain_.right)
Austin Schuh0aae9242018-03-14 19:49:44 -070046 .max_ss_voltage(max_drivetrain_voltage_)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000047 .Send();
48}
49
50void BaseAutonomousActor::InitializeEncoders() {
51 drivetrain_queue.status.FetchAnother();
52 initial_drivetrain_.left = drivetrain_queue.status->estimated_left_position;
53 initial_drivetrain_.right = drivetrain_queue.status->estimated_right_position;
54}
55
56void BaseAutonomousActor::StartDrive(double distance, double angle,
57 ProfileParameters linear,
58 ProfileParameters angular) {
59 LOG(INFO, "Driving distance %f, angle %f\n", distance, angle);
60 {
61 const double dangle = angle * dt_config_.robot_radius;
62 initial_drivetrain_.left += distance - dangle;
63 initial_drivetrain_.right += distance + dangle;
64 }
65
66 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
Austin Schuh78379ea2019-01-04 20:39:45 -080067 drivetrain_message->controller_type = 1;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000068 drivetrain_message->highgear = true;
Austin Schuh2b1fce02018-03-02 20:05:20 -080069 drivetrain_message->wheel = 0.0;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000070 drivetrain_message->throttle = 0.0;
71 drivetrain_message->left_goal = initial_drivetrain_.left;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000072 drivetrain_message->right_goal = initial_drivetrain_.right;
Austin Schuh0aae9242018-03-14 19:49:44 -070073 drivetrain_message->max_ss_voltage = max_drivetrain_voltage_;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000074 drivetrain_message->linear = linear;
75 drivetrain_message->angular = angular;
76
Austin Schuh6bcc2302019-03-23 22:28:06 -070077 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
Philipp Schrader4bd29b12017-02-22 04:42:27 +000078
79 drivetrain_message.Send();
80}
81
82void BaseAutonomousActor::WaitUntilDoneOrCanceled(
83 ::std::unique_ptr<aos::common::actions::Action> action) {
84 if (!action) {
85 LOG(ERROR, "No action, not waiting\n");
86 return;
87 }
88
89 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
90 ::std::chrono::milliseconds(5) / 2);
91 while (true) {
92 // Poll the running bit and see if we should cancel.
93 phased_loop.SleepUntilNext();
94 if (!action->Running() || ShouldCancel()) {
95 return;
96 }
97 }
98}
99
100bool BaseAutonomousActor::WaitForDriveDone() {
101 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
102 ::std::chrono::milliseconds(5) / 2);
103
104 while (true) {
105 if (ShouldCancel()) {
106 return false;
107 }
108 phased_loop.SleepUntilNext();
109 drivetrain_queue.status.FetchLatest();
110 if (IsDriveDone()) {
111 return true;
112 }
113 }
114}
115
116bool BaseAutonomousActor::IsDriveDone() {
Brian Silvermanf83678c2017-03-11 14:02:26 -0800117 static constexpr double kPositionTolerance = 0.02;
118 static constexpr double kVelocityTolerance = 0.10;
119 static constexpr double kProfileTolerance = 0.001;
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000120
121 if (drivetrain_queue.status.get()) {
122 if (::std::abs(drivetrain_queue.status->profiled_left_position_goal -
123 initial_drivetrain_.left) < kProfileTolerance &&
124 ::std::abs(drivetrain_queue.status->profiled_right_position_goal -
125 initial_drivetrain_.right) < kProfileTolerance &&
126 ::std::abs(drivetrain_queue.status->estimated_left_position -
127 initial_drivetrain_.left) < kPositionTolerance &&
128 ::std::abs(drivetrain_queue.status->estimated_right_position -
129 initial_drivetrain_.right) < kPositionTolerance &&
130 ::std::abs(drivetrain_queue.status->estimated_left_velocity) <
131 kVelocityTolerance &&
132 ::std::abs(drivetrain_queue.status->estimated_right_velocity) <
133 kVelocityTolerance) {
134 LOG(INFO, "Finished drive\n");
135 return true;
136 }
137 }
138 return false;
139}
140
141bool BaseAutonomousActor::WaitForAboveAngle(double angle) {
142 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
143 ::std::chrono::milliseconds(5) / 2);
144 while (true) {
145 if (ShouldCancel()) {
146 return false;
147 }
148 phased_loop.SleepUntilNext();
149 drivetrain_queue.status.FetchLatest();
150 if (IsDriveDone()) {
151 return true;
152 }
153 if (drivetrain_queue.status.get()) {
154 if (drivetrain_queue.status->ground_angle > angle) {
155 return true;
156 }
157 }
158 }
159}
160
161bool BaseAutonomousActor::WaitForBelowAngle(double angle) {
162 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
163 ::std::chrono::milliseconds(5) / 2);
164 while (true) {
165 if (ShouldCancel()) {
166 return false;
167 }
168 phased_loop.SleepUntilNext();
169 drivetrain_queue.status.FetchLatest();
170 if (IsDriveDone()) {
171 return true;
172 }
173 if (drivetrain_queue.status.get()) {
174 if (drivetrain_queue.status->ground_angle < angle) {
175 return true;
176 }
177 }
178 }
179}
180
181bool BaseAutonomousActor::WaitForMaxBy(double angle) {
182 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
183 ::std::chrono::milliseconds(5) / 2);
184 double max_angle = -M_PI;
185 while (true) {
186 if (ShouldCancel()) {
187 return false;
188 }
189 phased_loop.SleepUntilNext();
190 drivetrain_queue.status.FetchLatest();
191 if (IsDriveDone()) {
192 return true;
193 }
194 if (drivetrain_queue.status.get()) {
195 if (drivetrain_queue.status->ground_angle > max_angle) {
196 max_angle = drivetrain_queue.status->ground_angle;
197 }
198 if (drivetrain_queue.status->ground_angle < max_angle - angle) {
199 return true;
200 }
201 }
202 }
203}
204
205bool BaseAutonomousActor::WaitForDriveNear(double distance, double angle) {
206 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
207 ::std::chrono::milliseconds(5) / 2);
208 constexpr double kPositionTolerance = 0.02;
209 constexpr double kProfileTolerance = 0.001;
210
Austin Schuhe6af9992018-07-08 15:59:14 -0700211 bool drive_has_been_close = false;
212 bool turn_has_been_close = false;
213 bool printed_first = false;
214
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000215 while (true) {
216 if (ShouldCancel()) {
217 return false;
218 }
219 phased_loop.SleepUntilNext();
220 drivetrain_queue.status.FetchLatest();
221 if (drivetrain_queue.status.get()) {
222 const double left_profile_error =
223 (initial_drivetrain_.left -
224 drivetrain_queue.status->profiled_left_position_goal);
225 const double right_profile_error =
226 (initial_drivetrain_.right -
227 drivetrain_queue.status->profiled_right_position_goal);
228
229 const double left_error =
230 (initial_drivetrain_.left -
231 drivetrain_queue.status->estimated_left_position);
232 const double right_error =
233 (initial_drivetrain_.right -
234 drivetrain_queue.status->estimated_right_position);
235
236 const double profile_distance_to_go =
237 (left_profile_error + right_profile_error) / 2.0;
238 const double profile_angle_to_go =
239 (right_profile_error - left_profile_error) /
240 (dt_config_.robot_radius * 2.0);
241
242 const double distance_to_go = (left_error + right_error) / 2.0;
243 const double angle_to_go =
244 (right_error - left_error) / (dt_config_.robot_radius * 2.0);
245
Austin Schuhe6af9992018-07-08 15:59:14 -0700246 const bool drive_close =
247 ::std::abs(profile_distance_to_go) < distance + kProfileTolerance &&
248 ::std::abs(distance_to_go) < distance + kPositionTolerance;
249 const bool turn_close =
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000250 ::std::abs(profile_angle_to_go) < angle + kProfileTolerance &&
Austin Schuhe6af9992018-07-08 15:59:14 -0700251 ::std::abs(angle_to_go) < angle + kPositionTolerance;
252
253 drive_has_been_close |= drive_close;
254 turn_has_been_close |= turn_close;
255 if (drive_has_been_close && !turn_has_been_close && !printed_first) {
256 LOG(INFO, "Drive finished first\n");
257 printed_first = true;
258 } else if (!drive_has_been_close && turn_has_been_close &&
259 !printed_first) {
260 LOG(INFO, "Turn finished first\n");
261 printed_first = true;
262 }
263
264 if (drive_close && turn_close) {
265 LOG(INFO, "Closer than %f < %f distance, %f < %f angle\n",
266 distance_to_go, distance, angle_to_go, angle);
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000267 return true;
268 }
269 }
270 }
271}
272
Austin Schuh0aae9242018-03-14 19:49:44 -0700273bool BaseAutonomousActor::WaitForDriveProfileNear(double tolerance) {
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000274 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
275 ::std::chrono::milliseconds(5) / 2);
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000276 while (true) {
277 if (ShouldCancel()) {
278 return false;
279 }
280 phased_loop.SleepUntilNext();
281 drivetrain_queue.status.FetchLatest();
Austin Schuh0aae9242018-03-14 19:49:44 -0700282
283 const Eigen::Matrix<double, 7, 1> current_error =
284 (Eigen::Matrix<double, 7, 1>()
285 << initial_drivetrain_.left -
286 drivetrain_queue.status->profiled_left_position_goal,
287 0.0, initial_drivetrain_.right -
288 drivetrain_queue.status->profiled_right_position_goal,
289 0.0, 0.0, 0.0, 0.0)
290 .finished();
291 const Eigen::Matrix<double, 2, 1> linear_error =
292 dt_config_.LeftRightToLinear(current_error);
293
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000294 if (drivetrain_queue.status.get()) {
Austin Schuh0aae9242018-03-14 19:49:44 -0700295 if (::std::abs(linear_error(0)) < tolerance) {
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000296 LOG(INFO, "Finished drive\n");
297 return true;
298 }
299 }
300 }
301}
302
Austin Schuh0aae9242018-03-14 19:49:44 -0700303bool BaseAutonomousActor::WaitForDriveProfileDone() {
304 constexpr double kProfileTolerance = 0.001;
305 return WaitForDriveProfileNear(kProfileTolerance);
306}
307
308bool BaseAutonomousActor::WaitForTurnProfileNear(double tolerance) {
Austin Schuh546a0382017-04-16 19:10:18 -0700309 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
310 ::std::chrono::milliseconds(5) / 2);
Austin Schuh546a0382017-04-16 19:10:18 -0700311 while (true) {
312 if (ShouldCancel()) {
313 return false;
314 }
315 phased_loop.SleepUntilNext();
316 drivetrain_queue.status.FetchLatest();
Austin Schuh0aae9242018-03-14 19:49:44 -0700317
318 const Eigen::Matrix<double, 7, 1> current_error =
319 (Eigen::Matrix<double, 7, 1>()
320 << initial_drivetrain_.left -
321 drivetrain_queue.status->profiled_left_position_goal,
322 0.0, initial_drivetrain_.right -
323 drivetrain_queue.status->profiled_right_position_goal,
324 0.0, 0.0, 0.0, 0.0)
325 .finished();
326 const Eigen::Matrix<double, 2, 1> angular_error =
327 dt_config_.LeftRightToAngular(current_error);
328
Austin Schuh546a0382017-04-16 19:10:18 -0700329 if (drivetrain_queue.status.get()) {
Austin Schuh0aae9242018-03-14 19:49:44 -0700330 if (::std::abs(angular_error(0)) < tolerance) {
331 LOG(INFO, "Finished turn\n");
Austin Schuh546a0382017-04-16 19:10:18 -0700332 return true;
333 }
334 }
335 }
336}
337
Austin Schuh0aae9242018-03-14 19:49:44 -0700338bool BaseAutonomousActor::WaitForTurnProfileDone() {
339 constexpr double kProfileTolerance = 0.001;
340 return WaitForTurnProfileNear(kProfileTolerance);
341}
342
Austin Schuh546a0382017-04-16 19:10:18 -0700343double BaseAutonomousActor::DriveDistanceLeft() {
344 using ::frc971::control_loops::drivetrain_queue;
345 drivetrain_queue.status.FetchLatest();
346 if (drivetrain_queue.status.get()) {
347 const double left_error =
348 (initial_drivetrain_.left -
349 drivetrain_queue.status->estimated_left_position);
350 const double right_error =
351 (initial_drivetrain_.right -
352 drivetrain_queue.status->estimated_right_position);
353
354 return (left_error + right_error) / 2.0;
355 } else {
356 return 0;
357 }
358}
359
James Kuszmaul838040b2019-04-13 15:51:02 -0700360bool BaseAutonomousActor::SplineHandle::SplineDistanceRemaining(
361 double distance) {
362 drivetrain_queue.status.FetchLatest();
363 if (drivetrain_queue.status.get()) {
364 return drivetrain_queue.status->trajectory_logging.is_executing &&
365 drivetrain_queue.status->trajectory_logging.distance_remaining <
366 distance;
367 }
368 return false;
369}
370bool BaseAutonomousActor::SplineHandle::WaitForSplineDistanceRemaining(
371 double distance) {
372 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
373 ::std::chrono::milliseconds(5) / 2);
374 while (true) {
375 if (base_autonomous_actor_->ShouldCancel()) {
376 return false;
377 }
378 phased_loop.SleepUntilNext();
379 if (SplineDistanceRemaining(distance)) {
380 return true;
381 }
382 }
383}
384
James Kuszmaul8bb5df22019-05-01 21:40:08 -0500385void BaseAutonomousActor::LineFollowAtVelocity(double velocity, int hint) {
James Kuszmaul838040b2019-04-13 15:51:02 -0700386 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
387 drivetrain_message->controller_type = 3;
388 // TODO(james): Currently the 4.0 is copied from the
389 // line_follow_drivetrain.cc, but it is somewhat year-specific, so we should
390 // factor it out in some way.
391 drivetrain_message->throttle = velocity / 4.0;
392 drivetrain_message.Send();
Austin Schuhc087b102019-05-12 15:33:12 -0700393 auto target_hint = target_selector_hint_sender_.MakeMessage();
James Kuszmaul8bb5df22019-05-01 21:40:08 -0500394 target_hint->suggested_target = hint;
395 target_hint.Send();
James Kuszmaul838040b2019-04-13 15:51:02 -0700396}
397
Austin Schuh6bcc2302019-03-23 22:28:06 -0700398BaseAutonomousActor::SplineHandle BaseAutonomousActor::PlanSpline(
James Kuszmaulc73bb222019-04-07 12:15:35 -0700399 const ::frc971::MultiSpline &spline, SplineDirection direction) {
Austin Schuh6bcc2302019-03-23 22:28:06 -0700400 LOG(INFO, "Planning spline\n");
401
402 int32_t spline_handle = (++spline_handle_) | ((getpid() & 0xFFFF) << 15);
403
404 drivetrain_queue.goal.FetchLatest();
405
406 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
James Kuszmaulc864bcf2019-05-01 20:17:34 -0700407
408 int controller_type = 2;
409 if (drivetrain_queue.goal.get()) {
410 controller_type = drivetrain_queue.goal->controller_type;
411 drivetrain_message->throttle = drivetrain_queue.goal->throttle;
412 }
413 drivetrain_message->controller_type = controller_type;
Austin Schuh6bcc2302019-03-23 22:28:06 -0700414
415 drivetrain_message->spline = spline;
416 drivetrain_message->spline.spline_idx = spline_handle;
417 drivetrain_message->spline_handle = goal_spline_handle_;
James Kuszmaul29e417d2019-04-13 10:03:35 -0700418 drivetrain_message->spline.drive_spline_backwards =
James Kuszmaulc73bb222019-04-07 12:15:35 -0700419 direction == SplineDirection::kBackward;
Austin Schuh6bcc2302019-03-23 22:28:06 -0700420
421 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
422
423 drivetrain_message.Send();
424
425 return BaseAutonomousActor::SplineHandle(spline_handle, this);
426}
427
428bool BaseAutonomousActor::SplineHandle::IsPlanned() {
429 drivetrain_queue.status.FetchLatest();
Austin Schuhb5b79a52019-05-08 20:32:07 -0700430 LOG_STRUCT(DEBUG, "dts", *drivetrain_queue.status.get());
Austin Schuh6bcc2302019-03-23 22:28:06 -0700431 if (drivetrain_queue.status.get() &&
432 ((drivetrain_queue.status->trajectory_logging.planning_spline_idx ==
433 spline_handle_ &&
434 drivetrain_queue.status->trajectory_logging.planning_state == 3) ||
435 drivetrain_queue.status->trajectory_logging.current_spline_idx ==
436 spline_handle_)) {
437 return true;
438 }
439 return false;
440}
441
442bool BaseAutonomousActor::SplineHandle::WaitForPlan() {
443 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
444 ::std::chrono::milliseconds(5) / 2);
445 while (true) {
446 if (base_autonomous_actor_->ShouldCancel()) {
447 return false;
448 }
449 phased_loop.SleepUntilNext();
450 if (IsPlanned()) {
451 return true;
452 }
453 }
454}
455
456void BaseAutonomousActor::SplineHandle::Start() {
457 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
458 drivetrain_message->controller_type = 2;
459
460 LOG(INFO, "Starting spline\n");
461
462 drivetrain_message->spline_handle = spline_handle_;
463 base_autonomous_actor_->goal_spline_handle_ = spline_handle_;
464
465 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
466
467 drivetrain_message.Send();
468}
469
470bool BaseAutonomousActor::SplineHandle::IsDone() {
471 drivetrain_queue.status.FetchLatest();
472 LOG_STRUCT(INFO, "dts", *drivetrain_queue.status.get());
473
James Kuszmaulc73bb222019-04-07 12:15:35 -0700474 // We check that the spline we are waiting on is neither currently planning
James Kuszmaul29e417d2019-04-13 10:03:35 -0700475 // nor executing (we check is_executed because it is possible to receive
476 // status messages with is_executing false before the execution has started).
477 // We check for planning so that the user can go straight from starting the
478 // planner to executing without a WaitForPlan in between.
Austin Schuh6bcc2302019-03-23 22:28:06 -0700479 if (drivetrain_queue.status.get() &&
James Kuszmaul29e417d2019-04-13 10:03:35 -0700480 ((!drivetrain_queue.status->trajectory_logging.is_executed &&
481 drivetrain_queue.status->trajectory_logging.current_spline_idx ==
482 spline_handle_) ||
James Kuszmaulc73bb222019-04-07 12:15:35 -0700483 drivetrain_queue.status->trajectory_logging.planning_spline_idx ==
484 spline_handle_)) {
Austin Schuh6bcc2302019-03-23 22:28:06 -0700485 return false;
486 }
487 return true;
488}
489
490bool BaseAutonomousActor::SplineHandle::WaitForDone() {
491 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
492 ::std::chrono::milliseconds(5) / 2);
493 while (true) {
494 if (base_autonomous_actor_->ShouldCancel()) {
495 return false;
496 }
497 phased_loop.SleepUntilNext();
498 if (IsDone()) {
499 return true;
500 }
501 }
502}
503
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000504} // namespace autonomous
505} // namespace frc971