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);
296 wpi::units::second_t timestamp) {
299 if (m_odometryPoseBuffer.GetInternalBuffer().empty() ||
300 m_odometryPoseBuffer.GetInternalBuffer().front().first -
307 CleanUpVisionUpdates();
311 auto odometrySample = m_odometryPoseBuffer.Sample(timestamp);
313 if (!odometrySample) {
319 auto visionSample =
SampleAt(timestamp);
327 auto transform = visionRobotPose - visionSample.value();
334 transform.Y().value(),
335 transform.Z().value(),
336 transform.Rotation().X().value(),
337 transform.Rotation().Y().value(),
338 transform.Rotation().Z().value()};
342 wpi::units::meter_t{k_times_transform(0)},
343 wpi::units::meter_t{k_times_transform(1)},
344 wpi::units::meter_t{k_times_transform(2)},
345 Rotation3d{wpi::units::radian_t{k_times_transform(3)},
346 wpi::units::radian_t{k_times_transform(4)},
347 wpi::units::radian_t{k_times_transform(5)}}};
350 VisionUpdate visionUpdate{*visionSample + scaledTransform, *odometrySample};
351 m_visionUpdates[timestamp] = visionUpdate;
354 auto firstAfter = m_visionUpdates.upper_bound(timestamp);
355 m_visionUpdates.erase(firstAfter, m_visionUpdates.end());
359 m_poseEstimate = visionUpdate.Compensate(m_odometry.GetPose());
390 const Pose3d& visionRobotPose, wpi::units::second_t timestamp,
406 const WheelPositions& wheelPositions) {
423 const WheelPositions& wheelPositions) {
424 auto odometryEstimate = m_odometry.Update(gyroAngle, wheelPositions);
426 m_odometryPoseBuffer.AddSample(currentTime, odometryEstimate);
428 if (m_visionUpdates.empty()) {
429 m_poseEstimate = odometryEstimate;
431 auto visionUpdate = m_visionUpdates.rbegin()->second;
432 m_poseEstimate = visionUpdate.Compensate(odometryEstimate);
442 void CleanUpVisionUpdates() {
444 if (m_odometryPoseBuffer.GetInternalBuffer().empty()) {
449 wpi::units::second_t oldestOdometryTimestamp =
450 m_odometryPoseBuffer.GetInternalBuffer().front().first;
453 if (m_visionUpdates.empty() ||
454 oldestOdometryTimestamp < m_visionUpdates.begin()->first) {
463 auto newestNeededVisionUpdate =
464 m_visionUpdates.upper_bound(oldestOdometryTimestamp);
465 --newestNeededVisionUpdate;
468 m_visionUpdates.erase(m_visionUpdates.begin(), newestNeededVisionUpdate);
471 struct VisionUpdate {
486 Pose3d Compensate(
const Pose3d& pose)
const {
487 auto delta = pose - odometryPose;
488 return visionPose + delta;
492 static constexpr wpi::units::second_t kBufferDuration = 1.5_s;
494 Odometry3d<WheelPositions, WheelVelocities, WheelAccelerations>& m_odometry;
500 Eigen::DiagonalMatrix<double, 6> m_vision_K =
501 Eigen::DiagonalMatrix<double, 6>::Zero();
504 TimeInterpolatableBuffer<Pose3d> m_odometryPoseBuffer{kBufferDuration};
510 std::map<wpi::units::second_t, VisionUpdate> m_visionUpdates;
512 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:31
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:389
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:405
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:421
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:295
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:72
Represents a translation in 3D space.
Definition Translation3d.hpp:34
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