1. 为什么要在Halcon里折腾YOLO
干了七八年机器视觉项目,从最早的模板匹配、Blob分析,到后来的Halcon深度学习模块,再到这两年YOLO系列在工业检测里越来越普及,我最大的感受就是:没有银弹,只有组合拳。Halcon的深度学习目标检测确实好用,标注、训练、推理一条龙,但遇到小目标密集、类别频繁增删、或者需要快速迭代模型的场景,YOLO的灵活性和生态优势就体现出来了。反过来,Halcon在亚像素测量、形状匹配、3D点云处理上的积累,又是纯YOLO方案很难替代的。
所以“Halcon中如何进行目标检测-YOLO详解”这个题目,本质上不是二选一,而是怎么把YOLO的目标检测能力和Halcon的图像处理能力串起来。你可能已经用Halcon做了十几年视觉项目,现在客户突然要求检测一些外观缺陷、或者识别不同型号的工件,传统算子写规则写到头秃,这时候YOLO就是一个非常自然的补充。这篇文章我会从实际工程角度出发,把Halcon和YOLO结合的三条主流路线拆开讲清楚,包括数据怎么准备、模型怎么选、推理结果怎么和Halcon的坐标系对齐、以及我踩过的那些坑。
适合谁看?如果你是有Halcon基础、想引入深度学习目标检测的视觉工程师,或者你已经在用YOLO但想把它集成到Halcon的HDevelop流程里,这篇文章应该能帮你省下不少试错时间。全文会涉及具体的算子、代码片段、参数配置和排查思路,你可以直接抄作业,但建议先理解背后的逻辑再动手。
2. Halcon与YOLO结合的三种主流路线拆解
2.1 路线一:Halcon深度学习模块直接训练目标检测模型
Halcon从18.11版本开始引入深度学习,到23.11已经非常成熟。它的目标检测模型基于YOLOv3的架构思想做了大量工程优化,但封装成了Halcon自己的算子体系。你不需要写Python,不需要配环境,直接在HDevelop里就能完成标注、训练、评估、推理全流程。
这条路线最大的优势是闭环。标注用Halcon的标注工具,训练用train_dl_model_anomaly_dataset或者train_dl_model,推理用apply_dl_model,结果直接就是Halcon的DLResult结构,可以无缝接入后续的测量、定位、通信流程。而且Halcon的深度学习模型对工业场景做了很多优化,比如支持多类别、小目标、旋转框,在V100或者RTX系列显卡上推理速度也够用。
但它的局限也很明显:模型结构固定,你没法像YOLOv8那样随意改Backbone、换损失函数、加注意力机制。Halcon提供的是开箱即用的工业级方案,不是研究平台。如果你的场景需要频繁尝试新结构,或者数据集特别大(几十万张以上),Halcon的训练效率可能不如PyTorch生态。
2.2 路线二:YOLO训练+Halcon推理(ONNX中间格式)
这是目前工业项目里最常用的混合方案。你在PyTorch或者Ultralytics框架下训练YOLOv5/v8/v11,导出成ONNX格式,然后用Halcon的read_dl_model加载ONNX模型,通过apply_dl_model做推理。Halcon从20.11开始支持ONNX模型导入,到23.11对YOLO系列的支持已经比较完善。
这条路线的好处是训练灵活、推理统一。训练阶段你可以用YOLO的全套工具链:Mosaic增强、MixUp、自动锚框、超参数进化,想怎么调怎么调。推理阶段回到Halcon,用你熟悉的gen_rectangle1、reduce_domain、threshold做后处理,结果直接和Halcon的测量算子对接。
但ONNX导入有几个硬性约束:算子兼容性。YOLO里常用的SiLU激活函数、Focus层、SPPF模块,在Halcon的ONNX解析器里不一定全部支持。我实测下来,YOLOv5的某些版本导出ONNX后,Halcon加载会报Unsupported operator。解决办法是导出时做算子替换,比如把SiLU换成LeakyReLU,或者用onnx-simplifier做图优化。另外,Halcon对ONNX的动态维度支持有限,导出时最好固定batch size和输入尺寸。
2.3 路线三:Halcon做预处理+YOLO独立推理+结果回传
如果你的Halcon版本较老(比如19.11之前),或者ONNX导入总是出问题,那就走最原始的路线:Halcon负责图像采集、预处理、ROI提取,把处理好的图像保存或者通过内存共享传给YOLO的Python进程,YOLO推理完把检测框坐标写回文件或者Socket,Halcon再读取结果做后续处理。
这条路线听起来很笨,但稳定性最高。Halcon和Python完全解耦,YOLO那边可以用最新的Ultralytics库,Halcon这边用你熟悉的算子做图像增强。缺点是通信延迟和数据同步需要额外处理,实时性要求高的产线可能不太适合。我一般只在两种情况下用这条路线:一是Halcon版本太老不支持ONNX,二是YOLO模型里有Halcon不支持的算子,改起来太麻烦。
下面这张表可以帮你快速决策:
| 对比维度 | Halcon深度学习 | YOLO+ONNX+Halcon | Halcon预处理+YOLO独立 |
|---|---|---|---|
| 训练灵活性 | 低 | 高 | 高 |
| 推理集成度 | 高 | 中 | 低 |
| 算子兼容性 | 无问题 | 需验证 | 无问题 |
| 实时性 | 高 | 高 | 中 |
| 适合场景 | 工业标准检测 | 定制化检测 | 老版本Halcon或复杂模型 |
| 学习成本 | 低 | 中 | 高 |
3. 数据准备与标注:从Halcon标注到YOLO格式转换
3.1 Halcon深度学习标注工具的使用要点
Halcon的标注工具在Deep Learning Tool里,或者直接用dev_display_dl_data配合draw_rectangle1手动标。我一般推荐用Deep Learning Tool,因为它支持自动预标注:先用一个粗糙的模型跑一遍,把置信度高的检测框自动生成,人工只需要修正漏检和误检。这个功能在标注几千张图的时候能省一半时间。
标注时要注意几个细节:边界框要贴紧目标边缘,不要留太多背景。Halcon的深度学习模型对边界框的精度比较敏感,框太大或者太小都会影响训练效果。另外,类别标签要统一命名,不要出现“OK”“ok”“Ok”这种大小写混用的情况,Halcon会当成不同类别处理。
标注完成后,Halcon的数据集格式是.hdict文件,里面存储了图像路径、边界框坐标、类别ID。这个格式YOLO不认,需要转换。
3.2 从Halcon标注到YOLO格式的转换脚本
YOLO需要的标注格式是每张图一个.txt文件,每行class_id x_center y_center width height,坐标都是归一化到0-1之间的相对值。下面这个Python脚本可以把Halcon的.hdict转成YOLO格式:
import h5py import numpy as np import os def halcon_hdict_to_yolo(hdict_path, output_dir, class_names): """ 将Halcon的hdict标注文件转换为YOLO格式 hdict_path: Halcon导出的hdict文件路径 output_dir: YOLO标签输出目录 class_names: 类别名称列表,顺序对应class_id """ with h5py.File(hdict_path, 'r') as f: # Halcon hdict的结构需要根据实际导出方式调整 # 这里假设已经解析出每张图的bbox和类别 pass # 实际项目中我一般用Halcon导出CSV,再用pandas处理 # 因为hdict的解析比较麻烦,CSV更直观说实话,直接解析.hdict比较折腾,我一般会在Halcon里用write_dict把标注信息导出成CSV或者JSON,然后用Python做格式转换。Halcon这边可以这样操作:
* 假设DLDataset已经加载了标注数据 get_dict_param (DLDataset, 'image_ids', [], ImageIDs) for Index := 0 to |ImageIDs| - 1 by 1 get_dict_param (DLDataset, 'image_' + ImageIDs[Index], ['image_path', 'bbox_row1', 'bbox_col1', 'bbox_row2', 'bbox_col2', 'bbox_class_id'], [], Values) * 写入CSV write_tuple (Values, 'annotations.csv', Index) endfor拿到CSV后,用Python做归一化和格式转换就很简单了。这里有个坑:Halcon的坐标是(row, column),YOLO是(x, y),转换时要注意行列互换。另外Halcon的边界框是(row1, col1, row2, col2),YOLO需要的是中心点和宽高,计算方式如下:
x_center = (col1 + col2) / 2.0 / image_width y_center = (row1 + row2) / 2.0 / image_height width = (col2 - col1) / image_width height = (row2 - row1) / image_height3.3 数据集划分与增强策略
YOLO训练一般按7:2:1划分训练集、验证集、测试集。工业场景下样本往往不均衡,某些缺陷类别可能只有几十张图。这时候数据增强就很重要。YOLOv8自带Mosaic、MixUp、随机翻转、HSV调整,但工业图像有些增强要慎用:比如垂直翻转在字符识别场景会改变语义,大角度旋转会让边界框变得很奇怪。
我的经验是:几何增强只做小角度旋转(±10度)和水平翻转,颜色增强可以大胆用,因为工业相机的光源变化本来就大。另外,Mosaic增强在训练前期效果好,后期建议关闭,否则模型会过度依赖拼接图像,对单张图的检测精度下降。
Halcon这边做增强也很方便,rotate_image、mirror_image、scale_image这些算子可以批量生成增强样本。但要注意标注框也要同步变换,Halcon的affine_trans_region或者vector_angle_to_rigid配合affine_trans_pixel可以做到。
4. YOLO模型训练与Halcon推理集成实操
4.1 YOLOv8训练的关键参数配置
以YOLOv8为例,工业目标检测我一般用yolov8s或者yolov8m,再大在产线上跑不动。训练命令很简单:
yolo detect train data=dataset.yaml model=yolov8s.pt epochs=200 imgsz=640 batch=16 device=0但有几个参数需要根据工业场景调整:
imgsz:如果目标很小(比如小于32x32像素),建议用1280,但推理速度会下降。我实测下来,640在V100上大概5ms一帧,1280要15ms左右。batch:根据显存来,V100 32G可以跑到batch=32,RTX 3060 12G只能batch=8。lr0:初始学习率默认0.01,工业小数据集建议降到0.001,否则容易震荡。patience:早停轮数,默认50,我一般设30,避免过拟合。
训练过程中要盯着混淆矩阵和PR曲线。如果某个类别的AP一直上不去,大概率是标注质量问题或者样本太少。这时候可以回头检查标注框是否准确,或者用copy_paste增强补充样本。
4.2 导出ONNX并在Halcon中加载
YOLOv8训练完后,导出ONNX:
yolo export model=best.pt format=onnx opset=12 simplify=True dynamic=Falseopset=12是Halcon 23.11支持的最高版本,simplify=True会做图优化,去掉一些冗余算子。dynamic=False固定输入尺寸,避免Halcon解析动态维度出错。
导出后,在Halcon里加载:
read_dl_model ('best.onnx', DLModel) set_dl_model_param (DLModel, 'batch_size', 1) set_dl_model_param (DLModel, 'device', 'gpu')如果报错Unsupported operator,先用onnxruntime或者netron看一下模型结构,找到不支持的算子。常见的替换方案:
| 原算子 | 替换方案 | 说明 |
|---|---|---|
| SiLU | LeakyReLU | 精度略降,但Halcon支持 |
| Focus | Conv+Slice | YOLOv5特有,导出时用--include onnx会自动处理 |
| SPPF | MaxPool串联 | 部分Halcon版本不支持SPPF,需手动改结构 |
| Resize | 固定尺寸插值 | 动态Resize在Halcon里容易出问题 |
4.3 Halcon推理与后处理完整流程
加载模型后,推理流程如下:
* 读取图像 read_image (Image, 'test.jpg') * 预处理:缩放到模型输入尺寸 zoom_image_size (Image, ImageZoomed, 640, 640, 'bilinear') * 推理 apply_dl_model (DLModel, ImageZoomed, [], DLResult) * 获取检测结果 get_dl_model_result (DLResult, 'bbox', BBoxes) get_dl_model_result (DLResult, 'class_id', ClassIDs) get_dl_model_result (DLResult, 'confidence', Confidences)这里有个关键点:Halcon的apply_dl_model输出的边界框坐标是相对于模型输入尺寸的,需要映射回原图坐标。如果原图是2448x2048,模型输入是640x640,映射关系是:
* 计算缩放比例 ScaleX := 2448.0 / 640.0 ScaleY := 2048.0 / 640.0 * 映射边界框 BBoxRow1 := BBoxRow1 * ScaleY BBoxCol1 := BBoxCol1 * ScaleX BBoxRow2 := BBoxRow2 * ScaleY BBoxCol2 := BBoxCol2 * ScaleX后处理阶段,我一般会做NMS(非极大值抑制)和置信度过滤。Halcon的apply_dl_model其实已经内置了NMS,但阈值需要根据场景调。set_dl_model_param (DLModel, 'nms_threshold', 0.45),这个值太高会漏检重叠目标,太低会误检。
4.4 推理结果与Halcon测量算子的对接
检测框拿到后,就可以接入Halcon的测量流程了。比如检测一个圆形工件,先用YOLO定位到工件区域,再用reduce_domain把ROI抠出来,然后edges_sub_pix提取边缘,fit_circle_contour_xld拟合圆,最后get_circle_pose输出圆心和半径。
* 假设BBoxRow1, BBoxCol1, BBoxRow2, BBoxCol2是YOLO检测到的工件区域 gen_rectangle1 (ROI, BBoxRow1, BBoxCol1, BBoxRow2, BBoxCol2) reduce_domain (Image, ROI, ImageReduced) * 边缘提取 edges_sub_pix (ImageReduced, Edges, 'canny', 1.5, 20, 40) * 圆拟合 fit_circle_contour_xld (Edges, 'algebraic', -1, 0, 0, 3, 2, Row, Column, Radius, StartPhi, EndPhi, PointOrder)这样就把YOLO的检测能力和Halcon的测量能力结合起来了。YOLO负责“找到目标在哪里”,Halcon负责“测量目标的具体尺寸”。
5. 常见问题与排查技巧实录
5.1 ONNX加载失败:Unsupported operator
这是最常见的问题。Halcon的ONNX解析器不是万能的,YOLO版本越新,不支持的算子越多。排查步骤:
- 用
netron打开ONNX模型,看有哪些算子。 - 对照Halcon的ONNX算子支持列表(在Halcon安装目录的
doc文件夹里有)。 - 找到不支持的算子后,在导出ONNX前修改模型结构,或者用
onnx-simplifier做图优化。
我遇到过SiLU不支持的情况,解决办法是在YOLO的modules.py里把SiLU换成LeakyReLU,重新训练。虽然精度会降一点点,但Halcon能加载。
5.2 推理结果坐标偏移
这个问题一般是预处理不一致导致的。YOLO训练时用的预处理是letterbox(保持长宽比,填充灰边),而Halcon的zoom_image_size是直接拉伸。两者不一致,检测框就会偏移。
解决办法:在Halcon里也实现letterbox。先计算缩放比例,取长边缩放,短边填充。代码稍微复杂一点,但能保证和训练时一致。
* letterbox预处理 get_image_size (Image, Width, Height) Scale := min(640.0 / Width, 640.0 / Height) NewWidth := round(Width * Scale) NewHeight := round(Height * Scale) zoom_image_size (Image, ImageZoomed, NewWidth, NewHeight, 'bilinear') * 填充到640x640 PadX := (640 - NewWidth) / 2 PadY := (640 - NewHeight) / 2 gen_image_const (ImagePad, 'byte', 640, 640) set_grayval (ImagePad, ...) * 填充128灰边 paint_region (ImagePad, ImageZoomed, ImageResult, PadY, PadX, 'copy')5.3 小目标漏检严重
工业场景里小目标检测是个老大难。YOLOv8默认的imgsz=640,如果目标在原图里只有20x20像素,缩放到640后可能只剩5x5,特征几乎消失。
我的解决方案有三个:
- 提高输入尺寸:
imgsz=1280,小目标特征保留更多,但推理速度下降。 - 切图推理:把大图切成若干小块,分别推理,再合并结果。Halcon的
crop_domain配合tile_images可以做到。 - 修改YOLO结构:增加P2层检测头,专门检测小目标。这个需要改模型代码,适合有深度学习基础的工程师。
5.4 训练中BN崩溃
YOLO训练时如果batch太小,BatchNorm层的统计量会不稳定,导致loss突然变成NaN。这是yolo训练中bn崩溃这个热搜词的典型场景。
解决办法:
- 增大batch:至少
batch=8,最好16以上。 - 用SyncBN:多卡训练时用同步BN,单卡的话可以冻结BN层。
- 降低学习率:
lr0=0.001或者更低。 - 梯度裁剪:
yolo detect train ... clip_grad=10.0。
我实测下来,batch=8配合lr0=0.001,BN崩溃的概率会大大降低。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| ONNX加载报错 | 算子不支持 | netron查看算子 | 替换算子或简化模型 |
| 检测框偏移 | 预处理不一致 | 对比训练和推理的预处理 | 统一用letterbox |
| 小目标漏检 | 输入尺寸太小 | 统计目标像素尺寸 | 提高imgsz或切图 |
| BN崩溃 | batch太小 | 查看loss曲线 | 增大batch或降低lr |
| 推理速度慢 | 模型太大 | 测单帧耗时 | 换yolov8n/s或TensorRT |
| 类别混淆 | 标注不一致 | 检查标注文件 | 统一类别命名 |
| 过拟合 | 样本太少 | 看验证集loss | 增加增强或早停 |
6. 性能优化与部署建议
6.1 推理加速:从ONNX到TensorRT
Halcon的apply_dl_model在GPU上跑ONNX,速度已经不错,但如果产线节拍要求高(比如<10ms),可以考虑用TensorRT进一步加速。流程是:YOLO导出ONNX -> TensorRT转换 -> Halcon加载TensorRT引擎。不过Halcon对TensorRT的支持有限,我一般是在Python端用TensorRT推理,然后把结果传给Halcon。
6.2 多模型并行与资源管理
一个产线上可能有多个检测工位,每个工位跑不同的YOLO模型。Halcon的DLModel句柄可以同时加载多个模型,但显存要算好。yolov8s的ONNX模型大概40MB,加载到显存里大概占200MB,V100 32G可以同时跑十几个。如果显存不够,可以用set_dl_model_param (DLModel, 'device', 'cpu')把不常用的模型放到CPU上。
6.3 模型版本管理与热更新
工业现场最怕的就是模型更新要停机。我的做法是:Halcon程序启动时从指定目录加载模型,模型文件用版本号命名(如model_v1.2.onnx)。需要更新时,把新模型放到目录里,Halcon程序检测到文件变化后,用clear_dl_model释放旧模型,read_dl_model加载新模型。整个过程不需要重启Halcon程序。
* 热更新逻辑 ModelPath := 'model_v1.2.onnx' read_dl_model (ModelPath, DLModel) * 检测到新模型文件后 clear_dl_model (DLModel) read_dl_model ('model_v1.3.onnx', DLModel)6.4 实际产线部署的注意事项
- 光照稳定性:YOLO对光照变化比传统算子敏感,产线光源要定期校准。
- 相机触发同步:如果产线速度快,要用硬触发,避免丢帧。
- 异常处理:推理失败或者置信度全部低于阈值时,要有兜底逻辑,比如报警或者走人工复检。
- 日志记录:每帧的检测结果、耗时、置信度都要记录,方便追溯问题。
我在一个3C零件检测项目里,用Halcon+YOLOv8s的方案,检测6类缺陷,mAP@0.5做到0.92,单帧推理8ms,产线节拍完全跟得上。踩过的最大坑就是ONNX算子不兼容,折腾了两天才找到SiLU的问题。后来换成LeakyReLU重新训练,精度只掉了0.5%,但Halcon加载一次成功。
最后再分享一个小技巧:Halcon的apply_dl_model支持批量推理,如果你有多张图要处理,可以拼成一个batch,速度比单张快30%左右。但要注意显存占用,batch太大反而会OOM。我一般设batch_size=4,在V100上跑得最稳。