WPILibC++ 2023.4.3
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 <array>
9#include <functional>
10#include <map>
11#include <optional>
12#include <utility>
13#include <vector>
14
15#include <wpi/MathExtras.h>
16#include <wpi/SymbolExports.h>
17
18#include "frc/geometry/Pose2d.h"
19#include "units/time.h"
20
21namespace frc {
22
23/**
24 * The TimeInterpolatableBuffer provides an easy way to estimate past
25 * measurements. One application might be in conjunction with the
26 * DifferentialDrivePoseEstimator, where knowledge of the robot pose at the time
27 * when vision or other global measurement were recorded is necessary, or for
28 * recording the past angles of mechanisms as measured by encoders.
29 *
30 * When sampling this buffer, a user-provided function or wpi::Lerp can be
31 * used. For Pose2ds, we use Twists.
32 *
33 * @tparam T The type stored in this buffer.
34 */
35template <typename T>
37 public:
38 /**
39 * Create a new TimeInterpolatableBuffer.
40 *
41 * @param historySize The history size of the buffer.
42 * @param func The function used to interpolate between values.
43 */
44 TimeInterpolatableBuffer(units::second_t historySize,
45 std::function<T(const T&, const T&, double)> func)
46 : m_historySize(historySize), m_interpolatingFunc(func) {}
47
48 /**
49 * Create a new TimeInterpolatableBuffer. By default, the interpolation
50 * function is wpi::Lerp except for Pose2d, which uses the pose exponential.
51 *
52 * @param historySize The history size of the buffer.
53 */
54 explicit TimeInterpolatableBuffer(units::second_t historySize)
55 : m_historySize(historySize),
56 m_interpolatingFunc([](const T& start, const T& end, double t) {
57 return wpi::Lerp(start, end, t);
58 }) {}
59
60 /**
61 * Add a sample to the buffer.
62 *
63 * @param time The timestamp of the sample.
64 * @param sample The sample object.
65 */
66 void AddSample(units::second_t time, T sample) {
67 // Add the new state into the vector.
68 if (m_pastSnapshots.size() == 0 || time > m_pastSnapshots.back().first) {
69 m_pastSnapshots.emplace_back(time, sample);
70 } else {
71 auto first_after = std::upper_bound(
72 m_pastSnapshots.begin(), m_pastSnapshots.end(), time,
73 [](auto t, const auto& pair) { return t < pair.first; });
74
75 // Don't access this before ensuring first_after isn't first.
76 auto last_not_greater_than = first_after - 1;
77
78 if (first_after == m_pastSnapshots.begin() ||
79 last_not_greater_than == m_pastSnapshots.begin() ||
80 last_not_greater_than->first < time) {
81 // Two cases handled together:
82 // 1. All entries come after the sample
83 // 2. Some entries come before the sample, but none are recorded with
84 // the same time
85 m_pastSnapshots.insert(first_after, std::pair(time, sample));
86 } else {
87 // Final case:
88 // 3. An entry exists with the same recorded time.
89 last_not_greater_than->second = sample;
90 }
91 }
92 while (time - m_pastSnapshots[0].first > m_historySize) {
93 m_pastSnapshots.erase(m_pastSnapshots.begin());
94 }
95 }
96
97 /** Clear all old samples. */
98 void Clear() { m_pastSnapshots.clear(); }
99
100 /**
101 * Sample the buffer at the given time. If the buffer is empty, an empty
102 * optional is returned.
103 *
104 * @param time The time at which to sample the buffer.
105 */
106 std::optional<T> Sample(units::second_t time) {
107 if (m_pastSnapshots.empty()) {
108 return {};
109 }
110
111 // We will perform a binary search to find the index of the element in the
112 // vector that has a timestamp that is equal to or greater than the vision
113 // measurement timestamp.
114
115 if (time <= m_pastSnapshots.front().first) {
116 return m_pastSnapshots.front().second;
117 }
118 if (time > m_pastSnapshots.back().first) {
119 return m_pastSnapshots.back().second;
120 }
121 if (m_pastSnapshots.size() < 2) {
122 return m_pastSnapshots[0].second;
123 }
124
125 // Get the iterator which has a key no less than the requested key.
126 auto upper_bound = std::lower_bound(
127 m_pastSnapshots.begin(), m_pastSnapshots.end(), time,
128 [](const auto& pair, auto t) { return t > pair.first; });
129
130 if (upper_bound == m_pastSnapshots.begin()) {
131 return upper_bound->second;
132 }
133
134 auto lower_bound = upper_bound - 1;
135
136 double t = ((time - lower_bound->first) /
137 (upper_bound->first - lower_bound->first));
138
139 return m_interpolatingFunc(lower_bound->second, upper_bound->second, t);
140 }
141
142 /**
143 * Grant access to the internal sample buffer. Used in Pose Estimation to
144 * replay odometry inputs stored within this buffer.
145 */
146 std::vector<std::pair<units::second_t, T>>& GetInternalBuffer() {
147 return m_pastSnapshots;
148 }
149
150 private:
151 units::second_t m_historySize;
152 std::vector<std::pair<units::second_t, T>> m_pastSnapshots;
153 std::function<T(const T&, const T&, double)> m_interpolatingFunc;
154};
155
156// Template specialization to ensure that Pose2d uses pose exponential
157template <>
159 units::second_t historySize);
160
161} // namespace frc
#define WPILIB_DLLEXPORT
Definition: SymbolExports.h:36
The TimeInterpolatableBuffer provides an easy way to estimate past measurements.
Definition: TimeInterpolatableBuffer.h:36
void Clear()
Clear all old samples.
Definition: TimeInterpolatableBuffer.h:98
TimeInterpolatableBuffer(units::second_t historySize, std::function< T(const T &, const T &, double)> func)
Create a new TimeInterpolatableBuffer.
Definition: TimeInterpolatableBuffer.h:44
TimeInterpolatableBuffer(units::second_t historySize)
Create a new TimeInterpolatableBuffer.
Definition: TimeInterpolatableBuffer.h:54
std::vector< std::pair< units::second_t, T > > & GetInternalBuffer()
Grant access to the internal sample buffer.
Definition: TimeInterpolatableBuffer.h:146
std::optional< T > Sample(units::second_t time)
Sample the buffer at the given time.
Definition: TimeInterpolatableBuffer.h:106
void AddSample(units::second_t time, T sample)
Add a sample to the buffer.
Definition: TimeInterpolatableBuffer.h:66
EIGEN_CONSTEXPR Index first(const T &x) EIGEN_NOEXCEPT
Definition: IndexedViewHelper.h:81
static EIGEN_DEPRECATED const end_t end
Definition: IndexedViewHelper.h:181
Definition: AprilTagFieldLayout.h:22
constexpr T Lerp(const T &startValue, const T &endValue, double t)
Linearly interpolates between two values.
Definition: MathExtras.h:950