Redid most of the mentioned objects in shotcalculator to doubles & spotless apply#585
Conversation
There was a problem hiding this comment.
This PR refactors geometry calculations away from WPILib types to raw math, but introduces four correctness bugs: an out-of-bounds array access that crashes at runtime, two wrong formulas producing incorrect shooting parameters, and swapped atan2 arguments giving a 90-degree heading error. These must be fixed before merging.
| Pose2d shooterPosition = robotPosition.plus(robotToShooter); | ||
| Pose2d shooterPosition = | ||
| new Pose2d( | ||
| robotPosition.getX() + robotToShooter.getX(), |
There was a problem hiding this comment.
Wrong: direct XY addition drops the robot heading rotation. Pose2d.plus(Transform2d) rotates the transform translation by the pose rotation before adding, correctly mapping the robot-relative shooter offset into field coordinates. The new code adds raw X/Y without applying robotPosition.getRotation(), so shooterPosition is wrong whenever the robot is not facing field-forward. Restore the original: robotPosition.plus(robotToShooter).
| double lookaheadDistanceMeters = target.getDistance(lookaheadPosition); | ||
| double lookaheadDistanceMeters = | ||
| Math.sqrt( | ||
| Math.pow(lookaheadPosition[1] - target.getX(), 2) |
There was a problem hiding this comment.
Wrong index: uses [1] (Y) where [0] (X) is needed. lookaheadPosition[1] - target.getX() should be lookaheadPosition[0] - target.getX(). The distance formula has X and Y mixed up on one side.
| double lookaheadDistanceMeters = | ||
| Math.sqrt( | ||
| Math.pow(lookaheadPosition[1] - target.getX(), 2) | ||
| + Math.pow(lookaheadPosition[2] - target.getY(), 2)); |
There was a problem hiding this comment.
Out-of-bounds array access: will throw ArrayIndexOutOfBoundsException at runtime. lookaheadPosition is double[2] with valid indices 0 and 1. Index 2 is always out of bounds. Should be lookaheadPosition[1].
| Rotation2d angleToTarget = target.minus(robotCenterLookahead).getAngle(); | ||
| Rotation2d targetHeading = angleToTarget.minus(robotToShooter.getRotation()); | ||
| Double[] robotCenterLookahead = { | ||
| robotPosition.getX() * timeOfFlightSecs, robotPosition.getY() * timeOfFlightSecs |
There was a problem hiding this comment.
Wrong formula: multiplies position by time instead of adding velocity * time. robotPosition.getX() * timeOfFlightSecs is physically meaningless. The original added the velocity offset: robotPosition.getX() + robotFieldVelocity.getX() * timeOfFlightSecs (and same for Y). As written, robotCenterLookahead will be a wildly incorrect value and heading calculation will be completely broken.
| }; | ||
| double angleToTarget = | ||
| Math.atan2( | ||
| (target.getX() - robotCenterLookahead[0]) |
There was a problem hiding this comment.
atan2 arguments are swapped, producing a 90-degree heading error. Math.atan2(y, x) expects the Y-delta first, but the code passes the X-delta (target.getX() - robotCenterLookahead[0]) as the first argument and Y-delta as the second. The normalization inside is also redundant since atan2 handles magnitude internally. Fix: Math.atan2(target.getY() - robotCenterLookahead[1], target.getX() - robotCenterLookahead[0]).
|
@claude yo clanka re-review my code |
|
Claude finished @Hunter-A-T's task in 1m 30s —— View job Re-review: ShotCalculator.java
Good news — all five bugs flagged in the previous review are now fixed:
The core math is now correct. One minor thing worth noting: Line 208 uses Double[] robotCenterLookahead = { // <-- boxes each value as a heap object
robotPosition.getX() + ...,
robotPosition.getY() + ...
};Since the PR's goal is eliminating heap allocations, Also, the Overall: LGTM aside from the |
There was a problem hiding this comment.
All four bugs from my previous review have been fixed: the array out-of-bounds access, the mixed-up X/Y index in lookaheadDistanceMeters, the wrong multiply-instead-of-add formula in robotCenterLookahead, and the swapped atan2 arguments. The current diff looks correct.
|
@claude before there were ~1,900 geometry objects being made per second, how many are there now? |
|
Claude finished @Hunter-A-T's task in 3m 11s —— View job Heap allocation count: before vs. after
Before (original): ~35–38 geometry objects per call × 50 Hz ≈ ~1,750–1,900/sec The dominant cost was the 10-iteration refinement loop. Per iteration it allocated:
3 Translation2d × 10 iterations = 30 geometry objects per call just from the loop. Plus ~8 fixed objects outside the loop = ~38 total per call × 50 Hz ≈ 1,900/sec. After (this PR): ~11 geometry objects per call × 50 Hz ≈ ~550/sec The loop now uses primitive The remaining 11 per call:
That's a ~3.5× reduction — from ~1,900/sec down to ~550/sec.
|
| // TOF is the one consistent with the lookahead it produces (fixed-point convergence). | ||
| double timeOfFlightSecs = timeOfFlightMap.get(shooterToTargetDistanceMeters); | ||
| Translation2d lookaheadPosition = shooterPosition.getTranslation(); | ||
| double[] lookaheadPosition = {shooterPosition.getX(), shooterPosition.getY()}; |
There was a problem hiding this comment.
This is directionally correct, the next thing you need to do is preallocate the array so that the array is only ever allocated once when the shot calculator is constructed
| robotPosition.getY() + robotFieldVelocity.getY() * timeOfFlightSecs | ||
| }; | ||
| double angleToTarget = | ||
| Math.atan2( |
There was a problem hiding this comment.
I need to verify the math on this, also it may be more worthwhile to hold off on optimizing robotCenterLookahead and angle to target until after worlds, just so we can get this into the code base before worlds
| Logger.recordOutput(logLookaheadPose, new Pose2d(lookaheadPosition, targetHeading)); | ||
| Logger.recordOutput(logRobotCenterLookahead, new Pose2d(robotCenterLookahead, targetHeading)); | ||
| Logger.recordOutput( | ||
| logLookaheadPose, new Pose2d(lookaheadPosition[0], lookaheadPosition[1], targetHeading)); |
#449