在工业质检领域,多相机协同检测系统正成为提升生产效率的关键技术。我最近完成的一个项目,需要同时对产线上6个工位的产品进行表面缺陷检测,每个工位部署1台2000万像素的工业相机,检测速度要求达到每分钟60件。这种场景下,传统的单相机方案根本无法满足需求。
经过技术选型,最终确定采用VS2015+Qt5.9+Halcon20的技术栈组合。这个方案有几个突出优势:
实际测试发现,使用VS2019会出现Halcon运行时库加载异常,而Qt5.15与Halcon20的线程模型存在冲突。这也是选择这个特定版本组合的重要原因。
安装顺序有严格要求:
关键配置点:
在VS2015中创建Qt GUI项目后,需要调整以下关键配置:
cpp复制// 附加包含目录
$(HALCONROOT)\include
$(HALCONROOT)\include\halconcpp
$(QT_DIR)\include
// 附加库目录
$(HALCONROOT)\lib\$(Platform)
$(QT_DIR)\lib
链接器输入需要添加:
code复制halconcpp.lib
Qt5Widgets.lib
Qt5Core.lib
创建测试程序验证环境是否正常:
cpp复制#include <HalconCpp.h>
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
HalconCpp::HImage image;
image.ReadImage("printer_chip/printer_chip_01");
return a.exec();
}
如果能够正常读取图像且不报错,说明环境配置成功。
采用生产者-消费者模式设计:
mermaid复制graph TD
A[相机1] --> B[图像队列]
C[相机2] --> B
D[...] --> B
B --> E[处理线程池]
E --> F[结果数据库]
实测发现,当队列深度超过20帧时会出现内存激增,建议设置队列最大长度为15。
cpp复制class CameraController : public QObject {
Q_OBJECT
public:
explicit CameraController(int camId, QObject *parent = nullptr);
bool connectCamera();
void setExposure(double ms);
signals:
void imageCaptured(const HalconCpp::HImage &image);
private:
void grabLoop();
};
cpp复制class DefectDetector {
public:
struct DefectResult {
QRectF boundingBox;
int defectType;
double confidence;
};
QVector<DefectResult> detect(const HalconCpp::HImage &image);
};
cpp复制HalconCpp::HImage preprocess(const HalconCpp::HImage &src) {
// 1. 转换为灰度图
HImage gray = src.Rgb1ToGray();
// 2. 同态滤波增强
HImage enhanced = gray.HomMat2dUniform(0.5, 0.5);
// 3. 自适应阈值分割
HRegion regions = enhanced.Threshold(0, 128);
// 4. 形态学处理
HRegion cleaned = regions.OpeningCircle(3.5);
return cleaned;
}
使用Halcon的DLT(Deep Learning Tool)模块:
cpp复制void DefectDetector::loadModel(const QString &modelPath) {
HDict modelParams;
modelParams.SetDictTuple("type", "detection");
m_model.ReadDlModel(modelPath.toStdString(), modelParams);
}
QVector<DefectResult> DefectDetector::detect(const HImage &image) {
HDict dlSamples, dlResults;
dlSamples.SetDictTuple("image", image);
m_model.ApplyDlModel(dlSamples, dlResults);
// 解析检测结果...
}
实际应用中,建议先使用传统算法做初筛,再用深度学习模型精确定位,这样效率能提升40%以上。
Halcon对象必须及时清除:
cpp复制{
HImage image("printer_chip_01"); // 分配内存
// 处理代码...
image.Clear(); // 显式释放
} // 作用域结束也会自动释放
硬件触发方案:
软件同步方案(当无硬件支持时):
cpp复制void syncCapture() {
QVector<CameraController*> cameras;
QVector<HImage> images(cameras.size());
QSemaphore sem(0);
for(int i=0; i<cameras.size(); ++i) {
QObject::connect(cameras[i], &CameraController::imageCaptured,
[&,i](const HImage &img) {
images[i] = img;
sem.release();
});
}
// 同时触发所有相机
for(auto cam : cameras) {
cam->trigger();
}
// 等待所有相机完成采集
for(int i=0; i<cameras.size(); ++i) {
sem.acquire();
}
}
现象:图像出现条纹或部分缺失
解决方案:
错误代码:6001(算子参数错误)
排查步骤:
采用双缓冲显示技术:
cpp复制void ImageWidget::paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.drawImage(rect(), m_backBuffer);
}
void ImageWidget::updateImage(const QImage &img) {
m_backBuffer = img.scaled(size(), Qt::KeepAspectRatio);
update();
}
硬件配置要求:
部署检查清单:
日志系统实现:
cpp复制void Logger::writeLog(LogLevel level, const QString &msg) {
QString text = QString("[%1] %2: %3")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
.arg(levelToString(level))
.arg(msg);
QFile file("log.txt");
if(file.open(QIODevice::Append)) {
QTextStream stream(&file);
stream << text << "\n";
}
}
在实际产线部署时,建议先用模拟模式运行24小时验证稳定性。我们项目最终实现了99.2%的检测准确率,误检率控制在0.3%以下,完全满足客户要求。