2022-04-08 21:20:53 -07:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
|
|
|
|
|
|
|
|
|
package edu.wpi.first.math;
|
|
|
|
|
|
2022-10-26 22:20:08 -07:00
|
|
|
import edu.wpi.first.math.geometry.Pose3d;
|
2022-04-08 21:20:53 -07:00
|
|
|
import edu.wpi.first.math.geometry.Rotation2d;
|
2022-10-26 22:20:08 -07:00
|
|
|
import edu.wpi.first.math.geometry.Rotation3d;
|
|
|
|
|
import edu.wpi.first.math.geometry.Transform3d;
|
|
|
|
|
import edu.wpi.first.math.geometry.Translation3d;
|
2022-04-08 21:20:53 -07:00
|
|
|
|
|
|
|
|
public final class ComputerVisionUtil {
|
|
|
|
|
private ComputerVisionUtil() {
|
|
|
|
|
throw new AssertionError("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.
|
2022-05-06 18:36:58 +03:00
|
|
|
* @param targetYawRadians The yaw of the target in the camera's lens in radians.
|
2022-04-08 21:20:53 -07:00
|
|
|
* @return The estimated distance to the target in meters.
|
|
|
|
|
*/
|
|
|
|
|
public static double calculateDistanceToTarget(
|
|
|
|
|
double cameraHeightMeters,
|
|
|
|
|
double targetHeightMeters,
|
|
|
|
|
double cameraPitchRadians,
|
2022-05-06 18:36:58 +03:00
|
|
|
double targetPitchRadians,
|
|
|
|
|
double targetYawRadians) {
|
2022-04-08 21:20:53 -07:00
|
|
|
return (targetHeightMeters - cameraHeightMeters)
|
2022-05-06 18:36:58 +03:00
|
|
|
/ (Math.tan(cameraPitchRadians + targetPitchRadians) * Math.cos(targetYawRadians));
|
2022-04-08 21:20:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
2022-10-26 22:20:08 -07:00
|
|
|
* @param fieldToTarget A Pose3d representing the target position in the field coordinate system.
|
2022-04-08 21:20:53 -07:00
|
|
|
* @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
|
2022-10-26 22:20:08 -07:00
|
|
|
* new Transform3d(Units.inchesToMeters(3), Units.inchesToMeters(0), Units.inchesToMeters(0),
|
|
|
|
|
* new Rotation3d(Units.degreesToRadians(0))).
|
2022-04-08 21:20:53 -07:00
|
|
|
* @return The position of the robot in the field.
|
|
|
|
|
*/
|
2022-10-26 22:20:08 -07:00
|
|
|
public static Pose3d estimateFieldToRobot(
|
2022-04-08 21:20:53 -07:00
|
|
|
double cameraHeightMeters,
|
|
|
|
|
double targetHeightMeters,
|
|
|
|
|
double cameraPitchRadians,
|
|
|
|
|
double targetPitchRadians,
|
|
|
|
|
Rotation2d targetYaw,
|
|
|
|
|
Rotation2d gyroAngle,
|
2022-10-26 22:20:08 -07:00
|
|
|
Pose3d fieldToTarget,
|
|
|
|
|
Transform3d cameraToRobot) {
|
|
|
|
|
final var distanceAlongGround =
|
|
|
|
|
calculateDistanceToTarget(
|
|
|
|
|
cameraHeightMeters,
|
|
|
|
|
targetHeightMeters,
|
|
|
|
|
cameraPitchRadians,
|
|
|
|
|
targetPitchRadians,
|
|
|
|
|
targetYaw.getRadians());
|
|
|
|
|
final var range = Math.hypot(distanceAlongGround, targetHeightMeters - cameraHeightMeters);
|
2022-04-08 21:20:53 -07:00
|
|
|
return estimateFieldToRobot(
|
|
|
|
|
estimateCameraToTarget(
|
2022-10-26 22:20:08 -07:00
|
|
|
new Translation3d(
|
|
|
|
|
range, new Rotation3d(0.0, targetPitchRadians, targetYaw.getRadians())),
|
2022-04-08 21:20:53 -07:00
|
|
|
fieldToTarget,
|
|
|
|
|
gyroAngle),
|
|
|
|
|
fieldToTarget,
|
|
|
|
|
cameraToRobot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
2022-10-26 22:20:08 -07:00
|
|
|
* new Transform3d(Units.inchesToMeters(3), Units.inchesToMeters(0), Units.inchesToMeters(0),
|
|
|
|
|
* new Rotation3d(Units.degreesToRadians(0))).
|
2022-04-08 21:20:53 -07:00
|
|
|
* @return The position of the robot in the field.
|
|
|
|
|
*/
|
2022-10-26 22:20:08 -07:00
|
|
|
public static Pose3d estimateFieldToRobot(
|
|
|
|
|
Transform3d cameraToTarget, Pose3d fieldToTarget, Transform3d cameraToRobot) {
|
2022-04-08 21:20:53 -07:00
|
|
|
return estimateFieldToCamera(cameraToTarget, fieldToTarget).transformBy(cameraToRobot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-10-26 22:20:08 -07:00
|
|
|
* Estimates a {@link Transform3d} that maps the camera position to the target position, using the
|
2022-04-08 21:20:53 -07:00
|
|
|
* 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.
|
|
|
|
|
*
|
2022-10-26 22:20:08 -07:00
|
|
|
* @param cameraToTargetTranslation A Translation3d that encodes the x/y position of the target
|
2022-04-08 21:20:53 -07:00
|
|
|
* relative to the camera.
|
2022-10-26 22:20:08 -07:00
|
|
|
* @param fieldToTarget A Pose3d representing the target position in the field coordinate system.
|
2022-04-08 21:20:53 -07:00
|
|
|
* @param gyroAngle The current robot gyro angle, likely from odometry.
|
2022-10-26 22:20:08 -07:00
|
|
|
* @return A Transform3d that takes us from the camera to the target.
|
2022-04-08 21:20:53 -07:00
|
|
|
*/
|
2022-10-26 22:20:08 -07:00
|
|
|
public static Transform3d estimateCameraToTarget(
|
|
|
|
|
Translation3d cameraToTargetTranslation, Pose3d fieldToTarget, Rotation2d gyroAngle) {
|
2022-04-08 21:20:53 -07:00
|
|
|
// Map our camera at the origin out to our target, in the robot reference
|
|
|
|
|
// frame. Gyro angle is needed because there's a circle of possible camera
|
|
|
|
|
// poses for which the camera has the same yaw from camera to target.
|
2022-10-26 22:20:08 -07:00
|
|
|
return new Transform3d(
|
|
|
|
|
cameraToTargetTranslation,
|
|
|
|
|
new Rotation3d(0.0, 0.0, -gyroAngle.getRadians()).minus(fieldToTarget.getRotation()));
|
2022-04-08 21:20:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2022-10-26 22:20:08 -07:00
|
|
|
public static Pose3d estimateFieldToCamera(Transform3d cameraToTarget, Pose3d fieldToTarget) {
|
2022-04-08 21:20:53 -07:00
|
|
|
var targetToCamera = cameraToTarget.inverse();
|
|
|
|
|
return fieldToTarget.transformBy(targetToCamera);
|
|
|
|
|
}
|
|
|
|
|
}
|