メインコンテンツまでスキップ

FTC Java & Blocklyプログラミングガイド

(Blocklyのスクリーンショットは近日公開予定です!)

FTCプログラミングクイックスタートを必ず読んでください。

Javadoc: [Javadocへのリンクは近日公開予定]

完全な例のリポジトリ: 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にデータを要求する頻度を設定します(1秒間に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から数値を取得する方法です:

// 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パイプラインの「Advanced」タブで「Full 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結果

フィデューシャルは、Limelightが位置を特定するのに役立つ特殊なマーカー(AprilTagなど)です:

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("スナップショット", "すべてクリアしました!");