CTRE Phoenix 6 C++ 26.50.0-alpha-1
Loading...
Searching...
No Matches
SwerveDrivePoseEstimator.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
12#include <array>
13#include <map>
14
15namespace ctre {
16namespace phoenix6 {
17namespace swerve {
18namespace impl {
19
20/**
21 * \brief Class for swerve drive odometry. Odometry allows you to track the robot's
22 * position on the field over a course of a match using readings from your
23 * swerve drive encoders and swerve azimuth encoders.
24 *
25 * Teams can use odometry during the autonomous period for complex tasks like
26 * path following. Furthermore, odometry can be used for latency compensation
27 * when using computer-vision systems.
28 */
30public:
31 using WheelPositions = std::vector<SwerveModulePosition>;
32 using WheelVelocities = std::vector<SwerveModuleVelocity>;
33 using WheelAccelerations = std::vector<SwerveModuleAcceleration>;
34
35private:
36 SwerveDriveKinematics m_kinematics;
37 Pose2d m_pose;
38
39 WheelPositions m_previousWheelPositions;
40 Rotation2d m_previousAngle;
41 Rotation2d m_gyroOffset;
42
43public:
44 /**
45 * \brief Constructs a SwerveDriveOdometry object.
46 *
47 * \param kinematics The swerve drive kinematics for your drivetrain.
48 * \param gyroAngle The angle reported by the gyroscope.
49 * \param modulePositions The wheel positions reported by each module.
50 * \param initialPose The starting position of the robot on the field.
51 */
52 SwerveDriveOdometry(SwerveDriveKinematics kinematics, Rotation2d const &gyroAngle, WheelPositions modulePositions, Pose2d initialPose = Pose2d{}) :
53 m_kinematics{std::move(kinematics)},
54 m_pose{std::move(initialPose)},
55 m_previousWheelPositions{std::move(modulePositions)},
56 m_previousAngle{m_pose.Rotation()},
57 m_gyroOffset{(-gyroAngle).RotateBy(m_pose.Rotation())}
58 {}
59
60 /**
61 * \brief Resets the robot's position on the field.
62 *
63 * The gyroscope angle does not need to be reset here on the user's robot
64 * code. The library automatically takes care of offsetting the gyro angle.
65 *
66 * \param gyroAngle The angle reported by the gyroscope.
67 * \param wheelPositions The current distances measured by each wheel.
68 * \param pose The position on the field that your robot is at.
69 */
70 void ResetPosition(Rotation2d const &gyroAngle, WheelPositions wheelPositions, Pose2d const &pose)
71 {
72 m_pose = pose;
73 m_previousAngle = m_pose.Rotation();
74 m_gyroOffset = (-gyroAngle).RotateBy(m_pose.Rotation());
75 m_previousWheelPositions = std::move(wheelPositions);
76 }
77
78 /**
79 * \brief Resets the pose.
80 *
81 * \param pose The pose to reset to.
82 */
83 void ResetPose(Pose2d const &pose)
84 {
85 m_gyroOffset = m_gyroOffset.RotateBy(-m_pose.Rotation()).RotateBy(pose.Rotation());
86 m_pose = pose;
87 m_previousAngle = pose.Rotation();
88 }
89
90 /**
91 * \brief Resets the translation of the pose.
92 *
93 * \param translation The translation to reset to.
94 */
95 void ResetTranslation(Translation2d const &translation)
96 {
97 m_pose = Pose2d{translation, m_pose.Rotation()};
98 }
99
100 /**
101 * \brief Resets the rotation of the pose.
102 *
103 * \param rotation The rotation to reset to.
104 */
105 void ResetRotation(Rotation2d const &rotation)
106 {
107 m_gyroOffset = m_gyroOffset.RotateBy(-m_pose.Rotation()).RotateBy(rotation);
108 m_pose = Pose2d{m_pose.Translation(), rotation};
109 m_previousAngle = rotation;
110 }
111
112 /**
113 * \brief Returns the position of the robot on the field.
114 *
115 * \returns The pose of the robot.
116 */
117 Pose2d const &GetPose() const { return m_pose; }
118
119 /**
120 * \brief Updates the robot's position on the field using forward kinematics and
121 * integration of the pose over time. This method takes in an angle parameter
122 * which is used instead of the angular rate that is calculated from forward
123 * kinematics, in addition to the current distance measurement at each wheel.
124 *
125 * \param gyroAngle The angle reported by the gyroscope.
126 * \param wheelPositions The current distances measured by each wheel.
127 *
128 * \returns The new pose of the robot.
129 */
130 Pose2d const &Update(Rotation2d const &gyroAngle, WheelPositions wheelPositions);
131};
132
133/**
134 * \brief This class wraps Swerve Drive Odometry to fuse latency-compensated
135 * vision measurements with swerve drive encoder distance measurements. It is
136 * intended to be a drop-in for SwerveDriveOdometry.
137 *
138 * Update() should be called every robot loop.
139 *
140 * AddVisionMeasurement() can be called as infrequently as you want; if you
141 * never call it, then this class will behave as regular encoder odometry.
142 */
144public:
148
149private:
150 struct VisionUpdate {
151 /** \brief The vision-compensated pose estimate */
152 Pose2d visionPose;
153
154 /** \brief The pose estimated based solely on odometry */
155 Pose2d odometryPose;
156
157 /**
158 * \brief Returns the vision-compensated version of the pose. Specifically, changes
159 * the pose from being relative to this record's odometry pose to being
160 * relative to this record's vision pose.
161 *
162 * \param pose The pose to compensate.
163 * \returns The compensated pose.
164 */
165 Pose2d Compensate(const Pose2d& pose) const
166 {
167 auto const delta = pose - odometryPose;
168 return visionPose + delta;
169 }
170 };
171
172 static constexpr wpi::units::second_t kBufferDuration = 1.5_s;
173
174 SwerveDriveOdometry m_odometry;
175 std::array<double, 3> m_q{};
176 std::array<double, 3> m_r{};
177
178 struct VisionMatrices;
179 std::unique_ptr<VisionMatrices> m_matrices;
180
181 /* Maps timestamps to odometry-only pose estimates */
182 TimeInterpolatableBuffer<Pose2d> m_odometryPoseBuffer{kBufferDuration};
183 /*
184 * Maps timestamps to vision updates
185 * Always contains one entry before the oldest entry in m_odometryPoseBuffer,
186 * unless there have been no vision measurements after the last reset. May
187 * contain one entry while m_odometryPoseBuffer is empty to correct for
188 * translation/rotation after a call to ResetRotation/ResetTranslation.
189 */
190 std::map<wpi::units::second_t, VisionUpdate> m_visionUpdates;
191
192 Pose2d m_poseEstimate;
193
194 /**
195 * \brief Removes stale vision updates that won't affect sampling.
196 */
197 void CleanUpVisionUpdates();
198
199 /**
200 * \brief Updates the vision matrices to account for changes in standard deviations.
201 */
202 void UpdateVisionMatrices();
203
204public:
205 /**
206 * \brief Constructs a SwerveDrivePoseEstimator with default standard deviations
207 * for the model and vision measurements.
208 *
209 * The default standard deviations of the model states are
210 * 0.1 meters for x, 0.1 meters for y, and 0.1 radians for heading.
211 * The default standard deviations of the vision measurements are
212 * 0.9 meters for x, 0.9 meters for y, and 0.9 radians for heading.
213 *
214 * \param kinematics A correctly-configured kinematics object for your
215 * drivetrain.
216 * \param gyroAngle The current gyro angle.
217 * \param modulePositions The current distance and rotation measurements of
218 * the swerve modules.
219 * \param initialPose The starting pose estimate.
220 */
221 SwerveDrivePoseEstimator(SwerveDriveKinematics const &kinematics, Rotation2d const &gyroAngle,
222 WheelPositions modulePositions, Pose2d initialPose) :
223 SwerveDrivePoseEstimator{kinematics, gyroAngle, std::move(modulePositions), std::move(initialPose),
224 {0.1, 0.1, 0.1}, {0.9, 0.9, 0.9}}
225 {}
226
227 /**
228 * \brief Constructs a SwerveDrivePoseEstimator.
229 *
230 * \param kinematics A correctly-configured kinematics object for your
231 * drivetrain.
232 * \param gyroAngle The current gyro angle.
233 * \param modulePositions The current distance and rotation measurements of
234 * the swerve modules.
235 * \param initialPose The starting pose estimate.
236 * \param stateStdDevs Standard deviations of the pose estimate (x position in
237 * meters, y position in meters, and heading in radians). Increase these
238 * numbers to trust your state estimate less.
239 * \param visionMeasurementStdDevs Standard deviations of the vision pose
240 * measurement (x position in meters, y position in meters, and heading in
241 * radians). Increase these numbers to trust the vision pose measurement
242 * less.
243 */
244 SwerveDrivePoseEstimator(SwerveDriveKinematics const &kinematics, Rotation2d const &gyroAngle,
245 WheelPositions modulePositions, Pose2d initialPose,
246 std::array<double, 3> const &stateStdDevs, std::array<double, 3> const &visionMeasurementStdDevs);
247
249
250 /**
251 * \brief Sets the pose estimator's trust in robot odometry. This might be used
252 * to change trust in odometry after an impact with the wall or traversing a bump.
253 *
254 * \param stateStdDevs Standard deviations of the pose estimate (x position in
255 * meters, y position in meters, and heading in radians). Increase these
256 * numbers to trust your state estimate less.
257 */
258 void SetStateStdDevs(std::array<double, 3> const &stateStdDevs)
259 {
260 for (size_t i = 0; i < m_q.size(); ++i) {
261 m_q[i] = stateStdDevs[i] * stateStdDevs[i];
262 }
263 UpdateVisionMatrices();
264 }
265
266 /**
267 * \brief Sets the pose estimator's trust in vision measurements. This might be used
268 * to change trust in vision measurements after the autonomous period, or to
269 * change trust as distance to a vision target increases.
270 *
271 * \param visionMeasurementStdDevs Standard deviations of the vision pose
272 * measurement (x position in meters, y position in meters, and heading in
273 * radians). Increase these numbers to trust the vision pose measurement
274 * less.
275 */
276 void SetVisionMeasurementStdDevs(std::array<double, 3> const &visionMeasurementStdDevs)
277 {
278 for (size_t i = 0; i < m_r.size(); ++i) {
279 m_r[i] = visionMeasurementStdDevs[i] * visionMeasurementStdDevs[i];
280 }
281 UpdateVisionMatrices();
282 }
283
284 /**
285 * \brief Resets the robot's position on the field.
286 *
287 * The gyroscope angle does not need to be reset in the user's robot code.
288 * The library automatically takes care of offsetting the gyro angle.
289 *
290 * \param gyroAngle The current gyro angle.
291 * \param wheelPositions The distances traveled by the encoders.
292 * \param pose The estimated pose of the robot on the field.
293 */
294 void ResetPosition(Rotation2d const &gyroAngle, WheelPositions wheelPositions, Pose2d const &pose)
295 {
296 m_odometry.ResetPosition(gyroAngle, std::move(wheelPositions), pose);
297 m_odometryPoseBuffer.Clear();
298 m_visionUpdates.clear();
299 m_poseEstimate = m_odometry.GetPose();
300 }
301
302 /**
303 * \brief Resets the robot's pose.
304 *
305 * \param pose The pose to reset to.
306 */
307 void ResetPose(Pose2d const &pose)
308 {
309 m_odometry.ResetPose(pose);
310 m_odometryPoseBuffer.Clear();
311 m_visionUpdates.clear();
312 m_poseEstimate = m_odometry.GetPose();
313 }
314
315 /**
316 * \brief Resets the robot's translation.
317 *
318 * \param translation The pose to translation to.
319 */
320 void ResetTranslation(Translation2d const &translation)
321 {
322 m_odometry.ResetTranslation(translation);
323
324 std::optional<std::pair<wpi::units::second_t, VisionUpdate>> latestVisionUpdate =
325 m_visionUpdates.empty() ? std::nullopt : std::optional{*m_visionUpdates.crbegin()};
326 m_odometryPoseBuffer.Clear();
327 m_visionUpdates.clear();
328
329 if (latestVisionUpdate) {
330 VisionUpdate const visionUpdate{
331 Pose2d{translation, latestVisionUpdate->second.visionPose.Rotation()},
332 Pose2d{translation, latestVisionUpdate->second.odometryPose.Rotation()}
333 };
334 m_visionUpdates[latestVisionUpdate->first] = visionUpdate;
335 m_poseEstimate = visionUpdate.Compensate(m_odometry.GetPose());
336 } else {
337 m_poseEstimate = m_odometry.GetPose();
338 }
339 }
340
341 /**
342 * \brief Resets the robot's rotation.
343 *
344 * \param rotation The rotation to reset to.
345 */
346 void ResetRotation(Rotation2d const &rotation)
347 {
348 m_odometry.ResetRotation(rotation);
349
350 std::optional<std::pair<wpi::units::second_t, VisionUpdate>> latestVisionUpdate =
351 m_visionUpdates.empty() ? std::nullopt : std::optional{*m_visionUpdates.crbegin()};
352 m_odometryPoseBuffer.Clear();
353 m_visionUpdates.clear();
354
355 if (latestVisionUpdate) {
356 VisionUpdate const visionUpdate{
357 Pose2d{latestVisionUpdate->second.visionPose.Translation(), rotation},
358 Pose2d{latestVisionUpdate->second.odometryPose.Translation(), rotation}
359 };
360 m_visionUpdates[latestVisionUpdate->first] = visionUpdate;
361 m_poseEstimate = visionUpdate.Compensate(m_odometry.GetPose());
362 } else {
363 m_poseEstimate = m_odometry.GetPose();
364 }
365 }
366
367 /**
368 * \brief Gets the estimated robot pose.
369 *
370 * \returns The estimated robot pose in meters.
371 */
372 Pose2d GetEstimatedPosition() const
373 {
374 return m_poseEstimate;
375 }
376
377 /**
378 * \brief Return the pose at a given timestamp, if the buffer is not empty.
379 *
380 * \param timestamp The pose's timestamp.
381 * \returns The pose at the given timestamp (or std::nullopt if the buffer is
382 * empty).
383 */
384 std::optional<Pose2d> SampleAt(wpi::units::second_t timestamp) const;
385
386 /**
387 * \brief Adds a vision measurement to the Kalman Filter. This will correct
388 * the odometry pose estimate while still accounting for measurement noise.
389 *
390 * This method can be called as infrequently as you want, as long as you are
391 * calling Update() every loop.
392 *
393 * To promote stability of the pose estimate and make it robust to bad vision
394 * data, we recommend only adding vision measurements that are already within
395 * one meter or so of the current pose estimate.
396 *
397 * \param visionRobotPose The pose of the robot as measured by the vision
398 * camera.
399 * \param timestamp The timestamp of the vision measurement in seconds. Note
400 * that if you don't use your own time source by calling UpdateWithTime(),
401 * then you must use a timestamp with an epoch since system startup (i.e.,
402 * the epoch of this timestamp is the same epoch as utils#GetCurrentTime().
403 * This means that you should use utils#GetCurrentTime() as your time source
404 * in this case.
405 */
406 void AddVisionMeasurement(Pose2d const &visionRobotPose, wpi::units::second_t timestamp);
407
408 /**
409 * \brief Adds a vision measurement to the Kalman Filter. This will correct
410 * the odometry pose estimate while still accounting for measurement noise.
411 *
412 * This method can be called as infrequently as you want, as long as you are
413 * calling Update() every loop.
414 *
415 * To promote stability of the pose estimate and make it robust to bad vision
416 * data, we recommend only adding vision measurements that are already within
417 * one meter or so of the current pose estimate.
418 *
419 * Note that the vision measurement standard deviations passed into this
420 * method will continue to apply to future measurements until a subsequent
421 * call to SetVisionMeasurementStdDevs() or this method.
422 *
423 * \param visionRobotPose The pose of the robot as measured by the vision
424 * camera.
425 * \param timestamp The timestamp of the vision measurement in seconds. Note
426 * that if you don't use your own time source by calling UpdateWithTime(),
427 * then you must use a timestamp with an epoch since system startup (i.e.,
428 * the epoch of this timestamp is the same epoch as utils#GetCurrentTime().
429 * This means that you should use utils#GetCurrentTime() as your time source
430 * in this case.
431 * \param visionMeasurementStdDevs Standard deviations of the vision pose
432 * measurement (x position in meters, y position in meters, and heading in
433 * radians). Increase these numbers to trust the vision pose measurement
434 * less.
435 */
436 void AddVisionMeasurement(Pose2d const &visionRobotPose, wpi::units::second_t timestamp,
437 std::array<double, 3> const &visionMeasurementStdDevs)
438 {
439 SetVisionMeasurementStdDevs(visionMeasurementStdDevs);
440 AddVisionMeasurement(visionRobotPose, timestamp);
441 }
442
443 /**
444 * \brief Updates the pose estimator with wheel encoder and gyro information.
445 * This should be called every loop.
446 *
447 * \param gyroAngle The current gyro angle.
448 * \param wheelPositions The distances traveled by the encoders.
449 *
450 * \returns The estimated pose of the robot in meters.
451 */
452 Pose2d Update(Rotation2d const &gyroAngle, WheelPositions const &wheelPositions)
453 {
454 return UpdateWithTime(utils::GetCurrentTime(), gyroAngle, wheelPositions);
455 }
456
457 /**
458 * \brief Updates the pose estimator with wheel encoder and gyro information. This
459 * should be called every loop.
460 *
461 * \param currentTime The time at which this method was called.
462 * \param gyroAngle The current gyro angle.
463 * \param wheelPositions The distances traveled by the encoders.
464 *
465 * \returns The estimated pose of the robot in meters.
466 */
467 Pose2d UpdateWithTime(wpi::units::second_t currentTime, Rotation2d const &gyroAngle, WheelPositions const &wheelPositions)
468 {
469 auto odometryEstimate = m_odometry.Update(gyroAngle, wheelPositions);
470
471 m_odometryPoseBuffer.AddSample(currentTime, odometryEstimate);
472
473 if (m_visionUpdates.empty()) {
474 m_poseEstimate = std::move(odometryEstimate);
475 } else {
476 auto const visionUpdate = m_visionUpdates.rbegin()->second;
477 m_poseEstimate = visionUpdate.Compensate(odometryEstimate);
478 }
479
480 return GetEstimatedPosition();
481 }
482};
483
484}
485}
486}
487}
Class that converts a chassis velocity (dx, dy, and dtheta components) into individual module states ...
Definition SwerveDriveKinematics.hpp:41
Class for swerve drive odometry.
Definition SwerveDrivePoseEstimator.hpp:29
void ResetPosition(Rotation2d const &gyroAngle, WheelPositions wheelPositions, Pose2d const &pose)
Resets the robot's position on the field.
Definition SwerveDrivePoseEstimator.hpp:70
std::vector< SwerveModulePosition > WheelPositions
Definition SwerveDrivePoseEstimator.hpp:31
Pose2d const & GetPose() const
Returns the position of the robot on the field.
Definition SwerveDrivePoseEstimator.hpp:117
void ResetRotation(Rotation2d const &rotation)
Resets the rotation of the pose.
Definition SwerveDrivePoseEstimator.hpp:105
Pose2d const & Update(Rotation2d const &gyroAngle, WheelPositions wheelPositions)
Updates the robot's position on the field using forward kinematics and integration of the pose over t...
std::vector< SwerveModuleVelocity > WheelVelocities
Definition SwerveDrivePoseEstimator.hpp:32
std::vector< SwerveModuleAcceleration > WheelAccelerations
Definition SwerveDrivePoseEstimator.hpp:33
void ResetTranslation(Translation2d const &translation)
Resets the translation of the pose.
Definition SwerveDrivePoseEstimator.hpp:95
void ResetPose(Pose2d const &pose)
Resets the pose.
Definition SwerveDrivePoseEstimator.hpp:83
SwerveDriveOdometry(SwerveDriveKinematics kinematics, Rotation2d const &gyroAngle, WheelPositions modulePositions, Pose2d initialPose=Pose2d{})
Constructs a SwerveDriveOdometry object.
Definition SwerveDrivePoseEstimator.hpp:52
void ResetPosition(Rotation2d const &gyroAngle, WheelPositions wheelPositions, Pose2d const &pose)
Resets the robot's position on the field.
Definition SwerveDrivePoseEstimator.hpp:294
Pose2d UpdateWithTime(wpi::units::second_t currentTime, Rotation2d const &gyroAngle, WheelPositions const &wheelPositions)
Updates the pose estimator with wheel encoder and gyro information.
Definition SwerveDrivePoseEstimator.hpp:467
SwerveDrivePoseEstimator(SwerveDriveKinematics const &kinematics, Rotation2d const &gyroAngle, WheelPositions modulePositions, Pose2d initialPose, std::array< double, 3 > const &stateStdDevs, std::array< double, 3 > const &visionMeasurementStdDevs)
Constructs a SwerveDrivePoseEstimator.
Pose2d GetEstimatedPosition() const
Gets the estimated robot pose.
Definition SwerveDrivePoseEstimator.hpp:372
SwerveDriveOdometry::WheelPositions WheelPositions
Definition SwerveDrivePoseEstimator.hpp:145
void ResetTranslation(Translation2d const &translation)
Resets the robot's translation.
Definition SwerveDrivePoseEstimator.hpp:320
std::optional< Pose2d > SampleAt(wpi::units::second_t timestamp) const
Return the pose at a given timestamp, if the buffer is not empty.
void ResetPose(Pose2d const &pose)
Resets the robot's pose.
Definition SwerveDrivePoseEstimator.hpp:307
SwerveDrivePoseEstimator(SwerveDriveKinematics const &kinematics, Rotation2d const &gyroAngle, WheelPositions modulePositions, Pose2d initialPose)
Constructs a SwerveDrivePoseEstimator with default standard deviations for the model and vision measu...
Definition SwerveDrivePoseEstimator.hpp:221
void AddVisionMeasurement(Pose2d const &visionRobotPose, wpi::units::second_t timestamp)
Adds a vision measurement to the Kalman Filter.
void SetStateStdDevs(std::array< double, 3 > const &stateStdDevs)
Sets the pose estimator's trust in robot odometry.
Definition SwerveDrivePoseEstimator.hpp:258
SwerveDriveOdometry::WheelAccelerations WheelAccelerations
Definition SwerveDrivePoseEstimator.hpp:147
SwerveDriveOdometry::WheelVelocities WheelVelocities
Definition SwerveDrivePoseEstimator.hpp:146
void ResetRotation(Rotation2d const &rotation)
Resets the robot's rotation.
Definition SwerveDrivePoseEstimator.hpp:346
void AddVisionMeasurement(Pose2d const &visionRobotPose, wpi::units::second_t timestamp, std::array< double, 3 > const &visionMeasurementStdDevs)
Adds a vision measurement to the Kalman Filter.
Definition SwerveDrivePoseEstimator.hpp:436
void SetVisionMeasurementStdDevs(std::array< double, 3 > const &visionMeasurementStdDevs)
Sets the pose estimator's trust in vision measurements.
Definition SwerveDrivePoseEstimator.hpp:276
Pose2d Update(Rotation2d const &gyroAngle, WheelPositions const &wheelPositions)
Updates the pose estimator with wheel encoder and gyro information.
Definition SwerveDrivePoseEstimator.hpp:452
Definition SwerveDrivetrainImpl.hpp:21
Definition SwerveModule.hpp:28
wpi::units::second_t GetCurrentTime()
Get the current timestamp.
Definition Utils.hpp:27
Definition ExternalFeedbackConfigs.hpp:16
Definition motor_constants.h:14