blob: fc01edc818cf67471ebd85ecf839eb961343ffa2 [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"
12
Philipp Schrader4bd29b12017-02-22 04:42:27 +000013using ::frc971::control_loops::drivetrain_queue;
14using ::aos::monotonic_clock;
15namespace chrono = ::std::chrono;
16namespace this_thread = ::std::this_thread;
17
Brian Silvermanf83678c2017-03-11 14:02:26 -080018namespace frc971 {
19namespace autonomous {
Philipp Schrader4bd29b12017-02-22 04:42:27 +000020
21BaseAutonomousActor::BaseAutonomousActor(
22 AutonomousActionQueueGroup *s,
Austin Schuhbcce26a2018-03-26 23:41:24 -070023 const control_loops::drivetrain::DrivetrainConfig<double> &dt_config)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000024 : aos::common::actions::ActorBase<AutonomousActionQueueGroup>(s),
25 dt_config_(dt_config),
26 initial_drivetrain_({0.0, 0.0}) {}
27
28void BaseAutonomousActor::ResetDrivetrain() {
29 LOG(INFO, "resetting the drivetrain\n");
Austin Schuh0aae9242018-03-14 19:49:44 -070030 max_drivetrain_voltage_ = 12.0;
James Kuszmaulc73bb222019-04-07 12:15:35 -070031 goal_spline_handle_ = 0;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000032 drivetrain_queue.goal.MakeWithBuilder()
Austin Schuh78379ea2019-01-04 20:39:45 -080033 .controller_type(0)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000034 .highgear(true)
Austin Schuh2b1fce02018-03-02 20:05:20 -080035 .wheel(0.0)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000036 .throttle(0.0)
37 .left_goal(initial_drivetrain_.left)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000038 .right_goal(initial_drivetrain_.right)
Austin Schuh0aae9242018-03-14 19:49:44 -070039 .max_ss_voltage(max_drivetrain_voltage_)
Philipp Schrader4bd29b12017-02-22 04:42:27 +000040 .Send();
41}
42
43void BaseAutonomousActor::InitializeEncoders() {
44 drivetrain_queue.status.FetchAnother();
45 initial_drivetrain_.left = drivetrain_queue.status->estimated_left_position;
46 initial_drivetrain_.right = drivetrain_queue.status->estimated_right_position;
47}
48
49void BaseAutonomousActor::StartDrive(double distance, double angle,
50 ProfileParameters linear,
51 ProfileParameters angular) {
52 LOG(INFO, "Driving distance %f, angle %f\n", distance, angle);
53 {
54 const double dangle = angle * dt_config_.robot_radius;
55 initial_drivetrain_.left += distance - dangle;
56 initial_drivetrain_.right += distance + dangle;
57 }
58
59 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
Austin Schuh78379ea2019-01-04 20:39:45 -080060 drivetrain_message->controller_type = 1;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000061 drivetrain_message->highgear = true;
Austin Schuh2b1fce02018-03-02 20:05:20 -080062 drivetrain_message->wheel = 0.0;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000063 drivetrain_message->throttle = 0.0;
64 drivetrain_message->left_goal = initial_drivetrain_.left;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000065 drivetrain_message->right_goal = initial_drivetrain_.right;
Austin Schuh0aae9242018-03-14 19:49:44 -070066 drivetrain_message->max_ss_voltage = max_drivetrain_voltage_;
Philipp Schrader4bd29b12017-02-22 04:42:27 +000067 drivetrain_message->linear = linear;
68 drivetrain_message->angular = angular;
69
Austin Schuh6bcc2302019-03-23 22:28:06 -070070 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
Philipp Schrader4bd29b12017-02-22 04:42:27 +000071
72 drivetrain_message.Send();
73}
74
75void BaseAutonomousActor::WaitUntilDoneOrCanceled(
76 ::std::unique_ptr<aos::common::actions::Action> action) {
77 if (!action) {
78 LOG(ERROR, "No action, not waiting\n");
79 return;
80 }
81
82 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
83 ::std::chrono::milliseconds(5) / 2);
84 while (true) {
85 // Poll the running bit and see if we should cancel.
86 phased_loop.SleepUntilNext();
87 if (!action->Running() || ShouldCancel()) {
88 return;
89 }
90 }
91}
92
93bool BaseAutonomousActor::WaitForDriveDone() {
94 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
95 ::std::chrono::milliseconds(5) / 2);
96
97 while (true) {
98 if (ShouldCancel()) {
99 return false;
100 }
101 phased_loop.SleepUntilNext();
102 drivetrain_queue.status.FetchLatest();
103 if (IsDriveDone()) {
104 return true;
105 }
106 }
107}
108
109bool BaseAutonomousActor::IsDriveDone() {
Brian Silvermanf83678c2017-03-11 14:02:26 -0800110 static constexpr double kPositionTolerance = 0.02;
111 static constexpr double kVelocityTolerance = 0.10;
112 static constexpr double kProfileTolerance = 0.001;
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000113
114 if (drivetrain_queue.status.get()) {
115 if (::std::abs(drivetrain_queue.status->profiled_left_position_goal -
116 initial_drivetrain_.left) < kProfileTolerance &&
117 ::std::abs(drivetrain_queue.status->profiled_right_position_goal -
118 initial_drivetrain_.right) < kProfileTolerance &&
119 ::std::abs(drivetrain_queue.status->estimated_left_position -
120 initial_drivetrain_.left) < kPositionTolerance &&
121 ::std::abs(drivetrain_queue.status->estimated_right_position -
122 initial_drivetrain_.right) < kPositionTolerance &&
123 ::std::abs(drivetrain_queue.status->estimated_left_velocity) <
124 kVelocityTolerance &&
125 ::std::abs(drivetrain_queue.status->estimated_right_velocity) <
126 kVelocityTolerance) {
127 LOG(INFO, "Finished drive\n");
128 return true;
129 }
130 }
131 return false;
132}
133
134bool BaseAutonomousActor::WaitForAboveAngle(double angle) {
135 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
136 ::std::chrono::milliseconds(5) / 2);
137 while (true) {
138 if (ShouldCancel()) {
139 return false;
140 }
141 phased_loop.SleepUntilNext();
142 drivetrain_queue.status.FetchLatest();
143 if (IsDriveDone()) {
144 return true;
145 }
146 if (drivetrain_queue.status.get()) {
147 if (drivetrain_queue.status->ground_angle > angle) {
148 return true;
149 }
150 }
151 }
152}
153
154bool BaseAutonomousActor::WaitForBelowAngle(double angle) {
155 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
156 ::std::chrono::milliseconds(5) / 2);
157 while (true) {
158 if (ShouldCancel()) {
159 return false;
160 }
161 phased_loop.SleepUntilNext();
162 drivetrain_queue.status.FetchLatest();
163 if (IsDriveDone()) {
164 return true;
165 }
166 if (drivetrain_queue.status.get()) {
167 if (drivetrain_queue.status->ground_angle < angle) {
168 return true;
169 }
170 }
171 }
172}
173
174bool BaseAutonomousActor::WaitForMaxBy(double angle) {
175 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
176 ::std::chrono::milliseconds(5) / 2);
177 double max_angle = -M_PI;
178 while (true) {
179 if (ShouldCancel()) {
180 return false;
181 }
182 phased_loop.SleepUntilNext();
183 drivetrain_queue.status.FetchLatest();
184 if (IsDriveDone()) {
185 return true;
186 }
187 if (drivetrain_queue.status.get()) {
188 if (drivetrain_queue.status->ground_angle > max_angle) {
189 max_angle = drivetrain_queue.status->ground_angle;
190 }
191 if (drivetrain_queue.status->ground_angle < max_angle - angle) {
192 return true;
193 }
194 }
195 }
196}
197
198bool BaseAutonomousActor::WaitForDriveNear(double distance, double angle) {
199 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
200 ::std::chrono::milliseconds(5) / 2);
201 constexpr double kPositionTolerance = 0.02;
202 constexpr double kProfileTolerance = 0.001;
203
Austin Schuhe6af9992018-07-08 15:59:14 -0700204 bool drive_has_been_close = false;
205 bool turn_has_been_close = false;
206 bool printed_first = false;
207
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000208 while (true) {
209 if (ShouldCancel()) {
210 return false;
211 }
212 phased_loop.SleepUntilNext();
213 drivetrain_queue.status.FetchLatest();
214 if (drivetrain_queue.status.get()) {
215 const double left_profile_error =
216 (initial_drivetrain_.left -
217 drivetrain_queue.status->profiled_left_position_goal);
218 const double right_profile_error =
219 (initial_drivetrain_.right -
220 drivetrain_queue.status->profiled_right_position_goal);
221
222 const double left_error =
223 (initial_drivetrain_.left -
224 drivetrain_queue.status->estimated_left_position);
225 const double right_error =
226 (initial_drivetrain_.right -
227 drivetrain_queue.status->estimated_right_position);
228
229 const double profile_distance_to_go =
230 (left_profile_error + right_profile_error) / 2.0;
231 const double profile_angle_to_go =
232 (right_profile_error - left_profile_error) /
233 (dt_config_.robot_radius * 2.0);
234
235 const double distance_to_go = (left_error + right_error) / 2.0;
236 const double angle_to_go =
237 (right_error - left_error) / (dt_config_.robot_radius * 2.0);
238
Austin Schuhe6af9992018-07-08 15:59:14 -0700239 const bool drive_close =
240 ::std::abs(profile_distance_to_go) < distance + kProfileTolerance &&
241 ::std::abs(distance_to_go) < distance + kPositionTolerance;
242 const bool turn_close =
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000243 ::std::abs(profile_angle_to_go) < angle + kProfileTolerance &&
Austin Schuhe6af9992018-07-08 15:59:14 -0700244 ::std::abs(angle_to_go) < angle + kPositionTolerance;
245
246 drive_has_been_close |= drive_close;
247 turn_has_been_close |= turn_close;
248 if (drive_has_been_close && !turn_has_been_close && !printed_first) {
249 LOG(INFO, "Drive finished first\n");
250 printed_first = true;
251 } else if (!drive_has_been_close && turn_has_been_close &&
252 !printed_first) {
253 LOG(INFO, "Turn finished first\n");
254 printed_first = true;
255 }
256
257 if (drive_close && turn_close) {
258 LOG(INFO, "Closer than %f < %f distance, %f < %f angle\n",
259 distance_to_go, distance, angle_to_go, angle);
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000260 return true;
261 }
262 }
263 }
264}
265
Austin Schuh0aae9242018-03-14 19:49:44 -0700266bool BaseAutonomousActor::WaitForDriveProfileNear(double tolerance) {
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000267 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
268 ::std::chrono::milliseconds(5) / 2);
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000269 while (true) {
270 if (ShouldCancel()) {
271 return false;
272 }
273 phased_loop.SleepUntilNext();
274 drivetrain_queue.status.FetchLatest();
Austin Schuh0aae9242018-03-14 19:49:44 -0700275
276 const Eigen::Matrix<double, 7, 1> current_error =
277 (Eigen::Matrix<double, 7, 1>()
278 << initial_drivetrain_.left -
279 drivetrain_queue.status->profiled_left_position_goal,
280 0.0, initial_drivetrain_.right -
281 drivetrain_queue.status->profiled_right_position_goal,
282 0.0, 0.0, 0.0, 0.0)
283 .finished();
284 const Eigen::Matrix<double, 2, 1> linear_error =
285 dt_config_.LeftRightToLinear(current_error);
286
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000287 if (drivetrain_queue.status.get()) {
Austin Schuh0aae9242018-03-14 19:49:44 -0700288 if (::std::abs(linear_error(0)) < tolerance) {
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000289 LOG(INFO, "Finished drive\n");
290 return true;
291 }
292 }
293 }
294}
295
Austin Schuh0aae9242018-03-14 19:49:44 -0700296bool BaseAutonomousActor::WaitForDriveProfileDone() {
297 constexpr double kProfileTolerance = 0.001;
298 return WaitForDriveProfileNear(kProfileTolerance);
299}
300
301bool BaseAutonomousActor::WaitForTurnProfileNear(double tolerance) {
Austin Schuh546a0382017-04-16 19:10:18 -0700302 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
303 ::std::chrono::milliseconds(5) / 2);
Austin Schuh546a0382017-04-16 19:10:18 -0700304 while (true) {
305 if (ShouldCancel()) {
306 return false;
307 }
308 phased_loop.SleepUntilNext();
309 drivetrain_queue.status.FetchLatest();
Austin Schuh0aae9242018-03-14 19:49:44 -0700310
311 const Eigen::Matrix<double, 7, 1> current_error =
312 (Eigen::Matrix<double, 7, 1>()
313 << initial_drivetrain_.left -
314 drivetrain_queue.status->profiled_left_position_goal,
315 0.0, initial_drivetrain_.right -
316 drivetrain_queue.status->profiled_right_position_goal,
317 0.0, 0.0, 0.0, 0.0)
318 .finished();
319 const Eigen::Matrix<double, 2, 1> angular_error =
320 dt_config_.LeftRightToAngular(current_error);
321
Austin Schuh546a0382017-04-16 19:10:18 -0700322 if (drivetrain_queue.status.get()) {
Austin Schuh0aae9242018-03-14 19:49:44 -0700323 if (::std::abs(angular_error(0)) < tolerance) {
324 LOG(INFO, "Finished turn\n");
Austin Schuh546a0382017-04-16 19:10:18 -0700325 return true;
326 }
327 }
328 }
329}
330
Austin Schuh0aae9242018-03-14 19:49:44 -0700331bool BaseAutonomousActor::WaitForTurnProfileDone() {
332 constexpr double kProfileTolerance = 0.001;
333 return WaitForTurnProfileNear(kProfileTolerance);
334}
335
Austin Schuh546a0382017-04-16 19:10:18 -0700336double BaseAutonomousActor::DriveDistanceLeft() {
337 using ::frc971::control_loops::drivetrain_queue;
338 drivetrain_queue.status.FetchLatest();
339 if (drivetrain_queue.status.get()) {
340 const double left_error =
341 (initial_drivetrain_.left -
342 drivetrain_queue.status->estimated_left_position);
343 const double right_error =
344 (initial_drivetrain_.right -
345 drivetrain_queue.status->estimated_right_position);
346
347 return (left_error + right_error) / 2.0;
348 } else {
349 return 0;
350 }
351}
352
Austin Schuh6bcc2302019-03-23 22:28:06 -0700353BaseAutonomousActor::SplineHandle BaseAutonomousActor::PlanSpline(
James Kuszmaulc73bb222019-04-07 12:15:35 -0700354 const ::frc971::MultiSpline &spline, SplineDirection direction) {
Austin Schuh6bcc2302019-03-23 22:28:06 -0700355 LOG(INFO, "Planning spline\n");
356
357 int32_t spline_handle = (++spline_handle_) | ((getpid() & 0xFFFF) << 15);
358
359 drivetrain_queue.goal.FetchLatest();
360
361 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
362 drivetrain_message->controller_type = 2;
363
364 drivetrain_message->spline = spline;
365 drivetrain_message->spline.spline_idx = spline_handle;
366 drivetrain_message->spline_handle = goal_spline_handle_;
James Kuszmaulc73bb222019-04-07 12:15:35 -0700367 drivetrain_message->drive_spline_backwards =
368 direction == SplineDirection::kBackward;
Austin Schuh6bcc2302019-03-23 22:28:06 -0700369
370 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
371
372 drivetrain_message.Send();
373
374 return BaseAutonomousActor::SplineHandle(spline_handle, this);
375}
376
377bool BaseAutonomousActor::SplineHandle::IsPlanned() {
378 drivetrain_queue.status.FetchLatest();
379 LOG_STRUCT(INFO, "dts", *drivetrain_queue.status.get());
380 if (drivetrain_queue.status.get() &&
381 ((drivetrain_queue.status->trajectory_logging.planning_spline_idx ==
382 spline_handle_ &&
383 drivetrain_queue.status->trajectory_logging.planning_state == 3) ||
384 drivetrain_queue.status->trajectory_logging.current_spline_idx ==
385 spline_handle_)) {
386 return true;
387 }
388 return false;
389}
390
391bool BaseAutonomousActor::SplineHandle::WaitForPlan() {
392 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
393 ::std::chrono::milliseconds(5) / 2);
394 while (true) {
395 if (base_autonomous_actor_->ShouldCancel()) {
396 return false;
397 }
398 phased_loop.SleepUntilNext();
399 if (IsPlanned()) {
400 return true;
401 }
402 }
403}
404
405void BaseAutonomousActor::SplineHandle::Start() {
406 auto drivetrain_message = drivetrain_queue.goal.MakeMessage();
407 drivetrain_message->controller_type = 2;
408
409 LOG(INFO, "Starting spline\n");
410
411 drivetrain_message->spline_handle = spline_handle_;
412 base_autonomous_actor_->goal_spline_handle_ = spline_handle_;
413
414 LOG_STRUCT(DEBUG, "dtg", *drivetrain_message);
415
416 drivetrain_message.Send();
417}
418
419bool BaseAutonomousActor::SplineHandle::IsDone() {
420 drivetrain_queue.status.FetchLatest();
421 LOG_STRUCT(INFO, "dts", *drivetrain_queue.status.get());
422
James Kuszmaulc73bb222019-04-07 12:15:35 -0700423 // We check that the spline we are waiting on is neither currently planning
424 // nor executing; note that we do *not* check the is_executing bit because
425 // immediately after calling Start we may still receive an old Status message
426 // that has not been updated. We check for planning so that the user can go
427 // straight from starting the planner to executing without a WaitForPlan in
428 // between.
Austin Schuh6bcc2302019-03-23 22:28:06 -0700429 if (drivetrain_queue.status.get() &&
James Kuszmaulc73bb222019-04-07 12:15:35 -0700430 (drivetrain_queue.status->trajectory_logging.current_spline_idx ==
431 spline_handle_ ||
432 drivetrain_queue.status->trajectory_logging.planning_spline_idx ==
433 spline_handle_)) {
Austin Schuh6bcc2302019-03-23 22:28:06 -0700434 return false;
435 }
436 return true;
437}
438
439bool BaseAutonomousActor::SplineHandle::WaitForDone() {
440 ::aos::time::PhasedLoop phased_loop(::std::chrono::milliseconds(5),
441 ::std::chrono::milliseconds(5) / 2);
442 while (true) {
443 if (base_autonomous_actor_->ShouldCancel()) {
444 return false;
445 }
446 phased_loop.SleepUntilNext();
447 if (IsDone()) {
448 return true;
449 }
450 }
451}
452
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000453::std::unique_ptr<AutonomousAction> MakeAutonomousAction(
454 const AutonomousActionParams &params) {
455 return ::std::unique_ptr<AutonomousAction>(
456 new AutonomousAction(&autonomous_action, params));
457}
458
459} // namespace autonomous
460} // namespace frc971