我正在开发一个 Qt 应用程序来控制工业相机,特别是我需要在特定时间触发相机(例如,当各种照明设置到位时),并等到返回一帧。在最简单的情况下,以下代码可以很好地完成这项工作:
void AcquireFrame()
{
// Runs in the main GUI thread:
camera -> m_mutex.lock();
camera -> frameHasArrived = false;
camera -> m_mutex.unlock();
camera -> triggerImageAcquisition();
forever
{
camera -> m_mutex.lock()
bool isReady = camera -> frameHasArrived;
camera -> 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 -> m_mutex.lock();
camera -> frameHasArrived = true;
camera -> m_mutex.unlock();
}
...而且大多数情况下,这工作得很好。然而,这就是现实世界,偶尔相机接收不到触发信号或者电脑接收不到帧干净,上面的代码就会陷入死循环。
显而易见的做法是设置超时,因此如果在一定时间内未接收到帧,则可以再次尝试图像采集。修改后的代码如下所示:
void AcquireFrame()
{
camera -> m_mutex.lock();
camera -> frameHasArrived = false;
camera -> m_mutex.unlock();
camera -> triggerImageAcquisition();
QTime timeout;
timeout.start();
forever
{
timeout.restart();
fetch: camera -> m_mutex.lock()
bool isReady = camera -> frameHasArrived;
camera -> m_mutex.unlock()
if (isReady)
{
return;
}
else if (timeout.elapsed() > CAM_TIMEOUT)
{
// Assume the first trigger failed, so try again:
camera -> triggerImageAcquisition();
continue;
}
else
{
Sleep(10);
goto fetch;
}
}
}
现在,问题在于后一个版本的失败率(“不成功触发器”的比例)要高得多——至少一个数量级。此外,这段代码最终也会陷入无限循环,无论它尝试重新触发相机多少次,它都没有看到任何帧返回。在后一种情况下,杀死应用程序并检查相机表明相机处于完美的工作状态并耐心等待下一次触发,因此它似乎不是相机问题。我得出的结论是,实际上这是某种系统资源问题或线程冲突,因此 Qt 的事件循环不允许在适当的时间调用相机回调。
这有可能吗,实际上是否有更好的方法来做到这一点?
6月6日更新:
对于它的价值,自从我采用下面的方法(给相机对象一个额外的成员,即一个名为'm_condition'的QWaitCondition)以来,我没有看到更多问题:
void AcquireFrame()
{
bool frameReceived;
forever
{
camera -> triggerImageAcquisition();
camera -> m_mutex.lock();
frameReceived = camera -> m_condition.wait(&camera->m_mutex, CAM_TIMEOUT);
if (frameReceived)
{
// We received a frame from the camera, so can return:
camera -> 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 -> 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 -> m_condition.wakeOne();
}
这仍然具有暂停主线程的效果,直到我们接收到帧或经历超时,但现在我们已经消除了 Sleep() 并且 Qt 事件循环始终处于完全控制之下。我仍然不清楚为什么旧方法会引起这么多问题——我仍然怀疑某种系统资源限制——但这种新方法似乎更轻量级,而且效果更好。