diff --git a/wpimath/src/main/java/edu/wpi/first/math/ComputerVisionUtil.java b/wpimath/src/main/java/edu/wpi/first/math/ComputerVisionUtil.java index 61c9b1521a..77c4abc809 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/ComputerVisionUtil.java +++ b/wpimath/src/main/java/edu/wpi/first/math/ComputerVisionUtil.java @@ -5,10 +5,7 @@ package edu.wpi.first.math; import edu.wpi.first.math.geometry.Pose3d; -import edu.wpi.first.math.geometry.Rotation2d; -import edu.wpi.first.math.geometry.Rotation3d; import edu.wpi.first.math.geometry.Transform3d; -import edu.wpi.first.math.geometry.Translation3d; public final class ComputerVisionUtil { private ComputerVisionUtil() { @@ -16,135 +13,26 @@ public final class ComputerVisionUtil { } /** - * 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. + * Returns the robot's pose in the field coordinate system given an object's field-relative pose, + * the transformation from the camera's pose to the object's pose (obtained via computer vision), + * and the transformation from the robot's pose to the camera's pose. * - *

Units can be converted using the {@link edu.wpi.first.math.util.Units} class. + *

The object could be a target or a fiducial marker. * - * @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 targetYawRadians The yaw of the target in the camera's lens in radians. - * @return The estimated distance to the target in meters. + * @param objectInField An object's field-relative pose. + * @param cameraToObject The transformation from the camera's pose to the object's pose. This + * comes from computer vision. + * @param robotToCamera The transformation from the robot's pose to the camera's pose. This can + * either be a constant for a rigidly mounted camera, or variable if the camera is mounted to + * a turret. If the camera was mounted 3 inches behind the "origin" (usually physical center) + * of the robot, this would be new Transform3d(Units.inchesToMeters(3.0), 0.0, 0.0, new + * Rotation3d()). + * @return The robot's field-relative pose. */ - public static double calculateDistanceToTarget( - double cameraHeightMeters, - double targetHeightMeters, - double cameraPitchRadians, - double targetPitchRadians, - double targetYawRadians) { - return (targetHeightMeters - cameraHeightMeters) - / (Math.tan(cameraPitchRadians + targetPitchRadians) * Math.cos(targetYawRadians)); - } - - /** - * 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 Pose3d 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 - * new Transform3d(Units.inchesToMeters(3), Units.inchesToMeters(0), Units.inchesToMeters(0), - * new Rotation3d(Units.degreesToRadians(0))). - * @return The position of the robot in the field. - */ - public static Pose3d estimateFieldToRobot( - double cameraHeightMeters, - double targetHeightMeters, - double cameraPitchRadians, - double targetPitchRadians, - Rotation2d targetYaw, - Rotation2d gyroAngle, - Pose3d fieldToTarget, - Transform3d cameraToRobot) { - final var distanceAlongGround = - calculateDistanceToTarget( - cameraHeightMeters, - targetHeightMeters, - cameraPitchRadians, - targetPitchRadians, - targetYaw.getRadians()); - final var range = Math.hypot(distanceAlongGround, targetHeightMeters - cameraHeightMeters); - return estimateFieldToRobot( - estimateCameraToTarget( - new Translation3d( - range, new Rotation3d(0.0, targetPitchRadians, targetYaw.getRadians())), - 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 - * new Transform3d(Units.inchesToMeters(3), Units.inchesToMeters(0), Units.inchesToMeters(0), - * new Rotation3d(Units.degreesToRadians(0))). - * @return The position of the robot in the field. - */ - public static Pose3d estimateFieldToRobot( - Transform3d cameraToTarget, Pose3d fieldToTarget, Transform3d cameraToRobot) { - return estimateFieldToCamera(cameraToTarget, fieldToTarget).transformBy(cameraToRobot); - } - - /** - * Estimates a {@link Transform3d} 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 Translation3d that encodes the x/y position of the target - * relative to the camera. - * @param fieldToTarget A Pose3d representing the target position in the field coordinate system. - * @param gyroAngle The current robot gyro angle, likely from odometry. - * @return A Transform3d that takes us from the camera to the target. - */ - public static Transform3d estimateCameraToTarget( - Translation3d cameraToTargetTranslation, Pose3d fieldToTarget, Rotation2d gyroAngle) { - // 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. - return new Transform3d( - cameraToTargetTranslation, - new Rotation3d(0.0, 0.0, -gyroAngle.getRadians()).minus(fieldToTarget.getRotation())); - } - - /** - * 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 Pose3d estimateFieldToCamera(Transform3d cameraToTarget, Pose3d fieldToTarget) { - var targetToCamera = cameraToTarget.inverse(); - return fieldToTarget.transformBy(targetToCamera); + public static Pose3d objectToRobotPose( + Pose3d objectInField, Transform3d cameraToObject, Transform3d robotToCamera) { + final var objectToCamera = cameraToObject.inverse(); + final var cameraToRobot = robotToCamera.inverse(); + return objectInField.plus(objectToCamera).plus(cameraToRobot); } } diff --git a/wpimath/src/main/native/cpp/ComputerVisionUtil.cpp b/wpimath/src/main/native/cpp/ComputerVisionUtil.cpp index f8c2640c3f..97968dce90 100644 --- a/wpimath/src/main/native/cpp/ComputerVisionUtil.cpp +++ b/wpimath/src/main/native/cpp/ComputerVisionUtil.cpp @@ -4,61 +4,16 @@ #include "frc/ComputerVisionUtil.h" -#include "frc/geometry/Rotation3d.h" #include "units/math.h" namespace frc { -units::meter_t CalculateDistanceToTarget(units::meter_t cameraHeight, - units::meter_t targetHeight, - units::radian_t cameraPitch, - units::radian_t targetPitch, - units::radian_t targetYaw) { - return (targetHeight - cameraHeight) / - (units::math::tan(cameraPitch + targetPitch) * - units::math::cos(targetYaw)); -} - -frc::Pose3d EstimateFieldToRobot( - units::meter_t cameraHeight, units::meter_t targetHeight, - units::radian_t cameraPitch, units::radian_t targetPitch, - const frc::Rotation2d& targetYaw, const frc::Rotation2d& gyroAngle, - const frc::Pose3d& fieldToTarget, const frc::Transform3d& cameraToRobot) { - auto distanceAlongGround = - CalculateDistanceToTarget(cameraHeight, targetHeight, cameraPitch, - targetPitch, targetYaw.Radians()); - auto range = - units::math::hypot(distanceAlongGround, targetHeight - cameraHeight); - return EstimateFieldToRobot( - EstimateCameraToTarget( - Translation3d{range, - Rotation3d{0_rad, targetPitch, targetYaw.Radians()}}, - fieldToTarget, gyroAngle), - fieldToTarget, cameraToRobot); -} - -frc::Pose3d EstimateFieldToRobot(const frc::Transform3d& cameraToTarget, - const frc::Pose3d& fieldToTarget, - const frc::Transform3d& cameraToRobot) { - return EstimateFieldToCamera(cameraToTarget, fieldToTarget) - .TransformBy(cameraToRobot); -} - -frc::Transform3d EstimateCameraToTarget( - const frc::Translation3d& cameraToTargetTranslation, - const frc::Pose3d& fieldToTarget, const frc::Rotation2d& gyroAngle) { - // 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. - return Transform3d{cameraToTargetTranslation, - Rotation3d{0_rad, 0_rad, -gyroAngle.Radians()} - - fieldToTarget.Rotation()}; -} - -frc::Pose3d EstimateFieldToCamera(const frc::Transform3d& cameraToTarget, - const frc::Pose3d& fieldToTarget) { - auto targetToCamera = cameraToTarget.Inverse(); - return fieldToTarget.TransformBy(targetToCamera); +frc::Pose3d ObjectToRobotPose(const frc::Pose3d& objectInField, + const frc::Transform3d& cameraToObject, + const frc::Transform3d& robotToCamera) { + const auto objectToCamera = cameraToObject.Inverse(); + const auto cameraToRobot = robotToCamera.Inverse(); + return objectInField + objectToCamera + cameraToRobot; } } // namespace frc diff --git a/wpimath/src/main/native/include/frc/ComputerVisionUtil.h b/wpimath/src/main/native/include/frc/ComputerVisionUtil.h index d2eb4f218b..8744f46c24 100644 --- a/wpimath/src/main/native/include/frc/ComputerVisionUtil.h +++ b/wpimath/src/main/native/include/frc/ComputerVisionUtil.h @@ -7,117 +7,31 @@ #include #include "frc/geometry/Pose3d.h" -#include "frc/geometry/Rotation2d.h" #include "frc/geometry/Transform3d.h" -#include "frc/geometry/Translation3d.h" -#include "units/angle.h" -#include "units/length.h" namespace frc { /** - * 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. + * Returns the robot's pose in the field coordinate system given an object's + * field-relative pose, the transformation from the camera's pose to the + * object's pose (obtained via computer vision), and the transformation from the + * robot's pose to the camera's pose. * - * @param cameraHeight The height of the camera off the floor. - * @param targetHeight The height of the target off the floor. - * @param cameraPitch The pitch of the camera from the horizontal plane. - * Positive values up. - * @param targetPitch The pitch of the target in the camera's lens. Positive - * values up. - * @param targetYaw The yaw of the target in the camera's lens. - * @return The estimated distance to the target. + * The object could be a target or a fiducial marker. + * + * @param objectInField An object's field-relative pose. + * @param cameraToObject The transformation from the camera's pose to the + * object's pose. This comes from computer vision. + * @param robotToCamera The transformation from the robot's pose to the camera's + * pose. This can either be a constant for a rigidly mounted camera, or + * variable if the camera is mounted to a turret. If the camera was mounted 3 + * inches behind the "origin" (usually physical center) of the robot, this + * would be frc::Transform3d{3_in, 0_in, 0_in, frc::Rotation3d{}}. + * @return The robot's field-relative pose. */ WPILIB_DLLEXPORT -units::meter_t CalculateDistanceToTarget(units::meter_t cameraHeight, - units::meter_t targetHeight, - units::radian_t cameraPitch, - units::radian_t targetPitch, - units::radian_t targetYaw); - -/** - * Estimate the position of the robot in the field. - * - * @param cameraHeight The physical height of the camera off the floor. - * @param targetHeight The physical height of the target off the floor. 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 cameraPitch The pitch of the camera from the horizontal plane. - * Positive values up. - * @param targetPitch The pitch of the target in the camera's lens. 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 Pose3d 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 - * frc::Transform3d{3_in, 0_in, 0_in, frc::Rotation3d{}}. - * @return The position of the robot in the field. - */ -WPILIB_DLLEXPORT -frc::Pose3d EstimateFieldToRobot( - units::meter_t cameraHeight, units::meter_t targetHeight, - units::radian_t cameraPitch, units::radian_t targetPitch, - const frc::Rotation2d& targetYaw, const frc::Rotation2d& gyroAngle, - const frc::Pose3d& fieldToTarget, const frc::Transform3d& 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 - * frc::Transform3d{3_in, 0_in, 0_in, frc::Rotation3d{}}. - * @return The position of the robot in the field. - */ -WPILIB_DLLEXPORT -frc::Pose3d EstimateFieldToRobot(const frc::Transform3d& cameraToTarget, - const frc::Pose3d& fieldToTarget, - const frc::Transform3d& cameraToRobot); - -/** - * Estimates a Transform3d 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 Translation3d that encodes the x/y - * position of the target relative to the - * camera. - * @param fieldToTarget A Pose3d representing the target position in the field - * coordinate system. - * @param gyroAngle The current robot gyro angle, likely from odometry. - * @return A Transform3d that takes us from the camera to the target. - */ -WPILIB_DLLEXPORT -frc::Transform3d EstimateCameraToTarget( - const frc::Translation3d& cameraToTargetTranslation, - const frc::Pose3d& fieldToTarget, const frc::Rotation2d& gyroAngle); - -/** - * 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. - */ -WPILIB_DLLEXPORT -frc::Pose3d EstimateFieldToCamera(const frc::Transform3d& cameraToTarget, - const frc::Pose3d& fieldToTarget); +frc::Pose3d ObjectToRobotPose(const frc::Pose3d& objectInField, + const frc::Transform3d& cameraToObject, + const frc::Transform3d& robotToCamera); } // namespace frc diff --git a/wpimath/src/test/java/edu/wpi/first/math/ComputerVisionUtilTest.java b/wpimath/src/test/java/edu/wpi/first/math/ComputerVisionUtilTest.java index 5a6d949a82..29278475d7 100644 --- a/wpimath/src/test/java/edu/wpi/first/math/ComputerVisionUtilTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/ComputerVisionUtilTest.java @@ -4,108 +4,30 @@ package edu.wpi.first.math; +import static org.junit.jupiter.api.Assertions.assertEquals; + import edu.wpi.first.math.geometry.Pose3d; -import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.geometry.Rotation3d; import edu.wpi.first.math.geometry.Transform3d; import edu.wpi.first.math.geometry.Translation3d; import edu.wpi.first.math.util.Units; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class ComputerVisionUtilTest { @Test - void testCalculateDistanceToTarget() { - var cameraHeight = 1; - var targetHeight = 3; - var cameraPitch = Units.degreesToRadians(0); - var targetPitch = Units.degreesToRadians(30); - var targetYaw = Units.degreesToRadians(0); + void testObjectToRobotPose() { + var robot = new Pose3d(1.0, 2.0, 0.0, new Rotation3d(0.0, 0.0, Units.degreesToRadians(30.0))); + var cameraToObject = + new Transform3d( + new Translation3d(1.0, 1.0, 1.0), + new Rotation3d(0.0, Units.degreesToRadians(-20.0), Units.degreesToRadians(45.0))); + var robotToCamera = + new Transform3d( + new Translation3d(1.0, 0.0, 2.0), + new Rotation3d(0.0, 0.0, Units.degreesToRadians(25.0))); + Pose3d object = robot.plus(robotToCamera).plus(cameraToObject); - var distanceAlongGround = - ComputerVisionUtil.calculateDistanceToTarget( - cameraHeight, targetHeight, cameraPitch, targetPitch, targetYaw); - Assertions.assertEquals(3.464, distanceAlongGround, 0.01); - - cameraHeight = 1; - targetHeight = 2; - cameraPitch = Units.degreesToRadians(20); - targetPitch = Units.degreesToRadians(-10); - - distanceAlongGround = - ComputerVisionUtil.calculateDistanceToTarget( - cameraHeight, targetHeight, cameraPitch, targetPitch, targetYaw); - Assertions.assertEquals(5.671, distanceAlongGround, 0.01); - - cameraHeight = 3; - targetHeight = 1; - cameraPitch = Units.degreesToRadians(0); - targetPitch = Units.degreesToRadians(-30); - - distanceAlongGround = - ComputerVisionUtil.calculateDistanceToTarget( - cameraHeight, targetHeight, cameraPitch, targetPitch, targetYaw); - Assertions.assertEquals(3.464, distanceAlongGround, 0.01); - - cameraHeight = 1; - targetHeight = 3; - cameraPitch = Units.degreesToRadians(0); - targetPitch = Units.degreesToRadians(30); - targetYaw = Units.degreesToRadians(30); - - distanceAlongGround = - ComputerVisionUtil.calculateDistanceToTarget( - cameraHeight, targetHeight, cameraPitch, targetPitch, targetYaw); - Assertions.assertEquals(4, distanceAlongGround, 0.01); - } - - @Test - void testEstimateFieldToRobot() { - var cameraHeight = 1; - var targetHeight = 3; - var cameraPitch = 0; - var targetPitch = Units.degreesToRadians(30); - var targetYaw = new Rotation2d(); - var gyroAngle = new Rotation2d(); - var fieldToTarget = new Pose3d(); - var cameraToRobot = new Transform3d(); - - var distanceAlongGround = - ComputerVisionUtil.calculateDistanceToTarget( - cameraHeight, targetHeight, cameraPitch, targetPitch, targetYaw.getRadians()); - var range = Math.hypot(distanceAlongGround, targetHeight - cameraHeight); - var fieldToRobot = - ComputerVisionUtil.estimateFieldToRobot( - ComputerVisionUtil.estimateCameraToTarget( - new Translation3d(range, new Rotation3d(0.0, targetPitch, targetYaw.getRadians())), - fieldToTarget, - gyroAngle), - fieldToTarget, - cameraToRobot); - - Assertions.assertEquals(-3.464, fieldToRobot.getX(), 0.1); - Assertions.assertEquals(0, fieldToRobot.getY(), 0.1); - Assertions.assertEquals(2.0, fieldToRobot.getZ(), 0.1); - Assertions.assertEquals(0, Units.radiansToDegrees(fieldToRobot.getRotation().getZ()), 0.1); - - gyroAngle = Rotation2d.fromDegrees(-30); - - distanceAlongGround = - ComputerVisionUtil.calculateDistanceToTarget( - cameraHeight, targetHeight, cameraPitch, targetPitch, targetYaw.getRadians()); - range = Math.hypot(distanceAlongGround, targetHeight - cameraHeight); - fieldToRobot = - ComputerVisionUtil.estimateFieldToRobot( - ComputerVisionUtil.estimateCameraToTarget( - new Translation3d(range, new Rotation3d(0.0, targetPitch, targetYaw.getRadians())), - fieldToTarget, - gyroAngle), - fieldToTarget, - cameraToRobot); - - Assertions.assertEquals(-3.0, fieldToRobot.getX(), 0.1); - Assertions.assertEquals(1.732, fieldToRobot.getY(), 0.1); - Assertions.assertEquals(2.0, fieldToRobot.getZ(), 0.1); - Assertions.assertEquals(-30.0, Units.radiansToDegrees(fieldToRobot.getRotation().getZ()), 0.1); + assertEquals( + robot, ComputerVisionUtil.objectToRobotPose(object, cameraToObject, robotToCamera)); } } diff --git a/wpimath/src/test/native/cpp/ComputerVisionUtilTest.cpp b/wpimath/src/test/native/cpp/ComputerVisionUtilTest.cpp index 209407d383..e397af1338 100644 --- a/wpimath/src/test/native/cpp/ComputerVisionUtilTest.cpp +++ b/wpimath/src/test/native/cpp/ComputerVisionUtilTest.cpp @@ -3,95 +3,16 @@ // the WPILib BSD license file in the root directory of this project. #include "frc/ComputerVisionUtil.h" -#include "frc/geometry/Pose2d.h" -#include "frc/geometry/Rotation2d.h" -#include "frc/geometry/Transform2d.h" #include "gtest/gtest.h" -#include "units/angle.h" -#include "units/length.h" -TEST(ComputerVisionUtilTest, CalculateDistanceToTarget) { - auto cameraHeight = 1_m; - auto targetHeight = 3_m; - auto cameraPitch = 0_deg; - auto targetPitch = 30_deg; - auto targetYaw = 0_deg; +TEST(ComputerVisionUtilTest, ObjectToRobotPose) { + frc::Pose3d robot{1_m, 2_m, 0_m, frc::Rotation3d{0_deg, 0_deg, 30_deg}}; + frc::Transform3d cameraToObject{frc::Translation3d{1_m, 1_m, 1_m}, + frc::Rotation3d{0_deg, -20_deg, 45_deg}}; + frc::Transform3d robotToCamera{frc::Translation3d{1_m, 0_m, 2_m}, + frc::Rotation3d{0_deg, 0_deg, 25_deg}}; + frc::Pose3d object = robot + robotToCamera + cameraToObject; - auto distanceAlongGround = frc::CalculateDistanceToTarget( - cameraHeight, targetHeight, cameraPitch, targetPitch, targetYaw); - EXPECT_NEAR(3.464, distanceAlongGround.value(), 0.01); - - cameraHeight = 1_m; - targetHeight = 2_m; - cameraPitch = 20_deg; - targetPitch = -10_deg; - - distanceAlongGround = frc::CalculateDistanceToTarget( - cameraHeight, targetHeight, cameraPitch, targetPitch, targetYaw); - EXPECT_NEAR(5.671, distanceAlongGround.value(), 0.01); - - cameraHeight = 3_m; - targetHeight = 1_m; - cameraPitch = 0_deg; - targetPitch = -30_deg; - - distanceAlongGround = frc::CalculateDistanceToTarget( - cameraHeight, targetHeight, cameraPitch, targetPitch, targetYaw); - EXPECT_NEAR(3.464, distanceAlongGround.value(), 0.01); - - cameraHeight = 1_m; - targetHeight = 3_m; - cameraPitch = 0_deg; - targetPitch = 30_deg; - targetYaw = 30_deg; - - distanceAlongGround = frc::CalculateDistanceToTarget( - cameraHeight, targetHeight, cameraPitch, targetPitch, targetYaw); - EXPECT_NEAR(4, distanceAlongGround.value(), 0.01); -} - -TEST(ComputerVisionUtilTest, EstimateFieldToRobot) { - auto cameraHeight = 1_m; - auto targetHeight = 3_m; - auto cameraPitch = 0_deg; - auto targetPitch = 30_deg; - frc::Rotation2d targetYaw; - frc::Rotation2d gyroAngle; - frc::Pose3d fieldToTarget; - frc::Transform3d cameraToRobot; - - auto distanceAlongGround = - frc::CalculateDistanceToTarget(cameraHeight, targetHeight, cameraPitch, - targetPitch, targetYaw.Radians()); - auto range = - units::math::hypot(distanceAlongGround, targetHeight - cameraHeight); - auto fieldToRobot = frc::EstimateFieldToRobot( - frc::EstimateCameraToTarget( - frc::Translation3d{ - range, frc::Rotation3d{0_rad, targetPitch, targetYaw.Radians()}}, - fieldToTarget, gyroAngle), - fieldToTarget, cameraToRobot); - - EXPECT_NEAR(-3.464, fieldToRobot.X().value(), 0.1); - EXPECT_NEAR(0, fieldToRobot.Y().value(), 0.1); - EXPECT_NEAR(2.0, fieldToRobot.Z().value(), 0.1); - EXPECT_NEAR(0, fieldToRobot.Rotation().Z().value(), 0.1); - - gyroAngle = -30_deg; - - distanceAlongGround = - frc::CalculateDistanceToTarget(cameraHeight, targetHeight, cameraPitch, - targetPitch, targetYaw.Radians()); - range = units::math::hypot(distanceAlongGround, targetHeight - cameraHeight); - fieldToRobot = frc::EstimateFieldToRobot( - frc::EstimateCameraToTarget( - frc::Translation3d{ - range, frc::Rotation3d{0_rad, targetPitch, targetYaw.Radians()}}, - fieldToTarget, gyroAngle), - fieldToTarget, cameraToRobot); - - EXPECT_NEAR(-3.0, fieldToRobot.X().value(), 0.1); - EXPECT_NEAR(1.732, fieldToRobot.Y().value(), 0.1); - EXPECT_NEAR(2.0, fieldToRobot.Z().value(), 0.1); - EXPECT_NEAR(-30.0, units::degree_t{fieldToRobot.Rotation().Z()}.value(), 0.1); + EXPECT_EQ(robot, + frc::ObjectToRobotPose(object, cameraToObject, robotToCamera)); }