OV2311 camera on RPi 4B ignores 20µs exposure, captures ~10ms frames with ghosting

I’m trying to get short exposure frames (20–200 µs) from an Arducam 2MP OV2311 Global Shutter NoIR Mono Camera Module (Pivariety) on a Raspberry Pi 4B running Bookworm. The hardware trigger seems fine and I can capture frames, but the effective exposure time is way off.

Here’s my Picamera2 config:

  • RAW/video mode
  • AE disabled
  • ExposureTime set to values between 20–200 µs
  • AnalogueGain set manually
  • FrameDurationLimits set
  • Attempted to disable noise reduction

No matter what ExposureTime I set, the captured frames consistently show an effective integration time of around 10–12 ms, with clear temporal ghosting. It’s like the sensor is ignoring the exposure setting entirely.

Has anyone else run into this with the OV2311 on a Pi 4? Is there something in the pipeline that overrides these settings?

That’s an unusual one. A few things to check first:

  1. Can you confirm the ExposureTime values are actually being written to the sensor? Try reading them back with v4l2-ctl after you’ve started the stream.

  2. Which sensor modes are you testing? The OV2311 on Pivariety should have a few different resolution/framerate combos.

  3. What does your full Picamera2 configuration look like? Just want to make sure there isn’t something like an AE lock or a control that’s getting overridden elsewhere.

Good suggestions. Here’s what I found:

I used v4l2-ctl to both set and read back the exposure and gain values while streaming. The values reported back match exactly what I requested — so at the V4L2 level, the settings appear to be sticking.

I’ve tested two sensor modes:

  • 1280×720 @ 120 FPS
  • 1600×1300 @ 60 FPS

Both modes show the same behaviour: ~10–12 ms effective exposure regardless of the ExposureTime I set.

I also bumped up the buffer counts significantly to rule out frame-drop related artefacts. No change.

For more detail, I’ve been capturing metadata (ExposureTime, SensorTimestamp, Sequence) alongside the raw frames for offline analysis. The metadata reports the ExposureTime I requested, but the actual image content tells a completely different story.

Interesting — so the V4L2 control values are being accepted but the actual sensor behaviour doesn’t match. That narrows things down.

A few more things to try:

  1. Have you verified the hardware trigger is working as expected? The OV2311 Pivariety module has an external trigger mode — if that’s misconfigured, the sensor could be running in a free-running mode with its own internal exposure logic.

  2. Can you run the GPIO trigger script that came with the camera? That would confirm whether the trigger path itself is behaving.

  3. For a really solid sanity check — can you set up a simple LED that blinks at a known rate (e.g., an Arduino with a few LEDs counting in binary) and capture frames at your target exposure? That’ll give you a direct visual measurement of the effective integration time.

I ran the GPIO trigger script provided with the camera and the trigger signal looks correct on the scope — pulse timing matches what I’d expect.

I also built a simple LED ‘clock’ — basically a row of LEDs driven by a microcontroller that cycles through a binary count pattern at a known frequency. This gives a direct visual read of how long the shutter is actually open.

The result: each captured frame shows about 10–12 ms worth of LED state changes smeared across the image. For a 50 µs requested exposure, I should be seeing essentially a frozen snapshot of the LEDs. Instead, I’m getting motion blur spanning over 10 milliseconds. That’s more than 200× longer than requested.

At this point I’m starting to think the issue isn’t the sensor or the trigger — it’s something in the rpi/vc4 pipeline. My hypothesis is that the IPA (Image Processing Algorithm) or tuning file is enforcing some kind of temporal filtering, minimum frame duration, or multi-frame merging that completely overrides the sensor-level exposure control. The metadata says 50 µs but the pixels say 12 ms.

That LED clock test is really compelling — hard to argue with the visual evidence. You’re right to suspect the ISP pipeline at this point.

The libcamera/rpi pipeline on the Pi does have an IPA module that can apply temporal denoising, and some tuning files do enforce minimum integration times for certain modes. The OV2311 being a global shutter mono sensor is a bit of a niche use case, and the default tuning file may not have been designed with microsecond exposures in mind.

A couple of things you could dig into:

  1. Check if there’s a tuning file specific to the OV2311 being loaded — you can usually find these in /usr/share/libcamera/ipa/rpi/ or similar paths.

  2. Try setting the noise reduction mode explicitly to ‘Off’ via libcamera controls if you haven’t already (not just Picamera2 — sometimes there are multiple noise reduction controls at different levels).

Unfortunately, detailed documentation on the rpi/vc4 IPA internals and tuning file parameters is quite sparse publicly. The libcamera source tree has some comments in the tuning file format, but it’s pretty opaque.

Are there any other docs you’ve been able to find for this module beyond the quick start guide?

That’s actually part of the problem — the documentation available is very limited. The product page has a quick start guide and an external trigger mode page, but that’s about it. There’s nothing covering:

  • The Pivariety module’s sensor register interface in any detail
  • The rpi/vc4 IPA/tuning file format or which keys control temporal behaviour
  • Recommended libcamera pipeline settings for external trigger + short exposure use cases
  • Any tuning keys related to temporal averaging, minimum frame duration, or multi-frame processing
  • OV2311 register sequences to force hardware trigger behaviour and enforce strict ExposureTime timing

I’ve been through the libcamera source but the tuning file parser and the IPA implementation don’t make it obvious which parameters could be causing this.

Does anyone know if there are additional resources for this module? Or has anyone successfully modified the tuning file to disable whatever’s stretching the exposure?

I’ve looked into this further on our side. Here’s where things stand:

Since the camera is producing frames and the stream is functional, the hardware itself is operating normally from our perspective. The OV2311 module is doing what it’s designed to do — capture and deliver frames through the MIPI interface.

Regarding the exposure mismatch: debugging the specific ISP algorithms and low-level sensor register behaviour at this depth goes beyond what we can assist with through standard support. The interaction between libcamera’s IPA, the rpi/vc4 pipeline tuning files, and sensor register timing is firmly in the realm of advanced application-level development.

On the documentation front — I’m afraid we don’t have additional resources to release beyond what’s already on the product page. No additional OV2311 register maps are available beyond standard public documentation for that sensor.

I understand this isn’t the answer you’re looking for. The root cause — whether it’s the IPA/tuning file enforcing a minimum frame duration, temporal filtering in the vc4 pipeline, or something in the sensor register configuration — remains unconfirmed. But modifying any of those layers would require custom development work that falls outside what we can support.

I appreciate the honest response, even if it’s not what I was hoping for.

Just to summarise where this leaves things for anyone else who runs into this:

  • The OV2311 Pivariety module on a Pi 4B with Bookworm produces frames, but the effective exposure time cannot be reduced below approximately 10–12 ms regardless of the ExposureTime control value set via Picamera2 or v4l2-ctl.
  • V4L2 reports the requested exposure value correctly, but the actual image data shows integration over a much longer window.
  • This occurs across multiple sensor modes (720p@120, 1300p@60) and persists with increased buffer counts.
  • The hardware trigger path works correctly, and an LED clock test confirms the long effective exposure visually.
  • Support has confirmed the hardware is functional but cannot assist with IPA/tuning file modifications, sensor register-level configuration, or pipeline changes needed to achieve true microsecond exposures.

If anyone in the community has managed to work around this — whether by modifying the tuning file, bypassing the ISP entirely, or writing raw sensor register sequences for the OV2311 — I’d be very interested to hear about it. I’m currently looking at whether capturing raw Bayer frames with the ISP completely disabled is a viable path.

That’s a fair summary. One thing I would add for anyone else investigating this path — capturing raw Bayer frames with ISP bypass is probably the most promising direction if you need guaranteed microsecond-level exposure control. You’d lose the convenience of the libcamera pipeline but gain direct control over what the sensor is actually doing.

For what it’s worth, the standard support scope covers driver installation and verifying that the hardware produces a valid image stream, which we’ve confirmed. Register-level programming, ISP bypass, and tuning file reverse-engineering are firmly in the custom development category.

Best of luck with the raw capture approach — if you do crack it, please do share your findings here. This is the kind of thing that would be really valuable for the community.