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

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にデータを要求する頻度を設定します(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への数値送信
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.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結果

フィデューシャルは、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 + " ms)");
}

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