1515import java .util .List ;
1616import org .littletonrobotics .junction .Logger ;
1717
18+ /**
19+ * Vision subsystem responsible for processing camera inputs and providing pose observations
20+ * Bascically stolen from Pioneer's code but modified for number of cameras and camera positions
21+ */
1822public class Vision extends SubsystemBase {
1923
20- // Akit my savior
24+ // Vision IO instances and inputs
2125 private final VisionIO [] io ;
2226 private final VisionIOInputsAutoLogged [] inputs ;
2327 private final Alert [] disconnectedAlerts ;
@@ -44,11 +48,7 @@ public static Vision getInstance() {
4448 Drive .getInstance ()::getPose
4549 ),
4650 };
47- case TESTING -> new VisionIO [] {
48- new VisionIOPhotonVision (FRONT_LEFT_CAM_NAME , ROBOT_TO_FRONT_LEFT_CAMERA ),
49- new VisionIOPhotonVision (FRONT_RIGHT_CAM_NAME , ROBOT_TO_FRONT_RIGHT_CAMERA ),
50- };
51- case REPLAY -> new VisionIO [] {
51+ case TESTING , REPLAY -> new VisionIO [] {
5252 new VisionIOPhotonVision (FRONT_LEFT_CAM_NAME , ROBOT_TO_FRONT_LEFT_CAMERA ),
5353 new VisionIOPhotonVision (FRONT_RIGHT_CAM_NAME , ROBOT_TO_FRONT_RIGHT_CAMERA ),
5454 };
@@ -71,27 +71,22 @@ private Vision(VisionIO... io) {
7171 this .disconnectedAlerts = new Alert [io .length ];
7272 for (int i = 0 ; i < inputs .length ; i ++) {
7373 disconnectedAlerts [i ] = new Alert (
74- "Vision camera " + Integer . toString ( i ) + " is disconnected." ,
74+ "Vision camera " + i + " is disconnected." ,
7575 AlertType .kWarning
7676 );
7777 }
7878 }
7979
80- /**
81- * Returns the X angle to the best target, which can be used for simple servoing
82- * with vision.
83- *
84- * @param cameraIndex The index of the camera to use.
85- */
8680 public Rotation2d getTargetX (int cameraIndex ) {
8781 return inputs [cameraIndex ].latestTargetObservation .tx ();
8882 }
8983
9084 @ Override
9185 public void periodic () {
86+ // Update inputs and log data for each camera
9287 for (int i = 0 ; i < io .length ; i ++) {
9388 io [i ].updateInputs (inputs [i ]);
94- Logger .processInputs ("Vision/Camera" + Integer . toString ( i ) , inputs [i ]);
89+ Logger .processInputs ("Vision/Camera" + i , inputs [i ]);
9590 }
9691
9792 // Initialize logging values
@@ -100,12 +95,12 @@ public void periodic() {
10095 List <Pose3d > allRobotPosesAccepted = new LinkedList <>();
10196 List <Pose3d > allRobotPosesRejected = new LinkedList <>();
10297
103- // Loop over cameras
98+ // Process data for each camera
10499 for (int cameraIndex = 0 ; cameraIndex < io .length ; cameraIndex ++) {
105- // Update disconnected alert
100+ // Update disconnected alerts
106101 disconnectedAlerts [cameraIndex ].set (!inputs [cameraIndex ].connected );
107102
108- // Initialize logging values
103+ // Initialize logging values for this camera
109104 List <Pose3d > tagPoses = new LinkedList <>();
110105 List <Pose3d > robotPoses = new LinkedList <>();
111106 List <Pose3d > robotPosesAccepted = new LinkedList <>();
@@ -114,43 +109,15 @@ public void periodic() {
114109 // Add tag poses
115110 for (int tagId : inputs [cameraIndex ].tagIds ) {
116111 var tagPose = APRIL_TAG_FIELD_LAYOUT .getTagPose (tagId );
117- if (tagPose .isPresent ()) {
118- tagPoses .add (tagPose .get ());
119- }
112+ tagPose .ifPresent (tagPoses ::add );
120113 }
121114
122- // Loop over pose observations
115+ // Process pose observations
123116 for (var observation : inputs [cameraIndex ].poseObservations ) {
124- // Check whether to reject pose
125- boolean rejectPose =
126- observation .tagCount () == 0 || // Must have at least one tag
127- (observation .tagCount () == 1 && observation .ambiguity () > maxAmbiguity ) || // Cannot be high ambiguity
128- Math .abs (observation .pose ().getZ ()) > maxZError || // Must have realistic Z coordinate
129- // Must be within the field boundaries
130- observation .pose ().getX () <
131- 0.0 ||
132- observation .pose ().getX () > APRIL_TAG_FIELD_LAYOUT .getFieldLength () ||
133- observation .pose ().getY () < 0.0 ||
134- observation .pose ().getY () > APRIL_TAG_FIELD_LAYOUT .getFieldWidth ();
135-
136- Logger .recordOutput (
137- "Vision/Camera" + Integer .toString (cameraIndex ) + "/Tag Count" ,
138- observation .tagCount () == 0
139- );
140- Logger .recordOutput (
141- "Vision/Camera" + Integer .toString (cameraIndex ) + "/Ambiguous" ,
142- (observation .tagCount () == 1 && observation .ambiguity () > maxAmbiguity )
143- );
144- Logger .recordOutput (
145- "Vision/Camera" + Integer .toString (cameraIndex ) + "/Outside of Field X" ,
146- observation .pose ().getX () < 0.0 ||
147- observation .pose ().getX () > APRIL_TAG_FIELD_LAYOUT .getFieldLength ()
148- );
149- Logger .recordOutput (
150- "Vision/Camera" + Integer .toString (cameraIndex ) + "/Outside of Field Y" ,
151- observation .pose ().getY () < 0.0 ||
152- observation .pose ().getY () > APRIL_TAG_FIELD_LAYOUT .getFieldWidth ()
153- );
117+ boolean rejectPose = shouldRejectPose (observation );
118+
119+ // Log rejection reasons
120+ logRejectionReasons (cameraIndex , observation );
154121
155122 // Add pose to log
156123 robotPoses .add (observation .pose ());
@@ -166,68 +133,122 @@ public void periodic() {
166133 }
167134
168135 // Calculate standard deviations
169- double stdDevFactor =
170- Math .pow (observation .averageTagDistance (), 2.0 ) / observation .tagCount ();
171- double linearStdDev = linearStdDevBaseline * stdDevFactor ;
172- double angularStdDev = angularStdDevBaseline * stdDevFactor ;
173- if (observation .type () == PoseObservationType .MEGATAG_2 ) {
174- linearStdDev *= linearStdDevMegatag2Factor ;
175- angularStdDev *= angularStdDevMegatag2Factor ;
176- }
177- if (cameraIndex < cameraStdDevFactors .length ) {
178- linearStdDev *= cameraStdDevFactors [cameraIndex ];
179- angularStdDev *= cameraStdDevFactors [cameraIndex ];
180- }
136+ double [] stdDevs = calculateStandardDeviations (cameraIndex , observation );
181137
182138 // Send vision observation
183139 Drive .getInstance ()
184140 .addVisionMeasurement (
185141 observation .pose ().toPose2d (),
186142 observation .timestamp (),
187- VecBuilder .fill (linearStdDev , linearStdDev , angularStdDev )
143+ VecBuilder .fill (stdDevs [ 0 ], stdDevs [ 0 ], stdDevs [ 1 ] )
188144 );
189145 }
190146
191- // Log camera datadata
192- Logger .recordOutput (
193- "Vision/Camera" + Integer .toString (cameraIndex ) + "/TagPoses" ,
194- tagPoses .toArray (new Pose3d [tagPoses .size ()])
195- );
196- Logger .recordOutput (
197- "Vision/Camera" + Integer .toString (cameraIndex ) + "/RobotPoses" ,
198- robotPoses .toArray (new Pose3d [robotPoses .size ()])
199- );
200- Logger .recordOutput (
201- "Vision/Camera" + Integer .toString (cameraIndex ) + "/RobotPosesAccepted" ,
202- robotPosesAccepted .toArray (new Pose3d [robotPosesAccepted .size ()])
203- );
204- Logger .recordOutput (
205- "Vision/Camera" + Integer .toString (cameraIndex ) + "/RobotPosesRejected" ,
206- robotPosesRejected .toArray (new Pose3d [robotPosesRejected .size ()])
207- );
147+ // Log camera data
148+ logCameraData (cameraIndex , tagPoses , robotPoses , robotPosesAccepted , robotPosesRejected );
208149
150+ // Aggregate data
209151 allTagPoses .addAll (tagPoses );
210152 allRobotPoses .addAll (robotPoses );
211153 allRobotPosesAccepted .addAll (robotPosesAccepted );
212154 allRobotPosesRejected .addAll (robotPosesRejected );
213155 }
214156
215157 // Log summary data
158+ logSummaryData (allTagPoses , allRobotPoses , allRobotPosesAccepted , allRobotPosesRejected );
159+ }
160+
161+
162+ // Determines whether a pose observation should be rejected.
163+ private boolean shouldRejectPose (VisionIO .PoseObservation observation ) {
164+ return observation .tagCount () == 0 ||
165+ (observation .tagCount () == 1 && observation .ambiguity () > maxAmbiguity ) ||
166+ Math .abs (observation .pose ().getZ ()) > maxZError ||
167+ observation .pose ().getX () < 0.0 ||
168+ observation .pose ().getX () > APRIL_TAG_FIELD_LAYOUT .getFieldLength () ||
169+ observation .pose ().getY () < 0.0 ||
170+ observation .pose ().getY () > APRIL_TAG_FIELD_LAYOUT .getFieldWidth ();
171+ }
172+
173+ // Adds rejection reasons to logs
174+ private void logRejectionReasons (int cameraIndex , VisionIO .PoseObservation observation ) {
175+ Logger .recordOutput (
176+ "Vision/Camera" + cameraIndex + "/Tag Count" ,
177+ observation .tagCount () == 0
178+ );
179+ Logger .recordOutput (
180+ "Vision/Camera" + cameraIndex + "/Ambiguous" ,
181+ (observation .tagCount () == 1 && observation .ambiguity () > maxAmbiguity )
182+ );
183+ Logger .recordOutput (
184+ "Vision/Camera" + cameraIndex + "/Outside of Field X" ,
185+ observation .pose ().getX () < 0.0 ||
186+ observation .pose ().getX () > APRIL_TAG_FIELD_LAYOUT .getFieldLength ()
187+ );
188+ Logger .recordOutput (
189+ "Vision/Camera" + cameraIndex + "/Outside of Field Y" ,
190+ observation .pose ().getY () < 0.0 ||
191+ observation .pose ().getY () > APRIL_TAG_FIELD_LAYOUT .getFieldWidth ()
192+ );
193+ }
194+
195+ // Calculates standard deviations for a pose observation.
196+ private double [] calculateStandardDeviations (int cameraIndex , VisionIO .PoseObservation observation ) {
197+ double stdDevFactor = Math .pow (observation .averageTagDistance (), 2.0 ) / observation .tagCount ();
198+ double linearStdDev = linearStdDevBaseline * stdDevFactor ;
199+ double angularStdDev = angularStdDevBaseline * stdDevFactor ;
200+ if (observation .type () == PoseObservationType .MEGATAG_2 ) {
201+ linearStdDev *= linearStdDevMegatag2Factor ;
202+ angularStdDev *= angularStdDevMegatag2Factor ;
203+ }
204+ if (cameraIndex < cameraStdDevFactors .length ) {
205+ linearStdDev *= cameraStdDevFactors [cameraIndex ];
206+ angularStdDev *= cameraStdDevFactors [cameraIndex ];
207+ }
208+ return new double [] { linearStdDev , angularStdDev };
209+ }
210+
211+
212+ // Logs camera-specific data.
213+ private void logCameraData (int cameraIndex , List <Pose3d > tagPoses , List <Pose3d > robotPoses ,
214+ List <Pose3d > robotPosesAccepted , List <Pose3d > robotPosesRejected ) {
215+ Logger .recordOutput (
216+ "Vision/Camera" + cameraIndex + "/TagPoses" ,
217+ tagPoses .toArray (new Pose3d [0 ])
218+ );
219+ Logger .recordOutput (
220+ "Vision/Camera" + cameraIndex + "/RobotPoses" ,
221+ robotPoses .toArray (new Pose3d [0 ])
222+ );
223+ Logger .recordOutput (
224+ "Vision/Camera" + cameraIndex + "/RobotPosesAccepted" ,
225+ robotPosesAccepted .toArray (new Pose3d [0 ])
226+ );
227+ Logger .recordOutput (
228+ "Vision/Camera" + cameraIndex + "/RobotPosesRejected" ,
229+ robotPosesRejected .toArray (new Pose3d [0 ])
230+ );
231+ }
232+
233+
234+ // Logs summary data for all cameras.
235+ private void logSummaryData (List <Pose3d > allTagPoses , List <Pose3d > allRobotPoses ,
236+ List <Pose3d > allRobotPosesAccepted , List <Pose3d > allRobotPosesRejected ) {
216237 Logger .recordOutput (
217238 "Vision/Summary/TagPoses" ,
218- allTagPoses .toArray (new Pose3d [allTagPoses . size () ])
239+ allTagPoses .toArray (new Pose3d [0 ])
219240 );
220241 Logger .recordOutput (
221242 "Vision/Summary/RobotPoses" ,
222- allRobotPoses .toArray (new Pose3d [allRobotPoses . size () ])
243+ allRobotPoses .toArray (new Pose3d [0 ])
223244 );
224245 Logger .recordOutput (
225246 "Vision/Summary/RobotPosesAccepted" ,
226- allRobotPosesAccepted .toArray (new Pose3d [allRobotPosesAccepted . size () ])
247+ allRobotPosesAccepted .toArray (new Pose3d [0 ])
227248 );
228249 Logger .recordOutput (
229250 "Vision/Summary/RobotPosesRejected" ,
230- allRobotPosesRejected .toArray (new Pose3d [allRobotPosesRejected . size () ])
251+ allRobotPosesRejected .toArray (new Pose3d [0 ])
231252 );
232253 }
233254}
0 commit comments