Skip to content

Conversation

@levyishai
Copy link
Member

TODO: Implement a TimeInterpolatingBuffer inside the turret subsystem

Copilot AI review requested due to automatic review settings January 19, 2026 16:05
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces support for dynamic camera transforms, enabling cameras mounted on moving mechanisms (like turrets) to report their position correctly over time. The core change is the introduction of the DynamicCameraTransform class, which abstracts camera-to-robot transformations and supports both static and time-dependent camera positioning.

Changes:

  • Added DynamicCameraTransform class to handle both static and time-dependent camera transformations
  • Refactored all AprilTag camera IO classes to accept DynamicCameraTransform instead of Transform3d
  • Updated simulation camera to dynamically adjust camera position on each update cycle

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
DynamicCameraTransform.java New class providing abstraction for static and dynamic camera transforms with time-based lookups
AprilTagPhotonCameraIO.java Updated to use DynamicCameraTransform and retrieve time-based transforms for pose calculations
AprilTagSimulationCameraIO.java Added updateInputs override to continuously update simulated camera position
AprilTagLimelightIO.java Constructor signature updated to accept DynamicCameraTransform (not yet used due to incomplete implementation)
AprilTagCameraIO.java Factory method updated to accept and pass DynamicCameraTransform
AprilTagCameraConstants.java Updated enum to use DynamicCameraTransform in BiFunction signature
AprilTagCamera.java Added constructor overload for DynamicCameraTransform and refactored to use time-based transforms

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

Walkthrough

Introduces DynamicCameraTransform to provide time-varying camera-to-robot transforms and replaces static Transform3d usage across AprilTag camera IO and pose-estimation code. AprilTagCamera, AprilTagCameraIO, AprilTagCameraConstants, and IO implementations now accept DynamicCameraTransform; pose conversions and related flows propagate result timestamps. Adds TurretCameraTransformCalculator (with sampling/forecasting) and integrates turret angle/velocity updates in Turret. CameraConstants adds RIGHT_TURRET_CAMERA and LEFT_TURRET_CAMERA using dynamic transforms, and RobotContainer now supplies those turret cameras to the robot pose estimator.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Turret Camera' directly describes the primary feature being implemented across the changeset.
Description check ✅ Passed The description mentions implementing a TimeInterpolatingBuffer in the turret subsystem, which aligns with the TurretCameraTransformCalculator changes that use a TimeInterpolatableBuffer.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

🧪 Unit Test Generation v2 is now available!

We have significantly improved our unit test generation capabilities.

To enable: Add this to your .coderabbit.yaml configuration:

reviews:
  finishing_touches:
    unit_tests:
      enabled: true

Try it out by using the @coderabbitai generate unit tests command on your code files or under ✨ Finishing Touches on the walkthrough!

Have feedback? Share your thoughts on our Discord thread!


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/java/frc/trigon/robot/poseestimation/apriltagcamera/io/AprilTagPhotonCameraIO.java (2)

16-35: Add a fail‑fast null guard for dynamicCameraTransform.

This avoids deferred NPEs during pose estimation and makes misconfiguration obvious at construction time.

Proposed fix
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 ...
     public AprilTagPhotonCameraIO(String cameraName, DynamicCameraTransform dynamicCameraTransform) {
         photonCamera = new PhotonCamera(cameraName);
-        this.dynamicCameraTransform = dynamicCameraTransform;
+        this.dynamicCameraTransform = Objects.requireNonNull(dynamicCameraTransform, "dynamicCameraTransform");
     }

142-167: Add null guard for samplePoseAtTimestamp() result.

samplePoseAtTimestamp() returns null when the pose is outside the buffer window (early in match). Calling .getRotation() on null will crash. Similar code elsewhere guards against this (see ObjectDetectionCamera.java:89). Check the timestamp and fallback if the pose is unavailable, or handle the case where constrained solve-pnp cannot proceed.

@itsWihy
Copy link

itsWihy commented Jan 20, 2026

So we doing SOTM this season?

@Strflightmight09 Strflightmight09 linked an issue Jan 20, 2026 that may be closed by this pull request
@Strflightmight09 Strflightmight09 removed their assignment Jan 20, 2026
@levyishai
Copy link
Member Author

So we doing SOTM this season?

I have no idea what Shirt On The Mop means.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

@levyishai
Copy link
Member Author

Needs testing

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Comment on lines +56 to +80
private Rotation2d calculateTurretAngleAtTime(double timestampSeconds) {
if (isTimestampTooNew(timestampSeconds))
return estimateFutureTurretAngle(timestampSeconds);

return sampleTurretAngleAtTime(timestampSeconds);
}

private boolean isTimestampTooNew(double timestampSeconds) {
final Map.Entry<Double, Rotation2d> latestBufferEntry = getLatestBufferEntry();
if (latestBufferEntry == null)
return false;
final Double latestTimestamp = latestBufferEntry.getKey();
return timestampSeconds > latestTimestamp;
}

private Rotation2d estimateFutureTurretAngle(double futureTimestampSeconds) {
final Map.Entry<Double, Rotation2d> latestBufferEntry = getLatestBufferEntry();
if (latestBufferEntry == null)
return RobotContainer.TURRET.getCurrentSelfRelativeAngle();
final Double latestTimestamp = latestBufferEntry.getKey();
final Rotation2d latestAngle = latestBufferEntry.getValue();

final double timeDeltaSeconds = futureTimestampSeconds - latestTimestamp;
final double predictedRotations = latestVelocityRotationsPerSecond * timeDeltaSeconds;
return latestAngle.plus(Rotation2d.fromRotations(predictedRotations));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, locate and examine the file in question
fd "TurretCameraTransformCalculator.java"

Repository: Programming-TRIGON/RobotCode2026

Length of output: 161


🏁 Script executed:

# Search for downstream callers of the transform methods
rg -n "calculateRobotTo.*CameraAtTime|calculateRobotToCameraAtTime" --type java

Repository: Programming-TRIGON/RobotCode2026

Length of output: 1410


🏁 Script executed:

# Look for uses of DynamicCameraTransform and how null is handled
rg -n "DynamicCameraTransform" --type java -A 3 -B 1

Repository: Programming-TRIGON/RobotCode2026

Length of output: 13370


🏁 Script executed:

# Search for sampleTurretAngleAtTime to see if it can return null
rg -n "sampleTurretAngleAtTime" --type java -A 5 -B 1

Repository: Programming-TRIGON/RobotCode2026

Length of output: 1872


🏁 Script executed:

# Look at the buffer management and getLatestBufferEntry
rg -n "getLatestBufferEntry" --type java -A 3 -B 1

Repository: Programming-TRIGON/RobotCode2026

Length of output: 2190


🏁 Script executed:

cat -n src/main/java/frc/trigon/robot/subsystems/turret/TurretCameraTransformCalculator.java

Repository: Programming-TRIGON/RobotCode2026

Length of output: 4940


🏁 Script executed:

# Check how downstream AprilTagCamera uses the transform
rg -n "getTransform\|robotCenterToCameraFunction" src/main/java/frc/trigon/robot/poseestimation/apriltagcamera/ --type java -A 3 -B 1

Repository: Programming-TRIGON/RobotCode2026

Length of output: 58


🏁 Script executed:

# Search for where robotCenterToCameraFunction is invoked
rg -n "robotCenterToCameraFunction\.apply\|robotCenterToCameraFunction\.get\|robotCenterToCameraFunction\(" src/main/java/frc/trigon/robot/poseestimation/ --type java -A 2 -B 1

Repository: Programming-TRIGON/RobotCode2026

Length of output: 58


🏁 Script executed:

# Look for where AprilTagCamera's getTransform method is used
rg -n "getTransform\(\|\.dynamicCameraTransform" src/main/java/frc/trigon/robot/ --type java -A 2 -B 1 | head -80

Repository: Programming-TRIGON/RobotCode2026

Length of output: 58


🏁 Script executed:

# Find where the transform functions are called
rg -n "calculateRobotTo.*CameraAtTime\(" src/main/java/frc/trigon/robot/ --type java -A 5 -B 1

Repository: Programming-TRIGON/RobotCode2026

Length of output: 2144


🏁 Script executed:

# Search in AprilTagCamera for getTransform or transform retrieval
cat -n src/main/java/frc/trigon/robot/poseestimation/apriltagcamera/AprilTagCamera.java | head -100

Repository: Programming-TRIGON/RobotCode2026

Length of output: 5652


🏁 Script executed:

# Look for AprilTagCamera.update or process methods
rg -n "update\|process" src/main/java/frc/trigon/robot/poseestimation/apriltagcamera/AprilTagCamera.java --type java -A 10

Repository: Programming-TRIGON/RobotCode2026

Length of output: 58


🏁 Script executed:

# Get more of AprilTagCamera file to see calculateRobotPose
cat -n src/main/java/frc/trigon/robot/poseestimation/apriltagcamera/AprilTagCamera.java | tail -100

Repository: Programming-TRIGON/RobotCode2026

Length of output: 5868


🏁 Script executed:

# Search for calculateRobotPose to see full implementation
rg -n "calculateRobotPose" src/main/java/frc/trigon/robot/poseestimation/apriltagcamera/AprilTagCamera.java --type java -A 20

Repository: Programming-TRIGON/RobotCode2026

Length of output: 1866


🏁 Script executed:

# Check DynamicCameraTransform.calculate2dRobotPose implementation
rg -n "calculate2dRobotPose" src/main/java/frc/trigon/robot/poseestimation/apriltagcamera/DynamicCameraTransform.java --type java -A 10 -B 1

Repository: Programming-TRIGON/RobotCode2026

Length of output: 608


🏁 Script executed:

# Check get2dCameraToRobotCenter implementation
rg -n "get2dCameraToRobotCenter" src/main/java/frc/trigon/robot/poseestimation/apriltagcamera/DynamicCameraTransform.java --type java -A 10 -B 1

Repository: Programming-TRIGON/RobotCode2026

Length of output: 1090


🏁 Script executed:

# Check get3dCameraToRobotCenter implementation
rg -n "get3dCameraToRobotCenter" src/main/java/frc/trigon/robot/poseestimation/apriltagcamera/DynamicCameraTransform.java --type java -A 10 -B 1

Repository: Programming-TRIGON/RobotCode2026

Length of output: 1653


Handle empty buffer at startup to avoid null Transform3d propagating downstream.

With an empty buffer, isTimestampTooNew() returns false, so calculateTurretAngleAtTime() calls sampleTurretAngleAtTime(), which returns null. This null propagates through calculateRobotToCameraAtTime() to DynamicCameraTransform, where inverse() is called on a null Transform3d—causing an NPE when AprilTagCamera attempts pose estimation at startup. The fallback at line 74 is unreachable.

Treat an empty buffer as "too new" (return true from isTimestampTooNew()) so the fallback uses RobotContainer.TURRET.getCurrentSelfRelativeAngle(), or handle null Transform3d explicitly in the downstream chain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Turret Camera

5 participants