mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-19 00:41:41 +00:00
* Make MultiTagPNPResult and PNPResult singular * add java tests * Formatting fixes * bring in the rest of the little stuff * final things * Formatting fixes * add multisubscriber back * Formatting fixes * make comments better about x and y relationship
210 lines
10 KiB
Java
210 lines
10 KiB
Java
/*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) PhotonVision
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
package org.photonvision;
|
|
|
|
import edu.wpi.first.math.geometry.*;
|
|
|
|
public final class PhotonUtils {
|
|
private PhotonUtils() {
|
|
// Utility class
|
|
}
|
|
|
|
/**
|
|
* Algorithm from https://docs.limelightvision.io/en/latest/cs_estimating_distance.html Estimates
|
|
* range to a target using the target's elevation. This method can produce more stable results
|
|
* than SolvePNP when well tuned, if the full 6d robot pose is not required. Note that this method
|
|
* requires the camera to have 0 roll (not be skewed clockwise or CCW relative to the floor), and
|
|
* for there to exist a height differential between goal and camera. The larger this differential,
|
|
* the more accurate the distance estimate will be.
|
|
*
|
|
* <p>Units can be converted using the {@link edu.wpi.first.math.util.Units} class.
|
|
*
|
|
* @param cameraHeightMeters The physical height of the camera off the floor in meters.
|
|
* @param targetHeightMeters The physical height of the target off the floor in meters. This
|
|
* should be the height of whatever is being targeted (i.e. if the targeting region is set to
|
|
* top, this should be the height of the top of the target).
|
|
* @param cameraPitchRadians The pitch of the camera from the horizontal plane in radians.
|
|
* Positive values up.
|
|
* @param targetPitchRadians The pitch of the target in the camera's lens in radians. Positive
|
|
* values up.
|
|
* @return The estimated distance to the target in meters.
|
|
*/
|
|
public static double calculateDistanceToTargetMeters(
|
|
double cameraHeightMeters,
|
|
double targetHeightMeters,
|
|
double cameraPitchRadians,
|
|
double targetPitchRadians) {
|
|
return (targetHeightMeters - cameraHeightMeters)
|
|
/ Math.tan(cameraPitchRadians + targetPitchRadians);
|
|
}
|
|
|
|
/**
|
|
* Estimate the {@link Translation2d} of the target relative to the camera.
|
|
*
|
|
* @param targetDistanceMeters The distance to the target in meters.
|
|
* @param yaw The observed yaw of the target.
|
|
* @return The target's camera-relative translation.
|
|
*/
|
|
public static Translation2d estimateCameraToTargetTranslation(
|
|
double targetDistanceMeters, Rotation2d yaw) {
|
|
return new Translation2d(
|
|
yaw.getCos() * targetDistanceMeters, yaw.getSin() * targetDistanceMeters);
|
|
}
|
|
|
|
/**
|
|
* Estimate the position of the robot in the field.
|
|
*
|
|
* @param cameraHeightMeters The physical height of the camera off the floor in meters.
|
|
* @param targetHeightMeters The physical height of the target off the floor in meters. This
|
|
* should be the height of whatever is being targeted (i.e. if the targeting region is set to
|
|
* top, this should be the height of the top of the target).
|
|
* @param cameraPitchRadians The pitch of the camera from the horizontal plane in radians.
|
|
* Positive values up.
|
|
* @param targetPitchRadians The pitch of the target in the camera's lens in radians. Positive
|
|
* values up.
|
|
* @param targetYaw The observed yaw of the target. Note that this *must* be CCW-positive, and
|
|
* Photon returns CW-positive.
|
|
* @param gyroAngle The current robot gyro angle, likely from odometry.
|
|
* @param fieldToTarget A Pose2d representing the target position in the field coordinate system.
|
|
* @param cameraToRobot The position of the robot relative to the camera. If the camera was
|
|
* mounted 3 inches behind the "origin" (usually physical center) of the robot, this would be
|
|
* Transform2d(3 inches, 0 inches, 0 degrees).
|
|
* @return The position of the robot in the field.
|
|
*/
|
|
public static Pose2d estimateFieldToRobot(
|
|
double cameraHeightMeters,
|
|
double targetHeightMeters,
|
|
double cameraPitchRadians,
|
|
double targetPitchRadians,
|
|
Rotation2d targetYaw,
|
|
Rotation2d gyroAngle,
|
|
Pose2d fieldToTarget,
|
|
Transform2d cameraToRobot) {
|
|
return PhotonUtils.estimateFieldToRobot(
|
|
PhotonUtils.estimateCameraToTarget(
|
|
PhotonUtils.estimateCameraToTargetTranslation(
|
|
PhotonUtils.calculateDistanceToTargetMeters(
|
|
cameraHeightMeters, targetHeightMeters, cameraPitchRadians, targetPitchRadians),
|
|
targetYaw),
|
|
fieldToTarget,
|
|
gyroAngle),
|
|
fieldToTarget,
|
|
cameraToRobot);
|
|
}
|
|
|
|
/**
|
|
* Estimates a {@link Transform2d} that maps the camera position to the target position, using the
|
|
* robot's gyro. Note that the gyro angle provided *must* line up with the field coordinate system
|
|
* -- that is, it should read zero degrees when pointed towards the opposing alliance station, and
|
|
* increase as the robot rotates CCW.
|
|
*
|
|
* @param cameraToTargetTranslation A Translation2d that encodes the x/y position of the target
|
|
* relative to the camera.
|
|
* @param fieldToTarget A Pose2d representing the target position in the field coordinate system.
|
|
* @param gyroAngle The current robot gyro angle, likely from odometry.
|
|
* @return A Transform2d that takes us from the camera to the target.
|
|
*/
|
|
public static Transform2d estimateCameraToTarget(
|
|
Translation2d cameraToTargetTranslation, Pose2d fieldToTarget, Rotation2d gyroAngle) {
|
|
// This pose maps our camera at the origin out to our target, in the robot
|
|
// reference frame
|
|
// The translation part of this Transform2d is from the above step, and the
|
|
// rotation uses our robot's
|
|
// gyro.
|
|
return new Transform2d(
|
|
cameraToTargetTranslation, gyroAngle.times(-1).minus(fieldToTarget.getRotation()));
|
|
}
|
|
|
|
/**
|
|
* Estimates the pose of the robot in the field coordinate system, given the position of the
|
|
* target relative to the camera, the target relative to the field, and the robot relative to the
|
|
* camera.
|
|
*
|
|
* @param cameraToTarget The position of the target relative to the camera.
|
|
* @param fieldToTarget The position of the target in the field.
|
|
* @param cameraToRobot The position of the robot relative to the camera. If the camera was
|
|
* mounted 3 inches behind the "origin" (usually physical center) of the robot, this would be
|
|
* Transform2d(3 inches, 0 inches, 0 degrees).
|
|
* @return The position of the robot in the field.
|
|
*/
|
|
public static Pose2d estimateFieldToRobot(
|
|
Transform2d cameraToTarget, Pose2d fieldToTarget, Transform2d cameraToRobot) {
|
|
return estimateFieldToCamera(cameraToTarget, fieldToTarget).transformBy(cameraToRobot);
|
|
}
|
|
|
|
/**
|
|
* Estimates the pose of the camera in the field coordinate system, given the position of the
|
|
* target relative to the camera, and the target relative to the field. This *only* tracks the
|
|
* position of the camera, not the position of the robot itself.
|
|
*
|
|
* @param cameraToTarget The position of the target relative to the camera.
|
|
* @param fieldToTarget The position of the target in the field.
|
|
* @return The position of the camera in the field.
|
|
*/
|
|
public static Pose2d estimateFieldToCamera(Transform2d cameraToTarget, Pose2d fieldToTarget) {
|
|
var targetToCamera = cameraToTarget.inverse();
|
|
return fieldToTarget.transformBy(targetToCamera);
|
|
}
|
|
|
|
/**
|
|
* Estimates the pose of the robot in the field coordinate system, given the pose of the fiducial
|
|
* tag, the robot relative to the camera, and the target relative to the camera.
|
|
*
|
|
* @param fieldRelativeTagPose Pose3D the field relative pose of the target
|
|
* @param cameraToRobot Transform3D of the robot relative to the camera. Origin of the robot is
|
|
* defined as the center.
|
|
* @param cameraToTarget Transform3D of the target relative to the camera, returned by
|
|
* PhotonVision
|
|
* @return Transform3d Robot position relative to the field
|
|
*/
|
|
public static Pose3d estimateFieldToRobotAprilTag(
|
|
Transform3d cameraToTarget, Pose3d fieldRelativeTagPose, Transform3d cameraToRobot) {
|
|
return fieldRelativeTagPose.plus(cameraToTarget.inverse()).plus(cameraToRobot);
|
|
}
|
|
|
|
/**
|
|
* Returns the yaw between your robot and a target.
|
|
*
|
|
* @param robotPose Current pose of the robot
|
|
* @param targetPose Pose of the target on the field
|
|
* @return double Yaw to the target
|
|
*/
|
|
public static Rotation2d getYawToPose(Pose2d robotPose, Pose2d targetPose) {
|
|
Translation2d relativeTrl = targetPose.relativeTo(robotPose).getTranslation();
|
|
return new Rotation2d(relativeTrl.getX(), relativeTrl.getY());
|
|
}
|
|
|
|
/**
|
|
* Returns the distance between two poses
|
|
*
|
|
* @param robotPose Pose of the robot
|
|
* @param targetPose Pose of the target
|
|
* @return distance to the pose
|
|
*/
|
|
public static double getDistanceToPose(Pose2d robotPose, Pose2d targetPose) {
|
|
return robotPose.getTranslation().getDistance(targetPose.getTranslation());
|
|
}
|
|
}
|