blob: 5d5b6eb245fb99b426ff628acab808c862f8e8d8 [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
Adam Snaiderc4b3c192015-02-01 01:30:39 +000023namespace frc971 {
24namespace zeroing {
25
Lee Mracekcdd19ee2019-01-07 10:19:54 -050026template <typename TPosition, typename TZeroingConstants, typename TState>
Neil Balch1049be92017-02-15 23:20:49 -080027class ZeroingEstimator {
28 public:
Lee Mracekcdd19ee2019-01-07 10:19:54 -050029 using Position = TPosition;
30 using ZeroingConstants = TZeroingConstants;
31 using State = TState;
Tyler Chatow3c47f3c2020-01-29 20:45:23 -080032 virtual ~ZeroingEstimator() {}
Neil Balch1049be92017-02-15 23:20:49 -080033
34 // Returns true if the logic considers the corresponding mechanism to be
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000035 // zeroed.
Neil Balch1049be92017-02-15 23:20:49 -080036 virtual bool zeroed() const = 0;
37
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000038 // Returns the estimated position of the corresponding mechanism.
Neil Balch1049be92017-02-15 23:20:49 -080039 virtual double offset() const = 0;
40
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000041 // Returns true if there has been an error.
Neil Balch1049be92017-02-15 23:20:49 -080042 virtual bool error() const = 0;
Lee Mracekcdd19ee2019-01-07 10:19:54 -050043
44 // Returns true if an offset is ready.
45 virtual bool offset_ready() const = 0;
46
47 // Triggers an internal error. This is used for testing the error
48 // logic.
49 virtual void TriggerError() = 0;
50
51 // Resets the estimator, clearing error and zeroing states.
52 virtual void Reset() = 0;
53
54 // Updates the internal logic with new sensor values
55 virtual void UpdateEstimate(const Position &) = 0;
56
57 // Returns the state of the estimator
Alex Perrycb7da4b2019-08-28 19:35:56 -070058 virtual flatbuffers::Offset<State> GetEstimatorState(
59 flatbuffers::FlatBufferBuilder *fbb) const = 0;
Neil Balch1049be92017-02-15 23:20:49 -080060};
61
Austin Schuh66c59ba2019-01-26 20:34:35 -080062// Class to encapsulate the logic to decide when we are moving and which samples
63// are safe to use.
Alex Perrycb7da4b2019-08-28 19:35:56 -070064template <typename Position, typename PositionBuffer>
Austin Schuh66c59ba2019-01-26 20:34:35 -080065class MoveDetector {
66 public:
67 MoveDetector(size_t filter_size) {
68 buffered_samples_.reserve(filter_size);
69 Reset();
70 }
71
72 // Clears all the state.
73 void Reset() {
74 buffered_samples_.clear();
75 buffered_samples_idx_ = 0;
76 }
77
78 // Updates the detector with a new sample. Returns true if we are moving.
79 // buffer_size is the number of samples in the moving buffer, and
80 // zeroing_threshold is the max amount we can move within the period specified
81 // by buffer_size.
Alex Perrycb7da4b2019-08-28 19:35:56 -070082 bool Update(const PositionBuffer &position_buffer, size_t buffer_size,
Austin Schuh66c59ba2019-01-26 20:34:35 -080083 double zeroing_threshold) {
84 bool moving = true;
Alex Perrycb7da4b2019-08-28 19:35:56 -070085 Position position(position_buffer);
Austin Schuh66c59ba2019-01-26 20:34:35 -080086 if (buffered_samples_.size() < buffer_size) {
87 // Not enough samples to start determining if the robot is moving or not,
88 // don't use the samples yet.
89 buffered_samples_.push_back(position);
90 } else {
91 // Have enough samples to start determining if the robot is moving or not.
92 buffered_samples_[buffered_samples_idx_] = position;
93 const auto minmax_value = ::std::minmax_element(
94 buffered_samples_.begin(), buffered_samples_.end(),
95 [](const Position &left, const Position &right) {
96 return left.encoder < right.encoder;
97 });
98 const double min_value = minmax_value.first->encoder;
99 const double max_value = minmax_value.second->encoder;
100
101 if (::std::abs(max_value - min_value) < zeroing_threshold) {
102 // Robot isn't moving, use middle sample to determine offsets.
103 moving = false;
104 }
105 }
106 buffered_samples_idx_ = (buffered_samples_idx_ + 1) % buffer_size;
107 return moving;
108 }
109
110 // Returns a safe sample if we aren't moving as reported by Update.
111 const Position &GetSample() const {
112 // The robot is not moving, use the middle sample to determine offsets.
113 // The middle sample makes it so that we don't use the samples from the
114 // beginning or end of periods when the robot is moving.
115 const int middle_index =
116 (buffered_samples_idx_ + (buffered_samples_.size() / 2)) %
117 buffered_samples_.size();
118 return buffered_samples_[middle_index];
119 }
120
121 private:
122 // The last buffer_size samples.
123 ::std::vector<Position> buffered_samples_;
124 // The index to place the next sample in.
125 size_t buffered_samples_idx_;
126};
127
Brian Silvermana57b7012020-03-11 20:19:23 -0700128// A trivial ZeroingEstimator which just passes the position straight through.
Tyler Chatow3c47f3c2020-01-29 20:45:23 -0800129class RelativeEncoderZeroingEstimator
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800130 : public ZeroingEstimator<RelativePosition,
131 constants::RelativeEncoderZeroingConstants,
Tyler Chatow3c47f3c2020-01-29 20:45:23 -0800132 RelativeEncoderEstimatorState> {
133 public:
Siddhant Kanwar0e37f592022-02-21 19:26:50 -0800134 explicit RelativeEncoderZeroingEstimator(
135 const constants::RelativeEncoderZeroingConstants &) {}
Tyler Chatow3c47f3c2020-01-29 20:45:23 -0800136
137 // Update position with new position from encoder
138 void UpdateEstimate(const RelativePosition &position) override {
139 position_ = position.encoder();
140 }
141
142 // We alre always zeroed
143 bool zeroed() const override { return true; }
144
145 // Starting position of the joint
146 double offset() const override { return 0; }
147
148 // Has an error occured? Note: Only triggered by TriggerError()
149 bool error() const override { return error_; }
150
151 // Offset is always ready, since we are always zeroed.
152 bool offset_ready() const override { return true; }
153
154 void TriggerError() override { error_ = true; }
155
156 void Reset() override { error_ = false; }
157
158 flatbuffers::Offset<State> GetEstimatorState(
159 flatbuffers::FlatBufferBuilder *fbb) const override {
160 State::Builder builder(*fbb);
161 builder.add_error(error_);
162 builder.add_position(position_);
163 return builder.Finish();
164 }
165
166 private:
167 // Position from encoder relative to start
168 double position_ = 0;
169 bool error_ = false;
170};
171
Adam Snaiderb4119252015-02-15 01:30:57 +0000172} // namespace zeroing
173} // namespace frc971
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000174
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000175#endif // FRC971_ZEROING_ZEROING_H_