23#include "wpi/units/time.hpp"
50template <
typename WheelPositions,
typename WheelVelocities,
51 typename WheelAccelerations>
77 : m_odometry(odometry) {
78 for (
size_t i = 0; i < 4; ++i) {
79 m_q[i] = stateStdDevs[i] * stateStdDevs[i];
100 for (
size_t i = 0; i < 4; ++i) {
101 r[i] = visionMeasurementStdDevs[i] * visionMeasurementStdDevs[i];
106 for (
size_t row = 0; row < 4; ++row) {
107 if (m_q[row] == 0.0) {
108 m_vision_K.diagonal()[row] = 0.0;
110 m_vision_K.diagonal()[row] =
111 m_q[row] / (m_q[row] + std::sqrt(m_q[row] * r[row]));
114 double angle_gain = m_vision_K.diagonal()[3];
115 m_vision_K.diagonal()[4] = angle_gain;
116 m_vision_K.diagonal()[5] = angle_gain;
130 const WheelPositions& wheelPositions,
const Pose3d& pose) {
132 m_odometry.ResetPosition(gyroAngle, wheelPositions, pose);
133 m_odometryPoseBuffer.Clear();
134 m_visionUpdates.clear();
135 m_poseEstimate = m_odometry.GetPose();
144 m_odometry.ResetPose(pose);
145 m_odometryPoseBuffer.Clear();
146 m_visionUpdates.clear();
147 m_poseEstimate = m_odometry.GetPose();
155#if defined(__GNUC__) && !defined(__clang__)
156#pragma GCC diagnostic push
157#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
160 m_odometry.ResetTranslation(translation);
162 const std::optional<std::pair<units::second_t, VisionUpdate>>
164 m_visionUpdates.empty() ? std::nullopt
165 : std::optional{*m_visionUpdates.crbegin()};
166 m_odometryPoseBuffer.Clear();
167 m_visionUpdates.clear();
169 if (latestVisionUpdate) {
171 const VisionUpdate visionUpdate{
172 Pose3d{translation, latestVisionUpdate->second.visionPose.Rotation()},
174 latestVisionUpdate->second.odometryPose.Rotation()}};
175 m_visionUpdates[latestVisionUpdate->first] = visionUpdate;
176 m_poseEstimate = visionUpdate.Compensate(m_odometry.GetPose());
178 m_poseEstimate = m_odometry.GetPose();
181#if defined(__GNUC__) && !defined(__clang__)
182#pragma GCC diagnostic pop
190#if defined(__GNUC__) && !defined(__clang__)
191#pragma GCC diagnostic push
192#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
195 m_odometry.ResetRotation(rotation);
197 const std::optional<std::pair<units::second_t, VisionUpdate>>
199 m_visionUpdates.empty() ? std::nullopt
200 : std::optional{*m_visionUpdates.crbegin()};
201 m_odometryPoseBuffer.Clear();
202 m_visionUpdates.clear();
204 if (latestVisionUpdate) {
206 const VisionUpdate visionUpdate{
207 Pose3d{latestVisionUpdate->second.visionPose.Translation(), rotation},
208 Pose3d{latestVisionUpdate->second.odometryPose.Translation(),
210 m_visionUpdates[latestVisionUpdate->first] = visionUpdate;
211 m_poseEstimate = visionUpdate.Compensate(m_odometry.GetPose());
213 m_poseEstimate = m_odometry.GetPose();
216#if defined(__GNUC__) && !defined(__clang__)
217#pragma GCC diagnostic pop
234 std::optional<Pose3d>
SampleAt(wpi::units::second_t timestamp)
const {
236 if (m_odometryPoseBuffer.GetInternalBuffer().empty()) {
243 wpi::units::second_t oldestOdometryTimestamp =
244 m_odometryPoseBuffer.GetInternalBuffer().front().first;
245 wpi::units::second_t newestOdometryTimestamp =
246 m_odometryPoseBuffer.GetInternalBuffer().back().first;
248 std::clamp(timestamp, oldestOdometryTimestamp, newestOdometryTimestamp);
252 if (m_visionUpdates.empty() || timestamp < m_visionUpdates.begin()->first) {
253 return m_odometryPoseBuffer.Sample(timestamp);
261 auto floorIter = m_visionUpdates.upper_bound(timestamp);
263 auto visionUpdate = floorIter->second;
266 auto odometryEstimate = m_odometryPoseBuffer.Sample(timestamp);
270 if (odometryEstimate) {
271 return visionUpdate.Compensate(*odometryEstimate);
297 wpi::units::second_t timestamp) {
300 if (m_odometryPoseBuffer.GetInternalBuffer().empty() ||
301 m_odometryPoseBuffer.GetInternalBuffer().front().first -
308 CleanUpVisionUpdates();
312 auto odometrySample = m_odometryPoseBuffer.Sample(timestamp);
314 if (!odometrySample) {
320 auto visionSample =
SampleAt(timestamp);
328 auto transform = visionRobotPose - visionSample.value();
335 transform.Y().value(),
336 transform.Z().value(),
337 transform.Rotation().X().value(),
338 transform.Rotation().Y().value(),
339 transform.Rotation().Z().value()};
343 wpi::units::meter_t{k_times_transform(0)},
344 wpi::units::meter_t{k_times_transform(1)},
345 wpi::units::meter_t{k_times_transform(2)},
346 Rotation3d{wpi::units::radian_t{k_times_transform(3)},
347 wpi::units::radian_t{k_times_transform(4)},
348 wpi::units::radian_t{k_times_transform(5)}}};
351 VisionUpdate visionUpdate{*visionSample + scaledTransform, *odometrySample};
352 m_visionUpdates[timestamp] = visionUpdate;
355 auto firstAfter = m_visionUpdates.upper_bound(timestamp);
356 m_visionUpdates.erase(firstAfter, m_visionUpdates.end());
360 m_poseEstimate = visionUpdate.Compensate(m_odometry.GetPose());
392 const Pose3d& visionRobotPose, wpi::units::second_t timestamp,
408 const WheelPositions& wheelPositions) {
425 const WheelPositions& wheelPositions) {
426 auto odometryEstimate = m_odometry.Update(gyroAngle, wheelPositions);
428 m_odometryPoseBuffer.AddSample(currentTime, odometryEstimate);
430 if (m_visionUpdates.empty()) {
431 m_poseEstimate = odometryEstimate;
433 auto visionUpdate = m_visionUpdates.rbegin()->second;
434 m_poseEstimate = visionUpdate.Compensate(odometryEstimate);
444 void CleanUpVisionUpdates() {
446 if (m_odometryPoseBuffer.GetInternalBuffer().empty()) {
451 wpi::units::second_t oldestOdometryTimestamp =
452 m_odometryPoseBuffer.GetInternalBuffer().front().first;
455 if (m_visionUpdates.empty() ||
456 oldestOdometryTimestamp < m_visionUpdates.begin()->first) {
465 auto newestNeededVisionUpdate =
466 m_visionUpdates.upper_bound(oldestOdometryTimestamp);
467 --newestNeededVisionUpdate;
470 m_visionUpdates.erase(m_visionUpdates.begin(), newestNeededVisionUpdate);
473 struct VisionUpdate {
488 Pose3d Compensate(
const Pose3d& pose)
const {
489 auto delta = pose - odometryPose;
490 return visionPose + delta;
494 static constexpr wpi::units::second_t kBufferDuration = 1.5_s;
496 Odometry3d<WheelPositions, WheelVelocities, WheelAccelerations>& m_odometry;
502 Eigen::DiagonalMatrix<double, 6> m_vision_K =
503 Eigen::DiagonalMatrix<double, 6>::Zero();
506 TimeInterpolatableBuffer<Pose3d> m_odometryPoseBuffer{kBufferDuration};
512 std::map<wpi::units::second_t, VisionUpdate> m_visionUpdates;
514 Pose3d m_poseEstimate;
#define WPILIB_DLLEXPORT
Definition SymbolExports.hpp:36
Helper class that converts a chassis velocity (dx, dy, and dtheta components) into individual wheel v...
Definition Kinematics.hpp:26
static void ReportUsage(std::string_view resource, std::string_view data)
Definition MathShared.hpp:61
static wpi::units::second_t GetTimestamp()
Definition MathShared.hpp:65
Class for odometry.
Definition Odometry3d.hpp:31
Represents a 3D pose containing translational and rotational elements.
Definition Pose3d.hpp:28
void AddVisionMeasurement(const Pose3d &visionRobotPose, wpi::units::second_t timestamp, const wpi::util::array< double, 4 > &visionMeasurementStdDevs)
Adds a vision measurement to the Kalman Filter.
Definition PoseEstimator3d.hpp:391
PoseEstimator3d(Kinematics< WheelPositions, WheelVelocities, WheelAccelerations > &kinematics, Odometry3d< WheelPositions, WheelVelocities, WheelAccelerations > &odometry, const wpi::util::array< double, 4 > &stateStdDevs, const wpi::util::array< double, 4 > &visionMeasurementStdDevs)
Constructs a PoseEstimator3d.
Definition PoseEstimator3d.hpp:71
void ResetTranslation(const Translation3d &translation)
Resets the robot's translation.
Definition PoseEstimator3d.hpp:159
void ResetPosition(const Rotation3d &gyroAngle, const WheelPositions &wheelPositions, const Pose3d &pose)
Resets the robot's position on the field.
Definition PoseEstimator3d.hpp:129
void SetVisionMeasurementStdDevs(const wpi::util::array< double, 4 > &visionMeasurementStdDevs)
Sets the pose estimator's trust in vision measurements.
Definition PoseEstimator3d.hpp:96
Pose3d Update(const Rotation3d &gyroAngle, const WheelPositions &wheelPositions)
Updates the pose estimator with wheel encoder and gyro information.
Definition PoseEstimator3d.hpp:407
void ResetPose(const Pose3d &pose)
Resets the robot's pose.
Definition PoseEstimator3d.hpp:143
Pose3d UpdateWithTime(wpi::units::second_t currentTime, const Rotation3d &gyroAngle, const WheelPositions &wheelPositions)
Updates the pose estimator with wheel encoder and gyro information.
Definition PoseEstimator3d.hpp:423
Pose3d GetEstimatedPosition() const
Gets the estimated robot pose.
Definition PoseEstimator3d.hpp:225
void AddVisionMeasurement(const Pose3d &visionRobotPose, wpi::units::second_t timestamp)
Adds a vision measurement to the Kalman Filter.
Definition PoseEstimator3d.hpp:296
std::optional< Pose3d > SampleAt(wpi::units::second_t timestamp) const
Return the pose at a given timestamp, if the buffer is not empty.
Definition PoseEstimator3d.hpp:234
void ResetRotation(const Rotation3d &rotation)
Resets the robot's rotation.
Definition PoseEstimator3d.hpp:194
A rotation in a 3D coordinate frame, represented by a quaternion.
Definition Rotation3d.hpp:69
Represents a translation in 3D space.
Definition Translation3d.hpp:31
This class is a wrapper around std::array that does compile time size checking.
Definition array.hpp:26
Definition LinearSystem.hpp:20
Eigen::Vector< double, Size > Vectord
Definition EigenCore.hpp:12
constexpr empty_array_t empty_array
Definition array.hpp:16