blob: 3509d638fb9a001a2c1411da40d45708c29b2628 [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;
James Kuszmaul8bb5df22019-05-01 21:40:08 -050017using ::y2019::control_loops::drivetrain::target_selector_hint;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000018namespace chrono = ::std::chrono;
19namespace this_thread = ::std::this_thread;
20
Brian Silvermanf83678c2017-03-11 14:02:26 -080021namespace frc971 {
22namespace autonomous {
Philipp Schrader4bd29b12017-02-22 04:42:27 +000023
24BaseAutonomousActor::BaseAutonomousActor(
25 AutonomousActionQueueGroup *s,
Austin Schuhbcce26a2018-03-26 23:41:24 -070026 const control_loops::drivetrain::DrivetrainConfig<double> &dt_config)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000027 : aos::common::actions::ActorBase<AutonomousActionQueueGroup>(s),
28 dt_config_(dt_config),
29 initial_drivetrain_({0.0, 0.0}) {}
30
31void BaseAutonomousActor::ResetDrivetrain() {
32 LOG(INFO, "resetting the drivetrain\n");
Austin Schuh0aae9242018-03-14 19:49:44 -070033 max_drivetrain_voltage_ = 12.0;
James Kuszmaulc73bb222019-04-07 12:15:35 -070034 goal_spline_handle_ = 0;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000035 drivetrain_queue.goal.MakeWithBuilder()
Austin Schuh78379ea2019-01-04 20:39:45 -080036 .controller_type(0)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000037 .highgear(true)
Austin Schuh2b1fce02018-03-02 20:05:20 -080038 .wheel(0.0)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000039 .throttle(0.0)
40 .left_goal(initial_drivetrain_.left)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000041 .right_goal(initial_drivetrain_.right)
Austin Schuh0aae9242018-03-14 19:49:44 -070042 .max_ss_voltage(max_drivetrain_voltage_)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000043 .Send();
44}
45
46void BaseAutonomousActor::InitializeEncoders() {
47 drivetrain_queue.status.FetchAnother();
48 initial_drivetrain_.left = drivetrain_queue.status->estimated_left_position;
49 initial_drivetrain_.right = drivetrain_queue.status->estimated_right_position;
50}
51
52void BaseAutonomousActor::StartDrive(double distance, double angle,
53 ProfileParameters linear,
54 ProfileParameters angular) {
55 LOG(INFO, "Driving distance %f, angle %f\n", distance, angle);
56 {
57 const double dangle = angle * dt_config_.robot_radius;
58 initial_drivetrain_.left += distance - dangle;
59 initial_drivetrain_.right += distance + dangle;
60 }
61
62 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
Austin Schuh78379ea2019-01-04 20:39:45 -080063 drivetrain_message->controller_type = 1;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000064 drivetrain_message->highgear = true;
Austin Schuh2b1fce02018-03-02 20:05:20 -080065 drivetrain_message->wheel = 0.0;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000066 drivetrain_message->throttle = 0.0;
67 drivetrain_message->left_goal = initial_drivetrain_.left;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000068 drivetrain_message->right_goal = initial_drivetrain_.right;
Austin Schuh0aae9242018-03-14 19:49:44 -070069 drivetrain_message->max_ss_voltage = max_drivetrain_voltage_;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000070 drivetrain_message->linear = linear;
71 drivetrain_message->angular = angular;
72
Austin Schuh6bcc2302019-03-23 22:28:06 -070073 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
Philipp Schrader4bd29b12017-02-22 04:42:27 +000074
75 drivetrain_message.Send();
76}
77
78void BaseAutonomousActor::WaitUntilDoneOrCanceled(
79 ::std::unique_ptr<aos::common::actions::Action> action) {
80 if (!action) {
81 LOG(ERROR, "No action, not waiting\n");
82 return;
83 }
84
85 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
86 ::std::chrono::milliseconds(5) / 2);
87 while (true) {
88 // Poll the running bit and see if we should cancel.
89 phased_loop.SleepUntilNext();
90 if (!action->Running() || ShouldCancel()) {
91 return;
92 }
93 }
94}
95
96bool BaseAutonomousActor::WaitForDriveDone() {
97 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
98 ::std::chrono::milliseconds(5) / 2);
99
100 while (true) {
101 if (ShouldCancel()) {
102 return false;
103 }
104 phased_loop.SleepUntilNext();
105 drivetrain_queue.status.FetchLatest();
106 if (IsDriveDone()) {
107 return true;
108 }
109 }
110}
111
112bool BaseAutonomousActor::IsDriveDone() {
Brian Silvermanf83678c2017-03-11 14:02:26 -0800113 static constexpr double kPositionTolerance = 0.02;
114 static constexpr double kVelocityTolerance = 0.10;
115 static constexpr double kProfileTolerance = 0.001;
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000116
117 if (drivetrain_queue.status.get()) {
118 if (::std::abs(drivetrain_queue.status->profiled_left_position_goal -
119 initial_drivetrain_.left) < kProfileTolerance &&
120 ::std::abs(drivetrain_queue.status->profiled_right_position_goal -
121 initial_drivetrain_.right) < kProfileTolerance &&
122 ::std::abs(drivetrain_queue.status->estimated_left_position -
123 initial_drivetrain_.left) < kPositionTolerance &&
124 ::std::abs(drivetrain_queue.status->estimated_right_position -
125 initial_drivetrain_.right) < kPositionTolerance &&
126 ::std::abs(drivetrain_queue.status->estimated_left_velocity) <
127 kVelocityTolerance &&
128 ::std::abs(drivetrain_queue.status->estimated_right_velocity) <
129 kVelocityTolerance) {
130 LOG(INFO, "Finished drive\n");
131 return true;
132 }
133 }
134 return false;
135}
136
137bool BaseAutonomousActor::WaitForAboveAngle(double angle) {
138 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
139 ::std::chrono::milliseconds(5) / 2);
140 while (true) {
141 if (ShouldCancel()) {
142 return false;
143 }
144 phased_loop.SleepUntilNext();
145 drivetrain_queue.status.FetchLatest();
146 if (IsDriveDone()) {
147 return true;
148 }
149 if (drivetrain_queue.status.get()) {
150 if (drivetrain_queue.status->ground_angle > angle) {
151 return true;
152 }
153 }
154 }
155}
156
157bool BaseAutonomousActor::WaitForBelowAngle(double angle) {
158 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
159 ::std::chrono::milliseconds(5) / 2);
160 while (true) {
161 if (ShouldCancel()) {
162 return false;
163 }
164 phased_loop.SleepUntilNext();
165 drivetrain_queue.status.FetchLatest();
166 if (IsDriveDone()) {
167 return true;
168 }
169 if (drivetrain_queue.status.get()) {
170 if (drivetrain_queue.status->ground_angle < angle) {
171 return true;
172 }
173 }
174 }
175}
176
177bool BaseAutonomousActor::WaitForMaxBy(double angle) {
178 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
179 ::std::chrono::milliseconds(5) / 2);
180 double max_angle = -M_PI;
181 while (true) {
182 if (ShouldCancel()) {
183 return false;
184 }
185 phased_loop.SleepUntilNext();
186 drivetrain_queue.status.FetchLatest();
187 if (IsDriveDone()) {
188 return true;
189 }
190 if (drivetrain_queue.status.get()) {
191 if (drivetrain_queue.status->ground_angle > max_angle) {
192 max_angle = drivetrain_queue.status->ground_angle;
193 }
194 if (drivetrain_queue.status->ground_angle < max_angle - angle) {
195 return true;
196 }
197 }
198 }
199}
200
201bool BaseAutonomousActor::WaitForDriveNear(double distance, double angle) {
202 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
203 ::std::chrono::milliseconds(5) / 2);
204 constexpr double kPositionTolerance = 0.02;
205 constexpr double kProfileTolerance = 0.001;
206
Austin Schuhe6af9992018-07-08 15:59:14 -0700207 bool drive_has_been_close = false;
208 bool turn_has_been_close = false;
209 bool printed_first = false;
210
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000211 while (true) {
212 if (ShouldCancel()) {
213 return false;
214 }
215 phased_loop.SleepUntilNext();
216 drivetrain_queue.status.FetchLatest();
217 if (drivetrain_queue.status.get()) {
218 const double left_profile_error =
219 (initial_drivetrain_.left -
220 drivetrain_queue.status->profiled_left_position_goal);
221 const double right_profile_error =
222 (initial_drivetrain_.right -
223 drivetrain_queue.status->profiled_right_position_goal);
224
225 const double left_error =
226 (initial_drivetrain_.left -
227 drivetrain_queue.status->estimated_left_position);
228 const double right_error =
229 (initial_drivetrain_.right -
230 drivetrain_queue.status->estimated_right_position);
231
232 const double profile_distance_to_go =
233 (left_profile_error + right_profile_error) / 2.0;
234 const double profile_angle_to_go =
235 (right_profile_error - left_profile_error) /
236 (dt_config_.robot_radius * 2.0);
237
238 const double distance_to_go = (left_error + right_error) / 2.0;
239 const double angle_to_go =
240 (right_error - left_error) / (dt_config_.robot_radius * 2.0);
241
Austin Schuhe6af9992018-07-08 15:59:14 -0700242 const bool drive_close =
243 ::std::abs(profile_distance_to_go) < distance + kProfileTolerance &&
244 ::std::abs(distance_to_go) < distance + kPositionTolerance;
245 const bool turn_close =
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000246 ::std::abs(profile_angle_to_go) < angle + kProfileTolerance &&
Austin Schuhe6af9992018-07-08 15:59:14 -0700247 ::std::abs(angle_to_go) < angle + kPositionTolerance;
248
249 drive_has_been_close |= drive_close;
250 turn_has_been_close |= turn_close;
251 if (drive_has_been_close && !turn_has_been_close && !printed_first) {
252 LOG(INFO, "Drive finished first\n");
253 printed_first = true;
254 } else if (!drive_has_been_close && turn_has_been_close &&
255 !printed_first) {
256 LOG(INFO, "Turn finished first\n");
257 printed_first = true;
258 }
259
260 if (drive_close && turn_close) {
261 LOG(INFO, "Closer than %f < %f distance, %f < %f angle\n",
262 distance_to_go, distance, angle_to_go, angle);
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000263 return true;
264 }
265 }
266 }
267}
268
Austin Schuh0aae9242018-03-14 19:49:44 -0700269bool BaseAutonomousActor::WaitForDriveProfileNear(double tolerance) {
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000270 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
271 ::std::chrono::milliseconds(5) / 2);
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000272 while (true) {
273 if (ShouldCancel()) {
274 return false;
275 }
276 phased_loop.SleepUntilNext();
277 drivetrain_queue.status.FetchLatest();
Austin Schuh0aae9242018-03-14 19:49:44 -0700278
279 const Eigen::Matrix<double, 7, 1> current_error =
280 (Eigen::Matrix<double, 7, 1>()
281 << initial_drivetrain_.left -
282 drivetrain_queue.status->profiled_left_position_goal,
283 0.0, initial_drivetrain_.right -
284 drivetrain_queue.status->profiled_right_position_goal,
285 0.0, 0.0, 0.0, 0.0)
286 .finished();
287 const Eigen::Matrix<double, 2, 1> linear_error =
288 dt_config_.LeftRightToLinear(current_error);
289
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000290 if (drivetrain_queue.status.get()) {
Austin Schuh0aae9242018-03-14 19:49:44 -0700291 if (::std::abs(linear_error(0)) < tolerance) {
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000292 LOG(INFO, "Finished drive\n");
293 return true;
294 }
295 }
296 }
297}
298
Austin Schuh0aae9242018-03-14 19:49:44 -0700299bool BaseAutonomousActor::WaitForDriveProfileDone() {
300 constexpr double kProfileTolerance = 0.001;
301 return WaitForDriveProfileNear(kProfileTolerance);
302}
303
304bool BaseAutonomousActor::WaitForTurnProfileNear(double tolerance) {
Austin Schuh546a0382017-04-16 19:10:18 -0700305 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
306 ::std::chrono::milliseconds(5) / 2);
Austin Schuh546a0382017-04-16 19:10:18 -0700307 while (true) {
308 if (ShouldCancel()) {
309 return false;
310 }
311 phased_loop.SleepUntilNext();
312 drivetrain_queue.status.FetchLatest();
Austin Schuh0aae9242018-03-14 19:49:44 -0700313
314 const Eigen::Matrix<double, 7, 1> current_error =
315 (Eigen::Matrix<double, 7, 1>()
316 << initial_drivetrain_.left -
317 drivetrain_queue.status->profiled_left_position_goal,
318 0.0, initial_drivetrain_.right -
319 drivetrain_queue.status->profiled_right_position_goal,
320 0.0, 0.0, 0.0, 0.0)
321 .finished();
322 const Eigen::Matrix<double, 2, 1> angular_error =
323 dt_config_.LeftRightToAngular(current_error);
324
Austin Schuh546a0382017-04-16 19:10:18 -0700325 if (drivetrain_queue.status.get()) {
Austin Schuh0aae9242018-03-14 19:49:44 -0700326 if (::std::abs(angular_error(0)) < tolerance) {
327 LOG(INFO, "Finished turn\n");
Austin Schuh546a0382017-04-16 19:10:18 -0700328 return true;
329 }
330 }
331 }
332}
333
Austin Schuh0aae9242018-03-14 19:49:44 -0700334bool BaseAutonomousActor::WaitForTurnProfileDone() {
335 constexpr double kProfileTolerance = 0.001;
336 return WaitForTurnProfileNear(kProfileTolerance);
337}
338
Austin Schuh546a0382017-04-16 19:10:18 -0700339double BaseAutonomousActor::DriveDistanceLeft() {
340 using ::frc971::control_loops::drivetrain_queue;
341 drivetrain_queue.status.FetchLatest();
342 if (drivetrain_queue.status.get()) {
343 const double left_error =
344 (initial_drivetrain_.left -
345 drivetrain_queue.status->estimated_left_position);
346 const double right_error =
347 (initial_drivetrain_.right -
348 drivetrain_queue.status->estimated_right_position);
349
350 return (left_error + right_error) / 2.0;
351 } else {
352 return 0;
353 }
354}
355
James Kuszmaul838040b2019-04-13 15:51:02 -0700356bool BaseAutonomousActor::SplineHandle::SplineDistanceRemaining(
357 double distance) {
358 drivetrain_queue.status.FetchLatest();
359 if (drivetrain_queue.status.get()) {
360 return drivetrain_queue.status->trajectory_logging.is_executing &&
361 drivetrain_queue.status->trajectory_logging.distance_remaining <
362 distance;
363 }
364 return false;
365}
366bool BaseAutonomousActor::SplineHandle::WaitForSplineDistanceRemaining(
367 double distance) {
368 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
369 ::std::chrono::milliseconds(5) / 2);
370 while (true) {
371 if (base_autonomous_actor_->ShouldCancel()) {
372 return false;
373 }
374 phased_loop.SleepUntilNext();
375 if (SplineDistanceRemaining(distance)) {
376 return true;
377 }
378 }
379}
380
James Kuszmaul8bb5df22019-05-01 21:40:08 -0500381void BaseAutonomousActor::LineFollowAtVelocity(double velocity, int hint) {
James Kuszmaul838040b2019-04-13 15:51:02 -0700382 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
383 drivetrain_message->controller_type = 3;
384 // TODO(james): Currently the 4.0 is copied from the
385 // line_follow_drivetrain.cc, but it is somewhat year-specific, so we should
386 // factor it out in some way.
387 drivetrain_message->throttle = velocity / 4.0;
388 drivetrain_message.Send();
James Kuszmaul8bb5df22019-05-01 21:40:08 -0500389 auto target_hint = target_selector_hint.MakeMessage();
390 target_hint->suggested_target = hint;
391 target_hint.Send();
James Kuszmaul838040b2019-04-13 15:51:02 -0700392}
393
Austin Schuh6bcc2302019-03-23 22:28:06 -0700394BaseAutonomousActor::SplineHandle BaseAutonomousActor::PlanSpline(
James Kuszmaulc73bb222019-04-07 12:15:35 -0700395 const ::frc971::MultiSpline &spline, SplineDirection direction) {
Austin Schuh6bcc2302019-03-23 22:28:06 -0700396 LOG(INFO, "Planning spline\n");
397
398 int32_t spline_handle = (++spline_handle_) | ((getpid() & 0xFFFF) << 15);
399
400 drivetrain_queue.goal.FetchLatest();
401
402 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
403 drivetrain_message->controller_type = 2;
404
405 drivetrain_message->spline = spline;
406 drivetrain_message->spline.spline_idx = spline_handle;
407 drivetrain_message->spline_handle = goal_spline_handle_;
James Kuszmaul29e417d2019-04-13 10:03:35 -0700408 drivetrain_message->spline.drive_spline_backwards =
James Kuszmaulc73bb222019-04-07 12:15:35 -0700409 direction == SplineDirection::kBackward;
Austin Schuh6bcc2302019-03-23 22:28:06 -0700410
411 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
412
413 drivetrain_message.Send();
414
415 return BaseAutonomousActor::SplineHandle(spline_handle, this);
416}
417
418bool BaseAutonomousActor::SplineHandle::IsPlanned() {
419 drivetrain_queue.status.FetchLatest();
420 LOG_STRUCT(INFO, "dts", *drivetrain_queue.status.get());
421 if (drivetrain_queue.status.get() &&
422 ((drivetrain_queue.status->trajectory_logging.planning_spline_idx ==
423 spline_handle_ &&
424 drivetrain_queue.status->trajectory_logging.planning_state == 3) ||
425 drivetrain_queue.status->trajectory_logging.current_spline_idx ==
426 spline_handle_)) {
427 return true;
428 }
429 return false;
430}
431
432bool BaseAutonomousActor::SplineHandle::WaitForPlan() {
433 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
434 ::std::chrono::milliseconds(5) / 2);
435 while (true) {
436 if (base_autonomous_actor_->ShouldCancel()) {
437 return false;
438 }
439 phased_loop.SleepUntilNext();
440 if (IsPlanned()) {
441 return true;
442 }
443 }
444}
445
446void BaseAutonomousActor::SplineHandle::Start() {
447 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
448 drivetrain_message->controller_type = 2;
449
450 LOG(INFO, "Starting spline\n");
451
452 drivetrain_message->spline_handle = spline_handle_;
453 base_autonomous_actor_->goal_spline_handle_ = spline_handle_;
454
455 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
456
457 drivetrain_message.Send();
458}
459
460bool BaseAutonomousActor::SplineHandle::IsDone() {
461 drivetrain_queue.status.FetchLatest();
462 LOG_STRUCT(INFO, "dts", *drivetrain_queue.status.get());
463
James Kuszmaulc73bb222019-04-07 12:15:35 -0700464 // We check that the spline we are waiting on is neither currently planning
James Kuszmaul29e417d2019-04-13 10:03:35 -0700465 // nor executing (we check is_executed because it is possible to receive
466 // status messages with is_executing false before the execution has started).
467 // We check for planning so that the user can go straight from starting the
468 // planner to executing without a WaitForPlan in between.
Austin Schuh6bcc2302019-03-23 22:28:06 -0700469 if (drivetrain_queue.status.get() &&
James Kuszmaul29e417d2019-04-13 10:03:35 -0700470 ((!drivetrain_queue.status->trajectory_logging.is_executed &&
471 drivetrain_queue.status->trajectory_logging.current_spline_idx ==
472 spline_handle_) ||
James Kuszmaulc73bb222019-04-07 12:15:35 -0700473 drivetrain_queue.status->trajectory_logging.planning_spline_idx ==
474 spline_handle_)) {
Austin Schuh6bcc2302019-03-23 22:28:06 -0700475 return false;
476 }
477 return true;
478}
479
480bool BaseAutonomousActor::SplineHandle::WaitForDone() {
481 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
482 ::std::chrono::milliseconds(5) / 2);
483 while (true) {
484 if (base_autonomous_actor_->ShouldCancel()) {
485 return false;
486 }
487 phased_loop.SleepUntilNext();
488 if (IsDone()) {
489 return true;
490 }
491 }
492}
493
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000494::std::unique_ptr<AutonomousAction> MakeAutonomousAction(
495 const AutonomousActionParams &params) {
496 return ::std::unique_ptr<AutonomousAction>(
497 new AutonomousAction(&autonomous_action, params));
498}
499
500} // namespace autonomous
501} // namespace frc971