Usando varias cámaras

Cambio de vistas del Driver

Si está interesado en cambiar lo que ve el controlador, y está utilizando SmartDashboard, el visor de flujos del SmartDashboard CameraServer tiene una opción («Ruta de la cámara seleccionada») que lee la clave NetworkTables dada y cambia la «Elección de la cámara» a ese valor (mostrando esa cámara). El código del robot sólo tiene que establecer la clave NetworkTables con el nombre correcto de la cámara. Asumiendo que la «Ruta de la Cámara Seleccionada» se establece en «Selección de Cámara», el siguiente código utiliza el estado del botón de disparo del joystick 1 para mostrar la cámara1 y la cámara2.

UsbCamera camera1;
UsbCamera camera2;
Joystick joy1 = new Joystick(0);
NetworkTableEntry cameraSelection;

public Robot() {
    camera1 = CameraServer.startAutomaticCapture(0);
    camera2 = CameraServer.startAutomaticCapture(1);
    cameraSelection = NetworkTableInstance.getDefault().getTable("").getEntry("CameraSelection");
}
@Override
public void teleopPeriodic() {
    if (joy1.getTriggerPressed()) {
        System.out.println("Setting camera 2");
        cameraSelection.setString(camera2.getName());
    } else if (joy1.getTriggerReleased()) {
        System.out.println("Setting camera 1");
        cameraSelection.setString(camera1.getName());
    }
}
cs::UsbCamera camera1;
cs::UsbCamera camera2;
frc::Joystick joy1{0};
nt::NetworkTableEntry cameraSelection;
Robot() {
  camera1 = frc::CameraServer::StartAutomaticCapture(0);
  camera2 = frc::CameraServer::StartAutomaticCapture(1);
  cameraSelection = nt::NetworkTableInstance::GetDefault().GetTable("")->GetEntry("CameraSelection");
}
void TeleopPeriodic() override {
  if (joy1.GetTriggerPressed()) {
    std::cout << "Setting Camera 2" << std::endl;
    cameraSelection.SetString(camera2.GetName());
  } else if (joy1.GetTriggerReleased()) {
    std::cout << "Setting Camera 1" << std::endl;
    cameraSelection.SetString(camera1.GetName());
  }
}

Nota

Python requires you to place your image processing code in a separate file from your robot code. You can create robot.py and vision.py in the same directory.

robot.py contents:

import wpilib
from ntcore import NetworkTableInstance
class MyRobot(wpilib.TimedRobot):
    def robotInit(self):
        self.joy1 = wpilib.Joystick(0)
        self.cameraSelection = NetworkTableInstance.getDefault().getTable("").getEntry("CameraSelection")
        wpilib.CameraServer.launch("vision.py:main")
    def teleopPeriodic(self):
        if self.joy1.getTriggerPressed():
            print("Setting camera 2")
            self.cameraSelection.setString("USB Camera 1")
        elif self.joy1.getTriggerReleased():
            print("Setting camera 1")
            self.cameraSelection.setString("USB Camera 0")

vision.py contents:

from cscore import CameraServer
def main():
    CameraServer.enableLogging()
    camera1 = CameraServer.startAutomaticCapture(0)
    camera2 = CameraServer.startAutomaticCapture(1)
    CameraServer.waitForever()

pyproject.toml contents (this only shows the portions you need to update):

[tool.robotpy]
...
# Add cscore to the robotpy-extras list
robotpy_extras = ["cscore"]

Si está utilizando algún otro dashboard, puede cambiar la cámara que utiliza el servidor de la cámara de forma dinámica. Si abre un visor de transmisión nominalmente a camera1, el código del robot cambiará el contenido de la transmisión a camera1 o camera2 según el gatillo del control.

UsbCamera camera1;
UsbCamera camera2;
VideoSink server;
Joystick joy1 = new Joystick(0);

public Robot() {
    camera1 = CameraServer.startAutomaticCapture(0);
    camera2 = CameraServer.startAutomaticCapture(1);
    server = CameraServer.getServer();
}
@Override
public void teleopPeriodic() {
    if (joy1.getTriggerPressed()) {
        System.out.println("Setting camera 2");
        server.setSource(camera2);
    } else if (joy1.getTriggerReleased()) {
        System.out.println("Setting camera 1");
        server.setSource(camera1);
    }
}
cs::UsbCamera camera1;
cs::UsbCamera camera2;
cs::VideoSink server;
frc::Joystick joy1{0};
bool prevTrigger = false;
Robot() {
  camera1 = frc::CameraServer::StartAutomaticCapture(0);
  camera2 = frc::CameraServer::StartAutomaticCapture(1);
  server = frc::CameraServer::GetServer();
}
void TeleopPeriodic() override {
  if (joy1.GetTrigger() && !prevTrigger) {
    std::cout << "Setting Camera 2" << std::endl;
    server.SetSource(camera2);
  } else if (!joy1.GetTrigger() && prevTrigger) {
    std::cout << "Setting Camera 1" << std::endl;
    server.SetSource(camera1);
  }
  prevTrigger = joy1.GetTrigger();
}
# Setting the source directly via joystick isn't possible in Python, you
# should use NetworkTables as shown above instead

Mantener las transmisiones abiertas

De forma predeterminada, la biblioteca cscore es bastante agresiva al apagar las cámaras que no están en uso. Esto significa que cuando cambie de cámara, es posible que se desconecte de la cámara que no está en uso, por lo que volver a cambiar tendrá cierta demora cuando se reconecte a la cámara. Para mantener abiertas las conexiones de ambas cámaras, use el método `` SetConnectionStrategy() “” para decirle a la biblioteca que mantenga las transmisiones abiertas, incluso si no las está usando.

UsbCamera camera1;
UsbCamera camera2;
VideoSink server;
Joystick joy1 = new Joystick(0);

public Robot() {
    camera1 = CameraServer.startAutomaticCapture(0);
    camera2 = CameraServer.startAutomaticCapture(1);
    server = CameraServer.getServer();
    camera1.setConnectionStrategy(ConnectionStrategy.kKeepOpen);
    camera2.setConnectionStrategy(ConnectionStrategy.kKeepOpen);
}
@Override
public void teleopPeriodic() {
    if (joy1.getTriggerPressed()) {
        System.out.println("Setting camera 2");
        server.setSource(camera2);
    } else if (joy1.getTriggerReleased()) {
        System.out.println("Setting camera 1");
        server.setSource(camera1);
    }
}
cs::UsbCamera camera1;
cs::UsbCamera camera2;
cs::VideoSink server;
frc::Joystick joy1{0};
bool prevTrigger = false;
Robot() {
  camera1 = frc::CameraServer::StartAutomaticCapture(0);
  camera2 = frc::CameraServer::StartAutomaticCapture(1);
  server = frc::CameraServer::GetServer();
  camera1.SetConnectionStrategy(cs::VideoSource::ConnectionStrategy::kConnectionKeepOpen);
  camera2.SetConnectionStrategy(cs::VideoSource::ConnectionStrategy::kConnectionKeepOpen);
}
void TeleopPeriodic() override {
  if (joy1.GetTrigger() && !prevTrigger) {
    std::cout << "Setting Camera 2" << std::endl;
    server.SetSource(camera2);
  } else if (!joy1.GetTrigger() && prevTrigger) {
    std::cout << "Setting Camera 1" << std::endl;
    server.SetSource(camera1);
  }
  prevTrigger = joy1.GetTrigger();
}

Nota

Python requires you to place your image processing code in a separate file from your robot code. You can create robot.py and vision.py in the same directory.

robot.py contents:

import wpilib
from ntcore import NetworkTableInstance
class MyRobot(wpilib.TimedRobot):
    def robotInit(self):
        self.joy1 = wpilib.Joystick(0)
        self.cameraSelection = NetworkTableInstance.getDefault().getTable("").getEntry("CameraSelection")
        wpilib.CameraServer.launch("vision.py:main")
    def teleopPeriodic(self):
        if self.joy1.getTriggerPressed():
            print("Setting camera 2")
            self.cameraSelection.setString("USB Camera 1")
        elif self.joy1.getTriggerReleased():
            print("Setting camera 1")
            self.cameraSelection.setString("USB Camera 0")

vision.py contents:

from cscore import CameraServer, VideoSource
    def main():
    CameraServer.enableLogging()
    camera1 = CameraServer.startAutomaticCapture(0)
    camera2 = CameraServer.startAutomaticCapture(1)
    camera1.setConnectionStrategy(VideoSource.ConnectionStrategy.kConnectionKeepOpen)
    camera2.setConnectionStrategy(VideoSource.ConnectionStrategy.kConnectionKeepOpen)
        CameraServer.waitForever()

pyproject.toml contents (this only shows the portions you need to update):

[tool.robotpy]
...
# Add cscore to the robotpy-extras list
robotpy_extras = ["cscore"]

Nota

Si ambas cámaras son USB, puede encontrarse con limitaciones de ancho de banda USB con resoluciones más altas, ya que en todos estos casos la roboRIO transmitirá datos de ambas cámaras a la roboRIO simultáneamente (por un período corto en las opciones 1 y 2, y continuamente en la opción 3). Teóricamente es posible que la biblioteca evite esta simultaneidad en el caso de la opción 2 (solamente), pero esto no está implementado actualmente.

Las diferentes cámaras informan el uso de ancho de banda de manera diferente. La biblioteca le dirá si está llegando al límite; obtendrá este mensaje de error:

could not start streaming due to USB bandwidth limitations;
try a lower resolution or a different pixel format
(VIDIOC_STREAMON: No space left on device)

If you’re using Option 3 it will give you this error during the Robot constructor. Thus you should just try your desired resolution and adjusting as necessary until you both don’t get that error and don’t exceed the radio bandwidth limitations.