WPILibC++ 2025.0.0-alpha-1-24-g6478ba6
PoseEstimator.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 <map>
8#include <optional>
9#include <utility>
10#include <vector>
11
12#include <Eigen/Core>
13#include <wpi/SymbolExports.h>
14#include <wpi/array.h>
15
16#include "frc/geometry/Pose2d.h"
22#include "units/time.h"
23#include "wpimath/MathShared.h"
24
25namespace frc {
26/**
27 * This class wraps odometry to fuse latency-compensated
28 * vision measurements with encoder measurements. Robot code should not use this
29 * directly- Instead, use the particular type for your drivetrain (e.g.,
30 * DifferentialDrivePoseEstimator). It will correct for noisy vision
31 * measurements and encoder drift. It is intended to be an easy drop-in for
32 * Odometry.
33 *
34 * Update() should be called every robot loop.
35 *
36 * AddVisionMeasurement() can be called as infrequently as you want; if you
37 * never call it, then this class will behave like regular encoder odometry.
38 *
39 * @tparam WheelSpeeds Wheel speeds type.
40 * @tparam WheelPositions Wheel positions type.
41 */
42template <typename WheelSpeeds, typename WheelPositions>
44 public:
45 /**
46 * Constructs a PoseEstimator.
47 *
48 * @param kinematics A correctly-configured kinematics object for your
49 * drivetrain.
50 * @param odometry A correctly-configured odometry object for your drivetrain.
51 * @param stateStdDevs Standard deviations of the pose estimate (x position in
52 * meters, y position in meters, and heading in radians). Increase these
53 * numbers to trust your state estimate less.
54 * @param visionMeasurementStdDevs Standard deviations of the vision pose
55 * measurement (x position in meters, y position in meters, and heading in
56 * radians). Increase these numbers to trust the vision pose measurement
57 * less.
58 */
61 const wpi::array<double, 3>& stateStdDevs,
62 const wpi::array<double, 3>& visionMeasurementStdDevs);
63
64 /**
65 * Sets the pose estimator's trust in vision measurements. This might be used
66 * to change trust in vision measurements after the autonomous period, or to
67 * change trust as distance to a vision target increases.
68 *
69 * @param visionMeasurementStdDevs Standard deviations of the vision pose
70 * measurement (x position in meters, y position in meters, and heading in
71 * radians). Increase these numbers to trust the vision pose measurement
72 * less.
73 */
74 void SetVisionMeasurementStdDevs(
75 const wpi::array<double, 3>& visionMeasurementStdDevs);
76
77 /**
78 * Resets the robot's position on the field.
79 *
80 * The gyroscope angle does not need to be reset in the user's robot code.
81 * The library automatically takes care of offsetting the gyro angle.
82 *
83 * @param gyroAngle The current gyro angle.
84 * @param wheelPositions The distances traveled by the encoders.
85 * @param pose The estimated pose of the robot on the field.
86 */
87 void ResetPosition(const Rotation2d& gyroAngle,
88 const WheelPositions& wheelPositions, const Pose2d& pose);
89
90 /**
91 * Gets the estimated robot pose.
92 *
93 * @return The estimated robot pose in meters.
94 */
95 Pose2d GetEstimatedPosition() const;
96
97 /**
98 * Return the pose at a given timestamp, if the buffer is not empty.
99 *
100 * @param timestamp The pose's timestamp.
101 * @return The pose at the given timestamp (or std::nullopt if the buffer is
102 * empty).
103 */
104 std::optional<Pose2d> SampleAt(units::second_t timestamp) const;
105
106 /**
107 * Adds a vision measurement to the Kalman Filter. This will correct
108 * the odometry pose estimate while still accounting for measurement noise.
109 *
110 * This method can be called as infrequently as you want, as long as you are
111 * calling Update() every loop.
112 *
113 * To promote stability of the pose estimate and make it robust to bad vision
114 * data, we recommend only adding vision measurements that are already within
115 * one meter or so of the current pose estimate.
116 *
117 * @param visionRobotPose The pose of the robot as measured by the vision
118 * camera.
119 * @param timestamp The timestamp of the vision measurement in seconds. Note
120 * that if you don't use your own time source by calling UpdateWithTime(),
121 * then you must use a timestamp with an epoch since FPGA startup (i.e.,
122 * the epoch of this timestamp is the same epoch as
123 * frc::Timer::GetFPGATimestamp(). This means that you should use
124 * frc::Timer::GetFPGATimestamp() as your time source in this case.
125 */
126 void AddVisionMeasurement(const Pose2d& visionRobotPose,
127 units::second_t timestamp);
128
129 /**
130 * Adds a vision measurement to the Kalman Filter. This will correct
131 * the odometry pose estimate while still accounting for measurement noise.
132 *
133 * This method can be called as infrequently as you want, as long as you are
134 * calling Update() every loop.
135 *
136 * To promote stability of the pose estimate and make it robust to bad vision
137 * data, we recommend only adding vision measurements that are already within
138 * one meter or so of the current pose estimate.
139 *
140 * Note that the vision measurement standard deviations passed into this
141 * method will continue to apply to future measurements until a subsequent
142 * call to SetVisionMeasurementStdDevs() or this method.
143 *
144 * @param visionRobotPose The pose of the robot as measured by the vision
145 * camera.
146 * @param timestamp The timestamp of the vision measurement in seconds. Note
147 * that if you don't use your own time source by calling UpdateWithTime(),
148 * then you must use a timestamp with an epoch since FPGA startup (i.e.,
149 * the epoch of this timestamp is the same epoch as
150 * frc::Timer::GetFPGATimestamp(). This means that you should use
151 * frc::Timer::GetFPGATimestamp() as your time source in this case.
152 * @param visionMeasurementStdDevs Standard deviations of the vision pose
153 * measurement (x position in meters, y position in meters, and heading in
154 * radians). Increase these numbers to trust the vision pose measurement
155 * less.
156 */
158 const Pose2d& visionRobotPose, units::second_t timestamp,
159 const wpi::array<double, 3>& visionMeasurementStdDevs) {
160 SetVisionMeasurementStdDevs(visionMeasurementStdDevs);
161 AddVisionMeasurement(visionRobotPose, timestamp);
162 }
163
164 /**
165 * Updates the pose estimator with wheel encoder and gyro information. This
166 * should be called every loop.
167 *
168 * @param gyroAngle The current gyro angle.
169 * @param wheelPositions The distances traveled by the encoders.
170 *
171 * @return The estimated pose of the robot in meters.
172 */
173 Pose2d Update(const Rotation2d& gyroAngle,
174 const WheelPositions& wheelPositions);
175
176 /**
177 * Updates the pose estimator with wheel encoder and gyro information. This
178 * should be called every loop.
179 *
180 * @param currentTime The time at which this method was called.
181 * @param gyroAngle The current gyro angle.
182 * @param wheelPositions The distances traveled by the encoders.
183 *
184 * @return The estimated pose of the robot in meters.
185 */
186 Pose2d UpdateWithTime(units::second_t currentTime,
187 const Rotation2d& gyroAngle,
188 const WheelPositions& wheelPositions);
189
190 private:
191 /**
192 * Removes stale vision updates that won't affect sampling.
193 */
194 void CleanUpVisionUpdates();
195
196 struct VisionUpdate {
197 // The vision-compensated pose estimate
198 Pose2d visionPose;
199
200 // The pose estimated based solely on odometry
201 Pose2d odometryPose;
202
203 /**
204 * Returns the vision-compensated version of the pose. Specifically, changes
205 * the pose from being relative to this record's odometry pose to being
206 * relative to this record's vision pose.
207 *
208 * @param pose The pose to compensate.
209 * @return The compensated pose.
210 */
211 Pose2d Compensate(const Pose2d& pose) const {
212 auto delta = pose - odometryPose;
213 return visionPose + delta;
214 }
215 };
216
217 static constexpr units::second_t kBufferDuration = 1.5_s;
218
219 Odometry<WheelSpeeds, WheelPositions>& m_odometry;
221 Eigen::Matrix3d m_visionK = Eigen::Matrix3d::Zero();
222
223 // Maps timestamps to odometry-only pose estimates
224 TimeInterpolatableBuffer<Pose2d> m_odometryPoseBuffer{kBufferDuration};
225 // Maps timestamps to vision updates
226 // Always contains one entry before the oldest entry in m_odometryPoseBuffer,
227 // unless there have been no vision measurements after the last reset
228 std::map<units::second_t, VisionUpdate> m_visionUpdates;
229
230 Pose2d m_poseEstimate;
231};
232} // namespace frc
233
#define WPILIB_DLLEXPORT
Definition: SymbolExports.h:36
Helper class that converts a chassis velocity (dx, dy, and dtheta components) into individual wheel s...
Definition: Kinematics.h:24
Class for odometry.
Definition: Odometry.h:28
Represents a 2D pose containing translational and rotational elements.
Definition: Pose2d.h:23
This class wraps odometry to fuse latency-compensated vision measurements with encoder measurements.
Definition: PoseEstimator.h:43
void AddVisionMeasurement(const Pose2d &visionRobotPose, units::second_t timestamp, const wpi::array< double, 3 > &visionMeasurementStdDevs)
Adds a vision measurement to the Kalman Filter.
Definition: PoseEstimator.h:157
A rotation in a 2D coordinate frame represented by a point on the unit circle (cosine and sine).
Definition: Rotation2d.h:23
Definition: WheelPositions.h:11
Definition: AprilTagDetector_cv.h:11
class WPILIB_DLLEXPORT Pose2d
Definition: Transform2d.h:13
constexpr empty_array_t empty_array
Definition: array.h:16