跳到主要内容

FTC Java和Blockly编程指南

(Blockly截图即将推出!)

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

FTC Limelight Javadoc文档:Javadoc

基础FTC示例:FTC示例

完整示例代码库: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.getPosition().x;
double y = botpose.getPosition().y;
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.getPosition().x;
double y = botpose_mt2.getPosition().y;
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("快照", "全部清除!");