blob: 91fd16a9405bcd0c82f4019718222ede9a12d156 [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"
James Kuszmaul3c5b4d32020-02-11 17:22:14 -08006#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
James Kuszmaul3431d622019-02-17 17:07:44 -08007#include "frc971/control_loops/drivetrain/hybrid_ekf.h"
James Kuszmaul5bc6fc92019-03-01 21:50:06 -08008#include "frc971/control_loops/pose.h"
James Kuszmaul3431d622019-02-17 17:07:44 -08009
10namespace frc971 {
11namespace control_loops {
12namespace drivetrain {
13
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080014// An interface for target selection. This provides an object that will take in
15// state updates and then determine what poes we should be driving to.
16class TargetSelectorInterface {
17 public:
18 // Take the state as [x, y, theta, left_vel, right_vel]
19 // If unable to determine what target to go for, returns false. If a viable
20 // target is selected, then returns true and sets target_pose.
James Kuszmaulc4bd1612019-03-10 11:35:06 -070021 // command_speed is the goal speed of the current drivetrain, generally
22 // generated from the throttle and meant to signify driver intent.
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080023 // TODO(james): Some implementations may also want a drivetrain goal so that
24 // driver intent can be divined more directly.
James Kuszmaulc4bd1612019-03-10 11:35:06 -070025 virtual bool UpdateSelection(const ::Eigen::Matrix<double, 5, 1> &state,
26 double command_speed) = 0;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080027 // Gets the current target pose. Should only be called if UpdateSelection has
28 // returned true.
29 virtual TypedPose<double> TargetPose() const = 0;
James Kuszmaule093f512019-03-20 06:14:05 -070030 // The "radius" of the target--for y2019, we wanted to drive in so that a disc
31 // with radius r would hit the plane of the target at an offset of exactly r
32 // from the TargetPose--this is distinct from wanting the center of the
33 // robot to project straight onto the center of the target.
34 virtual double TargetRadius() const = 0;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080035};
36
James Kuszmaul3431d622019-02-17 17:07:44 -080037// Defines an interface for classes that provide field-global localization.
38class LocalizerInterface {
39 public:
40 // Perform a single step of the filter, using the information that is
41 // available on every drivetrain iteration.
42 // The user should pass in the U that the real system experienced from the
43 // previous timestep until now; internally, any filters will first perform a
44 // prediction step to get the estimate at time now, and then will apply
45 // corrections based on the encoder/gyro/accelerometer values from time now.
46 // TODO(james): Consider letting implementations subscribe to the sensor
47 // values themselves, and then only passing in U. This requires more
48 // coordination on timing, however.
49 virtual void Update(const ::Eigen::Matrix<double, 2, 1> &U,
50 ::aos::monotonic_clock::time_point now,
51 double left_encoder, double right_encoder,
James Kuszmaul3c5b4d32020-02-11 17:22:14 -080052 double gyro_rate, const Eigen::Vector3d &accel) = 0;
James Kuszmaulef428a02019-03-02 22:19:41 -080053 // Reset the absolute position of the estimator.
James Kuszmaul074429e2019-03-23 16:01:49 -070054 virtual void ResetPosition(::aos::monotonic_clock::time_point t, double x,
James Kuszmaul518640d2019-04-13 15:50:50 -070055 double y, double theta, double theta_uncertainty,
56 bool reset_theta) = 0;
James Kuszmaul3c5b4d32020-02-11 17:22:14 -080057 flatbuffers::Offset<LocalizerState> PopulateStatus(
58 flatbuffers::FlatBufferBuilder *fbb) {
59 LocalizerState::Builder builder(*fbb);
60 builder.add_x(x());
61 builder.add_y(y());
62 builder.add_theta(theta());
63 builder.add_left_velocity(left_velocity());
64 builder.add_right_velocity(right_velocity());
65 builder.add_left_encoder(left_encoder());
66 builder.add_right_encoder(right_encoder());
67 builder.add_left_voltage_error(left_voltage_error());
68 builder.add_right_voltage_error(right_voltage_error());
69 builder.add_angular_error(angular_error());
70 builder.add_longitudinal_velocity_offset(longitudinal_velocity_offset());
71 builder.add_lateral_velocity(lateral_velocity());
72 return builder.Finish();
73 }
James Kuszmaul3431d622019-02-17 17:07:44 -080074 // There are several subtly different norms floating around for state
James Kuszmaul3c5b4d32020-02-11 17:22:14 -080075 // matrices. In order to avoid that mess, we just provide direct accessors for
James Kuszmaul3431d622019-02-17 17:07:44 -080076 // the values that most people care about.
77 virtual double x() const = 0;
78 virtual double y() const = 0;
79 virtual double theta() const = 0;
80 virtual double left_velocity() const = 0;
81 virtual double right_velocity() const = 0;
James Kuszmaul074429e2019-03-23 16:01:49 -070082 virtual double left_encoder() const = 0;
83 virtual double right_encoder() const = 0;
James Kuszmaul3431d622019-02-17 17:07:44 -080084 virtual double left_voltage_error() const = 0;
85 virtual double right_voltage_error() const = 0;
James Kuszmaul3c5b4d32020-02-11 17:22:14 -080086 virtual double angular_error() const = 0;
87 virtual double longitudinal_velocity_offset() const = 0;
88 virtual double lateral_velocity() const = 0;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080089 virtual TargetSelectorInterface *target_selector() = 0;
90};
91
92// A target selector, primarily for testing purposes, that just lets a user
93// manually set the target selector state.
94class TrivialTargetSelector : public TargetSelectorInterface {
95 public:
James Kuszmaulc4bd1612019-03-10 11:35:06 -070096 bool UpdateSelection(const ::Eigen::Matrix<double, 5, 1> &, double) override {
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080097 return has_target_;
98 }
99 TypedPose<double> TargetPose() const override { return pose_; }
James Kuszmaule093f512019-03-20 06:14:05 -0700100 double TargetRadius() const override { return target_radius_; }
James Kuszmaul5bc6fc92019-03-01 21:50:06 -0800101
102 void set_pose(const TypedPose<double> &pose) { pose_ = pose; }
James Kuszmaule093f512019-03-20 06:14:05 -0700103 void set_target_radius(double radius) { target_radius_ = radius; }
James Kuszmaul5bc6fc92019-03-01 21:50:06 -0800104 void set_has_target(bool has_target) { has_target_ = has_target; }
105 bool has_target() const { return has_target_; }
106
107 private:
108 bool has_target_ = true;
109 TypedPose<double> pose_;
James Kuszmaule093f512019-03-20 06:14:05 -0700110 double target_radius_ = 0.0;
James Kuszmaul3431d622019-02-17 17:07:44 -0800111};
112
113// Uses the generic HybridEkf implementation to provide a basic field estimator.
114// This provides no method for using cameras or the such to get global
115// measurements and just assumes that you can dead-reckon perfectly.
116class DeadReckonEkf : public LocalizerInterface {
117 typedef HybridEkf<double> Ekf;
118 typedef typename Ekf::StateIdx StateIdx;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700119
James Kuszmaul3431d622019-02-17 17:07:44 -0800120 public:
Austin Schuh9fe68f72019-08-10 19:32:03 -0700121 DeadReckonEkf(::aos::EventLoop *event_loop,
122 const DrivetrainConfig<double> &dt_config)
123 : ekf_(dt_config) {
124 event_loop->OnRun([this, event_loop]() {
125 ekf_.ResetInitialState(event_loop->monotonic_now(), Ekf::State::Zero(),
126 ekf_.P());
127 });
James Kuszmaul5bc6fc92019-03-01 21:50:06 -0800128 target_selector_.set_has_target(false);
James Kuszmaul3431d622019-02-17 17:07:44 -0800129 }
130
131 void Update(const ::Eigen::Matrix<double, 2, 1> &U,
Austin Schuh9fe68f72019-08-10 19:32:03 -0700132 ::aos::monotonic_clock::time_point now, double left_encoder,
133 double right_encoder, double gyro_rate,
James Kuszmaul3c5b4d32020-02-11 17:22:14 -0800134 const Eigen::Vector3d &accel) override {
135 ekf_.UpdateEncodersAndGyro(left_encoder, right_encoder, gyro_rate, U, accel,
136 now);
James Kuszmaul3431d622019-02-17 17:07:44 -0800137 }
138
James Kuszmaul074429e2019-03-23 16:01:49 -0700139 void ResetPosition(::aos::monotonic_clock::time_point t, double x, double y,
James Kuszmaul518640d2019-04-13 15:50:50 -0700140 double theta, double /*theta_override*/,
141 bool /*reset_theta*/) override {
James Kuszmaulfedc4612019-03-10 11:24:51 -0700142 const double left_encoder = ekf_.X_hat(StateIdx::kLeftEncoder);
143 const double right_encoder = ekf_.X_hat(StateIdx::kRightEncoder);
James Kuszmaul3c5b4d32020-02-11 17:22:14 -0800144 ekf_.ResetInitialState(t,
145 (Ekf::State() << x, y, theta, left_encoder, 0,
146 right_encoder, 0, 0, 0, 0, 0, 0)
147 .finished(),
James Kuszmaul074429e2019-03-23 16:01:49 -0700148 ekf_.P());
James Kuszmaulef428a02019-03-02 22:19:41 -0800149 };
150
James Kuszmaul3431d622019-02-17 17:07:44 -0800151 double x() const override { return ekf_.X_hat(StateIdx::kX); }
152 double y() const override { return ekf_.X_hat(StateIdx::kY); }
153 double theta() const override { return ekf_.X_hat(StateIdx::kTheta); }
James Kuszmaul074429e2019-03-23 16:01:49 -0700154 double left_encoder() const override {
155 return ekf_.X_hat(StateIdx::kLeftEncoder);
156 }
157 double right_encoder() const override {
158 return ekf_.X_hat(StateIdx::kRightEncoder);
159 }
James Kuszmaul3431d622019-02-17 17:07:44 -0800160 double left_velocity() const override {
161 return ekf_.X_hat(StateIdx::kLeftVelocity);
162 }
163 double right_velocity() const override {
164 return ekf_.X_hat(StateIdx::kRightVelocity);
165 }
James Kuszmaul074429e2019-03-23 16:01:49 -0700166 double left_voltage_error() const override {
167 return ekf_.X_hat(StateIdx::kLeftVoltageError);
168 }
169 double right_voltage_error() const override {
170 return ekf_.X_hat(StateIdx::kRightVoltageError);
171 }
James Kuszmaul3c5b4d32020-02-11 17:22:14 -0800172 double angular_error() const override {
173 return ekf_.X_hat(StateIdx::kAngularError);
174 }
175 double longitudinal_velocity_offset() const override {
176 return ekf_.X_hat(StateIdx::kLongitudinalVelocityOffset);
177 }
178 double lateral_velocity() const override {
179 return ekf_.X_hat(StateIdx::kLateralVelocity);
180 }
James Kuszmaul3431d622019-02-17 17:07:44 -0800181
James Kuszmaul5bc6fc92019-03-01 21:50:06 -0800182 TrivialTargetSelector *target_selector() override {
183 return &target_selector_;
184 }
185
James Kuszmaul3431d622019-02-17 17:07:44 -0800186 private:
187 Ekf ekf_;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -0800188 TrivialTargetSelector target_selector_;
James Kuszmaul3431d622019-02-17 17:07:44 -0800189};
190
191} // namespace drivetrain
192} // namespace control_loops
193} // namespace frc971
194
195#endif // FRC971_CONTROL_LOOPS_DRIVETRAIN_FIELD_ESTIMATOR_H_