在机器人程序中使用生成的代码

GRIP生成可添加到FRC | reg |的类。在roboRIO上运行且没有大量其他代码的程序,根据输出结果驱动机器人。

这里包括一个完整的示例程序,该程序使用GRIP线程将机器人驱动到一块逆向反射材料上。

该程序旨在说明视觉代码的工作原理,并不一定代表编写机器人程序的最佳技术。在编写自己的程序时,请注意以下注意事项:

  1. 使用相机输出来操纵机器人可能会出现问题。在此示例中,捕获和处理图像的相机代码以慢得多的速率运行,这对于操纵机器人的控制回路是理想的。一个更好的,只是稍微复杂一点的解决方案是从摄像机获取航向及其处理速率,然后使用陀螺仪传感器将控制环转向这些航向的速度更快。

  2. 将视觉代码保留在包装线程的类中。编写面向对象代码的一种更好的方法是子类化或实例化生成的线程类,然后在其中处理OpenCV结果,而不是在机器人程序中进行处理。在此示例中,机器人代码通过操纵所得的OpenCV轮廓来提取行驶方向。通过在整个机器人程序中公开OpenCV代码,如果您拥有更好的视觉算法,则很难更改视觉算法。

迭代程序定义

package org.usfirst.frc.team190.robot;

import org.usfirst.frc.team190.grip.MyVisionPipeline;

import org.opencv.core.Rect;
import org.opencv.imgproc.Imgproc;

import edu.wpi.cscore.UsbCamera;
import edu.wpi.first.cameraserver.CameraServer;
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
import edu.wpi.first.wpilibj.PWMSparkMax;
import edu.wpi.first.wpilibj.TimedRobot;
import edu.wpi.first.vision.VisionRunner;
import edu.wpi.first.vision.VisionThread;

public class Robot extends TimedRobot {

    private static final int IMG_WIDTH = 320;
    private static final int IMG_HEIGHT = 240;

    private VisionThread visionThread;
    private double centerX = 0.0;
    private DifferentialDrive drive;
    private PWMSparkMax left;
    private PWMSparkMax right;

    private final Object imgLock = new Object();

在程序的第一部分,您可以看到用于该程序的WPILib类的所有import语句。

  • 图像宽度和高度**定义为320x240像素。

  • ** VisionThread **是WPILib类,可轻松在与机器人程序其余部分不同的线程中进行相机处理。

  • “ centerX ”值将是检测到的目标的计算出的中心X值。

  • **DifferentialDrive**将驱动电机封装在这个机器人上,并简化了驱动。

  • ** imgLock **是一个变量,用于同步访问每次图像获取过程中同时更新的数据以及处理坐标和操纵机器人的代码。

@Override
public void robotInit() {
    UsbCamera camera = CameraServer.startAutomaticCapture();
    camera.setResolution(IMG_WIDTH, IMG_HEIGHT);

    visionThread = new VisionThread(camera, new MyVisionPipeline(), pipeline -> {
        if (!pipeline.filterContoursOutput().isEmpty()) {
            Rect r = Imgproc.boundingRect(pipeline.filterContoursOutput().get(0));
            synchronized (imgLock) {
                centerX = r.x + (r.width / 2);
            }
        }
    });
    visionThread.start();

    left = new PWMSparkMax(0);
    right = new PWMSparkMax(1);
    drive = new DifferentialDrive(left, right);
}

程序启动时,** robotInit()**方法将被调用一次。它创建一个“ CameraServer”实例,该实例开始以请求的分辨率(IMG_HEIGHT的IMG_WIDTH)捕获图像。

接下来,将创建** VisionThread **类的实例。 VisionThread开始在单独的线程中异步捕获来自相机的图像。处理完每张图像后,将检索流水线在目标周围的“边界框”,并计算其“中心X”值。这个centerX值将是图像中矩形中心的x像素值。

VisionThread还带有** VisionPipeline 实例(在这里,我们有一个由GRIP生成的子类 MyVisionPipeline **),以及一个用于处理线程输出的回调。在此示例中,线程输出标记了某种目的或目标的轮廓列表(图像中区域的轮廓)。回调会找到第一个轮廓的边界框以找到其中心,然后将该值保存在变量centerX中。请注意分配周围的同步块:确保主机器人线程始终具有变量的最新值,只要它还使用**同步**块读取变量即可。

@Override
public void autonomousPeriodic() {
    double centerX;
    synchronized (imgLock) {
        centerX = this.centerX;
    }
    double turn = centerX - (IMG_WIDTH / 2);
    drive.arcadeDrive(-0.6, turn * 0.005);
}

这是程序的最后部分,在比赛的“自动阶段”中被重复调用。它获得目标的** centerX 像素值,并**减去图像宽度的一半,以将其更改为**当矩形在图像中居中时为零且为正或负的值。当目标中心位于框架的左侧或右侧时。**该值用于将机器人导向目标。

请注意开头的“同步”块。这将捕获VisionThread发现的最新centerX值的快照。