blob: 8fe381dc8523141d86907967e257427f3bd14b72 [file] [log] [blame]
Adam Snaiderc4b3c192015-02-01 01:30:39 +00001#ifndef FRC971_ZEROING_ZEROING_H_
2#define FRC971_ZEROING_ZEROING_H_
3
Austin Schuh66c59ba2019-01-26 20:34:35 -08004#include <algorithm>
5#include <cmath>
Philipp Schrader41d82912015-02-15 03:44:23 +00006#include <cstdint>
Adam Snaiderc4b3c192015-02-01 01:30:39 +00007#include <vector>
Philipp Schrader41d82912015-02-15 03:44:23 +00008
Alex Perrycb7da4b2019-08-28 19:35:56 -07009#include "flatbuffers/flatbuffers.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070010
Tyler Chatow3c47f3c2020-01-29 20:45:23 -080011#include "frc971/constants.h"
12#include "frc971/control_loops/control_loops_generated.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070013
Adam Snaiderb4119252015-02-15 01:30:57 +000014// TODO(pschrader): Flag an error if encoder index pulse is not n revolutions
15// away from the last one (i.e. got extra counts from noise, etc..)
16//
17// TODO(pschrader): Flag error if the pot disagrees too much with the encoder
18// after being zeroed.
19//
20// TODO(pschrader): Watch the offset over long periods of time and flag if it
21// gets too far away from the initial value.
22
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080023namespace frc971::zeroing {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000024
Lee Mracekcdd19ee2019-01-07 10:19:54 -050025template <typename TPosition, typename TZeroingConstants, typename TState>
Neil Balch1049be92017-02-15 23:20:49 -080026class ZeroingEstimator {
27 public:
Lee Mracekcdd19ee2019-01-07 10:19:54 -050028 using Position = TPosition;
29 using ZeroingConstants = TZeroingConstants;
30 using State = TState;
Tyler Chatow3c47f3c2020-01-29 20:45:23 -080031 virtual ~ZeroingEstimator() {}
Neil Balch1049be92017-02-15 23:20:49 -080032
33 // Returns true if the logic considers the corresponding mechanism to be
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000034 // zeroed.
Neil Balch1049be92017-02-15 23:20:49 -080035 virtual bool zeroed() const = 0;
36
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000037 // Returns the estimated position of the corresponding mechanism.
Neil Balch1049be92017-02-15 23:20:49 -080038 virtual double offset() const = 0;
39
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000040 // Returns true if there has been an error.
Neil Balch1049be92017-02-15 23:20:49 -080041 virtual bool error() const = 0;
Lee Mracekcdd19ee2019-01-07 10:19:54 -050042
43 // Returns true if an offset is ready.
44 virtual bool offset_ready() const = 0;
45
46 // Triggers an internal error. This is used for testing the error
47 // logic.
48 virtual void TriggerError() = 0;
49
50 // Resets the estimator, clearing error and zeroing states.
51 virtual void Reset() = 0;
52
53 // Updates the internal logic with new sensor values
54 virtual void UpdateEstimate(const Position &) = 0;
55
56 // Returns the state of the estimator
Alex Perrycb7da4b2019-08-28 19:35:56 -070057 virtual flatbuffers::Offset<State> GetEstimatorState(
58 flatbuffers::FlatBufferBuilder *fbb) const = 0;
Neil Balch1049be92017-02-15 23:20:49 -080059};
60
Austin Schuh66c59ba2019-01-26 20:34:35 -080061// Class to encapsulate the logic to decide when we are moving and which samples
62// are safe to use.
Alex Perrycb7da4b2019-08-28 19:35:56 -070063template <typename Position, typename PositionBuffer>
Austin Schuh66c59ba2019-01-26 20:34:35 -080064class MoveDetector {
65 public:
66 MoveDetector(size_t filter_size) {
67 buffered_samples_.reserve(filter_size);
68 Reset();
69 }
70
71 // Clears all the state.
72 void Reset() {
73 buffered_samples_.clear();
74 buffered_samples_idx_ = 0;
75 }
76
77 // Updates the detector with a new sample. Returns true if we are moving.
78 // buffer_size is the number of samples in the moving buffer, and
79 // zeroing_threshold is the max amount we can move within the period specified
80 // by buffer_size.
Alex Perrycb7da4b2019-08-28 19:35:56 -070081 bool Update(const PositionBuffer &position_buffer, size_t buffer_size,
Austin Schuh66c59ba2019-01-26 20:34:35 -080082 double zeroing_threshold) {
83 bool moving = true;
Alex Perrycb7da4b2019-08-28 19:35:56 -070084 Position position(position_buffer);
Austin Schuh66c59ba2019-01-26 20:34:35 -080085 if (buffered_samples_.size() < buffer_size) {
86 // Not enough samples to start determining if the robot is moving or not,
87 // don't use the samples yet.
88 buffered_samples_.push_back(position);
89 } else {
90 // Have enough samples to start determining if the robot is moving or not.
91 buffered_samples_[buffered_samples_idx_] = position;
92 const auto minmax_value = ::std::minmax_element(
93 buffered_samples_.begin(), buffered_samples_.end(),
94 [](const Position &left, const Position &right) {
95 return left.encoder < right.encoder;
96 });
97 const double min_value = minmax_value.first->encoder;
98 const double max_value = minmax_value.second->encoder;
99
100 if (::std::abs(max_value - min_value) < zeroing_threshold) {
101 // Robot isn't moving, use middle sample to determine offsets.
102 moving = false;
103 }
104 }
105 buffered_samples_idx_ = (buffered_samples_idx_ + 1) % buffer_size;
106 return moving;
107 }
108
109 // Returns a safe sample if we aren't moving as reported by Update.
110 const Position &GetSample() const {
111 // The robot is not moving, use the middle sample to determine offsets.
112 // The middle sample makes it so that we don't use the samples from the
113 // beginning or end of periods when the robot is moving.
114 const int middle_index =
115 (buffered_samples_idx_ + (buffered_samples_.size() / 2)) %
116 buffered_samples_.size();
117 return buffered_samples_[middle_index];
118 }
119
120 private:
121 // The last buffer_size samples.
122 ::std::vector<Position> buffered_samples_;
123 // The index to place the next sample in.
124 size_t buffered_samples_idx_;
125};
126
Brian Silvermana57b7012020-03-11 20:19:23 -0700127// A trivial ZeroingEstimator which just passes the position straight through.
Tyler Chatow3c47f3c2020-01-29 20:45:23 -0800128class RelativeEncoderZeroingEstimator
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800129 : public ZeroingEstimator<RelativePosition,
130 constants::RelativeEncoderZeroingConstants,
Tyler Chatow3c47f3c2020-01-29 20:45:23 -0800131 RelativeEncoderEstimatorState> {
132 public:
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800133 explicit RelativeEncoderZeroingEstimator(
134 const constants::RelativeEncoderZeroingConstants &) {}
Tyler Chatow3c47f3c2020-01-29 20:45:23 -0800135
136 // Update position with new position from encoder
137 void UpdateEstimate(const RelativePosition &position) override {
138 position_ = position.encoder();
139 }
140
141 // We alre always zeroed
142 bool zeroed() const override { return true; }
143
144 // Starting position of the joint
145 double offset() const override { return 0; }
146
147 // Has an error occured? Note: Only triggered by TriggerError()
148 bool error() const override { return error_; }
149
150 // Offset is always ready, since we are always zeroed.
151 bool offset_ready() const override { return true; }
152
153 void TriggerError() override { error_ = true; }
154
155 void Reset() override { error_ = false; }
156
157 flatbuffers::Offset<State> GetEstimatorState(
158 flatbuffers::FlatBufferBuilder *fbb) const override {
159 State::Builder builder(*fbb);
160 builder.add_error(error_);
161 builder.add_position(position_);
162 return builder.Finish();
163 }
164
165 private:
166 // Position from encoder relative to start
167 double position_ = 0;
168 bool error_ = false;
169};
170
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800171} // namespace frc971::zeroing
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000172
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000173#endif // FRC971_ZEROING_ZEROING_H_