|
| 1 | +package org.firstinspires.ftc.teamcode.lib.subsystems; |
| 2 | + |
| 3 | +import com.qualcomm.hardware.sparkfun.SparkFunOTOS; |
| 4 | +import com.qualcomm.robotcore.hardware.HardwareMap; |
| 5 | + |
| 6 | +import org.firstinspires.ftc.robotcore.external.Telemetry; |
| 7 | +import org.firstinspires.ftc.robotcore.external.hardware.camera.BuiltinCameraDirection; |
| 8 | +import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName; |
| 9 | +import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit; |
| 10 | +import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit; |
| 11 | +import org.firstinspires.ftc.robotcore.external.navigation.Position; |
| 12 | +import org.firstinspires.ftc.robotcore.external.navigation.YawPitchRollAngles; |
| 13 | +import org.firstinspires.ftc.vision.VisionPortal; |
| 14 | +import org.firstinspires.ftc.vision.apriltag.AprilTagDetection; |
| 15 | +import org.firstinspires.ftc.vision.apriltag.AprilTagProcessor; |
| 16 | + |
| 17 | +import java.util.List; |
| 18 | + |
| 19 | +public class LocalisationSubsystem { |
| 20 | + private static final boolean USE_WEBCAM = true; |
| 21 | + private static final double OTOS_LINEAR_SCALAR = 1.0; // todo: find exact value, need robot physically |
| 22 | + private static final double OTOS_ANGULAR_SCALAR = 1.0; // todo: find exact value, need robot physically |
| 23 | + private static final SparkFunOTOS.Pose2D OTOS_OFFSET = new SparkFunOTOS.Pose2D(0, 0, 0); // todo: find exact values, need robot physically |
| 24 | + |
| 25 | + private static final Position CAMERA_POSITION = new Position(DistanceUnit.METER, |
| 26 | + 0, 0, 0, 0); |
| 27 | + |
| 28 | + // pitch was init'd as -90 in demo, but set to 0 here for now (-90 made no sense) |
| 29 | + private static final YawPitchRollAngles CAMERA_ORIENTATION = new YawPitchRollAngles(AngleUnit.DEGREES, |
| 30 | + 0, -90, 0, 0); |
| 31 | + |
| 32 | + private final SparkFunOTOS otosSensor; |
| 33 | + private final Telemetry telemetry; |
| 34 | + private AprilTagProcessor aprilTag; |
| 35 | + private VisionPortal visionPortal; |
| 36 | + HardwareMap hardwareMap; |
| 37 | + |
| 38 | + // ROBOT X, Y and HDG |
| 39 | + public double robotX = 0.0; |
| 40 | + public double robotY = 0.0; |
| 41 | + public double robotH = 0.0; |
| 42 | + |
| 43 | + // STATES |
| 44 | + public final boolean hasPositionedFromCamera = false; // public, as there can be no turret rotation until the first camera reading has been made, and this var determines this. |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | + public LocalisationSubsystem(HardwareMap hardwareMap, Telemetry telemetry) { |
| 49 | + this.telemetry = telemetry; |
| 50 | + this.hardwareMap = hardwareMap; |
| 51 | + |
| 52 | + configureAprilTag(); |
| 53 | + |
| 54 | + otosSensor = hardwareMap.get(SparkFunOTOS.class, "sensor_otos"); |
| 55 | + configureOTOS(); |
| 56 | + |
| 57 | + telemetry.addLine("Localisation Subsystem Configured"); |
| 58 | + telemetry.update(); |
| 59 | + } |
| 60 | + |
| 61 | + public void periodic() { |
| 62 | + if (!hasPositionedFromCamera) { |
| 63 | + List<AprilTagDetection> freshDetections = aprilTag.getFreshDetections(); |
| 64 | + |
| 65 | + if (!freshDetections.isEmpty()) { |
| 66 | + // we cannot assume that there is only one detection, but we do know the last one is the latest. therefore, by going through all of them, we can know we are up to date. |
| 67 | + for (AprilTagDetection detection : freshDetections) { |
| 68 | + // as shown by demo code - no "Obelisk" tags can be used for positioning |
| 69 | + if (!detection.metadata.name.contains("Obelisk")) { |
| 70 | + robotX = detection.robotPose.getPosition().x; |
| 71 | + robotY = detection.robotPose.getPosition().y; |
| 72 | + robotH = detection.robotPose.getOrientation().getYaw(AngleUnit.DEGREES); |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + // update the OTOS's position data |
| 77 | + updateOTOS(); |
| 78 | + telemetry.addData("AprilUpdate", true); |
| 79 | + |
| 80 | + hasPositionedFromCamera = true; |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + positionFromOTOS(); |
| 85 | + telemetry.addData("AprilUpdate", false); |
| 86 | + |
| 87 | + telemetry.addData("RobotX", robotX); |
| 88 | + telemetry.addData("RobotY", robotY); |
| 89 | + telemetry.addData("RobotHDG", robotH); |
| 90 | + telemetry.update(); |
| 91 | + } |
| 92 | + |
| 93 | + /** |
| 94 | + * Updates the OTOS sensor to be at the camera's reported position. |
| 95 | + */ |
| 96 | + private void updateOTOS() { |
| 97 | + SparkFunOTOS.Pose2D currentPosition = new SparkFunOTOS.Pose2D( |
| 98 | + robotX, |
| 99 | + robotY, |
| 100 | + robotH |
| 101 | + ); |
| 102 | + otosSensor.setPosition(currentPosition); |
| 103 | + } |
| 104 | + |
| 105 | + /** |
| 106 | + * Updates the robot's position from the OTOS's reported position. |
| 107 | + */ |
| 108 | + private void positionFromOTOS() { |
| 109 | + SparkFunOTOS.Pose2D currentPosition = otosSensor.getPosition(); |
| 110 | + robotX = currentPosition.x; |
| 111 | + robotY = currentPosition.y; |
| 112 | + robotH = currentPosition.h; |
| 113 | + } |
| 114 | + |
| 115 | + private void configureOTOS() { |
| 116 | + // UNIT CALIBRATION |
| 117 | + otosSensor.setLinearUnit(DistanceUnit.METER); |
| 118 | + otosSensor.setAngularUnit(AngleUnit.DEGREES); |
| 119 | + |
| 120 | + // OFFSET ON ROBOT |
| 121 | + otosSensor.setOffset(OTOS_OFFSET); |
| 122 | + |
| 123 | + // SCALAR CALIBRATION |
| 124 | + otosSensor.setLinearScalar(OTOS_LINEAR_SCALAR); |
| 125 | + otosSensor.setAngularScalar(OTOS_ANGULAR_SCALAR); |
| 126 | + |
| 127 | + // CALIBRATE IMU |
| 128 | + otosSensor.calibrateImu(); |
| 129 | + otosSensor.resetTracking(); |
| 130 | + |
| 131 | + // starting position, will be updated by april tag positioning. |
| 132 | + SparkFunOTOS.Pose2D currentPosition = new SparkFunOTOS.Pose2D(0, 0, 0); |
| 133 | + otosSensor.setPosition(currentPosition); |
| 134 | + |
| 135 | + telemetry.addLine("OTOS Configured"); |
| 136 | + telemetry.update(); |
| 137 | + } |
| 138 | + |
| 139 | + |
| 140 | + private void configureAprilTag() { |
| 141 | + // april tag processor |
| 142 | + aprilTag = new AprilTagProcessor.Builder() |
| 143 | + .setCameraPose(CAMERA_POSITION, CAMERA_ORIENTATION) |
| 144 | + .build(); |
| 145 | + |
| 146 | + // vision portal |
| 147 | + VisionPortal.Builder builder = new VisionPortal.Builder(); |
| 148 | + |
| 149 | + // NOTE: USE_WEBCAM is set at top of class. idk what to set it, so it is currently true as the example code used that. |
| 150 | + if (USE_WEBCAM) { |
| 151 | + builder.setCamera(hardwareMap.get(WebcamName.class, "webcam")); |
| 152 | + } else { |
| 153 | + builder.setCamera(BuiltinCameraDirection.FRONT); // used to be BACK in e.g. code |
| 154 | + } |
| 155 | + |
| 156 | + // enable the processor |
| 157 | + builder.addProcessor(aprilTag); |
| 158 | + visionPortal = builder.build(); |
| 159 | + telemetry.addLine("April Tag Process Configured"); |
| 160 | + telemetry.update(); |
| 161 | + } |
| 162 | +} |
0 commit comments