跳到主要内容

FTC Java 和 Blockly 编程指南

(Blockly 截图即将推出!)

请确保您已阅读 FTC 编程快速入门

FTC Limelight Javadoc:Javadoc

基础 FTC 示例:FTC 示例

完整示例仓库:Limelight FTC 示例仓库

成功秘诀

  • 先做简单的事情。在 FRC 中,我们了解到最优秀的软件团队往往使用最简单的方法。例如,2024 年 FRC 2056 队使用标准的 90FPS 颜色管道而不是神经网络来追踪游戏元件。

以下是您开始编程时应该问的问题类型示例:在遥控操作期间,您是否需要知道机器人在场地上的位置,还是只需要横移直到十字准星对准特定标签(strafeSpeed = result.getTx()*.03)?

关键概念

1. 初始化

我们需要在机器人代码中设置 Limelight3A。

import com.qualcomm.hardware.limelightvision.LLResult;
import com.qualcomm.hardware.limelightvision.LLResultTypes;
import com.qualcomm.hardware.limelightvision.LLStatus;
import com.qualcomm.hardware.limelightvision.Limelight3A;
Limelight3A limelight;

@Override
public void init() {
limelight = hardwareMap.get(Limelight3A.class, "limelight");
limelight.setPollRateHz(100); // 设置我们向 Limelight 请求数据的频率(每秒 100 次)
limelight.start(); // 告诉 Limelight 开始检测!
}

2. 管道管理

管道就像小型的、可即时切换的程序,改变 Limelight 观察世界的方式。您可以在 Limelight 网页界面中设置 10 个不同的管道,每个用于不同的任务。以下是切换管道的方法:

limelight.pipelineSwitch(0); // 切换到管道 0

这是即发即忘的操作。Limelight 会在几毫秒内更改管道,但您的代码不会等待此操作完成就继续执行。如果您想检查当前管道索引,请调用

result.getPipelineIndex()

请参阅下一节了解如何获取 LLResult 对象。

3. 获取和使用结果

LLResult 就像一个装满 Limelight 所见信息的容器。以下是我们获取和使用该信息的方法:

LLResult result = limelight.getLatestResult();
if (result != null && result.isValid()) {
double tx = result.getTx(); // 目标左右偏移量(度)
double ty = result.getTy(); // 目标上下偏移量(度)
double ta = result.getTa(); // 目标看起来有多大(图像的 0%-100%)

telemetry.addData("Target X", tx);
telemetry.addData("Target Y", ty);
telemetry.addData("Target Area", ta);
} else {
telemetry.addData("Limelight", "No Targets");
}

4. 与 Python SnapScripts 通信

您可以在网页界面中编写自己的 Python SnapScript 管道。使用我们基于 LLM 的 SnapScript 生成器来帮助您编写代码。

以下是如何从机器人代码向 Python 发送数字并获取返回数字的方法:

// 向 Python 发送数字
double[] inputs = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0};
limelight.updatePythonInputs(inputs);

// 从 Python 获取数字
double[] pythonOutputs = result.getPythonOutput();
if (pythonOutputs != null && pythonOutputs.length > 0) {
double firstOutput = pythonOutputs[0];
telemetry.addData("Python output:", firstOutput);
}

5. 我的机器人在哪里?(MegaTag 1)

Limelight 可以使用 AprilTag 帮助确定您的机器人在场地上的位置。您的 Limelight 预装了当前比赛的 AprilTag 地图,但您可以使用我们的 3D 地图构建器设计和上传自己的地图。

在尝试获取机器人位置之前,请执行以下操作:

  1. 在网页界面的 AprilTag 管道"高级"选项卡中启用"Full 3D"。
  2. 使用网页界面将相机相对于机器人底盘中心进行定位。

botPose 的坐标系与标准 FTC 坐标系匹配。

  • (0,0,0) 是场地地面的中心
  • 对于非菱形配置,0 度偏航角意味着蓝色联盟在机器人左侧,红色联盟在机器人右侧。
if (result != null && result.isValid()) {
Pose3D botpose = result.getBotpose();
if (botpose != null) {
double x = botpose.getPosition().x;
double y = botpose.getPosition().y;
telemetry.addData("MT1 Location", "(" + x + ", " + y + ")");
}
}

6. 我的机器人在哪里?(MegaTag 2)

MegaTag 2 类似于 MegaTag 1,但它融合了您的 IMU 数据以提高精度:

// 首先,告诉 Limelight 您的机器人面向哪个方向
double robotYaw = imu.getAngularOrientation().firstAngle;
limelight.updateRobotOrientation(robotYaw);
if (result != null && result.isValid()) {
Pose3D botpose_mt2 = result.getBotpose_MT2();
if (botpose_mt2 != null) {
double x = botpose_mt2.getPosition().x;
double y = botpose_mt2.getPosition().y;
telemetry.addData("MT2 Location:", "(" + x + ", " + y + ")");
}
}

7. 内部结果类型

根据您配置管道的方式,您将可以访问父 LLResults 对象中不同类型的详细结果列表。

您可能不需要使用这些结果列表。我们建议尽可能使用基本的 getTx()、getTy()。

7.1 颜色结果

颜色结果帮助查找彩色目标:

List<ColorResult> colorTargets = result.getColorResults();
for (ColorResult colorTarget : colorTargets) {
double x = detection.getTargetXDegrees(); // 位置(左右)
double y = detection.getTargetYDegrees(); // 位置(上下)
double area = colorTarget.getTargetArea(); // 大小(0-100)
telemetry.addData("Color Target", "takes up " + area + "% of the image");
}

7.2 基准标记/AprilTag 结果

基准标记是特殊标记(如 AprilTag),帮助 Limelight 确定其位置:

List<FiducialResult> fiducials = result.getFiducialResults();
for (FiducialResult fiducial : fiducials) {
int id = fiducial.getFiducialId(); // 基准标记的 ID 号
double x = detection.getTargetXDegrees(); // 位置(左右)
double y = detection.getTargetYDegrees(); // 位置(上下)
double StrafeDistance_3D = fiducial.getRobotPoseTargetSpace().getY();
telemetry.addData("Fiducial " + id, "is " + distance + " meters away");
}

如果您想利用每个 FiducialResult 中的 3D 位姿信息,可以使用以下方法:

fiducial.getRobotPoseTargetSpace(); // 机器人相对于 AprilTag 坐标系的位姿(最有用)
fiducial.getCameraPoseTargetSpace(); // 相机相对于 AprilTag 的位姿(有用)
fiducial.getRobotPoseFieldSpace(); // 仅基于此标签的机器人在场地坐标系中的位姿(有用)
fiducial.getTargetPoseCameraSpace(); // AprilTag 在相机坐标系中的位姿(不太有用)
fiducial.getTargetPoseRobotSpace(); // AprilTag 在机器人坐标系中的位姿(不太有用)

7.3 条形码结果

Limelight 的条形码管道擅长检测和追踪二维码。

List<BarcodeResult> barcodes = result.getBarcodeResults();
for (BarcodeResult barcode : barcodes) {
String data = barcode.getData(); // 条形码内容
String family = barcode.getFamily(); // 条形码类型
telemetry.addData("Barcode", data + " (" + family + ")");
}

7.4 分类器结果

神经分类器允许 Limelight 说"我认为这是一张...的图像"。

List<ClassifierResult> classifications = result.getClassifierResults();
for (ClassifierResult classification : classifications) {
String className = classification.getClassName(); // Limelight 认为它看到了什么
double confidence = classification.getConfidence(); // 置信度分数
telemetry.addData("I see a", className + " (" + confidence + "%)");
}

7.5 检测器结果

检测器查找特定对象并告诉我们它们在哪里:

List<DetectorResult> detections = result.getDetectorResults();
for (DetectorResult detection : detections) {
String className = detection.getClassName(); // 检测到了什么
double x = detection.getTargetXDegrees(); // 位置(左右)
double y = detection.getTargetYDegrees(); // 位置(上下)
telemetry.addData(className, "at (" + x + ", " + y + ") degrees");
}

8. 数据是否新鲜?

有时,我们想知道结果数据的年龄(以毫秒为单位)。

long staleness = result.getStaleness();
if (staleness < 100) { // 小于 100 毫秒
telemetry.addData("Data", "Good");
} else {
telemetry.addData("Data", "Old (" + staleness + " ms)");
}

9. 自定义场地地图

您可以告诉 Limelight 您的特定场地布局:

LLFieldMap fieldMap = new LLFieldMap(); // 您需要用场地数据填充它
boolean success = limelight.uploadFieldmap(fieldMap, null); // null 表示使用默认槽位
if (success) {
telemetry.addData("Field Map", "Uploaded successfully!");
} else {
telemetry.addData("Field Map", "Oops, upload failed");
}

10. 拍摄快照:

Limelight 可以拍摄快照,帮助您在场外调试管道:

limelight.captureSnapshot("auto_pov_10s");

在网页界面的输入选项卡中,您可以选择此快照作为"图像源"来检查/调整您的管道。

要清除旧快照:

limelight.deleteSnapshots();
telemetry.addData("Snapshots", "All cleared out!");