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