Note that there are some explanatory texts on larger screens.

plurals
  1. POControlling a camera from Qt: interaction of OS threads with the event loop
    primarykey
    data
    text
    <p>I'm working on a Qt application to control an industrial camera, and in particular I need to trigger the camera at a particular time (when various illumination settings have been put in place, for example), and wait until a frame is returned. In the simplest case, the following code does the job nicely:</p> <pre><code>void AcquireFrame() { // Runs in the main GUI thread: camera -&gt; m_mutex.lock(); camera -&gt; frameHasArrived = false; camera -&gt; m_mutex.unlock(); camera -&gt; triggerImageAcquisition(); forever { camera -&gt; m_mutex.lock() bool isReady = camera -&gt; frameHasArrived; camera -&gt; m_mutex.unlock() if (isReady) { return; } else { Sleep(10); } } } void callback(camera *) { // Called by the camera driver from a separate OS thread - not a Qt thread - // when a frame is ready: camera -&gt; m_mutex.lock(); camera -&gt; frameHasArrived = true; camera -&gt; m_mutex.unlock(); } </code></pre> <p>...and most of the time this works perfectly well. However, this being the real world, occasionally the camera will fail to receive the trigger or the computer will fail to receive the frame cleanly, and the above code will then go into an infinite loop.</p> <p>The obvious thing to do is to put in a timeout, so if the frame is not received within a certain time then the image acquisition can be attempted again. The revised code looks like:</p> <pre><code>void AcquireFrame() { camera -&gt; m_mutex.lock(); camera -&gt; frameHasArrived = false; camera -&gt; m_mutex.unlock(); camera -&gt; triggerImageAcquisition(); QTime timeout; timeout.start(); forever { timeout.restart(); fetch: camera -&gt; m_mutex.lock() bool isReady = camera -&gt; frameHasArrived; camera -&gt; m_mutex.unlock() if (isReady) { return; } else if (timeout.elapsed() &gt; CAM_TIMEOUT) { // Assume the first trigger failed, so try again: camera -&gt; triggerImageAcquisition(); continue; } else { Sleep(10); goto fetch; } } } </code></pre> <p>Now, the problem is that with this latter version the failure rate (the proportion of 'unsuccessful triggers') is much, much higher - at least an order of magnitude. Moreover, this code too will eventually find itself in an infinite loop where, however many times it tries to re-trigger the camera, it never sees a frame come back. Under these latter circumstances, killing the application and checking the camera reveals that the camera is in perfect working order and patiently waiting for its next trigger, so it doesn't appear to be a camera problem. I'm coming to the conclusion that in fact it's some sort of a system resource issue or a thread conflict, so that Qt's event loop is not allowing the camera callback to be called at the proper time.</p> <p>Is this likely, and is there in fact a better way of doing this?</p> <hr> <p>Update on 6th June: </p> <p>For what it's worth, I've seen no more problems since I adopted the method below (having given the camera object an extra member, namely a QWaitCondition called 'm_condition'):</p> <pre><code>void AcquireFrame() { bool frameReceived; forever { camera -&gt; triggerImageAcquisition(); camera -&gt; m_mutex.lock(); frameReceived = camera -&gt; m_condition.wait(&amp;camera-&gt;m_mutex, CAM_TIMEOUT); if (frameReceived) { // We received a frame from the camera, so can return: camera -&gt; m_mutex.unlock(); return; } // If we got to here, then the wait condition must have timed out. We need to // unlock the mutex, go back to the beginning of the 'forever' loop and try // again: camera -&gt; m_mutex.unlock(); } } void callback (camera *) { // Called by the camera driver from a separate OS thread - // not a QThread - when a frame is ready: camera -&gt; m_condition.wakeOne(); } </code></pre> <p>This still has the effect of pausing the main thread until we have either received a frame or experienced a timeout, but now we have eliminated the Sleep() and the Qt event loop remains in full control throughout. It's still not clear to me why the old method caused so many problems - I still suspect some sort of system resource limitation - but this new approach seems to be more lightweight and certainly works better.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload