blob: f0685ddb9a48b1dbd74851710ca5eeddd416b700 [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
4#include "frc971/control_loops/drivetrain/drivetrain_config.h"
5#include "frc971/control_loops/drivetrain/hybrid_ekf.h"
James Kuszmaul5bc6fc92019-03-01 21:50:06 -08006#include "frc971/control_loops/pose.h"
James Kuszmaul3431d622019-02-17 17:07:44 -08007
8namespace frc971 {
9namespace control_loops {
10namespace drivetrain {
11
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080012// An interface for target selection. This provides an object that will take in
13// state updates and then determine what poes we should be driving to.
14class TargetSelectorInterface {
15 public:
16 // Take the state as [x, y, theta, left_vel, right_vel]
17 // If unable to determine what target to go for, returns false. If a viable
18 // target is selected, then returns true and sets target_pose.
James Kuszmaulc4bd1612019-03-10 11:35:06 -070019 // command_speed is the goal speed of the current drivetrain, generally
20 // generated from the throttle and meant to signify driver intent.
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080021 // TODO(james): Some implementations may also want a drivetrain goal so that
22 // driver intent can be divined more directly.
James Kuszmaulc4bd1612019-03-10 11:35:06 -070023 virtual bool UpdateSelection(const ::Eigen::Matrix<double, 5, 1> &state,
24 double command_speed) = 0;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080025 // Gets the current target pose. Should only be called if UpdateSelection has
26 // returned true.
27 virtual TypedPose<double> TargetPose() const = 0;
28};
29
James Kuszmaul3431d622019-02-17 17:07:44 -080030// Defines an interface for classes that provide field-global localization.
31class LocalizerInterface {
32 public:
33 // Perform a single step of the filter, using the information that is
34 // available on every drivetrain iteration.
35 // The user should pass in the U that the real system experienced from the
36 // previous timestep until now; internally, any filters will first perform a
37 // prediction step to get the estimate at time now, and then will apply
38 // corrections based on the encoder/gyro/accelerometer values from time now.
39 // TODO(james): Consider letting implementations subscribe to the sensor
40 // values themselves, and then only passing in U. This requires more
41 // coordination on timing, however.
42 virtual void Update(const ::Eigen::Matrix<double, 2, 1> &U,
43 ::aos::monotonic_clock::time_point now,
44 double left_encoder, double right_encoder,
45 double gyro_rate, double longitudinal_accelerometer) = 0;
James Kuszmaulef428a02019-03-02 22:19:41 -080046 // Reset the absolute position of the estimator.
47 virtual void ResetPosition(double x, double y, double theta) = 0;
James Kuszmaul3431d622019-02-17 17:07:44 -080048 // There are several subtly different norms floating around for state
49 // matrices. In order to avoid that mess, we jus tprovide direct accessors for
50 // the values that most people care about.
51 virtual double x() const = 0;
52 virtual double y() const = 0;
53 virtual double theta() const = 0;
54 virtual double left_velocity() const = 0;
55 virtual double right_velocity() const = 0;
56 virtual double left_voltage_error() const = 0;
57 virtual double right_voltage_error() const = 0;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080058 virtual TargetSelectorInterface *target_selector() = 0;
59};
60
61// A target selector, primarily for testing purposes, that just lets a user
62// manually set the target selector state.
63class TrivialTargetSelector : public TargetSelectorInterface {
64 public:
James Kuszmaulc4bd1612019-03-10 11:35:06 -070065 bool UpdateSelection(const ::Eigen::Matrix<double, 5, 1> &, double) override {
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080066 return has_target_;
67 }
68 TypedPose<double> TargetPose() const override { return pose_; }
69
70 void set_pose(const TypedPose<double> &pose) { pose_ = pose; }
71 void set_has_target(bool has_target) { has_target_ = has_target; }
72 bool has_target() const { return has_target_; }
73
74 private:
75 bool has_target_ = true;
76 TypedPose<double> pose_;
James Kuszmaul3431d622019-02-17 17:07:44 -080077};
78
79// Uses the generic HybridEkf implementation to provide a basic field estimator.
80// This provides no method for using cameras or the such to get global
81// measurements and just assumes that you can dead-reckon perfectly.
82class DeadReckonEkf : public LocalizerInterface {
83 typedef HybridEkf<double> Ekf;
84 typedef typename Ekf::StateIdx StateIdx;
85 public:
86 DeadReckonEkf(const DrivetrainConfig<double> &dt_config) : ekf_(dt_config) {
87 ekf_.ResetInitialState(::aos::monotonic_clock::now(), Ekf::State::Zero(),
88 ekf_.P());
James Kuszmaul5bc6fc92019-03-01 21:50:06 -080089 target_selector_.set_has_target(false);
James Kuszmaul3431d622019-02-17 17:07:44 -080090 }
91
92 void Update(const ::Eigen::Matrix<double, 2, 1> &U,
93 ::aos::monotonic_clock::time_point now,
94 double left_encoder, double right_encoder,
95 double gyro_rate,
96 double /*longitudinal_accelerometer*/) override {
97 ekf_.UpdateEncodersAndGyro(left_encoder, right_encoder, gyro_rate, U, now);
98 }
99
James Kuszmaulef428a02019-03-02 22:19:41 -0800100 void ResetPosition(double x, double y, double theta) override {
James Kuszmaulfedc4612019-03-10 11:24:51 -0700101 const double left_encoder = ekf_.X_hat(StateIdx::kLeftEncoder);
102 const double right_encoder = ekf_.X_hat(StateIdx::kRightEncoder);
James Kuszmaulef428a02019-03-02 22:19:41 -0800103 ekf_.ResetInitialState(
104 ::aos::monotonic_clock::now(),
James Kuszmaulfedc4612019-03-10 11:24:51 -0700105 (Ekf::State() << x, y, theta, left_encoder, 0, right_encoder, 0)
106 .finished(),
James Kuszmaulef428a02019-03-02 22:19:41 -0800107 ekf_.P());
108 };
109
James Kuszmaul3431d622019-02-17 17:07:44 -0800110 double x() const override { return ekf_.X_hat(StateIdx::kX); }
111 double y() const override { return ekf_.X_hat(StateIdx::kY); }
112 double theta() const override { return ekf_.X_hat(StateIdx::kTheta); }
113 double left_velocity() const override {
114 return ekf_.X_hat(StateIdx::kLeftVelocity);
115 }
116 double right_velocity() const override {
117 return ekf_.X_hat(StateIdx::kRightVelocity);
118 }
James Kuszmaulfedc4612019-03-10 11:24:51 -0700119 double left_voltage_error() const override { return 0.0; }
120 double right_voltage_error() const override { return 0.0; }
James Kuszmaul3431d622019-02-17 17:07:44 -0800121
James Kuszmaul5bc6fc92019-03-01 21:50:06 -0800122 TrivialTargetSelector *target_selector() override {
123 return &target_selector_;
124 }
125
James Kuszmaul3431d622019-02-17 17:07:44 -0800126 private:
127 Ekf ekf_;
James Kuszmaul5bc6fc92019-03-01 21:50:06 -0800128 TrivialTargetSelector target_selector_;
James Kuszmaul3431d622019-02-17 17:07:44 -0800129};
130
131} // namespace drivetrain
132} // namespace control_loops
133} // namespace frc971
134
135#endif // FRC971_CONTROL_LOOPS_DRIVETRAIN_FIELD_ESTIMATOR_H_