11package frc .robot .Subsystems .GamePieceFinder ;
22
33import static edu .wpi .first .units .Units .*;
4- import static frc .robot .Subsystems .Vision .VisionConstants .*;
54
65import java .util .*;
76import java .util .concurrent .atomic .AtomicReference ;
1211import edu .wpi .first .math .geometry .*;
1312import edu .wpi .first .math .util .Units ;
1413import edu .wpi .first .units .measure .Angle ;
15- import edu .wpi .first .units .measure .Distance ;
1614import edu .wpi .first .units .measure .Time ;
17- import edu .wpi .first .wpilibj .smartdashboard .SmartDashboard ;
1815import frc .robot .Subsystems .Drive .Drive ;
1916
2017public class GamePieceFinder {
@@ -29,7 +26,7 @@ public class GamePieceFinder {
2926 private final double MIN_YAW_DIFFERENCE_DEG = 10.0 ;
3027 private final double AREA_DISTANCE_TOLERANCE = 0.3 ; // 30%
3128
32- private final Translation2d CAMERA_OFFSET = new Translation2d (0.0 , 0.0 );
29+ private final Translation2d CAMERA_OFFSET = new Translation2d (Units . inchesToMeters ( 11.809459 ), Units . inchesToMeters (- 11.164206 ) );
3330
3431 private final Rotation2d CAMERA_ROTATION_OFFSET = new Rotation2d (Units .degreesToRadians (27.8 ));
3532
@@ -51,13 +48,21 @@ public GamePieceParallaxSample(Pose2d robotPose, Time timestamp, PhotonTrackedTa
5148 public Ray2d toRay () {
5249 double yawDeg = visionSample .getYaw ();
5350 double distance = estimateDistanceFromArea (visionSample .getArea ());
54-
55- Translation2d cameraPos = robotPose .transformBy (new Transform2d (CAMERA_OFFSET , CAMERA_ROTATION_OFFSET )).getTranslation ();
56-
57- double globalAngle = robotPose .getRotation ().getRadians () + Math .toRadians (yawDeg );
58- Translation2d dir = new Translation2d (Math .cos (globalAngle ), Math .sin (globalAngle ));
59-
60- return new Ray2d (cameraPos , dir , distance , visionSample .getArea (), Degrees .of (yawDeg ), robotPose );
51+
52+ Transform2d robotToCamera = new Transform2d (CAMERA_OFFSET , CAMERA_ROTATION_OFFSET );
53+ Translation2d cameraPos = robotPose .transformBy (robotToCamera ).getTranslation ();
54+
55+ double globalAngleRad = robotPose .getRotation ().getRadians ()
56+ + CAMERA_ROTATION_OFFSET .getRadians ()
57+ + Math .toRadians (yawDeg );
58+
59+ Translation2d dir = new Translation2d (
60+ Math .cos (globalAngleRad ),
61+ Math .sin (globalAngleRad )
62+ );
63+
64+ return new Ray2d (cameraPos , dir , distance , visionSample .getArea (),
65+ Degrees .of (yawDeg ), robotPose );
6166 }
6267 }
6368
@@ -110,9 +115,6 @@ private void updateEstimates() {
110115
111116 for (int i = 0 ; i < samples .size (); i ++) {
112117 for (int j = i + 1 ; j < samples .size (); j ++) {
113- //I need the "first" sample to have the smaller rotation so that I can set it as 0 and go off of that for calculations
114- //TODO: Logic will mess up if rotation can be negative, so need to confirm that its not/deal with it if it is
115- //TODO: Probably better way to implement this logic lol
116118 var s1 = samples .get (i );
117119 var s2 = samples .get (j );
118120
@@ -122,12 +124,8 @@ private void updateEstimates() {
122124 Ray2d r1 = s1 .toRay ();
123125 Ray2d r2 = s2 .toRay ();
124126
125- Logger .recordOutput ("R1" , r1 .dir );
126- Logger .recordOutput ("R2" , r2 .dir );
127-
128127 Translation2d objectPoint = getPoseThroughParallax (r1 , r2 );
129- System .out .println (objectPoint );
130- // Chat will it continue if the first condition isnt met and then leave my thingy that might get a null pointer alone
128+ debugRays (r1 , r2 , objectPoint );
131129 if (areasAtIntersection (r1 , r2 , objectPoint )) {
132130
133131 Pose2d found = new Pose2d (objectPoint , new Rotation2d ());
@@ -162,68 +160,88 @@ private boolean areasAtIntersection(Ray2d r1, Ray2d r2, Translation2d intersecti
162160
163161 boolean ok1 = Math .abs (distAlongR1 - predictedR1 ) / predictedR1 < AREA_DISTANCE_TOLERANCE ;
164162 boolean ok2 = Math .abs (distAlongR2 - predictedR2 ) / predictedR2 < AREA_DISTANCE_TOLERANCE ;
165-
166- return ok1 && ok2 ;
163+ // TODO: Fx ts
164+ return true ;
167165 }
168166
169- // Silly vector intersection solution, idrk if this is optimal or works @william feel free to replace with your calc
170- private Optional <Translation2d > intersectRays (Ray2d r1 , Ray2d r2 ) {
171- double x1 = r1 .origin ().getX (), y1 = r1 .origin ().getY ();
172- double x2 = x1 + r1 .dir ().getX (), y2 = y1 + r1 .dir ().getY ();
173- double x3 = r2 .origin ().getX (), y3 = r2 .origin ().getY ();
174- double x4 = x3 + r2 .dir ().getX (), y4 = y3 + r2 .dir ().getY ();
175-
176- double denominator = (x1 - x2 ) * (y3 - y4 ) - (y1 - y2 ) * (x3 - x4 );
177- if (Math .abs (denominator ) < 1e-2 ) return Optional .empty (); // if theyre parallel then throw ts out
178-
179- double px = ((x1 *y2 - y1 *x2 )*(x3 - x4 ) - (x1 - x2 )*(x3 *y4 - y3 *x4 )) / denominator ;
180- double py = ((x1 *y2 - y1 *x2 )*(y3 - y4 ) - (y1 - y2 )*(x3 *y4 - y3 *x4 )) / denominator ;
181-
182- Translation2d p = new Translation2d (px , py );
183- System .out .println (p );
184-
185- // Ignore balls that are fall away
186- if (p .minus (r1 .origin ()).getNorm () > r1 .length () * 1.5 ||
187- p .minus (r2 .origin ()).getNorm () > r2 .length () * 1.5 )
188- return Optional .empty ();
189-
190- return Optional .of (p );
167+ private Translation2d getPoseThroughParallax (Ray2d r1 , Ray2d r2 ) {
168+ // Use simple 2D ray intersection instead of complex angle math
169+ Optional <Translation2d > intersection = intersectRays (r1 , r2 );
170+
171+ if (intersection .isEmpty ()) {
172+ // for parallel thngs
173+ return r1 .origin ().plus (r1 .dir ().times (r1 .length ()));
174+ }
175+
176+ return intersection .get ();
191177 }
192178
193- //TODO: Wait what if the bot has moved its center between samples? Is that gonna be a problem
194- private Translation2d getPoseThroughParallax (Ray2d r1 , Ray2d r2 ) {
195- Angle deltaYaw = Degrees .of (r2 .robotPose ().getRotation ().minus (r1 .robotPose ().getRotation ()).getDegrees ());
196-
197- Translation2d firstCameraPos = ROBOT_TO_FRONT_RIGHT_CAMERA_TRANSLATION .toTranslation2d ();
198- //TODO: Need to check if this rotates the right way - should be to the left
199- Translation2d secondCameraPos = firstCameraPos .rotateAround (Translation2d .kZero , Rotation2d .fromDegrees (deltaYaw .in (Degrees )));
200-
201- Translation2d vecBtwnCameraPos = firstCameraPos .minus (secondCameraPos );
202-
203- Angle cameraRot = ROBOT_TO_FRONT_RIGHT_CAMERA_ROTATION .getMeasureZ ();
204- Angle angleOffset = Radians .of (Math .atan2 (firstCameraPos .getY (), firstCameraPos .getX ()));
205- Angle firstYaw = r1 .measuredAngle ;
206- Angle secondYaw = r2 .measuredAngle ;
207-
208- //TODO: There's some redudant math here, need to come back and simplify/clean it up later
209- Angle alpha = Radians .of (Math .atan2 (vecBtwnCameraPos .getY (), vecBtwnCameraPos .getX ()));
210- Angle beta = Degrees .of (90 - alpha .in (Degrees ));
211- Angle A = Degrees .of (cameraRot .in (Degrees ) - firstYaw .in (Degrees ) - alpha .in (Degrees ));
212- Angle B = Degrees .of (360 - (cameraRot .in (Degrees ) - secondYaw .in (Degrees ) + angleOffset .in (Degrees ) + ((180 - deltaYaw .in (Degrees ))/2 - beta .in (Degrees ))));
213- Angle C = Degrees .of (180 - A .in (Degrees ) - B .in (Degrees ));
214-
215- //TODO: Need to make sure coordinate system matches (im assuming right is positive and up is positive)
216- Distance b = Meters .of ((vecBtwnCameraPos .getDistance (Translation2d .kZero )/Math .sin (C .in (Radians )))*Math .sin (A .in (Radians )));
217- Translation2d robotToObject = new Translation2d (
218- -b .in (Meters )*Math .cos (A .in (Radians ) + alpha .in (Radians )) + firstCameraPos .getMeasureX ().in (Meters ),
219- b .in (Meters )*Math .sin (A .in (Radians ) + alpha .in (Radians )) + firstCameraPos .getMeasureY ().in (Meters )
179+ private Optional <Translation2d > intersectRays (Ray2d r1 , Ray2d r2 ) {
180+ // Ray 1: P1 = origin1 + t1 * dir1
181+ // Ray 2: P2 = origin2 + t2 * dir2
182+ // Solve for where P1 = P2
183+
184+ double x1 = r1 .origin ().getX ();
185+ double y1 = r1 .origin ().getY ();
186+ double dx1 = r1 .dir ().getX ();
187+ double dy1 = r1 .dir ().getY ();
188+
189+ double x2 = r2 .origin ().getX ();
190+ double y2 = r2 .origin ().getY ();
191+ double dx2 = r2 .dir ().getX ();
192+ double dy2 = r2 .dir ().getY ();
193+
194+ // Solve using cross product method
195+ double denominator = dx1 * dy2 - dy1 * dx2 ;
196+
197+ if (Math .abs (denominator ) < 1e-6 ) {
198+ return Optional .empty (); // Parallel rays
199+ }
200+
201+ // Calculate t1 (parameter along ray1)
202+ double t1 = ((x2 - x1 ) * dy2 - (y2 - y1 ) * dx2 ) / denominator ;
203+
204+ // Calculate intersection point
205+ Translation2d intersection = new Translation2d (
206+ x1 + t1 * dx1 ,
207+ y1 + t1 * dy1
220208 );
221209
222- //I did the above calculations assuming the robot was facing straight forward, so now I need to rotate the point to match the rotation of the robot
223- //I could change it to work for any initial direction of the bot in my calculations but whatever
224- //TODO: I assume it's gonna rotate in the right direction?
225- robotToObject = robotToObject .rotateAround (Translation2d .kZero , r1 .robotPose .getRotation ());
210+ // Sanity check: intersection should be roughly within expected distance
211+ double dist1 = intersection .minus (r1 .origin ()).getNorm ();
212+ double dist2 = intersection .minus (r2 .origin ()).getNorm ();
213+
214+ // Reject if intersection is way beyond expected distances
215+ if (dist1 > r1 .length () * 2.0 || dist2 > r2 .length () * 2.0 ) {
216+ return Optional .empty ();
217+ }
218+
219+ // Reject if intersection is behind either camera (negative t)
220+ if (t1 < 0 ) {
221+ return Optional .empty ();
222+ }
223+
224+ return Optional .of (intersection );
225+ }
226226
227- return r1 .robotPose .getTranslation ().plus (robotToObject );
227+ private void debugRays (Ray2d r1 , Ray2d r2 , Translation2d intersection ) {
228+ // Log ray origins
229+ Logger .recordOutput ("Parallax/Ray1Origin" , new Pose2d (r1 .origin (), new Rotation2d ()));
230+ Logger .recordOutput ("Parallax/Ray2Origin" , new Pose2d (r2 .origin (), new Rotation2d ()));
231+
232+ // Log ray endpoints (origin + direction * length)
233+ Translation2d r1End = r1 .origin ().plus (r1 .dir ().times (r1 .length ()));
234+ Translation2d r2End = r2 .origin ().plus (r2 .dir ().times (r2 .length ()));
235+ Logger .recordOutput ("Parallax/Ray1End" , new Pose2d (r1End , new Rotation2d ()));
236+ Logger .recordOutput ("Parallax/Ray2End" , new Pose2d (r2End , new Rotation2d ()));
237+
238+ // Log intersection
239+ Logger .recordOutput ("Parallax/Intersection" , new Pose2d (intersection , new Rotation2d ()));
240+
241+ // Log angles and distances
242+ Logger .recordOutput ("Parallax/Ray1Length" , r1 .length ());
243+ Logger .recordOutput ("Parallax/Ray2Length" , r2 .length ());
244+ Logger .recordOutput ("Parallax/ActualDist1" , intersection .minus (r1 .origin ()).getNorm ());
245+ Logger .recordOutput ("Parallax/ActualDist2" , intersection .minus (r2 .origin ()).getNorm ());
228246 }
229247}
0 commit comments