blob: 4d0c1ccd1ec816a76a6fbe1b641cae3a24234b6c [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"
Tyler Chatow3c47f3c2020-01-29 20:45:23 -080010#include "frc971/constants.h"
11#include "frc971/control_loops/control_loops_generated.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070012
Adam Snaiderb4119252015-02-15 01:30:57 +000013// TODO(pschrader): Flag an error if encoder index pulse is not n revolutions
14// away from the last one (i.e. got extra counts from noise, etc..)
15//
16// TODO(pschrader): Flag error if the pot disagrees too much with the encoder
17// after being zeroed.
18//
19// TODO(pschrader): Watch the offset over long periods of time and flag if it
20// gets too far away from the initial value.
21
Adam Snaiderc4b3c192015-02-01 01:30:39 +000022namespace frc971 {
23namespace zeroing {
24
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
129 : public ZeroingEstimator<RelativePosition, void,
130 RelativeEncoderEstimatorState> {
131 public:
132 explicit RelativeEncoderZeroingEstimator() {}
133
134 // Update position with new position from encoder
135 void UpdateEstimate(const RelativePosition &position) override {
136 position_ = position.encoder();
137 }
138
139 // We alre always zeroed
140 bool zeroed() const override { return true; }
141
142 // Starting position of the joint
143 double offset() const override { return 0; }
144
145 // Has an error occured? Note: Only triggered by TriggerError()
146 bool error() const override { return error_; }
147
148 // Offset is always ready, since we are always zeroed.
149 bool offset_ready() const override { return true; }
150
151 void TriggerError() override { error_ = true; }
152
153 void Reset() override { error_ = false; }
154
155 flatbuffers::Offset<State> GetEstimatorState(
156 flatbuffers::FlatBufferBuilder *fbb) const override {
157 State::Builder builder(*fbb);
158 builder.add_error(error_);
159 builder.add_position(position_);
160 return builder.Finish();
161 }
162
163 private:
164 // Position from encoder relative to start
165 double position_ = 0;
166 bool error_ = false;
167};
168
Adam Snaiderb4119252015-02-15 01:30:57 +0000169} // namespace zeroing
170} // namespace frc971
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000171
Brian Silvermana57b7012020-03-11 20:19:23 -0700172// TODO(Brian): Actually split these targets apart. Need to convert all the
173// reverse dependencies to #include what they actually need...
174#include "frc971/zeroing/absolute_encoder.h"
175#include "frc971/zeroing/hall_effect_and_position.h"
176#include "frc971/zeroing/pot_and_absolute_encoder.h"
177#include "frc971/zeroing/pot_and_index.h"
178#include "frc971/zeroing/pulse_index.h"
179
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000180#endif // FRC971_ZEROING_ZEROING_H_