11package frc .robot .subsystems ;
22
33import static frc .robot .Constants .*;
4+ import static frc .robot .Constants .VisionConstants .*;
45
56import java .util .HashMap ;
67import java .util .Map ;
@@ -35,13 +36,6 @@ public class PoseEstimationSubsystem extends SubsystemBase {
3536 private final Map <PhotonCamera , PhotonPoseEstimator > m_cameras = new HashMap <PhotonCamera , PhotonPoseEstimator >();
3637 private final DriveSubsystem m_driveSubsystem ;
3738
38- /**
39- * Standard deviations of the pose estimate (x position in meters, y position in
40- * meters, and heading in radians). Increase these numbers to trust your state
41- * estimate less.
42- */
43- private static final Vector <N3 > stateStdDevs = VecBuilder .fill (0.05 , 0.05 , Units .degreesToRadians (5 ));
44-
4539 /**
4640 * Standard deviations of the vision pose measurement (x position in meters, y
4741 * position in meters, and heading in radians). Smaller values will cause
@@ -72,7 +66,7 @@ public PoseEstimationSubsystem(DriveSubsystem driveSubsystem) {
7266 driveSubsystem .getHeading (),
7367 driveSubsystem .getModulePositions (),
7468 new Pose2d (),
75- stateStdDevs ,
69+ kStateStdDevs ,
7670 visionMeasurementStdDevs );
7771 m_detectedPosePublisher = NetworkTableInstance .getDefault ()
7872 .getStructTopic ("/SmartDashboard/Pose@PhotonPoseEstimator" , Pose2d .struct )
@@ -107,7 +101,7 @@ public void periodic() {
107101 for (var camEntry : m_cameras .entrySet ()) {
108102 var camera = camEntry .getKey ();
109103 var poseEstimator = camEntry .getValue ();
110- for (var result : camera .getAllUnreadResults ()) { // for every result r
104+ for (var result : camera .getAllUnreadResults ()) { // for every result
111105 if (firstCamera || result .getTargets ().size () > 1 ) { // TODO: use both camera results equally, don't
112106 // prioritize cam1
113107 Optional <EstimatedRobotPose > pose = poseEstimator .update (result );
@@ -149,67 +143,54 @@ public Pose2d targetOdometryCentricPose(Pose2d fieldCentricPose) {
149143 }
150144
151145 /**
152- * Finds the {@code Pose2d} of the {@code AprilTag} that is closest to the
153- * robot.
154- * Returns null if there is no such {@code AprilTag}).
155- *
156- * @param angleOfCoverageInDegrees the angular coverage (in degrees) within
157- * which {@code AprilTag}s are considered (maximum: 180)
158- * @param distanceThresholdInMeters the maximum distance (in meters) within
159- * which {@code AprilTag}s are considered
160- * @return the {@code Pose2d} of the {@code AprilTag} that is closest to the
161- * robot ({@code null} if no such {@code AprilTag})
162- */
163- public Pose2d closestTagPose (double angleOfCoverageInDegrees , double distanceThresholdInMeters ) {
164- var i = closestTagID (getEstimatedPose (), angleOfCoverageInDegrees , distanceThresholdInMeters );
165- return i == null ? null : kFieldLayout .getTagPose (i ).get ().toPose2d ();
166- }
167-
168- /**
169- * Finds the {@code Pose2d} of the {@code AprilTag} that is closest (in terms of
170- * angular displacement) to the robot when the specified {@code Translation2d}
171- * is applied to the robot.
146+ * Finds the pose of the {@code AprilTag} that is closest to the robot, either
147+ * in terms of distance or angle (based on user selection).
172148 *
173- * @param translation a {@code Translation2d}
149+ * @param angleOfCoverage the angular coverage (in degrees) within
150+ * which AprilTags are considered (maximum: 180)
174151 * @param distanceThresholdInMeters the maximum distance (in meters) within
175- * which {@code AprilTag}s are considered
176- * @return the {@code Pose2d} of the {@code AprilTag} that is closest (in terms
177- * of angular displacement) to the robot when the specified
178- * {@code Translation2d} is applied to the robot
152+ * which AprilTags are considered
153+ * @param isClosestByDistance true for distance-based tag filtering.
154+ * false for angle-based tag filtering.
155+ * @return the {@code Pose2d} of the {@code AprilTag} closest to the robot.
156+ * Returns null if there is no such AprilTag.
157+ * @apiNote
179158 */
180- public Pose2d closestTagPose (Translation2d translation , double distanceThresholdInMeters ) {
181- var pose = getEstimatedPose ();
182- if (translation .getNorm () > 0 )
183- pose = new Pose2d (pose .getTranslation (), translation .getAngle ());
184- var i = closestTagID (pose , distanceThresholdInMeters );
185- return i == null ? null : kFieldLayout .getTagPose (i ).get ().toPose2d ();
159+ public Pose2d closestTagPose (Optional <Double > angleOfCoverage , double distanceThresholdInMeters ,
160+ boolean isClosestByDistance ) {
161+ var id = (isClosestByDistance )
162+ ? distanceClosestTagID ( // returns ID of tag closest in terms of distance
163+ getEstimatedPose (), distanceThresholdInMeters , angleOfCoverage )
164+ : angleClosestTagID ( // returns ID of tag closest in terms of angular displacement
165+ getEstimatedPose (), distanceThresholdInMeters , angleOfCoverage );
166+ return id == null ? null : kFieldLayout .getTagPose (id ).get ().toPose2d ();
186167 }
187168
188169 /**
189170 * Determines the ID of the {@code AprilTag} that is closest to the specified
190171 * {@code Pose2d} ({@code null} if no such {@code AprilTag}).
191172 *
192173 * @param pose a {@code Pose2d}
193- * @param angleOfCoverageInDegrees the angular coverage (in degrees) within
174+ * @param angleOfCoverage the angular coverage (in degrees) within
194175 * which {@code AprilTag}s are considered (maximum: 180)
195176 * @param distanceThresholdInMeters the maximum distance (in meters) within
196177 * which {@code AprilTag}s are considered
197178 * @return the ID of the {@code AprilTag} that is closest to the specified
198179 * {@code Pose2d} ({@code null} if no such {@code AprilTag})
199180 */
200- public static Integer closestTagID (Pose2d pose , double angleOfCoverageInDegrees , double distanceThresholdInMeters ) {
201- var s = kFieldLayout .getTags ().stream ()
202- // consider only the tags facing toward the robot
203- .filter (
204- tag -> Math .abs (
205- tag .pose .getTranslation ().toTranslation2d ().minus (pose .getTranslation ()).getAngle ()
206- .minus (tag .pose .toPose2d ().getRotation ()).getDegrees ()) > 90 )
207- .filter ( // consider only the tags within the angle of coverage
208- tag -> Math .abs (
209- angularDisplacement (pose , tag .pose .toPose2d ()).getDegrees ()) < angleOfCoverageInDegrees )
210- .map (tag -> Map .entry (tag .ID , Math .abs (translationalDisplacement (pose , tag .pose .toPose2d ())))) // distance
211- .filter (tag -> tag .getValue () < distanceThresholdInMeters ); // only tags sufficently close
181+ public static Integer distanceClosestTagID (Pose2d pose , double distanceThresholdInMeters ,
182+ Optional <Double > angleOfCoverage ) {
183+ var s = kFieldLayout .getTags ().stream ();
184+ if (angleOfCoverage .isPresent ()) {
185+ s .filter (
186+ tag -> (translationalDisplacement (pose , tag .pose .toPose2d ()) < distanceThresholdInMeters
187+ && translationalDisplacement (pose , tag .pose .toPose2d ()) > 0 ));
188+ } else {
189+ s .filter (tag -> Math .abs (angularDisplacement (pose , tag .pose .toPose2d ()).getDegrees ()) < 90 );
190+ }
212191 Optional <Entry <Integer , Double >> closest = s
192+ .map (tag -> Map .entry (tag .ID , Math .abs (translationalDisplacement (pose , tag .pose .toPose2d ()))))
193+ .filter (tag -> tag .getValue () < distanceThresholdInMeters )
213194 .reduce ((tag1 , tag2 ) -> tag1 .getValue () < tag2 .getValue () ? tag1 : tag2 );
214195 if (closest .isPresent ()) {
215196 return closest .get ().getKey ();
@@ -225,19 +206,25 @@ public static Integer closestTagID(Pose2d pose, double angleOfCoverageInDegrees,
225206 * @param pose a {@code Pose2d}
226207 * @param distanceThresholdInMeters the maximum distance (in meters) within
227208 * which {@code AprilTag}s are considered
209+ * @param angleOfCoverage The angle of coverage (in degrees) TODO
228210 * @return the ID of the {@code AprilTag} that is closest (in terms of angular
229211 * displacement) to the specified {@code Pose2d} ({@code null} if no
230212 * such {@code AprilTag})
231213 */
232- public static Integer closestTagID (Pose2d pose , double distanceThresholdInMeters ) {
233- var s = kFieldLayout .getTags ().stream ()
234- // consider only the tags facing toward the robot
235- .filter (
236- tag -> (translationalDisplacement (pose , tag .pose .toPose2d ()) < distanceThresholdInMeters
237- && translationalDisplacement (pose , tag .pose .toPose2d ()) > 0 ))
238- // only tags sufficently close
239- .map (tag -> Map .entry (tag .ID , Math .abs (angularDisplacement (pose , tag .pose .toPose2d ()).getDegrees ())));
214+ public static Integer angleClosestTagID (Pose2d pose , double distanceThresholdInMeters ,
215+ Optional <Double > angleOfCoverage ) {
216+ var s = kFieldLayout .getTags ().stream ();
217+ if (angleOfCoverage .isPresent ()) {
218+ s .filter (
219+ tag -> Math
220+ .abs (angularDisplacement (pose , tag .pose .toPose2d ()).getDegrees ()) < angleOfCoverage
221+ .get ());
222+ } else {
223+ s .filter (tag -> Math .abs (angularDisplacement (pose , tag .pose .toPose2d ()).getDegrees ()) < 90 );
224+ }
240225 Optional <Entry <Integer , Double >> closest = s
226+ .map (tag -> Map .entry (tag .ID , Math .abs (translationalDisplacement (pose , tag .pose .toPose2d ()))))
227+ .filter (tag -> tag .getValue () < distanceThresholdInMeters )
241228 .reduce ((tag1 , tag2 ) -> tag1 .getValue () < tag2 .getValue () ? tag1 : tag2 );
242229 if (closest .isPresent ()) {
243230 return closest .get ().getKey ();
0 commit comments