资讯动态

LabVIEW机器学习集成实践:从ONNX模型到稳定推理链路

发布时间:2026/10/22 19:02:12 来源:尧图企业网站定制
简介面向测试测量与虚拟仪器开发者的LabVIEW机器学习源码包定位于将AI能力融入图形化编程环境解决测控系统中数据智能分析与自动化决策需求。压缩包约25.91MB整体围绕在LabVIEW中完成机器学习流程展开适合具备基础LabVIEW操作、希望进一步掌握分类、回归或聚类应用的工程师与研究人员。目前已有72人学习浏览。源码保留了从数据采集、特征处理到模型训练与验证的完整链路用户可查看并修改VI逻辑理解图形化编程与虚拟仪器框架下如何调用算法也能借助其中的用户界面快速搭建交互式智能测量原型。对于测绘测量、实时监测等需要数据驱动判断的工程场景这份代码提供了从物理量采集到模型落地的示范能帮助使用者避开常见的数据接口与算法封装问题提升测控系统的智能化程度。由于LabVIEW以VI为基本单位代码可读性与复用性较强适合边读边改边验证。1. AI_LabVIEW机器学习.zip它装的不是工具箱而是一条能跑通的链路拿到 AI_LabVIEW机器学习.zip 这个压缩包的人背景通常很一致LabVIEW 用得熟采集、运动控制、数据记录都做过但一提到机器学习就默认要换 Python 或 C 重写界面。这个包想解决的事情是让现有 LabVIEW 工程直接用上模型推理而不是把整套系统推倒重来。拆开之后你大概率会看到模型权重、推理脚本、示例 VI 和依赖清单这几类文件组合起来就是一条从「传感器数据进来」到「推理结果出去」的完整链路。它适合两类人。一类是做机器视觉或设备预测维护的工程师想在现有采集程序里加一个分类或回归结果另一类是刚接触 AI 但不想换平台的开发者想先评估 LabVIEW 里做这件事的成本和坑。它不是深度学习训练教程不负责让你从零搭网络它解决的是「一个训练好的模型怎么在 LabVIEW 里稳定跑起来」这个工程问题。2. LabVIEW 做机器学习的三种技术路线模型从哪来、推理在哪跑在打开 VI 之前先回答一个关键问题模型是谁训练的推理由谁来执行。LabVIEW 本身没有成熟的训练生态所以几乎所有人走的都是「外部训练 LabVIEW 推理」这个组合。外部训练产出模型文件LabVIEW 负责加载和执行。同一个模型文件按执行方式可以分成三种主流路线选型的差别直接决定后面的开发量和排查难度。2.1 三条路线先横向比一遍再决定用哪条常见做法是模型用 PyTorch 或 TensorFlow 训练导出成通用格式然后落到 LabVIEW 这一侧做推理。但「落进去」的方式不同结果也不同。我一般会把路线分成三类先想清楚再动手路线模型执行位置适合场景主要限制Python 节点调用推理脚本Python 进程原型验证、低频分类、已有 Python 推理代码数据拷贝开销、位数必须一致、UI 容易阻塞ONNX Runtime 推理Python 进程或 C 封装需要跨框架复用、性能稳定算子兼容要处理、预处理要跟训练端约定外部推理服务 / DLL独立进程高并发、GPU 批量、多个 LabVIEW 工程共用通信协议要自己定义、开发量翻倍如果你的场景是「设备旁边一台工控机相机触发一次几百毫秒内出一个结果」第一种就够用这也是这个压缩包最可能默认采用的方式。如果要做连续检测、每秒要处理多帧建议直接考虑第二种把 ONNX Runtime 当作一个稳定的黑匣子函数去调。第三种一般出现在多台设备共享一张 GPU、或者模型太大不能常驻的场景LabVIEW 只负责发请求收结果开发量最大但隔离性最好。2.2 Python 节点为什么是默认选项机制与边界LabVIEW 的 Python 节点位于函数面板的「互联接口」里。它的工作方式是LabVIEW 启动一个 Python 进程调用指定脚本里的函数把参数传过去再把返回值拿回来。整个过程是同步阻塞的也就是说 VI 调用 Python 节点的那一刻必须等脚本执行完才能继续往下走。这个机制决定了它的三个使用边界。第一Python 解释器位数必须和 LabVIEW 一致32 位 LabVIEW 配 64 位 Python 会直接起不来进程。第二传入的数据类型有限制数值、字符串、数组、cluster 都能传但 numpy 数组不能直接作为返回值返回需要.tolist()转成 Python list。第三数据是拷贝过去的不是共享内存所以大图像数组每次传参都有开销图像分辨率越高单次调用耗时越明显。我一般在项目初期会严格遵守「Python 节点只做推理不做复杂预处理」这个原则。图像缩放、颜色转换尽量在 LabVIEW 侧做Python 侧只接收已经排好序的数组做归一化和模型推理。这样两边职责清晰出问题时也容易定位。2.3 ONNX 是中间格式也是最容易埋坑的一层大部分训练代码用的是 PyTorch但 LabVIEW 侧不可能直接加载 PyTorch 的.pt文件除非你在 Python 节点里也装 PyTorch那样环境体积会大很多。所以中间格式我常用 ONNX。它把网络结构和权重打包成一个文件ONNX Runtime 负责加载和执行不依赖原始训练框架。ONNX 的坑集中在导出环节。最常遇到的是算子不兼容模型里如果用了自定义层或者比较新的算子导出后会报Unsupported operator。另一个常见的坑是动态维度没处理好训练时输入是固定 224×224导出时没加动态轴等你想在 LabVIEW 里传入别的分辨率就会报维度错误。所以我导出的习惯是先固定尺寸导出跑通后再考虑动态轴不要一开始就追求「什么尺寸都能传」。2.4 模型转换与验证PyTorch 导出 ONNX 的最小脚本下面这个脚本是把 PyTorch 模型转成 ONNX 的最小示例也是我每次新项目都会用的起点import torch import torch.onnx # 加载训练好的权重强制放到 CPU 上导出更稳 model torch.load(weights.pth, map_locationcpu) model.eval() # 用与训练时一致的分辨率造一个假输入 dummy_input torch.randn(1, 3, 224, 224) torch.onnx.export( model, dummy_input, model.onnx, opset_version11, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}}, ) print(export done)逻辑上map_locationcpu是为了避免在带 GPU 的机器上导出时留下设备绑定换一台没显卡的工控机就加载失败。opset_version11是一个兼容性较好的折中值版本太高要求 ONNX Runtime 比较新版本太低有些算子不支持。dynamic_axes只把 batch 维设为动态其他维度保持固定这样既能支持批量测试又不会让导出过程复杂化。导出后用 ONNX Runtime 自己先跑一遍确认输出和 PyTorch 原模型一致再进 LabVIEW。这一步能过滤掉一大半后面会翻车的问题。没有这一步你在 LabVIEW 里看到错误时会很难判断是模型问题、转换问题还是接线问题。3. 把一个模型接进 LabVIEW 工程目录规划、Python 节点与第一个推理 VI模型文件有了接下来的问题是怎么把它组织进 LabVIEW 工程里。很多人习惯把模型、脚本、VI 全放在桌面或下载目录跑通一个 Demo 没问题但一旦要交给别人维护或者换一台电脑部署各种绝对路径问题会集中爆发。我在碰这个压缩包时做的第一件事不是打开 VI而是先规划目录。3.1 解压后先做工程化目录规划别在桌面堆文件解压 AI_LabVIEW机器学习.zip 之后我一般会按功能而不是文件类型重新组织目录让模型、脚本、VI 三者分开project_root/ ├─ weights/ │ ├─ model.onnx │ └─ labels.txt ├─ py/ │ ├─ infer.py │ └─ preprocess.py ├─ vi/ │ ├─ main.vi │ └─ utils/ └─ requirements.txt这个结构里weights/放模型和标签文件py/放 Python 推理脚本vi/放 LabVIEW 程序requirements.txt记录 Python 依赖。这样做的直接好处是LabVIEW 里的路径用相对路径就能定位模型换版本时只替换weights/下的文件不动 VI 逻辑。路径管理上我有一个固定习惯在 VI 里用Current VI Path生成工程根目录的绝对路径再拼上相对路径而不是直接把文件路径写死成C:\Users\xxx\Desktop\...。这样整个工程文件夹拷到任何一台机器都能跑不会被桌面路径绑架。3.2 用 Python 节点加载 ONNX第一个推理 VI 的搭建步骤下一步是写 Python 推理脚本。我的infer.py长这样import numpy as np import onnxruntime as ort # session 在模块加载时只创建一次避免每帧都做初始化 _session None def get_session(): global _session if _session is None: _session ort.InferenceSession( weights/model.onnx, providers[CPUExecutionProvider] ) return _session def predict(image_bgr_list, mean, std): session get_session() # LabVIEW 传进来的是 list先转成 numpy 数组 x np.asarray(image_bgr_list, dtypenp.float32) # 归一化除以 255 再减均值除标准差 x (x / 255.0 - mean) / std # HWC - NCHW给模型一个 batch 维 x x.transpose(0, 3, 1, 2) out session.run(None, {input: x}) return out[0].tolist()逻辑说明get_session()用全局变量缓存 InferenceSession这样模型只在第一次调用时加载后续推理直接复用。image_bgr_list是 LabVIEW 通过 Python 节点传入的三维数组形状是(batch, height, width, 3)转成 numpy 后先归一化再通过transpose(0, 3, 1, 2)把维度变成(batch, 3, height, width)这是模型训练时最常见的 NCHW 布局。参数说明mean和std必须和训练时一致常见的是 ImageNet 统计值如果是自己训练的模型要用训练集统计值否则精度会明显下降。CPUExecutionProvider表示先用 CPU 跑通确实需要加速再换 GPU。LabVIEW 侧的接线是这样在程序框图上放置 Python 节点节点配置里填 Python 解释器路径、脚本路径和函数名predict。输入侧把图像数组连接给image_bgr_list把均值数组和标准差数组分别连给mean和std输出侧是 list 类型用「变体转数组」节点转成普通数组再取最大值下标映射到labels.txt里的类别名。3.3 图像、数组、文件三种数据入口的接线方式LabVIEW 里图像数据有三种常见来路接法略有不同第一种是图片文件用 IMAQ 的读取函数读进来转成数组后直接进 Python 节点。优点是调试方便可以拿一张固定图反复验证。第二种是相机实时帧采集函数每 Grab 一帧就转成数组入推理这是产线最常见的形态但要注意帧率和推理耗时的匹配后面第 6 章会专门讲。第三种是已有的信号数组或特征数组不用走图像链路直接把波形数据 reshape 成模型输入形状适合振动分析、故障诊断这类非视觉场景。不管你用哪种入口有一个原则不变Python 节点只接收「已经排好序的数组」。排序这件事在 LabVIEW 侧做因为 LabVIEW 对图像数组的内存布局最清楚。把排序逻辑放 LabVIEW把数值计算放 Python两边都省心。4. 决定识别精度的前处理与关键参数BGR 归一化、batch、线程与预热模型在训练集上精度再高进了 LabVIEW 也可能掉得一塌糊涂。我踩过最深的一个坑是同一张图在 Python 里识别正确传到 LabVIEW 里就错。后来发现问题根本不在模型而在图像进入模型之前的预处理和参数配置。这一章说的就是这些「不写在网络结构里、但决定成败」的细节。4.1 图像通道顺序、颜色空间和归一化参数通道顺序是第一道坎。用 OpenCV 读图得到的是 BGR用 PyTorch 训练时通常先转成 RGBLabVIEW 的 IMAQ 彩色图像有的是 RGB有的视觉函数返回的是 HSL 或 Bayer 格式。如果模型训练时用的是 RGB而 LabVIEW 传进去的是 BGR模型看到的就是颜色错乱的数据分类结果自然不对而且这种错误很难靠调参数修复只能从源头对齐。我的处理方式是在脚本里固定一个转换约定比如全部按 BGR 输入脚本内部保持 BGR只做归一化。这样可以避免每次调用都纠结颜色。归一化参数同样要固定MEAN np.array([0.485, 0.456, 0.406], dtypenp.float32) STD np.array([0.229, 0.224, 0.225], dtypenp.float32)MEAN和STD是 ImageNet 分类模型最常见的归一化参数如果你的模型是在 ImageNet 预训练基础上微调的直接用这一组通常没问题。如果是完全自己训练的模型要回到训练脚本里把统计值抄过来。灰度图要特别注意不要为了凑三通道把灰度图复制三次再走 RGB 模型前置灰度模型训练时用的可能就是单通道均值复制成三通道反而引入偏差。4.2 batch 大小与线程数把推理当做一个黑匣子函数去压测Python 节点默认一次调用传一帧数据所以batch1是最稳的配置。想提高吞吐不要急着改模型先把推理函数当黑匣子压测一遍。在脚本里用time.perf_counter()记录单次推理耗时跑 100 次看平均值和最大值。平均值低但最大值很高说明系统里有抖动可能是线程争抢或内存分配。onnxruntime 的线程数通过SessionOptions控制。我的经验是intra_op_num_threads4是一个不错的起点不是越大越好在四核工控机上设 8 线程反而会因为上下文切换变慢。核心参数整理如下参数建议初始值出现卡顿时的调整intra_op_num_threads4降到 2避免和采集线程抢核batch_size1缓存 N 帧后批量推理吞吐更高但延迟变大providerCPU先 CPU 跑通确有需要再切 GPU环境变量 OMP_NUM_THREADS4和上面保持一致避免两个线程池打架4.3 预热、超时与握手别把模型初始化算进单次推理第一次调用推理函数时模型加载、算子初始化、内存分配都会发生耗时可能达到几秒。如果你在产线上把这个时间算进节拍第一件产品就会被判定为超时。这其实是正常的只需要预热。做法是程序启动时在后台偷偷调用一次推理结果直接丢弃把初始化时间消化在设备启动阶段。Python 节点本身没有超时参数一旦脚本卡死LabVIEW 会一直等下去。所以我习惯在脚本里给session.run()套上try/except任何异常都返回一个带错误码的结构而不是让进程默默卡住。LabVIEW 侧对应检查返回值发现错误码就跳过当前帧保证程序不中断。这一套「预热 异常返回 丢帧」组合是 LabVIEW 里做稳定推理的底线配置。5. 避坑LabVIEW 集成机器学习最常见的 5 个翻车点与排查清单这一章全是血泪经验。每一条我都见过不止一次而且第一次出现时都让人想摔键盘。按「现象 → 原因 → 解决」写清楚你在复现这个压缩包时可以直接对照排查。5.1 Python 进程起不来32 位与 64 位不匹配现象Python 节点运行时报错提示无法启动 Python 进程或者返回的错误码没有明确指向。程序框图上连线看起来都对但一执行就红。原因LabVIEW 是 32 位版本却装了 64 位 Python反过来也一样。Python 节点的进程启动机制要求解释器位数与 LabVIEW 一致。解决先确认两边位数。在命令行里执行python -c import struct; print(struct.calcsize(P) * 8)输出 64 表示是 64 位 Python。然后对照 LabVIEW 的版本位数不一致就换一个匹配的 Python 环境。如果用 conda要把路径指到python.exe不是指到激活脚本或快捷方式。5.2 第一次推理特别慢模型加载计入了计时现象程序第一次调用推理耗时 5 到 10 秒之后每帧只要 20 毫秒。如果只测单次推理会得出「这个模型没法用」的结论。原因InferenceSession的创建、权重文件读取、算子初始化都发生在第一次run()时。这是本来就该花的初始化时间不是模型推理慢。解决把模型加载提前到程序启动阶段。启动时调用一次空推理结果丢弃预热完成后再进入正式流程。测推理耗时也要分开统计首次加载耗时和稳定推理耗时两个指标不要混在一起。5.3 中文路径导致模型加载失败现象模型文件明明存在但InferenceSession抛异常提示文件不存在。把路径改成英文后一切正常。原因部分 ONNX Runtime 版本对非 ASCII 路径支持不完善中文目录名或文件名在底层文件访问时编码不一致导致加载失败。解决工程根目录和所有子目录只用英文命名模型固定放在weights/下。不要直接放在桌面桌面路径往往包含用户名而用户名可能是中文的。这个问题一旦出现改路径比改代码快得多。5.4 识别结果全是错的通道顺序与图像类型没对齐现象同一个模型在 Python 里测试精度很高接到 LabVIEW 后结果几乎全错但程序不报任何错误。原因图像通道顺序不一致模型训练用的是 RGBLabVIEW 传进来的是 BGR或者图像位深不对模型要 uint8实际传了 float64也可能是彩色图被某些视觉函数转换成了 HSL。解决先别急着怀疑模型在脚本里把接收到的数组保存成一张图片和原图用 Python 读回来对比看颜色通道是否正常。这一步能快速定位是通道顺序问题、位深问题还是尺寸问题。确定原因后在 LabVIEW 侧做一次固定转换并用测试图建立回归基准以后每次改代码都跑一遍对比。5.5 内存持续上涨session 被反复创建现象程序跑半小时内存占用从 300 MB 涨到 1.5 GB最后系统卡顿甚至崩溃。原因把InferenceSession的创建写在了推理函数内部每调用一次就新建一个 session旧 session 没有被释放或者每次推理都把大数组追加到某个列表里没有清理。解决session 创建移到模块顶层或全局缓存只初始化一次。LabVIEW 侧避免在循环里反复创建大数组使用原地操作节点减少内存拷贝。跑长期测试时把内存占用曲线记录下来确认稳定后才算真正合格。6. 进阶技巧用 Queue 把采集和推理拆成两段告别界面卡死如果前面的步骤都跑通了你会发现最后卡住你的往往不是模型精度而是界面响应和帧率匹配。Python 节点是同步阻塞的如果直接在采集循环里调用推理一帧推理 200 毫秒整个采集就停 200 毫秒前面板按钮也跟着卡死。解决办法是用 LabVIEW 自带的队列函数把采集和推理拆成两个循环。6.1 生产者消费者队列的最小结构采集循环作为生产者只做三件事抓帧、转成数组、写入队列。推理循环作为消费者只做三件事从队列取出数组、调用 Python 节点、把结果显示到前面板。两个循环并行运行互不等待。队列用有界队列深度我一般设 3。消费端处理不过来时新进的帧会把最旧的帧挤掉保证界面上显示的始终是最新结果而不是堆积的旧帧。这个策略在相机 30 帧、推理 5 帧的场景下非常有效。队列深度适用场景表现1推理速度约等于采集速度接近同步队列基本为空3采集 30 帧推理 5 帧显示最新帧中间帧丢弃5大分辨率图像缓冲更多但显示延迟变高6.2 验证方法与我的固定习惯拆完队列后在采集循环和推理循环各加一个帧计数器前面板显示两个计数差就能直观看到积压趋势。如果差值持续增大说明推理能力跟不上采集速度要么降低采集帧率要么缩小输入图像要么换 GPU。这个差值就是你的系统余量比任何估算都准。我现在做 LabVIEW 机器学习项目的习惯是先把目录结构定死再把 Python 节点当做一个延迟稳定的函数去压测最后才用队列解耦采集和推理。这个顺序让我少翻了很多次车。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑