001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.math.kinematics; 006 007import edu.wpi.first.math.MathSharedStore; 008import edu.wpi.first.math.MathUsageId; 009import edu.wpi.first.math.geometry.Pose2d; 010import edu.wpi.first.math.geometry.Rotation2d; 011 012/** 013 * Class for swerve drive odometry. Odometry allows you to track the robot's position on the field 014 * over a course of a match using readings from your swerve drive encoders and swerve azimuth 015 * encoders. 016 * 017 * <p>Teams can use odometry during the autonomous period for complex tasks like path following. 018 * Furthermore, odometry can be used for latency compensation when using computer-vision systems. 019 */ 020public class SwerveDriveOdometry { 021 private final SwerveDriveKinematics m_kinematics; 022 private Pose2d m_poseMeters; 023 024 private Rotation2d m_gyroOffset; 025 private Rotation2d m_previousAngle; 026 private final int m_numModules; 027 private SwerveModulePosition[] m_previousModulePositions; 028 029 /** 030 * Constructs a SwerveDriveOdometry object. 031 * 032 * @param kinematics The swerve drive kinematics for your drivetrain. 033 * @param gyroAngle The angle reported by the gyroscope. 034 * @param modulePositions The wheel positions reported by each module. 035 * @param initialPose The starting position of the robot on the field. 036 */ 037 public SwerveDriveOdometry( 038 SwerveDriveKinematics kinematics, 039 Rotation2d gyroAngle, 040 SwerveModulePosition[] modulePositions, 041 Pose2d initialPose) { 042 m_kinematics = kinematics; 043 m_poseMeters = initialPose; 044 m_gyroOffset = m_poseMeters.getRotation().minus(gyroAngle); 045 m_previousAngle = initialPose.getRotation(); 046 m_numModules = modulePositions.length; 047 048 m_previousModulePositions = new SwerveModulePosition[m_numModules]; 049 for (int index = 0; index < m_numModules; index++) { 050 m_previousModulePositions[index] = 051 new SwerveModulePosition( 052 modulePositions[index].distanceMeters, modulePositions[index].angle); 053 } 054 055 MathSharedStore.reportUsage(MathUsageId.kOdometry_SwerveDrive, 1); 056 } 057 058 /** 059 * Constructs a SwerveDriveOdometry object with the default pose at the origin. 060 * 061 * @param kinematics The swerve drive kinematics for your drivetrain. 062 * @param gyroAngle The angle reported by the gyroscope. 063 * @param modulePositions The wheel positions reported by each module. 064 */ 065 public SwerveDriveOdometry( 066 SwerveDriveKinematics kinematics, 067 Rotation2d gyroAngle, 068 SwerveModulePosition[] modulePositions) { 069 this(kinematics, gyroAngle, modulePositions, new Pose2d()); 070 } 071 072 /** 073 * Resets the robot's position on the field. 074 * 075 * <p>The gyroscope angle does not need to be reset here on the user's robot code. The library 076 * automatically takes care of offsetting the gyro angle. 077 * 078 * <p>Similarly, module positions do not need to be reset in user code. 079 * 080 * @param gyroAngle The angle reported by the gyroscope. 081 * @param modulePositions The wheel positions reported by each module., 082 * @param pose The position on the field that your robot is at. 083 */ 084 public void resetPosition( 085 Rotation2d gyroAngle, SwerveModulePosition[] modulePositions, Pose2d pose) { 086 if (modulePositions.length != m_numModules) { 087 throw new IllegalArgumentException( 088 "Number of modules is not consistent with number of wheel locations provided in " 089 + "constructor"); 090 } 091 092 m_poseMeters = pose; 093 m_previousAngle = pose.getRotation(); 094 m_gyroOffset = m_poseMeters.getRotation().minus(gyroAngle); 095 for (int index = 0; index < m_numModules; index++) { 096 m_previousModulePositions[index] = 097 new SwerveModulePosition( 098 modulePositions[index].distanceMeters, modulePositions[index].angle); 099 } 100 } 101 102 /** 103 * Returns the position of the robot on the field. 104 * 105 * @return The pose of the robot (x and y are in meters). 106 */ 107 public Pose2d getPoseMeters() { 108 return m_poseMeters; 109 } 110 111 /** 112 * Updates the robot's position on the field using forward kinematics and integration of the pose 113 * over time. This method automatically calculates the current time to calculate period 114 * (difference between two timestamps). The period is used to calculate the change in distance 115 * from a velocity. This also takes in an angle parameter which is used instead of the angular 116 * rate that is calculated from forward kinematics. 117 * 118 * @param gyroAngle The angle reported by the gyroscope. 119 * @param modulePositions The current position of all swerve modules. Please provide the positions 120 * in the same order in which you instantiated your SwerveDriveKinematics. 121 * @return The new pose of the robot. 122 */ 123 public Pose2d update(Rotation2d gyroAngle, SwerveModulePosition[] modulePositions) { 124 if (modulePositions.length != m_numModules) { 125 throw new IllegalArgumentException( 126 "Number of modules is not consistent with number of wheel locations provided in " 127 + "constructor"); 128 } 129 130 var moduleDeltas = new SwerveModulePosition[m_numModules]; 131 for (int index = 0; index < m_numModules; index++) { 132 var current = modulePositions[index]; 133 var previous = m_previousModulePositions[index]; 134 135 moduleDeltas[index] = 136 new SwerveModulePosition(current.distanceMeters - previous.distanceMeters, current.angle); 137 previous.distanceMeters = current.distanceMeters; 138 } 139 140 var angle = gyroAngle.plus(m_gyroOffset); 141 142 var twist = m_kinematics.toTwist2d(moduleDeltas); 143 twist.dtheta = angle.minus(m_previousAngle).getRadians(); 144 145 var newPose = m_poseMeters.exp(twist); 146 147 m_previousAngle = angle; 148 m_poseMeters = new Pose2d(newPose.getTranslation(), angle); 149 150 return m_poseMeters; 151 } 152}