CTRE Phoenix 6 C++ 25.0.0-beta-4
Loading...
Searching...
No Matches
TimeInterpolatableBuffer.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) Cross The Road Electronics.  All rights reserved.
3 * License information can be found in CTRE_LICENSE.txt
4 * For support and suggestions contact support@ctr-electronics.com or file
5 * an issue tracker at https://github.com/CrossTheRoadElec/Phoenix-Releases
6 */
7#pragma once
8
10#include <units/time.h>
11#include <functional>
12#include <optional>
13
14namespace ctre {
15namespace phoenix6 {
16namespace swerve {
17
18/**
19 * The TimeInterpolatableBuffer provides an easy way to estimate past
20 * measurements. One application might be in conjunction with the
21 * DifferentialDrivePoseEstimator, where knowledge of the robot pose at the time
22 * when vision or other global measurement were recorded is necessary, or for
23 * recording the past angles of mechanisms as measured by encoders.
24 *
25 * When sampling this buffer, a user-provided function or Lerp can be
26 * used. For Pose2ds, we use Twists.
27 *
28 * @tparam T The type stored in this buffer.
29 */
30template <typename T>
32 public:
33 /**
34 * Create a new TimeInterpolatableBuffer.
35 *
36 * @param historySize The history size of the buffer.
37 * @param func The function used to interpolate between values.
38 */
39 TimeInterpolatableBuffer(units::second_t historySize,
40 std::function<T(const T&, const T&, double)> func)
41 : m_historySize(historySize), m_interpolatingFunc(func) {}
42
43 /**
44 * Create a new TimeInterpolatableBuffer. By default, the interpolation
45 * function is Lerp except for Pose2d, which uses the pose exponential.
46 *
47 * @param historySize The history size of the buffer.
48 */
49 explicit TimeInterpolatableBuffer(units::second_t historySize)
50 : m_historySize(historySize),
51 m_interpolatingFunc([](const T& start, const T& end, double t) {
52 return Lerp(start, end, t);
53 }) {}
54
55 /**
56 * Add a sample to the buffer.
57 *
58 * @param time The timestamp of the sample.
59 * @param sample The sample object.
60 */
61 void AddSample(units::second_t time, T sample) {
62 // Add the new state into the vector
63 if (m_pastSnapshots.size() == 0 || time > m_pastSnapshots.back().first) {
64 m_pastSnapshots.emplace_back(time, sample);
65 } else {
66 auto first_after = std::upper_bound(
67 m_pastSnapshots.begin(), m_pastSnapshots.end(), time,
68 [](auto t, const auto& pair) { return t < pair.first; });
69
70 if (first_after == m_pastSnapshots.begin()) {
71 // All entries come after the sample
72 m_pastSnapshots.insert(first_after, std::pair{time, sample});
73 } else if (auto last_not_greater_than = first_after - 1;
74 last_not_greater_than == m_pastSnapshots.begin() ||
75 last_not_greater_than->first < time) {
76 // Some entries come before the sample, but none are recorded with the
77 // same time
78 m_pastSnapshots.insert(first_after, std::pair{time, sample});
79 } else {
80 // An entry exists with the same recorded time
81 last_not_greater_than->second = sample;
82 }
83 }
84 while (time - m_pastSnapshots[0].first > m_historySize) {
85 m_pastSnapshots.erase(m_pastSnapshots.begin());
86 }
87 }
88
89 /** Clear all old samples. */
90 void Clear() { m_pastSnapshots.clear(); }
91
92 /**
93 * Sample the buffer at the given time. If the buffer is empty, an empty
94 * optional is returned.
95 *
96 * @param time The time at which to sample the buffer.
97 */
98 std::optional<T> Sample(units::second_t time) const {
99 if (m_pastSnapshots.empty()) {
100 return {};
101 }
102
103 // We will perform a binary search to find the index of the element in the
104 // vector that has a timestamp that is equal to or greater than the vision
105 // measurement timestamp.
106
107 if (time <= m_pastSnapshots.front().first) {
108 return m_pastSnapshots.front().second;
109 }
110 if (time > m_pastSnapshots.back().first) {
111 return m_pastSnapshots.back().second;
112 }
113 if (m_pastSnapshots.size() < 2) {
114 return m_pastSnapshots[0].second;
115 }
116
117 // Get the iterator which has a key no less than the requested key.
118 auto upper_bound = std::lower_bound(
119 m_pastSnapshots.begin(), m_pastSnapshots.end(), time,
120 [](const auto& pair, auto t) { return t > pair.first; });
121
122 if (upper_bound == m_pastSnapshots.begin()) {
123 return upper_bound->second;
124 }
125
126 auto lower_bound = upper_bound - 1;
127
128 double t = ((time - lower_bound->first) /
129 (upper_bound->first - lower_bound->first));
130
131 return m_interpolatingFunc(lower_bound->second, upper_bound->second, t);
132 }
133
134 /**
135 * Grant access to the internal sample buffer. Used in Pose Estimation to
136 * replay odometry inputs stored within this buffer.
137 */
138 std::vector<std::pair<units::second_t, T>>& GetInternalBuffer() {
139 return m_pastSnapshots;
140 }
141
142 /**
143 * Grant access to the internal sample buffer.
144 */
145 const std::vector<std::pair<units::second_t, T>>& GetInternalBuffer() const {
146 return m_pastSnapshots;
147 }
148
149 private:
150 units::second_t m_historySize;
151 std::vector<std::pair<units::second_t, T>> m_pastSnapshots;
152 std::function<T(const T&, const T&, double)> m_interpolatingFunc;
153};
154
155// Template specialization to ensure that Pose2d uses pose exponential
156template <>
158 units::second_t historySize)
159 : m_historySize(historySize),
160 m_interpolatingFunc([](const Pose2d& start, const Pose2d& end, double t) {
161 if (t < 0) {
162 return start;
163 } else if (t >= 1) {
164 return end;
165 } else {
166 Twist2d twist = start.Log(end);
167 Twist2d scaledTwist = twist * t;
168 return start.Exp(scaledTwist);
169 }
170 }) {}
171
172}
173}
174}
The TimeInterpolatableBuffer provides an easy way to estimate past measurements.
Definition TimeInterpolatableBuffer.hpp:31
void Clear()
Clear all old samples.
Definition TimeInterpolatableBuffer.hpp:90
std::optional< T > Sample(units::second_t time) const
Sample the buffer at the given time.
Definition TimeInterpolatableBuffer.hpp:98
TimeInterpolatableBuffer(units::second_t historySize)
Create a new TimeInterpolatableBuffer.
Definition TimeInterpolatableBuffer.hpp:49
std::vector< std::pair< units::second_t, T > > & GetInternalBuffer()
Grant access to the internal sample buffer.
Definition TimeInterpolatableBuffer.hpp:138
TimeInterpolatableBuffer(units::second_t historySize, std::function< T(const T &, const T &, double)> func)
Create a new TimeInterpolatableBuffer.
Definition TimeInterpolatableBuffer.hpp:39
void AddSample(units::second_t time, T sample)
Add a sample to the buffer.
Definition TimeInterpolatableBuffer.hpp:61
const std::vector< std::pair< units::second_t, T > > & GetInternalBuffer() const
Grant access to the internal sample buffer.
Definition TimeInterpolatableBuffer.hpp:145
Definition StatusCodes.h:18