4

I am currently attempting to synchronize four raspberry pi 3s using Pi Camera V2s to take photos of a stopwatch at the same time. I am using a function generator to send a signal to the GPIO pin. The images are occasionally .033 seconds apart. I believe I have pinpointed the source of this. I used a profiler on a python script that simply captures an image. Looking at the processes, a "sleep" function of duration .033 seconds is being called usually 14 times. It sometimes, however, is called 15 times, which would account for the discrepancy. I believe this extra instance of sleep is occurring before the shutter begins to open, which would make the cameras images captured .033 seconds apart. I cannot, however, for the life of me, find how/where the shutter is triggered to open in the picamera python library. My thought is that if instead of it trying to decide how long to wait, I hard coded a wait period long enough to ensure everything is in order, the cameras will all capture the image at the same time. If anyone knows of where to find when the shutter is being opened or any other insight into the matter, that would help a lot. Thanks guys. I can attempt to further clarify or provide further information if needed.

MaxwellG
  • 41
  • 2
  • This answer may shed some light on the situation. – Dave Jones Jul 23 '16 at 14:19
  • For initial clarification, we had the cameras pointed at a digital stopwatch. That is not quite what my problem is. All of the cameras start their capture at the same time. Occasionally, the sleep function running parallel to the encoding process will run 1 extra time causing one of the images to show a time .033 seconds later than the other camera. (It happens to either camera). The issue doesn't compound. Often times the next image is in sync. The issue does not compound because after every picture is taken, the python script waits for a trigger from a function generator before capturing. – MaxwellG Jul 25 '16 at 13:17
  • P.S. I was wondering if you plan on updating your picamera library for the camera v2, or if this has already been done. I ask because I had to update the picamera.array file myself to get it to work with the new camera, and I assume your work with that would be better than mine. – MaxwellG Jul 25 '16 at 13:18
  • picamera 1.11 was released a couple of weeks ago with V2 camera module support, but a bug with unencoded captures under Python 3 caused 1.12 to be released the week after (1.11's now in Raspbian, 1.12 not quite yet - both available on PyPI though). As to your sync issue, 0.033 seconds is about one frame's worth which is exactly what I'd expect to see from cameras with different init times but the same capture time (because capture doesn't actually start a capture, it just causes the camera to wait for the next complete frame and pass it back). – Dave Jones Jul 25 '16 at 13:50
  • Glad to see you're supporting it! I just checked it out. One of the fixes we had previously made to picamera.array was to remove the numpy delete function, as this took over 1 whole second to complete. Instead, we simply built a new array as we rebuild the 4 10-bit numbers from the 5 8-bit numbers. This shaved ~1.5 seconds off the time it took to save a raw image. We also had it save as a 2D array instead of 3D. The zeros in the 3D array cause the file to be much larger in size. Also, most debayering algorithms (such as Matlab's) take 2D raw data as an input. – MaxwellG Jul 25 '16 at 14:52
  • Also, if we used our function generator to initialize the camera and take the picture instead of just taking the picture, do you think that could work? Thanks again. – MaxwellG Jul 25 '16 at 14:54
  • Initializing the camera in your generator will get you better sync (assuming all the generators fire close together, which will depend on how you're polling for GPIO state). I'd also recommend setting resolution and framerate in the constructor to avoid mode switching (reduces inconsistency). Do feel free to open a PR with any improvements to the de-bayer stuff - I can't say it's something I look at much (it's mostly in there because "it was there", but it's pretty niche functionality that I never really use myself). – Dave Jones Jul 25 '16 at 14:59
  • @DaveJones We are using while not(GPIO.input(inputPin)): pass to wait until the pin reads high. Is there a more optimal way to do this? Also, we are using a single function wired to both Pis. – MaxwellG Jul 25 '16 at 16:15
  • Yup, much more efficient to use wait_for_edge; it uses epoll under the covers so you won't miss any edges and the processor's not spinning on Python byte-code – Dave Jones Jul 25 '16 at 20:19
  • Thanks! We just ran another test using wait_for_edge method. 3 out of our 15 pictures were desynced by that same .033s margin. These desynced pictures were not consecutive. At this point, the cameras should be initialized at the same time, and they should be told to capture at the same time (separate pulses). Do you have any other ideas for things we could change to further sync the images? Thanks again for all your help. – MaxwellG Jul 25 '16 at 21:28
  • @DaveJones after thinking about it overnight, the only way I can see this working outside of some advanced networking would be if the camera were not constantly grabbing frames once it is initialized. I know this feature serves an important purpose for most people, but we are only interested in the raw data anyway. Do you know if there is any way to edit the code at any level to disable this feature? Thanks for all of the help by the way. – MaxwellG Jul 26 '16 at 13:54
  • The only major (simple) improvement I can recommend is, when initializing the camera in the generator, make sure you specify resolution and framerate in the constructor to avoid camera resets (speeds up init, but also makes it more consistent in timing). If that doesn't eliminate the de-sync then either something else is going on in the code, or Python's not consistent enough at camera init. Still, that leaves framerate_delta for sync, but that's non-trivial (involves a PID controller) – Dave Jones Jul 26 '16 at 20:29

1 Answers1

1

When you call the capture function of the picamera object it is starting the encoding and waiting for a callback to reset wait and so indicate it is complete (or for CAPTURE_TIMEOUT to be exceeded):

        encoder.start(output)
        # Wait for the callback to set the event indicating the end of
        # image capture
        if not encoder.wait(self.CAPTURE_TIMEOUT):
            raise PiCameraRuntimeError(
                'Timed out waiting for capture to end')

the encoder.wait function looks like this:

    result = self.event.wait(timeout)
    if result:
        self.stop()
        # Check whether the callback set an exception
        if self.exception:
            raise self.exception
    return result

and if you look at how event.wait is implemented (here's a great SE question that goes into detail on it), it is made up of a number of smaller (than the timeout) delays (getting longer as time goes on) and after each one checking if the lock can be acquired.

I would expect there to be very small variances in the time that each Pi takes to acquire the lock, and possibly one occasionally rolls over to a 15th sleep.

So, in summary, not something that you have any control over...

KennetRunner
  • 1,050
  • 7
  • 13
  • Thank you for the response. So, the event basically tells the capture to wait until the encoding is complete. If I see that the process at most takes some arbitrary amount of time x, could I just have the pi's not use the event to be told how long to wait and instead tell them to all wait some arbitrary amount of time x + y. That way, they should all be waiting the same amount of time to continue instead of waiting 14 or 15 steps, thus desyncing. – MaxwellG Jul 24 '16 at 13:54
  • I don't believe that functionality is available in the picamera library. You could add it, or write your own, but that is a much bigger task... If your concern is that one camera is frequently going to take longer and drift more and more out of sync then adding a variable/compensated delay after each capture might help 'resync' them for the next capture... That said, I defer to the comment on the OP by @Dave Jones , he clearly understand the inner workings better than I do. – KennetRunner Jul 24 '16 at 14:13