6
6
import com .qualcomm .robotcore .eventloop .opmode .Autonomous ;
7
7
import com .qualcomm .robotcore .eventloop .opmode .LinearOpMode ;
8
8
9
+ import org .firstinspires .ftc .robotcore .external .ClassFactory ;
9
10
import org .firstinspires .ftc .robotcore .external .Telemetry ;
11
+ import org .firstinspires .ftc .robotcore .external .hardware .camera .CameraName ;
10
12
import org .firstinspires .ftc .robotcore .external .hardware .camera .WebcamName ;
11
13
import org .firstinspires .ftc .teamcode .drive .SampleMecanumDrive ;
12
14
import org .firstinspires .ftc .vision .VisionPortal ;
18
20
19
21
@ Autonomous (name = "AprilTag Test" )
20
22
public class ApriltagTest extends LinearOpMode {
21
- @ Override
22
- public void runOpMode () throws InterruptedException {
23
- Telemetry telemetry = new MultipleTelemetry (this .telemetry , FtcDashboard .getInstance ().getTelemetry ());
24
- SampleMecanumDrive drive = new SampleMecanumDrive (hardwareMap );
23
+ private VisionPortal visionPortal ;
24
+ private AprilTagProcessor aprilTag ;
25
25
26
- AprilTagProcessor aprilTag = new AprilTagProcessor .Builder ().build ();
27
- VisionPortal .Builder builder = new VisionPortal .Builder ();
28
- builder .setCamera (hardwareMap .get (WebcamName .class , "Webcam 1" ));
29
- builder .addProcessor (aprilTag );
30
- VisionPortal visionPortal = builder .build ();
26
+ /**
27
+ * The array of webcams that will be used in the pipeline
28
+ */
29
+ private WebcamName [] webcams ;
31
30
32
31
32
+ /**
33
+ * Transform the angle into the first quadrant
34
+ */
35
+ private double quadrantTransform (double angle ) {
36
+ if (angle < 0 || angle > 360 ) {
37
+ throw new IllegalArgumentException ("Angle must be between 0 and 360" );
38
+ }
33
39
34
- waitForStart ();
35
-
36
- while (opModeIsActive ()) {
37
- //drive.setPoseEstimate(new Pose2d(0, 0, 0));
38
- //drive.update();
39
- List <AprilTagDetection > currentDetections = aprilTag .getDetections ();
40
- telemetry .addData ("# AprilTags Detected" , currentDetections .size ());
41
-
42
- for (AprilTagDetection detection : currentDetections ) {
43
- if (detection .metadata != null ) {
44
- //double theta_need = detection.ftcPose.yaw + detection.ftcPose.bearing;
45
- //double theta_need_rad = Math.toRadians(theta_need);
46
- telemetry .addData ("Range" , detection .ftcPose .range );
47
- //telemetry.addData("Theta Need", theta_need);
48
- telemetry .addData ("Yaw" , detection .ftcPose .yaw );
49
- telemetry .addData ("Bearing" , detection .ftcPose .bearing );
50
- telemetry .addData ("X, Y" , detection .ftcPose .x + ", " + detection .ftcPose .y );
51
- double thetaNeed = detection .ftcPose .yaw - detection .ftcPose .bearing ;
52
- telemetry .addData ("Theta Need" , thetaNeed );
53
- double a = detection .ftcPose .range * Math .cos (Math .toRadians (thetaNeed ));
54
- double b = detection .ftcPose .range * Math .sin (Math .toRadians (thetaNeed ));
55
- telemetry .addData ("a" , a );
56
- telemetry .addData ("b" , b );
57
- //double absolutex = detection.metadata.fieldPosition.get(0) + a; // + for lower side
58
- //double absolutey = detection.metadata.fieldPosition.get(1) - b; // - for lower side
40
+ if (angle < 90 ) {
41
+ return angle ;
42
+ } else if (angle < 180 ) {
43
+ return 180 - angle ;
44
+ } else if (angle < 270 ) {
45
+ return angle - 180 ;
46
+ } else {
47
+ return 360 - angle ;
48
+ }
49
+ }
59
50
60
- //Vector2d absVector = new Vector2d(detection.metadata.fieldPosition.get(0) + a, detection.metadata.fieldPosition.get(1) - b);
61
- //absVector.rotated(detection.metadata.fieldOrientation.toOrientation(AxesReference.INTRINSIC, AxesOrder.XYZ, AngleUnit.RADIANS).thirdAngle);
51
+ /**
52
+ * Switch to the camera at the given index in `webcams`
53
+ * @param cameraIndex the index of the camera to switch to
54
+ */
55
+ private void switchCamera (int cameraIndex ) {
56
+ // Wait for the visionPortal status to be STREAMING before we attempt to switch cameras
57
+ android .util .Log .i ("AprilTag Test" , "Waiting for camera to start STREAMING before switching cameras" );
58
+ while (visionPortal .getCameraState () != VisionPortal .CameraState .STREAMING ) {
59
+ sleep (10 );
60
+ }
61
+ android .util .Log .i ("AprilTag Test" , "Camera is STREAMING, switching cameras..." );
62
+ // Get the current camera
63
+ CameraName currentCamera = visionPortal .getActiveCamera ();
64
+ // Find the index of the current camera in the array
65
+ int currentCameraIndex = -1 ;
66
+ for (int i = 0 ; i < webcams .length ; i ++) {
67
+ if (webcams [i ] == currentCamera ) {
68
+ currentCameraIndex = i ;
69
+ break ;
70
+ }
71
+ }
72
+ // If the current camera is not the one we want to switch to, switch to it
73
+ if (currentCameraIndex != cameraIndex ) {
74
+ visionPortal .setActiveCamera (webcams [cameraIndex ]);
75
+ // Flush any detections from the previous camera
76
+ aprilTag .getFreshDetections ();
77
+ }
78
+ }
62
79
63
- //int[] NEED_TO_ROTATE = {1,2,3,4,5,6};
64
- List <Integer > NEED_TO_ROTATE = Arrays .asList (1 ,2 ,3 ,4 ,5 ,6 );
80
+ private Pose2d estimateCameraPoseFromAprilTags (int camera ) {
81
+ // Switch to the camera
82
+ switchCamera (camera );
83
+ // Get the detections
84
+ List <AprilTagDetection > detections = aprilTag .getFreshDetections ();
85
+ // Wait for detections to not be null
86
+ // TODO: Timeout
87
+ while (detections == null ) {
88
+ detections = aprilTag .getFreshDetections ();
89
+ }
90
+ // If there are no detections, return null
91
+ // TODO: Multiple tries?
92
+ if (detections .size () == 0 ) {
93
+ return null ;
94
+ }
95
+ // Get the first detection
96
+ // TODO: Pick the best detection?
97
+ AprilTagDetection detection = detections .get (0 );
98
+
99
+ double thetaNeed = detection .ftcPose .yaw - detection .ftcPose .bearing ;
100
+ double a = detection .ftcPose .range * Math .cos (Math .toRadians (thetaNeed ));
101
+ double b = detection .ftcPose .range * Math .sin (Math .toRadians (thetaNeed ));
102
+
103
+ List <Integer > NEED_TO_ROTATE = Arrays .asList (1 , 2 , 3 , 4 , 5 , 6 );
104
+
105
+ double absX , absY , absRot ;
106
+ if (NEED_TO_ROTATE .contains (detection .id )) {
107
+ absX = detection .metadata .fieldPosition .get (0 ) - a ; // + for lower side
108
+ absY = detection .metadata .fieldPosition .get (1 ) + b ; // - for lower side
109
+ absRot = 180 - detection .ftcPose .yaw + 180 ;
110
+ } else {
111
+ absX = detection .metadata .fieldPosition .get (0 ) + a ; // + for lower side
112
+ absY = detection .metadata .fieldPosition .get (1 ) - b ; // - for lower side
113
+ absRot = 180 - detection .ftcPose .yaw ;
114
+ }
65
115
66
- double absX , absY , absRot ;
67
- if (NEED_TO_ROTATE .contains (detection .id )) {
68
- absX = detection .metadata .fieldPosition .get (0 ) - a ; // + for lower side
69
- absY = detection .metadata .fieldPosition .get (1 ) + b ; // - for lower side
70
- absRot = 180 -detection .ftcPose .yaw + 180 ;
116
+ absRot = absRot % 360 ;
71
117
72
- } else {
73
- absX = detection .metadata .fieldPosition .get (0 ) + a ; // + for lower side
74
- absY = detection .metadata .fieldPosition .get (1 ) - b ; // - for lower side
75
- absRot = 180 -detection .ftcPose .yaw ;
76
- }
118
+ return new Pose2d (absX , absY , Math .toRadians (absRot ));
119
+ }
120
+ @ Override
121
+ public void runOpMode () throws InterruptedException {
122
+ Telemetry telemetry = new MultipleTelemetry (this .telemetry , FtcDashboard .getInstance ().getTelemetry ());
123
+ SampleMecanumDrive drive = new SampleMecanumDrive (hardwareMap );
77
124
125
+ aprilTag = new AprilTagProcessor .Builder ().build ();
78
126
127
+ // Add the cameras to the array
128
+ webcams = new WebcamName [3 ];
129
+ webcams [0 ] = hardwareMap .get (WebcamName .class , "Webcam 1" );
130
+ webcams [1 ] = hardwareMap .get (WebcamName .class , "Webcam 2" );
131
+ webcams [2 ] = hardwareMap .get (WebcamName .class , "Webcam 3" );
79
132
80
- //telemetry.addData("First Angle", detection.metadata.fieldOrientation.toOrientation(AxesReference.EXTRINSIC, AxesOrder.XYZ, AngleUnit.DEGREES).firstAngle);
81
- //telemetry.addData("Second Angle", detection.metadata.fieldOrientation.toOrientation(AxesReference.EXTRINSIC, AxesOrder.XYZ, AngleUnit.DEGREES).secondAngle );
82
- //telemetry.addData("Third Angle", detection.metadata.fieldOrientation.toOrientation(AxesReference.EXTRINSIC, AxesOrder.XYZ, AngleUnit.DEGREES).thirdAngle);
133
+ CameraName switchableCamera = ClassFactory .getInstance ()
134
+ .getCameraManager ().nameForSwitchableCamera (webcams );
83
135
136
+ // Create the vision portal by using a builder.
137
+ visionPortal = new VisionPortal .Builder ()
138
+ .setCamera (switchableCamera )
139
+ .addProcessor (aprilTag )
140
+ .build ();
84
141
142
+ android .util .Log .i ("AprilTag Test" , "Waiting for start..." );
143
+ switchCamera (1 );
144
+ waitForStart ();
85
145
86
- //telemetry.addData("position x, position y", absolutex + "," + absolutey);
146
+ while (opModeIsActive ()) {
147
+ Pose2d cameraPose = estimateCameraPoseFromAprilTags (1 );
87
148
149
+ if (cameraPose != null ) {
150
+ android .util .Log .i ("AprilTag Test" , "Camera pose: " + cameraPose );
88
151
89
- telemetry .addData ("Field Centric Deg" , 180 - detection .ftcPose .yaw );
152
+ double CAMERA_OFFSET = 10 ; // Center camera
153
+ double CAMERA_ANGLE = Math .toRadians (50 );
90
154
91
- Pose2d absPose = new Pose2d (absX , absY , Math .toRadians (absRot ));
155
+ double offsetX = CAMERA_OFFSET * Math .cos (cameraPose .getHeading ());
156
+ double offsetY = CAMERA_OFFSET * Math .sin (cameraPose .getHeading ());
92
157
93
- //Vector2d vec = new Vector2d(absolutex, absolutey).rotated(180);
94
- //Pose2d poseFlipped = new Pose2d(vec.getX(),vec.getY(), Math.toRadians(-detection.ftcPose.yaw));
158
+ android .util .Log .i ("AprilTag Test" , "Camera offset: " + offsetX + ", " + offsetY );
95
159
96
- drive .setPoseEstimate (absPose );
97
- //drive.update();
160
+ double robotX = cameraPose .getX () - offsetX ;
161
+ double robotY = cameraPose .getY () - offsetY ;
162
+ double robotHeading = cameraPose .getHeading () - CAMERA_ANGLE ;
98
163
164
+ Pose2d robotPose = new Pose2d (robotX , robotY , robotHeading );
99
165
100
- //telemetry.addData("Relative X", detection.ftcPose.range * Math.cos(Math.toRadians(detection.ftcPose.yaw)));
101
- //telemetry.addData("Relative Y", detection.ftcPose.range * Math.sin(Math.toRadians(detection.ftcPose.yaw)));
166
+ android .util .Log .i ("AprilTag Test" , "Robot pose: " + robotPose );
102
167
103
- //Orientation.getOrientation()
104
- //telemetry.addLine(String.format("\n==== (ID %d) %s", detection.id, detection.metadata.name));
105
- //telemetry.addLine(String.format("XYZ %6.1f %6.1f %6.1f (inch)", detection.ftcPose.x, detection.ftcPose.y, detection.ftcPose.z));
168
+ drive .setPoseEstimate (robotPose );
106
169
107
- //telemetry.addLine(String.format("Field XYZ %6.1f %6.1f %6.1f (inch)", detection.metadata.fieldPosition.get(0), detection.metadata.fieldPosition.get(1), detection.metadata.fieldPosition.get(2)));
108
- }
109
- //telemetry.update();
170
+ drive .update ();
110
171
}
111
-
112
- drive .update ();
113
-
114
172
}
115
173
116
174
visionPortal .close ();
117
-
118
175
}
119
- }
176
+ }
0 commit comments