blob: 2589d4b299fa08840fbc4dae0db5e71227968979 [file] [log] [blame]
James Kuszmaul3431d622019-02-17 17:07:44 -08001#ifndef FRC971_CONTROL_LOOPS_DRIVETRAIN_FIELD_ESTIMATOR_H_
2#define FRC971_CONTROL_LOOPS_DRIVETRAIN_FIELD_ESTIMATOR_H_
3
Alex Perrycb7da4b2019-08-28 19:35:56 -07004#include "aos/events/event_loop.h"
James Kuszmaul3431d622019-02-17 17:07:44 -08005#include "frc971/control_loops/drivetrain/drivetrain_config.h"
6#include "frc971/control_loops/drivetrain/hybrid_ekf.h"
James Kuszmaul5bc6fc92019-03-01 21:50:06 -08007#include "frc971/control_loops/pose.h"
James Kuszmaul3431d622019-02-17 17:07:44 -08008
9namespace frc971 {
10namespace control_loops {
11namespace drivetrain {
12
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080013// An interface for target selection. This provides an object that will take in
14// state updates and then determine what poes we should be driving to.
15class TargetSelectorInterface {
16 public:
17 // Take the state as [x, y, theta, left_vel, right_vel]
18 // If unable to determine what target to go for, returns false. If a viable
19 // target is selected, then returns true and sets target_pose.
James Kuszmaulc4bd1612019-03-10 11:35:06 -070020 // command_speed is the goal speed of the current drivetrain, generally
21 // generated from the throttle and meant to signify driver intent.
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080022 // TODO(james): Some implementations may also want a drivetrain goal so that
23 // driver intent can be divined more directly.
James Kuszmaulc4bd1612019-03-10 11:35:06 -070024 virtual bool UpdateSelection(const ::Eigen::Matrix<double, 5, 1> &state,
25 double command_speed) = 0;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080026 // Gets the current target pose. Should only be called if UpdateSelection has
27 // returned true.
28 virtual TypedPose<double> TargetPose() const = 0;
James Kuszmaule093f512019-03-20 06:14:05 -070029 // The "radius" of the target--for y2019, we wanted to drive in so that a disc
30 // with radius r would hit the plane of the target at an offset of exactly r
31 // from the TargetPose--this is distinct from wanting the center of the
32 // robot to project straight onto the center of the target.
33 virtual double TargetRadius() const = 0;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080034};
35
James Kuszmaul3431d622019-02-17 17:07:44 -080036// Defines an interface for classes that provide field-global localization.
37class LocalizerInterface {
38 public:
39 // Perform a single step of the filter, using the information that is
40 // available on every drivetrain iteration.
41 // The user should pass in the U that the real system experienced from the
42 // previous timestep until now; internally, any filters will first perform a
43 // prediction step to get the estimate at time now, and then will apply
44 // corrections based on the encoder/gyro/accelerometer values from time now.
45 // TODO(james): Consider letting implementations subscribe to the sensor
46 // values themselves, and then only passing in U. This requires more
47 // coordination on timing, however.
48 virtual void Update(const ::Eigen::Matrix<double, 2, 1> &U,
49 ::aos::monotonic_clock::time_point now,
50 double left_encoder, double right_encoder,
51 double gyro_rate, double longitudinal_accelerometer) = 0;
James Kuszmaulef428a02019-03-02 22:19:41 -080052 // Reset the absolute position of the estimator.
James Kuszmaul074429e2019-03-23 16:01:49 -070053 virtual void ResetPosition(::aos::monotonic_clock::time_point t, double x,
James Kuszmaul518640d2019-04-13 15:50:50 -070054 double y, double theta, double theta_uncertainty,
55 bool reset_theta) = 0;
James Kuszmaul3431d622019-02-17 17:07:44 -080056 // There are several subtly different norms floating around for state
57 // matrices. In order to avoid that mess, we jus tprovide direct accessors for
58 // the values that most people care about.
59 virtual double x() const = 0;
60 virtual double y() const = 0;
61 virtual double theta() const = 0;
62 virtual double left_velocity() const = 0;
63 virtual double right_velocity() const = 0;
James Kuszmaul074429e2019-03-23 16:01:49 -070064 virtual double left_encoder() const = 0;
65 virtual double right_encoder() const = 0;
James Kuszmaul3431d622019-02-17 17:07:44 -080066 virtual double left_voltage_error() const = 0;
67 virtual double right_voltage_error() const = 0;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080068 virtual TargetSelectorInterface *target_selector() = 0;
69};
70
71// A target selector, primarily for testing purposes, that just lets a user
72// manually set the target selector state.
73class TrivialTargetSelector : public TargetSelectorInterface {
74 public:
James Kuszmaulc4bd1612019-03-10 11:35:06 -070075 bool UpdateSelection(const ::Eigen::Matrix<double, 5, 1> &, double) override {
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080076 return has_target_;
77 }
78 TypedPose<double> TargetPose() const override { return pose_; }
James Kuszmaule093f512019-03-20 06:14:05 -070079 double TargetRadius() const override { return target_radius_; }
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080080
81 void set_pose(const TypedPose<double> &pose) { pose_ = pose; }
James Kuszmaule093f512019-03-20 06:14:05 -070082 void set_target_radius(double radius) { target_radius_ = radius; }
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080083 void set_has_target(bool has_target) { has_target_ = has_target; }
84 bool has_target() const { return has_target_; }
85
86 private:
87 bool has_target_ = true;
88 TypedPose<double> pose_;
James Kuszmaule093f512019-03-20 06:14:05 -070089 double target_radius_ = 0.0;
James Kuszmaul3431d622019-02-17 17:07:44 -080090};
91
92// Uses the generic HybridEkf implementation to provide a basic field estimator.
93// This provides no method for using cameras or the such to get global
94// measurements and just assumes that you can dead-reckon perfectly.
95class DeadReckonEkf : public LocalizerInterface {
96 typedef HybridEkf<double> Ekf;
97 typedef typename Ekf::StateIdx StateIdx;
Austin Schuh9fe68f72019-08-10 19:32:03 -070098
James Kuszmaul3431d622019-02-17 17:07:44 -080099 public:
Austin Schuh9fe68f72019-08-10 19:32:03 -0700100 DeadReckonEkf(::aos::EventLoop *event_loop,
101 const DrivetrainConfig<double> &dt_config)
102 : ekf_(dt_config) {
103 event_loop->OnRun([this, event_loop]() {
104 ekf_.ResetInitialState(event_loop->monotonic_now(), Ekf::State::Zero(),
105 ekf_.P());
106 });
James Kuszmaul5bc6fc92019-03-01 21:50:06 -0800107 target_selector_.set_has_target(false);
James Kuszmaul3431d622019-02-17 17:07:44 -0800108 }
109
110 void Update(const ::Eigen::Matrix<double, 2, 1> &U,
Austin Schuh9fe68f72019-08-10 19:32:03 -0700111 ::aos::monotonic_clock::time_point now, double left_encoder,
112 double right_encoder, double gyro_rate,
113 double /*longitudinal_accelerometer*/) override {
James Kuszmaul3431d622019-02-17 17:07:44 -0800114 ekf_.UpdateEncodersAndGyro(left_encoder, right_encoder, gyro_rate, U, now);
115 }
116
James Kuszmaul074429e2019-03-23 16:01:49 -0700117 void ResetPosition(::aos::monotonic_clock::time_point t, double x, double y,
James Kuszmaul518640d2019-04-13 15:50:50 -0700118 double theta, double /*theta_override*/,
119 bool /*reset_theta*/) override {
James Kuszmaulfedc4612019-03-10 11:24:51 -0700120 const double left_encoder = ekf_.X_hat(StateIdx::kLeftEncoder);
121 const double right_encoder = ekf_.X_hat(StateIdx::kRightEncoder);
James Kuszmaul074429e2019-03-23 16:01:49 -0700122 ekf_.ResetInitialState(t, (Ekf::State() << x, y, theta, left_encoder, 0,
Austin Schuh9fe68f72019-08-10 19:32:03 -0700123 right_encoder, 0, 0, 0,
124 0).finished(),
James Kuszmaul074429e2019-03-23 16:01:49 -0700125 ekf_.P());
James Kuszmaulef428a02019-03-02 22:19:41 -0800126 };
127
James Kuszmaul3431d622019-02-17 17:07:44 -0800128 double x() const override { return ekf_.X_hat(StateIdx::kX); }
129 double y() const override { return ekf_.X_hat(StateIdx::kY); }
130 double theta() const override { return ekf_.X_hat(StateIdx::kTheta); }
James Kuszmaul074429e2019-03-23 16:01:49 -0700131 double left_encoder() const override {
132 return ekf_.X_hat(StateIdx::kLeftEncoder);
133 }
134 double right_encoder() const override {
135 return ekf_.X_hat(StateIdx::kRightEncoder);
136 }
James Kuszmaul3431d622019-02-17 17:07:44 -0800137 double left_velocity() const override {
138 return ekf_.X_hat(StateIdx::kLeftVelocity);
139 }
140 double right_velocity() const override {
141 return ekf_.X_hat(StateIdx::kRightVelocity);
142 }
James Kuszmaul074429e2019-03-23 16:01:49 -0700143 double left_voltage_error() const override {
144 return ekf_.X_hat(StateIdx::kLeftVoltageError);
145 }
146 double right_voltage_error() const override {
147 return ekf_.X_hat(StateIdx::kRightVoltageError);
148 }
James Kuszmaul3431d622019-02-17 17:07:44 -0800149
James Kuszmaul5bc6fc92019-03-01 21:50:06 -0800150 TrivialTargetSelector *target_selector() override {
151 return &target_selector_;
152 }
153
James Kuszmaul3431d622019-02-17 17:07:44 -0800154 private:
155 Ekf ekf_;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -0800156 TrivialTargetSelector target_selector_;
James Kuszmaul3431d622019-02-17 17:07:44 -0800157};
158
159} // namespace drivetrain
160} // namespace control_loops
161} // namespace frc971
162
163#endif // FRC971_CONTROL_LOOPS_DRIVETRAIN_FIELD_ESTIMATOR_H_