Skip to content

Commit cd89b72

Browse files
committed
Refactor GamePieceFinder: update camera offset and simplify ray intersection logic
1 parent 86c65fa commit cd89b72

File tree

1 file changed

+93
-75
lines changed

1 file changed

+93
-75
lines changed

src/main/java/frc/robot/Subsystems/GamePieceFinder/GamePieceFinder.java

Lines changed: 93 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package frc.robot.Subsystems.GamePieceFinder;
22

33
import static edu.wpi.first.units.Units.*;
4-
import static frc.robot.Subsystems.Vision.VisionConstants.*;
54

65
import java.util.*;
76
import java.util.concurrent.atomic.AtomicReference;
@@ -12,9 +11,7 @@
1211
import edu.wpi.first.math.geometry.*;
1312
import edu.wpi.first.math.util.Units;
1413
import edu.wpi.first.units.measure.Angle;
15-
import edu.wpi.first.units.measure.Distance;
1614
import edu.wpi.first.units.measure.Time;
17-
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
1815
import frc.robot.Subsystems.Drive.Drive;
1916

2017
public 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

Comments
 (0)