Utilisation de plusieurs caméras

Changer le panorama pour le pilote

Si vous souhaitez simplement changer ce que voit le pilote et que vous utilisez SmartDashboard, le lecteur de flux SmartDashboard CameraServer a une option (« Selected Camera Path ») qui lit la clé NetworkTables et modifie le « Camera Choice » à cette valeur (affichage de cette caméra). Le code du robot doit alors simplement définir la clé NetworkTables sur le nom correct de la caméra. En supposant que « Selected Camera Path » est réglé sur « CameraSelection », le code suivant utilise l’état du bouton de déclenchement du joystick 1 pour afficher la caméra1 et la caméra2.

UsbCamera camera1;
UsbCamera camera2;
Joystick joy1 = new Joystick(0);
NetworkTableEntry cameraSelection;
@Override
public void robotInit() {
    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;
void RobotInit() override {
  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());
  }
}

Note

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 vous utilisez un autre tableau de bord, vous pouvez changer dynamiquement la caméra utilisée par le serveur de caméra. Si vous ouvrez un visualiseur de flux relié à camera1, le code du robot changera le contenu du flux en camera1 ou camera2 en lorsque le déclencheur de la manette est pesé.

UsbCamera camera1;
UsbCamera camera2;
VideoSink server;
Joystick joy1 = new Joystick(0);
@Override
public void robotInit() {
    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;
void RobotInit() override {
  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

Garder les flux ouverts

Par défaut, la librairie « cscore » est assez prompte pour éteindre les caméras non utilisées. Cela signifie que lorsque vous changez de caméra, la librairie peut détacher la caméra non utilisée, avec la conséquence d’un retard possible lors de la reconnexion à un autre caméra. Pour garder les deux connexions de caméra ouvertes, utilisez la méthode SetConnectionStrategy() pour indiquer à la librairie de garder les flux ouverts, même si vous ne les utilisez pas.

UsbCamera camera1;
UsbCamera camera2;
VideoSink server;
Joystick joy1 = new Joystick(0);
@Override
public void robotInit() {
    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;
void RobotInit() override {
  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();
}

Note

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"]

Note

Si les deux caméras sont USB, vous pouvez rencontrer des limitations de bande passante USB avec des résolutions plus élevées. Dans tous ces cas, le roboRIO va diffuser simultanément les données des deux caméras vers le roboRIO (pendant une courte période avec les options 1 et 2, et en continu avec l’option 3). Il est théoriquement possible pour la librairie d’éviter cette simultanéité dans le cas de l’option 2 uniquement, mais ceci n’est pas encore implémenté dans cette version..

Différentes caméras signalent l’utilisation de la bande passante différemment. La librairie vous indiquera si vous atteignez la limite; vous obtiendrez ce message d’erreur:

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)

Si vous utilisez l’option 3, vous obtiendrez cette erreur durant l’exécution de RobotInit(). Pour palier à cet inconvénient, vous devez simplement essayer une résolution plus basse et l’ajuster si nécessaire jusqu’à ce que vous n’obteniez plus cette erreur en respectant les limites de la bande passante radio.