1
+ // Copyright (c) FIRST and other WPILib contributors.
2
+ // Open Source Software; you can modify and/or share it under the terms of
3
+ // the WPILib BSD license file in the root directory of this project.
4
+
5
+ package frc .robot .subsystems ;
6
+
7
+ import java .util .Optional ;
8
+
9
+ import org .photonvision .targeting .PhotonTrackedTarget ;
10
+
11
+ import com .nrg948 .preferences .RobotPreferences ;
12
+ import com .nrg948 .preferences .RobotPreferencesLayout ;
13
+ import com .nrg948 .preferences .RobotPreferencesValue ;
14
+
15
+ import edu .wpi .first .cscore .HttpCamera ;
16
+ import edu .wpi .first .cscore .HttpCamera .HttpCameraKind ;
17
+ import edu .wpi .first .cscore .VideoSource ;
18
+ import edu .wpi .first .math .estimator .SwerveDrivePoseEstimator ;
19
+ import edu .wpi .first .math .geometry .Pose3d ;
20
+ import edu .wpi .first .math .geometry .Rotation3d ;
21
+ import edu .wpi .first .math .geometry .Transform3d ;
22
+ import edu .wpi .first .math .geometry .Translation3d ;
23
+ import edu .wpi .first .util .datalog .DoubleLogEntry ;
24
+ import edu .wpi .first .wpilibj .DataLogManager ;
25
+ import edu .wpi .first .wpilibj .shuffleboard .BuiltInLayouts ;
26
+ import edu .wpi .first .wpilibj .shuffleboard .BuiltInWidgets ;
27
+ import edu .wpi .first .wpilibj .shuffleboard .Shuffleboard ;
28
+ import edu .wpi .first .wpilibj .shuffleboard .ShuffleboardLayout ;
29
+ import edu .wpi .first .wpilibj .shuffleboard .ShuffleboardTab ;
30
+ import edu .wpi .first .wpilibj .smartdashboard .SendableChooser ;
31
+ import frc .robot .Constants .RobotConstants ;
32
+
33
+ /**
34
+ * This subsystem is responsible for getting target information from
35
+ * PhotonVision.
36
+ */
37
+
38
+ @ RobotPreferencesLayout (groupName = "AprilTag" , row = 1 , column = 4 , width = 2 , height = 1 )
39
+ public class AprilTagSubsystem extends PhotonVisionSubsystemBase {
40
+
41
+ @ RobotPreferencesValue
42
+ public static final RobotPreferences .BooleanValue enableTab = new RobotPreferences .BooleanValue (
43
+ "AprilTag" , "Enable Tab" , false );
44
+
45
+ private DoubleLogEntry targetXLogger = new DoubleLogEntry (DataLogManager .getLog (), "AprilTag/Target X" );
46
+ private DoubleLogEntry targetYLogger = new DoubleLogEntry (DataLogManager .getLog (), "AprilTag/Target Y" );
47
+ private DoubleLogEntry targetAngleLogger = new DoubleLogEntry (DataLogManager .getLog (), "AprilTag/Target Angle" );
48
+
49
+ private SendableChooser <Integer > aprilTagIdChooser = new SendableChooser <Integer >();
50
+
51
+ /** Creates a new PhotonVisionSubsystem. */
52
+ public AprilTagSubsystem () {
53
+ super ("AprilTagCamera" , RobotConstants .APRILTAG_CAMERA_TO_ROBOT );
54
+
55
+ for (int i = 1 ; i <= 16 ; i ++) {
56
+ aprilTagIdChooser .addOption (String .valueOf (i ), i );
57
+ }
58
+ }
59
+
60
+ @ Override
61
+ public void updatePoseEstimate (SwerveDrivePoseEstimator estimator , Pose3d targetPose ) {
62
+ if (hasTargets ()) {
63
+ Transform3d cameraToRobot = getCameraToRobotTransform ();
64
+ Transform3d targetToCamera = new Transform3d (
65
+ new Translation3d (
66
+ getDistanceToBestTarget (),
67
+ new Rotation3d (0 , 0 , Math .toRadians (-getAngleToBestTarget ()))),
68
+ cameraToRobot .getRotation ()).inverse ();
69
+ Pose3d cameraPose = targetPose .transformBy (targetToCamera );
70
+ Pose3d robotPose = cameraPose .transformBy (cameraToRobot );
71
+
72
+ estimator .addVisionMeasurement (robotPose .toPose2d (), getTargetTimestamp ());
73
+ }
74
+
75
+ targetXLogger .append (targetPose .getX ());
76
+ targetYLogger .append (targetPose .getY ());
77
+ targetAngleLogger .append (Math .toRadians (targetPose .getRotation ().getAngle ()));
78
+ }
79
+
80
+ /**
81
+ * Returns the AprilTag target of the input ID.
82
+ *
83
+ * @param id The AprilTag ID.
84
+ * @return The target with the input ID.
85
+ */
86
+ public Optional <PhotonTrackedTarget > getTarget (int id ) {
87
+ return getTargets ().stream ().filter (target -> target .getFiducialId () == id ).findFirst ();
88
+ }
89
+
90
+ /**
91
+ * Returns the transform from the camera to the AprilTag with input ID.
92
+ * @param id The AprilTag ID.
93
+ * @return The transform from the camera to the AprilTag with input ID.
94
+ */
95
+ public Transform3d getCameraToTarget (int id ) {
96
+ return getTarget (id ).get ().getBestCameraToTarget ();
97
+ }
98
+
99
+ /**
100
+ * Returns the distance to the target with the input ID. Returns 0 if target not found.
101
+ *
102
+ * @param id The AprilTag ID.
103
+ * @return The distance to the target with the input ID.
104
+ */
105
+ public double getDistanceToTarget (int id ) {
106
+ Optional <PhotonTrackedTarget > target = getTarget (id );
107
+ if (target .isEmpty ()) {
108
+ return 0.0 ;
109
+ }
110
+ var bestCameraToTarget = target .get ().getBestCameraToTarget ();
111
+ return Math .hypot (bestCameraToTarget .getX (), bestCameraToTarget .getY ());
112
+ }
113
+
114
+ /**
115
+ * Returns the angle to the target with the input ID. Returns 0 if target not found.
116
+ *
117
+ * @param id The AprilTag ID.
118
+ * @return The angle to the target with the input ID.
119
+ */
120
+ public double getAngleToTarget (int id ) {
121
+ Optional <PhotonTrackedTarget > target = getTarget (id );
122
+ if (target .isEmpty ()) {
123
+ return 0.0 ;
124
+ }
125
+ return target .get ().getYaw ();
126
+ }
127
+
128
+ /**
129
+ * Adds a tab for April Tag in Shuffleboard.
130
+ */
131
+ public void addShuffleboardTab () {
132
+ if (!enableTab .getValue ()) {
133
+ return ;
134
+ }
135
+
136
+ ShuffleboardTab visionTab = Shuffleboard .getTab ("April Tag" );
137
+ ShuffleboardLayout targetLayout = visionTab .getLayout ("Target Info" , BuiltInLayouts .kList )
138
+ .withPosition (0 , 0 )
139
+ .withSize (2 , 5 );
140
+ targetLayout .add ("Id Selection" , aprilTagIdChooser ).withWidget (BuiltInWidgets .kComboBoxChooser );
141
+ targetLayout .addBoolean ("Has Target" , this ::hasTargets );
142
+ targetLayout .addDouble ("Distance" , () -> getDistanceToTarget (aprilTagIdChooser .getSelected ()));
143
+ targetLayout .addDouble ("Angle" , () -> getAngleToTarget (aprilTagIdChooser .getSelected ()));
144
+
145
+ VideoSource video = new HttpCamera ("photonvision_Port_1182_MJPEG_Server" , "http://10.9.48.11:1182/?action=stream" ,
146
+ HttpCameraKind .kMJPGStreamer );
147
+ visionTab .add ("April Tag" , video )
148
+ .withWidget (BuiltInWidgets .kCameraStream )
149
+ .withPosition (2 , 0 )
150
+ .withSize (4 , 3 );
151
+ }
152
+ }
0 commit comments