blob: bc12fd69cd905022267148983e003a3e597c9f85 [file] [log] [blame]
James Kuszmaul5398fae2020-02-17 16:44:03 -08001#ifndef Y2020_CONTROL_LOOPS_DRIVETRAIN_LOCALIZER_H_
2#define Y2020_CONTROL_LOOPS_DRIVETRAIN_LOCALIZER_H_
3
James Kuszmaulc6723cf2020-03-01 14:45:59 -08004#include <string_view>
5
James Kuszmaul5398fae2020-02-17 16:44:03 -08006#include "aos/containers/ring_buffer.h"
James Kuszmaul5ff8a862021-09-25 17:29:43 -07007#include "aos/containers/sized_array.h"
James Kuszmaul5398fae2020-02-17 16:44:03 -08008#include "aos/events/event_loop.h"
James Kuszmaul958b21e2020-02-26 21:51:40 -08009#include "aos/network/message_bridge_server_generated.h"
James Kuszmaul5398fae2020-02-17 16:44:03 -080010#include "frc971/control_loops/drivetrain/hybrid_ekf.h"
11#include "frc971/control_loops/drivetrain/localizer.h"
James Kuszmaul5ff8a862021-09-25 17:29:43 -070012#include "y2020/control_loops/drivetrain/localizer_debug_generated.h"
James Kuszmaul2971b5a2023-01-29 15:49:32 -080013#include "y2020/control_loops/superstructure/superstructure_status_generated.h"
James Kuszmaul5398fae2020-02-17 16:44:03 -080014#include "y2020/vision/sift/sift_generated.h"
15
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080016namespace y2020::control_loops::drivetrain {
James Kuszmaul5398fae2020-02-17 16:44:03 -080017
18// This class handles the localization for the 2020 robot. In order to handle
19// camera updates, we get the ImageMatchResult message from the cameras and then
20// project the result onto the 2-D X/Y plane and use the implied robot
21// position/heading from that as the measurement. This is distinct from 2019,
22// when we used a heading/distance/skew measurement update. This is because
23// updating with x/y/theta directly seems to be better conditioned (even if it
24// may not reflect the measurement noise quite as accurately). The poor
25// conditioning seemed to work in 2019, but due to the addition of a couple of
26// velocity offset states that allow us to use the accelerometer more
27// effectively, things started to become unstable.
28class Localizer : public frc971::control_loops::drivetrain::LocalizerInterface {
29 public:
James Kuszmauld478f872020-03-16 20:54:27 -070030 typedef frc971::control_loops::TypedPose<float> Pose;
31 typedef frc971::control_loops::drivetrain::HybridEkf<float> HybridEkf;
James Kuszmaul5398fae2020-02-17 16:44:03 -080032 typedef typename HybridEkf::State State;
33 typedef typename HybridEkf::StateIdx StateIdx;
34 typedef typename HybridEkf::StateSquare StateSquare;
35 typedef typename HybridEkf::Input Input;
36 typedef typename HybridEkf::Output Output;
37 Localizer(aos::EventLoop *event_loop,
38 const frc971::control_loops::drivetrain::DrivetrainConfig<double>
39 &dt_config);
James Kuszmauld478f872020-03-16 20:54:27 -070040 frc971::control_loops::drivetrain::HybridEkf<double>::State Xhat()
41 const override {
42 return ekf_.X_hat().cast<double>();
43 }
James Kuszmaul5398fae2020-02-17 16:44:03 -080044 frc971::control_loops::drivetrain::TrivialTargetSelector *target_selector()
45 override {
46 return &target_selector_;
47 }
48
49 void Update(const ::Eigen::Matrix<double, 2, 1> &U,
50 aos::monotonic_clock::time_point now, double left_encoder,
51 double right_encoder, double gyro_rate,
52 const Eigen::Vector3d &accel) override;
53
James Kuszmaulbcd96fc2020-10-12 20:29:32 -070054 void Reset(aos::monotonic_clock::time_point t,
55 const frc971::control_loops::drivetrain::HybridEkf<double>::State
56 &state) override;
James Kuszmaul5398fae2020-02-17 16:44:03 -080057
58 void ResetPosition(aos::monotonic_clock::time_point t, double x, double y,
59 double theta, double /*theta_override*/,
60 bool /*reset_theta*/) override {
61 const double left_encoder = ekf_.X_hat(StateIdx::kLeftEncoder);
62 const double right_encoder = ekf_.X_hat(StateIdx::kRightEncoder);
James Kuszmauld478f872020-03-16 20:54:27 -070063 ekf_.ResetInitialState(t,
64 (HybridEkf::State() << x, y, theta, left_encoder, 0,
65 right_encoder, 0, 0, 0, 0, 0, 0)
66 .finished(),
James Kuszmaul5398fae2020-02-17 16:44:03 -080067 ekf_.P());
James Kuszmaul9c128122021-03-22 22:24:36 -070068 }
James Kuszmaul5398fae2020-02-17 16:44:03 -080069
70 private:
71 // Storage for a single turret position data point.
72 struct TurretData {
73 aos::monotonic_clock::time_point receive_time =
74 aos::monotonic_clock::min_time;
75 double position = 0.0; // rad
76 double velocity = 0.0; // rad/sec
77 };
78
James Kuszmaulf75ecd62021-10-23 14:33:46 -070079 static constexpr size_t kNumRejectionReasons =
80 static_cast<int>(RejectionReason::MAX) -
81 static_cast<int>(RejectionReason::MIN) + 1;
82
83 struct Statistics {
84 int total_accepted = 0;
85 int total_candidates = 0;
86 static_assert(0 == static_cast<int>(RejectionReason::MIN));
87 static_assert(
88 kNumRejectionReasons ==
89 sizeof(
90 std::invoke_result<decltype(EnumValuesRejectionReason)>::type) /
91 sizeof(RejectionReason),
92 "RejectionReason has non-contiguous error values.");
93 std::array<int, kNumRejectionReasons> rejection_counts;
94 };
95
James Kuszmaul2971b5a2023-01-29 15:49:32 -080096 class Corrector : public HybridEkf::ExpectedObservationFunctor {
97 public:
98 Corrector(const Eigen::Matrix<float, 4, 4> &H_field_target,
99 const Pose &pose_robot_target, const State &state_at_capture,
100 const Eigen::Vector3f &Z,
101 std::optional<RejectionReason> *correction_rejection)
102 : H_field_target_(H_field_target),
103 pose_robot_target_(pose_robot_target),
104 state_at_capture_(state_at_capture),
105 Z_(Z),
106 correction_rejection_(correction_rejection) {
107 H_.setZero();
108 H_(0, StateIdx::kX) = 1;
109 H_(1, StateIdx::kY) = 1;
110 H_(2, StateIdx::kTheta) = 1;
111 }
112 Output H(const State &, const Input &) final;
113 Eigen::Matrix<float, HybridEkf::kNOutputs, HybridEkf::kNStates> DHDX(
114 const State &) final {
115 return H_;
116 }
117
118 private:
119 Eigen::Matrix<float, HybridEkf::kNOutputs, HybridEkf::kNStates> H_;
120 const Eigen::Matrix<float, 4, 4> H_field_target_;
121 Pose pose_robot_target_;
122 const State state_at_capture_;
123 const Eigen::Vector3f &Z_;
124 std::optional<RejectionReason> *correction_rejection_;
125 };
126
James Kuszmaulc6723cf2020-03-01 14:45:59 -0800127 // Processes new image data from the given pi and updates the EKF.
James Kuszmaul5ff8a862021-09-25 17:29:43 -0700128 aos::SizedArray<flatbuffers::Offset<ImageMatchDebug>, 5> HandleImageMatch(
129 size_t camera_index, std::string_view pi,
130 const frc971::vision::sift::ImageMatchResult &result,
131 aos::monotonic_clock::time_point now,
132 flatbuffers::FlatBufferBuilder *fbb);
James Kuszmaul5398fae2020-02-17 16:44:03 -0800133
134 // Processes the most recent turret position and stores it in the turret_data_
135 // buffer.
136 void HandleSuperstructureStatus(
137 const y2020::control_loops::superstructure::Status &status);
138
139 // Retrieves the turret data closest to the provided time.
140 TurretData GetTurretDataForTime(aos::monotonic_clock::time_point time);
141
142 aos::EventLoop *const event_loop_;
143 const frc971::control_loops::drivetrain::DrivetrainConfig<double> dt_config_;
144 HybridEkf ekf_;
James Kuszmaul2971b5a2023-01-29 15:49:32 -0800145 HybridEkf::ExpectedObservationAllocator<Corrector> observations_;
James Kuszmaul5398fae2020-02-17 16:44:03 -0800146
147 std::vector<aos::Fetcher<frc971::vision::sift::ImageMatchResult>>
148 image_fetchers_;
149
James Kuszmaul958b21e2020-02-26 21:51:40 -0800150 aos::Fetcher<aos::message_bridge::ServerStatistics> clock_offset_fetcher_;
151
James Kuszmaul5ff8a862021-09-25 17:29:43 -0700152 aos::Sender<y2020::control_loops::drivetrain::LocalizerDebug> debug_sender_;
153
James Kuszmaul5398fae2020-02-17 16:44:03 -0800154 // Buffer of recent turret data--this is used so that when we receive a camera
155 // frame from the turret, we can back out what the turret angle was at that
156 // time.
157 aos::RingBuffer<TurretData, 200> turret_data_;
158
159 // Target selector to allow us to satisfy the LocalizerInterface requirements.
160 frc971::control_loops::drivetrain::TrivialTargetSelector target_selector_;
James Kuszmaulf75ecd62021-10-23 14:33:46 -0700161
162 Statistics statistics_;
James Kuszmaul5398fae2020-02-17 16:44:03 -0800163};
164
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800165} // namespace y2020::control_loops::drivetrain
James Kuszmaul5398fae2020-02-17 16:44:03 -0800166
167#endif // Y2020_CONTROL_LOOPS_DRIVETRAIN_LOCALIZER_H_