使用 Limelight Lib 进行 FRC 编程 (WPILib Java & C++)
- https://github.com/LimelightVision/limelightlib-wpijava
- https://github.com/LimelightVision/limelightlib-wpicpp
JavaDocs: https://limelightlib-wpijava-reference.limelightvision.io
使用方法
这是一个单文件库。你只需要从最新版本 (https://github.com/LimelightVision/limelightlib-wpijava/releases) 中复制 LimelightHelpers.java 文件到你的 Java 项目的 "robot" 文件夹中。你不需要为你的 Limelight 创建任何对象 - 这个库简单且实用,最大限度地提高了易用性和可靠性。
成功技巧
- 从简单开始!许多成功的 FRC 团队有效地使用基本方法。例如,2056 队在 2024 年使用标准的 90FPS 颜色管道而不是神经网络来跟踪游戏部件。
- 思考你真正需要什么:你是需要完整的场地定位,还是简单的目标居中就足够了(例如,
driveSpeed = result.getTx() * 0.03
)?
关键概念
1. 基本用法
每个方法都接受一个Limelight名称参数。留空或设为null以使用"limelight":
// 基本目标数据
double tx = LimelightHelpers.getTX(""); // 从准星到目标的水平偏移(度)
double ty = LimelightHelpers.getTY(""); // 从准星到目标的垂直偏移(度)
double ta = LimelightHelpers.getTA(""); // 目标面积(图像的0%到100%)
boolean hasTarget = LimelightHelpers.getTV(""); // 是否有有效目标?
double txnc = LimelightHelpers.getTXNC(""); // 从主像素/点到目标的水平偏移(度)
double tync = LimelightHelpers.getTYNC(""); // 从主像素/点到目标的垂直偏移(度)
2. 管道管理
管道就像可即时切换的程序,可以改变Limelight处理图像的方式。您可以在网页界面中设置10个不同的管道:
// 切换到管道0
LimelightHelpers.setPipelineIndex("", 0);
3. LED控制
对于带有明亮照明LED的Limelight,您可以在不同情况下控制LED:
// 让当前管道控制LED
LimelightHelpers.setLEDMode_PipelineControl("");
// 强制LED开启/关闭/闪烁
LimelightHelpers.setLEDMode_ForceOn("");
LimelightHelpers.setLEDMode_ForceOff("");
LimelightHelpers.setLEDMode_ForceBlink("");
4. 使用MegaTag进行场地定位
(更多详情请参阅MegaTag文档和Swerve姿态估计教程。)
// 在您的周期函数中:
LimelightHelpers.PoseEstimate limelightMeasurement = LimelightHelpers.getBotPoseEstimate_wpiBlue("limelight");
if (limelightMeasurement.tagCount >= 2) { // 只有看到多个标签时才信任测量
m_poseEstimator.setVisionMeasurementStdDevs(VecBuilder.fill(0.7, 0.7, 9999999));
m_poseEstimator.addVisionMeasurement(
limelightMeasurement.pose,
limelightMeasurement.timestampSeconds
);
}
5. 使用MegaTag2进行场地定位
(更多详情请参阅MegaTag2文档和Swerve姿态估计教程。)
MegaTag2通过融合机器人方向数据与视觉来增强定位精度。通过提供陀螺仪数据,您可以帮助MegaTag2约束定位问题,即使只有一个标签可见也能提供出色的结果:
// 首先,告诉Limelight您机器人的当前方向
double robotYaw = m_gyro.getYaw();
LimelightHelpers.SetRobotOrientation("", robotYaw, 0.0, 0.0, 0.0, 0.0, 0.0);
// 获取姿态估计
LimelightHelpers.PoseEstimate limelightMeasurement = LimelightHelpers.getBotPoseEstimate_wpiBlue("");
// 将其添加到您的姿态估计器
m_poseEstimator.setVisionMeasurementStdDevs(VecBuilder.fill(.5, .5, 9999999));
m_poseEstimator.addVisionMeasurement(
limelightMeasurement.pose,
limelightMeasurement.timestampSeconds
);
6. 特殊AprilTag功能
// 设置自定义裁剪窗口以提高性能(每个值为-1到1 )
LimelightHelpers.setCropWindow("", -0.5, 0.5, -0.5, 0.5);
// 更改相对于机器人中心的相机姿态(x前进,y左侧,z向上,单位度)
LimelightHelpers.setCameraPose_RobotSpace("",
0.5, // 前进偏移(米)
0.0, // 侧向偏移(米)
0.5, // 高度偏移(米)
0.0, // 横滚(度)
30.0, // 俯仰(度)
0.0 // 偏航(度)
);
// 设置AprilTag偏移跟踪点(米)
LimelightHelpers.setFiducial3DOffset("",
0.0, // 前进偏移
0.0, // 侧向偏移
0.5 // 高度偏移
);
// 配置AprilTag检测
LimelightHelpers.SetFiducialIDFiltersOverride("", new int[]{1, 2, 3, 4}); // 只跟踪这些标签ID
LimelightHelpers.SetFiducialDownscalingOverride("", 2.0f); // 以半分辨率处理以提高帧率并减少范围
7. Python集成
与自定义Python SnapScripts通信:
// 向Python发送数据
double[] dataToSend = {1.0, 2.0, 3.0};
LimelightHelpers.setPythonScriptData("", dataToSend);
// 从Python获取数据
double[] dataFromPython = LimelightHelpers.getPythonScriptData("");
8. 从NetworkTables获取详细结果(原始目标)
为了获得最佳性能,您可以直接从NetworkTables访问原始目标数据,绕过JSON解析:
// 获取原始AprilTag/基准点数据
RawFiducial[] fiducials = LimelightHelpers.getRawFiducials("");
for (RawFiducial fiducial : fiducials) {
int id = fiducial.id; // 标签ID
double txnc = fiducial.txnc; // X偏移(无准星)
double tync = fiducial.tync; // Y偏移(无准星)
double ta = fiducial.ta; // 目标面积
double distToCamera = fiducial.distToCamera; // 到相机的距离
double distToRobot = fiducial.distToRobot; // 到机器人的距离
double ambiguity = fiducial.ambiguity; // 标签姿态歧义
}
// 获取原始神经网络检测器结果
RawDetection[] detections = LimelightHelpers.getRawDetections("");
for (RawDetection detection : detections) {
int classID = detection.classId;
double txnc = detection.txnc;
double tync = detection.tync;
double ta = detection.ta;
// 如需访问角点坐标
double corner0X = detection.corner0_X;
double corner0Y = detection.corner0_Y;
// ... 角点1-3类似可用
}
9. 从JSON获取详细结果(Results对象)
Results对象提供对所有Limelight数据类型的全面访问。在RoboRIO上解析可能需要长达2毫秒,因此我们不建议在FRC中使用这种方法:
LimelightResults results = LimelightHelpers.getLatestResults("");
if (results.valid) {
// 颜色/反光目标
if (results.targets_Retro.length > 0) {
LimelightTarget_Retro target = results.targets_Retro[0];
double skew = target.ts; // 目标倾斜/旋转
double shortSide = target.short_side; // 最短边(像素)
double longSide = target.long_side; // 最长边(像素)
Pose3d targetPose = target.getCameraPose_TargetSpace();
}
// AprilTags/基准点
if (results.targets_Fiducials.length > 0) {
LimelightTarget_Fiducial tag = results.targets_Fiducials[0];
double id = tag.fiducialID; // 标签ID
String family = tag.fiducialFamily; // 标签系列(例如"16h5")
// 3D姿态数据
Pose3d robotPoseField = tag.getRobotPose_FieldSpace(); // 机器人在场地空间中的姿态
Pose3d cameraPoseTag = tag.getCameraPose_TargetSpace(); // 相机相对于标签的姿态
Pose3d robotPoseTag = tag.getRobotPose_TargetSpace(); // 机器人相对于标签的姿态
Pose3d tagPoseCamera = tag.getTargetPose_CameraSpace(); // 标签相对于相机的姿态
Pose3d tagPoseRobot = tag.getTargetPose_RobotSpace(); // 标签相对于机器人的姿态
// 2D目标数据
double tx = tag.tx; // 与准星的水平偏移
double ty = tag.ty; // 与准星的垂直偏移
double ta = tag.ta; // 目标面积(图像的0-100%)
}
// 神经网络检测
if (results.targets_Detector.length > 0) {
LimelightTarget_Detector detection = results.targets_Detector[0];
String className = detection.className;
double confidence = detection.confidence;
double area = detection.ta;
}
// 分类器结果
if (results.targets_Classifier.length > 0) {
LimelightTarget_Classifier result = results.targets_Classifier[0];
String class_name = result.className;
double confidence = result.confidence;
int classID = (int)result.classID;
}
// 条形码/二维码结果
if (results.targets_Barcode.length > 0) {
LimelightTarget_Barcode barcode = results.targets_Barcode[0];
String data = barcode.data;
String family = barcode.family;
}
}
10. 快照管理
拍摄快照用于离线管道调整:
// 拍摄快照以便稍后在网页界面中查看
LimelightHelpers.takeSnapshot("", "auto_shot_1");
11. 其他功能
// 更改流模式
LimelightHelpers.setStreamMode_Standard(""); // 并排流
LimelightHelpers.setStreamMode_PiPMain(""); // 次要流在角落
LimelightHelpers.setStreamMode_PiPSecondary(""); // 主要流在角落