
在工业视觉圈子里Halcon 一直是那个稳如老狗的存在——算子丰富、亚像素精度高、产线跑几年不带崩的。但这两年有个变化特别明显越来越多的项目开始要求既要 Halcon 的精度又要 YOLO 的泛化能力。客户拿着手机拍的一堆乱七八糟的样品照片说你帮我框出来传统模板匹配和形状匹配直接歇菜这时候 YOLO 就成了绕不开的选项。问题来了Halcon 自己那套深度学习模块基于 HALCON Deep Learning和 YOLO 到底怎么选、怎么配合、怎么在 Halcon 里落地目标检测这里面的门道比想象中多得多。这篇内容就是把我自己在产线项目里反复折腾 Halcon YOLO 的经验摊开讲从底层原理到算子调用从数据标注到模型部署适合已经会用 Halcon 基础算子、想往深度学习方向走的视觉工程师也适合做 YOLO 但想把结果接进 Halcon 做后续测量的朋友。1. 先搞清楚 Halcon 目标检测和 YOLO 到底是不是一回事很多人一上来就问Halcon 里怎么跑 YOLO这个问题本身就暴露了一个认知偏差——Halcon 的目标检测和 YOLO 的目标检测虽然都叫目标检测但底层逻辑、输出形式、适用场景完全不是一码事。不把这件事掰扯清楚后面写代码全是坑。1.1 Halcon 原生目标检测的三条技术路线Halcon 做目标检测严格来说有三条路可走。第一条是传统方法基于形状匹配find_shape_model、模板匹配find_ncc_model、边缘提取edges_sub_pix这些算子靠的是几何特征和灰度相关性。这条路在工件位置固定、光照稳定的产线上依然是首选速度快、精度高、不需要训练数据。第二条是Halcon 自带的深度学习模块从 Halcon 18 开始引入到 20.11 之后逐渐成熟提供read_dl_model、apply_dl_model这类算子支持分类、检测、分割。它的检测模型底层其实也是类似 YOLO 的单阶段检测思路但封装成了 Halcon 自己的算子接口。第三条就是外部训练 YOLO把推理结果喂给 Halcon 做后处理这也是目前工业界最流行的混合方案。三条路线的核心差异在于传统方法依赖人工设计特征Halcon 深度学习依赖 Halcon 自己的训练流程和数据格式而外部 YOLO 方案则把训练和推理解耦Halcon 只负责用结果。选哪条路取决于你的数据量、泛化要求和部署环境。1.2 YOLO 的核心机制为什么适合工业检测YOLO 全称 You Only Look Once核心思想是把目标检测变成一个回归问题——一张图丢进去网络直接输出所有框的坐标和类别不需要像 Faster R-CNN 那样先出候选区域再分类。这个单阶段设计带来的最大好处就是速度快早期 YOLOv3 在 GPU 上就能跑到实时现在的 YOLOv5、YOLOv8 更是把速度和精度平衡得很好。对工业检测来说YOLO 真正吸引人的地方不是速度而是泛化能力。传统 Halcon 模板匹配遇到工件旋转角度超过 ±5 度、表面有油污、光照变化匹配分数就掉得厉害。YOLO 因为是在大量样本上训练出来的对这些干扰有天然的鲁棒性。我做过一个五金件检测项目同一批工件表面有轻微氧化模板匹配的分数从 0.9 掉到 0.6而 YOLO 训练时把氧化样本也标进去检测置信度稳定在 0.85 以上。但 YOLO 也有软肋定位精度不如 Halcon 的亚像素边缘。YOLO 输出的框是像素级的边界框回归的误差通常在 1-3 个像素而 Halcon 的edges_sub_pix能做到 0.1 像素级别。所以工业上常见的做法是YOLO 负责找到目标大概在哪Halcon 负责在目标区域内做精确测量。1.3 两者结合时的数据流与接口设计把 YOLO 和 Halcon 串起来数据流大致是这样的相机采图 → YOLO 推理得到检测框 → 把框的坐标通常是[x1, y1, x2, y2, class_id, confidence]传给 Halcon → Halcon 用gen_rectangle1生成 ROI → 在 ROI 内做边缘提取、拟合、测量。这里有个关键细节坐标系要对齐。YOLO 训练时用的图像尺寸比如 640×640和 Halcon 读进来的原图尺寸往往不一样YOLO 输出的框是相对于网络输入尺寸的必须按比例映射回原图坐标。我见过太多人在这里翻车——YOLO 框看着挺准一转到 Halcon 里 ROI 就偏了就是因为忘了做坐标缩放。接口设计上最省事的方式是 YOLO 推理完把结果写成 JSON 或 CSVHalcon 用read_tuple或文件读取算子解析。如果追求实时性可以用 socket 通信或者共享内存但工业现场一般 30fps 以下用文件交换完全够用。2. 在 Halcon 里调用 YOLO 的三种落地方式搞清楚概念之后具体怎么在 Halcon 环境里用上 YOLO这是实操层面最核心的问题。我试过至少五种方案最后沉淀下来三种比较靠谱的各有适用场景。2.1 方式一Python 推理 Halcon 读结果最推荐这是目前最成熟、坑最少的方案。流程是Python 端用ultralytics库加载 YOLO 模型.pt或导出的.onnx对图像做推理把检测结果存成文本文件Halcon 端用read_tuple读入结果再用gen_rectangle1生成 ROI 做后续处理。为什么推荐这种方式因为 Python 生态对 YOLO 的支持是最好的ultralytics一行代码就能推理模型更新、版本切换都很方便。Halcon 这边只需要读一个文本文件不涉及任何深度学习算子稳定性极高。具体操作上Python 端大概是这样from ultralytics import YOLO import cv2 model YOLO(best.pt) img cv2.imread(test.jpg) results model(img, conf0.5) with open(result.txt, w) as f: for box in results[0].boxes: x1, y1, x2, y2 box.xyxy[0].tolist() cls int(box.cls[0]) conf float(box.conf[0]) f.write(f{x1} {y1} {x2} {y2} {cls} {conf}\n)Halcon 端读取open_file(result.txt, input, FileHandle) while (fread_line(FileHandle, Line) ! -1) tuple_split(Line, , Substrings) tuple_number(Substrings, Numbers) X1 : Numbers[0] Y1 : Numbers[1] X2 : Numbers[2] Y2 : Numbers[3] gen_rectangle1(Rectangle, Y1, X1, Y2, X2) * 在 Rectangle 内做后续测量 endwhile close_file(FileHandle)这个方案的关键注意点图像路径要统一。Python 读的图和 Halcon 读的图必须是同一张否则坐标对不上。我一般让 Halcon 负责采图并存盘Python 读同一个路径的图这样绝对不会错位。2.2 方式二ONNX Runtime 在 Halcon 外部进程推理如果产线环境不允许装 Python或者客户要求纯 C 部署那就用 ONNX Runtime。YOLO 训练完导出成 ONNX 格式用 C 写一个推理程序读图、推理、输出结果文件Halcon 再读结果。这种方式比 Python 方案麻烦但部署更干净。ONNX Runtime 的 C API 不算复杂核心就是Ort::Session加载模型、构造输入 tensor、Run推理、解析输出。YOLOv5 和 YOLOv8 的 ONNX 输出格式略有不同v5 输出是[1, 25200, 85]v8 是[1, 84, 8400]解析时要对应处理。实测下来ONNX Runtime 在 CPU 上的推理速度比 PyTorch 快 20%-30%如果产线机器没有 GPU这个优势很明显。但要注意 ONNX 导出时的 opset 版本YOLOv8 建议用 opset 12 以上否则某些算子不支持。2.3 方式三Halcon 深度学习模块直接训练检测模型如果实在不想引入外部依赖Halcon 自己的深度学习模块也能做目标检测。流程是用 Halcon 的标注工具DLTool或MVTec Deep Learning Tool标注数据导出成 Halcon 的 dataset 格式然后用train_dl_model训练最后apply_dl_model推理。这条路的好处是全流程在 Halcon 内闭环不需要 Python、不需要 ONNX部署时一个 Halcon 运行时搞定。但缺点也很明显Halcon 的检测模型训练速度慢数据增强选项少而且模型结构不透明调参空间小。我试过用 Halcon 训练一个 500 张图的检测模型训练了 4 个小时mAP 只有 0.72同样的数据用 YOLOv8 训练 1 小时mAP 到 0.85。所以除非客户强制要求我一般不用这条路。三种方式的对比对比项Python HalconONNX Runtime HalconHalcon 深度学习部署复杂度中需 Python 环境高需编译 C低纯 Halcon训练灵活性高高低推理速度中快中精度上限高高中适合场景快速验证、小批量产线部署、无 Python纯 Halcon 环境3. YOLO 模型训练环节里那些决定成败的细节模型训练是 YOLO 方案里最花时间、也最容易出问题的环节。很多人以为标注完丢进去训练就行实际上从数据准备到超参设置每一步都有讲究。3.1 数据集标注的质量直接决定上限YOLO 训练用的是 YOLO 格式的标注每张图对应一个.txt文件每行是class_id x_center y_center width height坐标都是归一化到 0-1 的。标注工具推荐labelImg或X-AnyLabeling后者支持自动标注能省不少事。标注质量上有几个坑必须避开。第一是框要贴紧目标边缘不要留太多背景也不要切掉目标。我见过标注员为了省事框画得比目标大一圈训练出来的模型框也偏大后面 Halcon 做 ROI 时把周围干扰都框进去了。第二是类别要一致同一个目标在不同图里不能一会儿标成 screw 一会儿标成 bolt。第三是难样本要标全遮挡的、模糊的、边缘的目标如果漏标模型会把这些当成背景导致漏检。数据量上工业检测一般每个类别 200-500 张图起步类别多的话按比例增加。如果数据不够可以用 YOLO 自带的增强mosaic、mixup、随机翻转但要注意工业图像翻转后可能不符合实际场景比如字符检测翻转后字符就反了这种增强要关掉。3.2 预训练模型选择和迁移学习策略YOLO 官方提供了在 COCO 数据集上预训练的模型yolov8n.pt、yolov8s.pt等工业检测一定要用预训练权重做迁移学习不要从零训练。原因很简单COCO 有 80 类、十几万张图预训练模型已经学到了边缘、纹理、形状这些通用特征你的工业数据只需要微调就能收敛。模型大小选择上nnano最快但精度最低xextra large最准但最慢。工业产线如果对速度要求高比如 60fps 以上选n或s如果精度优先比如缺陷检测选m或l。我一般先用s跑一版看效果不够再换大的。迁移学习的策略是冻结 backbone只训练 head。YOLOv8 里可以通过设置freeze10来冻结前 10 层这样训练速度快、不容易过拟合。等 head 收敛后再解冻全部微调几个 epoch。学习率上初始用 0.01用余弦退火调度训练 100-300 个 epoch 基本够用。3.3 训练过程中 loss 不下降的排查思路训练时最怕遇到 loss 不降或者震荡。我总结了一套排查顺序先看数据用labelImg随机抽 20 张图检查标注有没有错位、漏标再看学习率太大导致震荡太小导致不收敛可以先用 0.001 试然后看 batch size太小梯度不稳定太大显存不够一般 8-16 比较稳最后看数据分布如果某一类样本特别少模型会偏向多数类需要做类别平衡。还有一个隐蔽的坑是图像尺寸。YOLO 默认输入 640×640如果你的原图是 4000×3000直接 resize 到 640 会丢失大量细节小目标直接消失。这种情况要么用imgsz1280训练要么把大图切块训练。我做过一个 PCB 缺陷检测原图 5000×4000切块成 640×640 后训练小缺陷检出率从 0.4 提升到 0.9。4. 把 YOLO 结果接进 Halcon 做精确测量的完整链路YOLO 给出框只是第一步工业检测真正要的是尺寸、角度、位置这些精确值这部分必须靠 Halcon 来完成。这一章讲怎么把两者串成一条完整的测量链路。4.1 从检测框到 ROI 的坐标转换YOLO 输出的框是相对于网络输入尺寸的比如你训练时用 640×640但 Halcon 读的原图是 2448×2048那框的坐标必须按比例放大。转换公式是scale_x 原图宽 / 网络输入宽 scale_y 原图高 / 网络输入高 x1_orig x1 * scale_x y1_orig y1 * scale_y x2_orig x2 * scale_x y2_orig y2 * scale_y如果训练时用了 letterbox保持宽高比填充转换会更复杂需要先减去 padding 再缩放。我建议训练时直接用rectTrue或者固定尺寸避免 letterbox 带来的坐标换算麻烦。转换完之后用gen_rectangle1生成 ROIgen_rectangle1(RoiRect, y1_orig, x1_orig, y2_orig, x2_orig) reduce_domain(Image, RoiRect, ImageReduced)这里有个经验ROI 要适当放大 5-10 个像素。因为 YOLO 的框本身有误差如果 ROI 刚好卡在目标边缘Halcon 做边缘提取时可能把目标边缘切掉。放大一点让边缘完整落在 ROI 内后续测量才准。4.2 在 ROI 内做亚像素边缘提取与拟合ROI 生成后Halcon 的强项就发挥出来了。以测量一个圆形工件的直径为例流程是在 ROI 内用edges_sub_pix提取亚像素边缘用segment_contours_xld分割轮廓用fit_circle_contour_xld拟合圆最后circle_center和circle_radius得到圆心和半径。edges_sub_pix(ImageReduced, Edges, canny, 1.5, 20, 40) segment_contours_xld(Edges, ContoursSplit, lines_circles, 5, 4, 2) fit_circle_contour_xld(ContoursSplit, algebraic, -1, 0, 0, 3, 2, Row, Column, Radius, StartPhi, EndPhi, PointOrder)参数上canny的 alpha 值这里是 1.5控制平滑程度值越大边缘越平滑但细节越少。工业图像一般用 1.0-2.0。fit_circle_contour_xld的最后一个参数是迭代次数3-5 次足够。实测下来这套流程的测量精度能到 0.05 像素换算成实际尺寸取决于标定精度。如果相机标定做得好用calibrate_cameras做畸变校正整体精度能到 ±0.01mm。4.3 多目标场景下的结果匹配与去重一张图里有多个目标时YOLO 会输出多个框Halcon 需要对每个框分别处理。这里有个容易忽略的问题YOLO 可能对同一个目标输出多个重叠框尤其是 NMS 阈值设得高的时候需要在 Halcon 端做去重。去重的逻辑是计算两个框的 IoU交并比如果 IoU 大于 0.5保留置信度高的那个。Halcon 里可以用intersection和area_center算子算交集面积再除以并集面积。或者更简单在 Python 端就把 NMS 做好Halcon 只处理去重后的结果。多目标匹配上如果目标有固定排列比如托盘上的工件可以用sort_region按位置排序再和模板位置对应。如果是无序的那就每个框独立处理最后汇总结果。5. 部署到产线时绕不开的性能与稳定性问题实验室跑通和产线稳定运行是两码事。这一章讲部署阶段最容易翻车的几个点。5.1 推理速度与产线节拍的匹配产线节拍是硬指标比如 1 秒 2 个工件那单次检测必须在 500ms 内完成。YOLO 推理时间取决于模型大小和硬件yolov8n在 CPU 上大概 30-50msyolov8s在 GPU 上 10-20msyolov8m在 GPU 上 30-50ms。加上 Halcon 的图像采集和测量整体时间要留 30% 余量。如果速度不够优化方向有几个换更小的模型、降低输入分辨率、用 TensorRT 加速、把 Halcon 测量和 YOLO 推理并行化。我做过一个项目YOLO 推理 80msHalcon 测量 120ms串行 200ms 超过节拍改成两个线程并行后降到 130ms刚好达标。5.2 光照变化和样本漂移的应对产线运行几个月后光源老化、镜头积灰、工件批次变化都会导致图像分布漂移YOLO 的检测率下降。应对方法是定期用新样本做增量训练或者在生产环境收集难样本攒够一批后重新训练。另一个实用技巧是加置信度阈值监控。正常运行时置信度分布是稳定的如果某天平均置信度突然从 0.85 掉到 0.6说明图像分布变了该检查光源或者重新训练了。这个监控可以在 Halcon 端做把每次的置信度存下来画个趋势图。5.3 Halcon 授权与运行时环境的坑Halcon 的授权是个老生常谈的问题。深度学习模块需要额外的 license如果客户只买了基础版read_dl_model这类算子是用不了的。所以如果方案里要用 Halcon 深度学习一定要提前确认 license 类型。运行时环境上Halcon 的版本兼容性要注意。用 Halcon 20.11 写的程序在 22.11 上可能因为算子行为变化跑不通。产线部署时建议锁定版本不要随意升级。另外Halcon 的环境变量HALCONROOT和HALCONARCH要配好否则hdevelop能跑但导出的程序跑不了。6. 几个真实项目里踩出来的经验最后分享几个我在实际项目里踩过的坑都是文档里不会写的。第一个是YOLO 框的坐标系问题。有一次项目Python 端用 OpenCV 读图BGRHalcon 读图灰度两边图像内容一样但通道顺序不同导致 YOLO 框的位置和 Halcon 的 ROI 差了半个像素。后来统一用 Halcon 存图、Python 读同一张图问题消失。这个坑很隐蔽因为差半个像素肉眼看不出但测量结果就是偏。第二个是小目标检测的输入分辨率。做电子元件检测时目标只有 20×20 像素YOLO 默认 640 输入下目标缩到 5×5根本检不到。解决办法是把原图切块每块 640×640 训练或者用imgsz1280训练。切块方案更稳但要注意块与块之间的重叠避免目标被切断。第三个是Halcon 和 YOLO 的线程冲突。Halcon 的某些算子比如find_shape_model内部是多线程的如果和 YOLO 推理线程同时跑CPU 抢占会导致两边都变慢。解决办法是给 Halcon 设置set_system(thread_num, 1)限制线程数或者把 YOLO 推理放到独立进程。第四个是模型版本管理。产线上跑着 v1 模型你训练了 v2 想更新直接替换文件可能导致程序崩溃。正确做法是模型文件带版本号程序启动时读配置决定加载哪个版本更新时先停线再替换。这套 Halcon YOLO 的方案我在三个产线项目里跑过最长的已经稳定运行一年多。核心体会就是YOLO 负责找得到Halcon 负责测得准两者各司其职不要指望一个方案解决所有问题。训练阶段多花时间在数据质量上部署阶段多留余量在性能上剩下的就是常规的工程问题了。