Multiple USB Camera Switching Lag

Hello,
So I am having an odd issue. I managed to get multiple USB cameras working, where you can switch it with the start button. However, Now my auto shoot function takes SIGNIFICANTLY longer to complete. Its so wierd, its like the dual usb camera causes the lag. Here is my teams code.

RobotHardwarePrototypeBot and RobotHardwareCompbot have the autoshoot function causing said lag.

Any help is appreciated, thanks.

We’re also using multiple USB Cameras on the RoboRIO, albeit with a slightly modified CameraServer class. We also experience about a 1.5s “hiccup” in the video feed on any camera change. We haven’t traced it down yet, but believe it has to do with initializing and starting to get image data from the cameras themselves.

Right now our code is such that switching feeds blocks the main Robot thread, but it’s only about 30ms or so, our next move is to move the switching code into the thread itself and simply signal the CameraServer thread from the main thread.

At quick glance it wasn’t clear that you’re running your image serving code in a separate thread, or at least the gathering of the image code from the camera appears to be in your main thread. If your delay is caused by the actual video itself not being ready this may not help, but if you’re getting lag in other parts of your robot moving the image capture (NIVision.IMAQdxGrab) into the thread with the CameraServer could help.

Thanks For the help. I should probably have linked my camera code too :stuck_out_tongue:

Thats im doing currently. is that what you mean? My NIVIsion.IMAGdxGrab is currently called right before my setImage function in updateCam()

Through Debugging, i just confirmed the NIVIsion.IMAGdxGrab function is causing my delay. How the heck do i fix this?

Ok, I took a deeper look into what you’re doing… Best I can tell, in Robot.java, which executes on the main thread (along with your drive commands and most, if not everything else) initializes RobotVisionDualUSB (Line 50 - and reinit on 87?). It also loops through and updates the images as well (lines 79, 102, 117).

The problem is, the image capture routine is slow, be it because it’s doing a lot of work, or some other reason, it’s just not fast. If you were to run those tasks on a separate thread it could be just as slow – but slow alongside the rest of your code that’s running at speed. (The RoboRIO is a dual-core processor, so it can run two threads simultaneously.)

I suggest you take a look at the WPILib code for CameraServer for some idea of what they are doing there. Starting at line 181, when you start capture you’ll see it create a new Thread in Java (line 189) that just captures images. That’s the only code that runs the capture() function (line 199) - which is where the images are loaded (line 200).

(If your head isn’t spinning yet – that WPILib code is a mess, read on. If you’re already lost, jump past this section.)

Granted we’re a rookie team, and I’m a rookie mentor, but… The WPILib CameraServer class does most of what we want, aside from handle multiple cameras, so why reinvent the wheel? Instead we took that CameraServer class and made a few modifications in DynamicCameraServer. (Code base is in major flux, hopefully that link will work for awhile.) All our changes are commented (Look for “// FRC5881 Mod”) and what we’ve done is to implement a way to tell the capture thread to stop, and a way to see that is has. Then we restart it with the new camera.

Most of the magic starts in the switchAutomaticCapture() function on line 209. Because the capture thread only runs when m_autoCaptureStarted is true (see line 251), we flip that to false, and wait for the next loop in capture() which ends and sets m_autoCaptureRunning to false. That in turn lets the switchAutomaticCapture() function (which has been sitting spinning and waiting for up to 1s - line 218 - which usually takes only about 30-40ms, may trigger a warning about motor updates… we’re working on this still) know it can close the old camera feed and start the new one and restart the capture() thread.

There are probably about a half-dozen better ways to do this and not block the main thread (like we do at 218) at all, but 30-40ms or so is a far better cry than what it was before. The other downside, that we might not be able to fix, is it does take about 1s-1.5s for the new video to start up on the driver station. Maybe it’s the close/open of the camera, maybe not.

(If your head was spinning before, start reading here)

The fundamental issue I think you have, first off, is you’re entire code is running on one single thread. Thus while you’re processing cameras, commands from the DS (move, turn, stop) aren’t being executed. What you need to look at is multithreaded operation and move video out of the main thread.

Hope this helps.

All I know is we experienced serious lag in competition…that the drivers could only use the camera for lining up goal shots and not for driving unless you want to destroy the bot by running into stuff.

Yeah, the video runs at a bit of a delay (about 3/4s for us), but I haven’t figured out why yet… I have a hunch on what it is, but haven’t had time to track it down yet.

Without having looked at any of your actual code (sorry, pressed for time) I can tell you that we encountered similar behavior and solved it by opening and initializing both USBCamera instances at the beginning, then passing one or the other in to our CustomCameraServer.startAutomaticCapture method.

We made a slight modification (available gratis on our github) which stops streaming on one camera, then starts it on the other. The delay seemed to be with initializing the cameras, not starting capture.

 
public synchronized void startAutomaticCapture(USBCamera camera) {
        if (camera==null) {
            return;
        }

        if (m_autoCaptureStarted) {
            m_camera.stopCapture();
            m_camera = camera;
            m_camera.startCapture();
            return;
        }
        m_autoCaptureStarted = true;
        m_camera = camera;

        m_camera.startCapture();

        Thread captureThread = new Thread(new Runnable() {
            @Override
            public void run() {
                capture();
            }
        });
        captureThread.setName("Camera Capture Thread");
        captureThread.start();
    }