跳到主要内容

FTC Java 和 Blockly 编程指南

(Blockly 截图即将推出!)

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

Javadoc: [Javadoc 链接即将推出]

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

成功提示

  • 先做简单的事。在 FRC 中,我们了解到最好的软件团队通常使用最简单的方法。例如,FRC 2056 队在 2024 年使用标准的 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("目标 X", tx);
telemetry.addData("目标 Y", ty);
telemetry.addData("目标面积", ta);
} else {
telemetry.addData("Limelight", "无目标");
}

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 输出:", firstOutput);
}

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

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

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

  1. 在网页界面的 AprilTag 管道的"高级"选项卡中启用"完全 3D"。
  2. 使用网页界面将相机相对于机器人足迹中心定位。

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

  • (0,0,0) 是场地地板的中心
  • 对于非菱形配置,0 度偏航意味着蓝色联盟在机器人的左侧,红色联盟在机器人的右侧。
if (result != null && result.isValid()) {
Pose3D botpose = result.getBotpose();
if (botpose != null) {
double x = botpose.getX();
double y = botpose.getY();
telemetry.addData("MT1 位置", "(" + 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.getX();
double y = botpose_mt2.getY();
telemetry.addData("MT2 位置:", "(" + 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("颜色目标", "占图像的 " + area + "%");
}

7.2 基准/AprilTag 结果

基准是特殊的标记(如 AprilTags),可帮助 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("基准 " + id, "距离 " + distance + " 米");
}

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

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

7.3 条形码结果

Limelight 的条形码管道擅长检测和跟踪 QR 码。

List<BarcodeResult> barcodes = result.getBarcodeResults();
for (BarcodeResult barcode : barcodes) {
String data = barcode.getData(); // 条形码的内容
String family = barcode.getFamily(); // 条形码的类型
telemetry.addData("条形码", 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("我看到一个", 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, "在 (" + x + ", " + y + ") 度");
}

8. 数据是否新鲜?

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

long staleness = result.getStaleness();
if (staleness < 100) { // 少于 100 毫秒
telemetry.addData("数据", "良好");
} else {
telemetry.addData("数据", "过时 (" + staleness + " 毫秒)");
}

9. 自定义场地地图

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

LLFieldMap fieldMap = new LLFieldMap(); // 您需要用场地数据填充这个
boolean success = limelight.uploadFieldmap(fieldMap, null); // null 表示使用默认槽位
if (success) {
telemetry.addData("场地地图", "上传成功!");
} else {
telemetry.addData("场地地图", "糟糕,上传失败");
}

10. 拍摄快照:

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

limelight.captureSnapshot("auto_pov_10s");

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

要清除旧快照:

limelight.deleteSnapshots();
telemetry.addData("快照", "全部清除!");