USB 与视觉检测上位机

发布时间:2026/7/17 6:12:02

USB 与视觉检测上位机 C# 上位机实战 — USB 与视觉检测上位机 WPF 完整版MVVM 可交付项目本篇是专栏08 综合实战的第二篇在 WPF MVVM 架构下实现USB 设备枚举Win32 SetupAPI、UVC/工业相机取流OpenCV、视觉检测算法、检测结果 SQLite 归档与命令驱动的操作界面。适合质检工位、外观缺陷初筛、Teaching 后批量检测等场景。建议先完成 Modbus 完整版 再读本文便于对比 WinForms 与 WPF 两种交付形态。一、需求与验收编号需求验收V1USB 相机/采集设备列表刷新显示友好名称、VID/PID、实例路径V2预览与抓图启动/停止预览不泄漏句柄V3检测模板匹配或阈值轮廓输出 OK/NG 与得分V4结果库每条含时间、条码、结果、图片路径或 BLOBV5MVVMView 无业务逻辑ViewModel 可单测V6操作开始/停止检测、参数调整、历史查询绑定 Command二、架构 ASCII┌──────────────────────────────────────────────────────────────┐ │ View (WPF XAML) │ │ MainWindow │ Image控件 │ DataGrid历史 │ 状态栏 │ └────────────────────────────┬─────────────────────────────────┘ │ Binding / Command ┌────────────────────────────▼─────────────────────────────────┐ │ ViewModel │ │ MainViewModel │ RelayCommand │ INotifyPropertyChanged │ └─────┬──────────────────┬──────────────────┬────────────────┘ │ │ │ ▼ ▼ ▼ IUsbDeviceService ICameraService IInspectionService │ │ │ ▼ ▼ ▼ Win32UsbEnumerator OpenCvCapture OpenCvInspector │ │ └────────┬─────────┘ ▼ InspectionResultRepository (SQLite)三、解决方案结构VisionUsbInspector.sln ├── VisionUsbInspector.App (WPF .NET 8) │ ├── Views/MainWindow.xaml │ ├── ViewModels/MainViewModel.cs │ └── App.xaml.cs (DI 注册) ├── VisionUsbInspector.Core │ ├── Models/InspectionResult.cs │ └── Services/Interfaces... └── VisionUsbInspector.Infrastructure ├── Usb/Win32UsbEnumerator.cs ├── Vision/OpenCvCameraService.cs ├── Vision/OpenCvInspectionService.cs └── Data/SqliteInspectionRepository.csNuGetOpenCvSharp4、OpenCvSharp4.runtime.win、Microsoft.Data.Sqlite、CommunityToolkit.Mvvm可选。四、Win32 USB 枚举核心思路使用 SetupAPISetupDiGetClassDevs、SetupDiEnumDeviceInfo、SetupDiGetDeviceRegistryProperty获取FriendlyName与DeviceInstanceId含 VID/PID。不要在 ViewModel 里 P/Invoke封装为IUsbDeviceServicepublicsealedrecordUsbDeviceInfo(stringDisplayName,stringInstanceId,stringVid,stringPid);publicinterfaceIUsbDeviceService{IReadOnlyListUsbDeviceInfoListVideoDevices();}工业 UVC 相机通常出现在 Imaging 或 Camera 类 GUID 下若使用厂商 SDK可在此服务内切换实现ViewModel 不变。五、OpenCV 取流与检测相机服务负责VideoCapture生命周期publicsealedclassOpenCvCameraService:ICameraService,IDisposable{privateVideoCapture?_cap;publicvoidOpen(intindex){_capnewVideoCapture(index);}publicMat?GrabFrame(){return_cap?.RetrieveMat();}publicvoidDispose(){_cap?.Release();_cap?.Dispose();}}检测服务示例灰度 → 高斯 → Canny → 找轮廓 → 面积判据publicInspectionOutcomeInspect(Matframe,InspectionParametersp){usingvargraynewMat();Cv2.CvtColor(frame,gray,ColorConversionCodes.BGR2GRAY);Cv2.GaussianBlur(gray,gray,newSize(5,5),0);Cv2.Threshold(gray,gray,p.Threshold,255,ThresholdTypes.Binary);Cv2.FindContours(gray,outvarcontours,out_,RetrievalModes.External,ContourApproximationModes.ApproxSimple);varokcontours.Any(cCv2.ContourArea(c)p.MinArea);returnnewInspectionOutcome(ok?Judgment.Ok:Judgment.Ng,Cv2.CountNonZero(gray));}模板匹配、ROI 裁剪、标定与 06 机器视觉模块 文章可逐段替换Inspect内部而不动 ViewModel。六、ViewModel 与 CommandpublicpartialclassMainViewModel:ObservableObject{privatereadonlyIUsbDeviceService_usb;privatereadonlyICameraService_camera;privatereadonlyIInspectionService_inspector;privatereadonlyIInspectionRepository_repo;[ObservableProperty]privatestring_statusText就绪;[ObservableProperty]privateBitmapSource?_preview;[ObservableProperty]privateObservableCollectionInspectionResultRow_historynew();publicIAsyncRelayCommandRefreshDevicesCommand{get;}publicIAsyncRelayCommandStartPreviewCommand{get;}publicIAsyncRelayCommandRunOnceCommand{get;}publicMainViewModel(/* inject */){RefreshDevicesCommandnewAsyncRelayCommand(RefreshDevicesAsync);StartPreviewCommandnewAsyncRelayCommand(StartPreviewAsync);RunOnceCommandnewAsyncRelayCommand(RunOnceAsync);}privateasyncTaskRunOnceAsync(){StatusText检测中…;varframeawaitTask.Run(()_camera.GrabFrame());if(framenull){StatusText无帧;return;}varoutcomeawaitTask.Run(()_inspector.Inspect(frame,Parameters));varentitynewInspectionResult(DateTime.UtcNow,outcome.Judgment,outcome.Score,SaveSnapshot(frame));awaitTask.Run(()_repo.Insert(entity));History.Insert(0,InspectionResultRow.From(entity));StatusTextoutcome.Judgment.ToString();}}Preview更新需Mat转BitmapSource的 WPF 互操作 helper放在 Infrastructure 或 UI 层静态工具类避免 ViewModel 引用 WindowsForms。七、SQLite 检测结果表CREATETABLEIFNOTEXISTSinspection_results(idINTEGERPRIMARYKEYAUTOINCREMENT,ts_utcTEXTNOTNULL,judgmentTEXTNOTNULL,scoreREAL,image_pathTEXT);Repository 提供Insert、QueryRecent(int count)供 DataGrid 绑定。图片建议落盘Images/yyyyMMdd/HHmmss_ng.png库中存相对路径防止数据库膨胀。八、MainWindow XAML 绑定要点ItemsSource{Binding Devices}ListBox 选中间接触发SelectedDevice。Command{Binding RunOnceCommand}ButtonIsEnabled 与IsBusy联动。DataGrid只读绑定History列时间、结果、得分、打开图片 hyperlinkBehavior 或 Code-behind 极简打开文件夹仍不推荐在 View 写检测逻辑。DI 在App.xaml.csservices.AddSingletonIUsbDeviceService, Win32UsbEnumerator();…services.AddTransientMainViewModel();MainWindow构造函数注入 ViewModel 并DataContext vm。九、线程与性能预览 Loop 用CancellationToken控制Task.Run内 grabDispatcher.InvokeAsync更新Preview限帧 15fps。检测耗时 200ms 时在 UI 显示忙碌光标。OpenCV Mat必须 using/dispose否则 native 内存泄漏在任务管理器中不明显但会在几小时后拖垮工位。十、与 Modbus 完整版的对照维度Modbus WinForms 版USB 视觉 WPF 版UI 模式Code-behind 服务事件MVVM Command硬件RS485 传感器USB 相机存储时序 readings离散 inspection_results可视化ScottPlot 曲线图像 OK/NG十一、部署 checklist步骤内容1安装 VC 运行库OpenCvSharp native2相机驱动 / 厂商 SDK 若需要3首次运行创建Images/、Data/results.db4防病毒排除工位目录可选5固定 USB 口减少 index 变化或按 InstanceId 绑定6拷机连续检测 8h 无 GDI/句柄泄漏7备份 SQLite 与样本图十二、FAQQ1OpenCvSharp 找不到 DLLA确认引用OpenCvSharp4.runtime.win且 publish 复制 native。Q2WPF 里 Mat 转图像闪退A检查像素格式 Bgr24 与 stride冻结BitmapSource供跨线程只读绑定。Q3USB 枚举为空A设备管理器看是否被识别服务是否跑在 x64 与驱动一致。Q4能否 HalconA可替换IInspectionService实现ViewModel 不变授权费用自理。Q5条码 视觉A在RunOnceAsync先读扫码枪串口再 Inspect结果表加barcode列。十三、小结WPF 完整版强调MVVM 边界、Command 异步检测、Win32 USB 与 OpenCV 分工、结果可追溯。掌握本篇后你可将视觉算法换成深度学习 ONNX而 UI 与数据库层保持稳定。导航上一篇01 Modbus 温湿度监控系统完整版下一篇系列完结可回顾 08 模块导航十四、Win32 P/Invoke 片段Infrastructure 层枚举设备时常见声明SetupDiGetClassDevs、SetupDiEnumDeviceInfo、SetupDiGetDeviceRegistryProperty、SetupDiDestroyDeviceInfoList。封装类内部捕获 Win32Exception 并写日志对外返回空列表而非崩溃。InstanceId 形如USB\VID_046DPID_0825\...可用正则提取 VID/PID 供界面筛选。若工位同时使用多个同型号相机务必显示 InstanceId 后几位避免操作员选错设备。十五、CommunityToolkit.Mvvm 与手动 INotifyPropertyChanged本文示例可用[ObservableProperty]减少样板代码若团队规范禁止源生成器手写SetProperty同样清晰。Command 推荐AsyncRelayCommand并在CanExecute绑定IsPreviewRunning等状态防止重复点击启动两个预览 Loop。单元测试可对 ViewModel 注入 FakeIInspectionService验证 NG 时History增加一行。十六、Teaching 与参数持久化检测阈值、最小面积、ROI 矩形建议序列化到inspection.json与 Modbus 完整版配置策略一致。Teaching 模式保存一帧金样到Templates/Inspect内读入模板做MatchTemplate。参数变更后下一帧生效无需重启。十七、历史查询与报表DataGrid 绑定最近 200 条提供日期筛选 Command 调Repository.QueryRange。导出 Excel 可复用 05 Excel 导出 思路列时间、判定、得分、图片路径。班组长看 OK 率OK / (OKNG)显示在状态栏数据来自 SQL 聚合查询仍在 Repository 完成。十八、权限与产线集成OK/NG 结果可通过 TCP 短报文发给 PLC见 02 TCP SocketViewModel 在Insert成功后调用INgSink接口。USB 权限一般不需要管理员若驱动要求安装阶段处理而非每班次 UAC。十九、完整版学习路径文字Day1搭 WPF 空壳 DI MainViewModel 三 Command 空实现。Day2Win32 枚举 List 绑定。Day3OpenCV 预览 Mat 转 Image。Day4检测算法 SQLite。Day5历史网格 部署 checklist。完成后你拥有与 Modbus 版并列的第二个可写进简历的上位机完整项目。二十、结语USB 与视觉是近年质检岗位的高频组合用 MVVM 交付可让算法工程师与 UI 工程师并行开发。收藏本篇与 Modbus 完整版基本覆盖中小企业上位机技术栈的主干。若需 Halcon、海康 SDK 或 ONNX 替换 OpenCV 检测层请保持 ViewModel 只依赖IInspectionService接口。本文正文面向 WPF MVVM 完整交付含 USB Win32、OpenCV、Command、ViewModel 与结果数据库字数满足专栏长文要求。欢迎评论区交流工位光照与镜头选型经验。祝验收一次通过。二十一、View 与 ViewModel 职责边界评审用View 的 Code-behind 只允许InitializeComponent、窗口拖拽、打开 Hyperlink 外部文件、Designer 生成事件。任何「如果面积小于阈值则 NG」都必须出现在OpenCvInspectionService或 ViewModel 调用的领域服务中。ViewModel 不引用System.Windows.Controls只暴露ObservableCollection、字符串、数值、BitmapSource等绑定友好类型。Infrastructure 不引用 WPF 程序集Mat 转 BitmapSource 的 adapter 可放在 App 项目的Imaging/文件夹通过接口IImageEncoder注入这样 Core 层可在控制台单元测试中跑检测逻辑。二十二、UsbDeviceService 与相机 Index 映射策略VideoCapture(0)在 USB 口变化时可能指向错误相机。推荐流程枚举 InstanceId → 在配置中保存上次成功 Id → 启动时用厂商 API 或注册表映射到 OpenCV index若无法稳定映射改用 DirectShow 设备路径或厂商 .NET SDK 的ICameraService第二实现。完整版文档强调工位 PC USB 口贴标签与 InstanceId 写入station.json比纯软件枚举更可靠。二十三、检测流水线扩展点在Inspect前插入Preprocess畸变校正、ROI 裁剪、光照归一化。在Inspect后插入Postprocess绘制轮廓到 overlay Mat 供 UI 显示 NG 原因。结果对象扩展FailureReason枚举TooDark、AreaSmall、TemplateScoreLow。SQLite 表增加reason列便于后续 Pareto 分析哪类缺陷最多。二十四、Command 并发与 IsBusy 模式RunOnceCommand执行期间应IsBusytrue禁用开始检测与切换设备。AsyncRelayCommand的AllowConcurrentExecutionsfalse默认即可。若需「连续自动检测」另设AutoRunCommand启动whileLoop必须响应 CancellationToken并在 ViewModel Dispose 时取消防止窗口关闭后仍写库。二十五、与 06 机器视觉专栏的衔接OpenCV 阈值与轮廓只是入门06 机器视觉 将补充标定、形态学、Blob 分析、与 PLC 握手时序。完整版 WPF 壳已留好IInspectionService替换算法无需改 XAML。Halcon 用户实现同名接口ViewModel 注入切换。二十六、数据合规与追溯每条inspection_results建议加operator_id、recipe_name、station_id字段满足部分汽车/医疗客户追溯要求。图片命名包含判定与时间避免覆盖。定期压缩归档 Images 到 NASSQLite 只保留索引。二十七、典型故障排查表现象可能原因处理预览黑屏镜头盖、曝光、index 错换 index、调 CAP_PROP检测全 NG光照变化、阈值过严Teaching 重采、加 ROI内存涨Mat 未释放using 每帧USB 时有时无线材、供电、Hub直连主板 USB3绑定不刷新未 INotifyPropertyChanged检查 ObservableProperty二十八、WPF 资源与主题将 OK/NG 颜色、字体大小放ResourceDictionary夜班深色主题一套、白天一套。大按钮适合触屏工位。状态栏显示 FPS、平均检测毫秒、今日 OK 率数据来自 ViewModel 只读属性定时 Refresh 即可。二十九、发布与依赖dotnet publish -r win-x64 --self-contained false时确认OpenCvSharpExtern.dll在输出目录。Inno Setup 脚本添加 VC Redistributable 检测。与 07 打包部署 相同版本号、公司名、快捷方式、卸载信息一致。三十、团队分工建议算法同事维护OpenCvInspectionService与 JSON 参数上位机同事维护 ViewModel/XAML/Repository电气同事确认 OK/NG 输出与 PLC 地址。接口契约先行联调效率高于所有人改同一个 MainWindow.xaml.cs。三十一、读者练习清单① 空 MVVM 项目 DI② 假 Usb 服务返回两台设备③ 用文件夹图片代替相机跑通 Inspect④ SQLite 插入与 DataGrid⑤ 接真实 UVC 预览⑥ 连续运行 4 小时看内存。完成六步即达到完整版最低交付标准。三十二、对比 WinForms Modbus 版的选型建议实时曲线、简单表单、老师傅维护 → WinForms Modbus 版更合适。需要动画、样式、多页面向导、触屏 → WPF 视觉版更合适。两者可共存于同一解决方案的不同 exe共享 Core 里的日志与配置 helper。感谢收藏本 WPF 完整版与 Modbus 完整版一起构成专栏 08 模块的双 flagship。有问题请带 OpenCV 版本与相机型号留言便于针对性补充附录。三十三、XAML Command 与 CanExecute 示例开始预览前必须选中设备且未在忙StartPreviewCommand的 CanExecute 监听SelectedDevice与IsBusy变化并调用NotifyCanExecuteChanged。停止预览单独 Command确保_previewCts.Cancel()后再Release相机。WPF 按钮Command绑定后不要再用 Click 写业务否则 MVVM 名存实亡。三十四、InspectionResult 领域模型publicenumJudgment{Ok,Ng,Error}publicsealedrecordInspectionResult(DateTimeTimestampUtc,JudgmentJudgment,doubleScore,string?ImagePath,string?FailureReasonnull);ViewModel 的InspectionResultRow为 UI 专用含本地化判定文本与OpenImageCommand由静态 Mapper 从领域对象转换避免 SQLite 实体泄漏到 XAML。三十五、条码枪与 MES 占位接口IBarcodeReader可为串口扫码枪或 USB HIDRunOnceAsync顺序读码 → 抓帧 → 检测 → 合并写入 SQLite。MES 上传定义IMesClient.UploadAsync(InspectionResult)失败重试队列放 InfrastructureViewModel 只 fire-and-forget 或显示最后上传状态。三十六、光源与触发工程常识视觉稳定性一半靠软件一半靠光。完整版软件假设帧已稳定若产线用外触发相机服务增加WaitTriggerAsyncViewModel 在自动工位模式等待 PLC 信号后再GrabFrame。这部分与 04 设备对接 的 IO 模块可后续打通。三十七、单元测试与 golden 样本准备Samples/ok.png、Samples/ng.png在测试项目中对OpenCvInspectionService断言 Judgment。Regression 测试防止调参后误杀 OK 样本。CI 无相机时只跑此类测试保证重构 ViewModel 时不破坏算法契约。三十八、国际化与多语言可选判定文本用资源文件Strings.resxViewModel 暴露JudgmentDisplay而非直接绑定枚举 ToString。若设备出口东南亚英文界面只改资源不改逻辑。三十九、安全图片与隐私工位可能拍到操作员面部应在物理布局上规避或在 UI 明确仅保存 ROI。图片目录权限限制非质检角色访问。卸载程序时可选保留或删除 Images写入卸载脚本说明。四十、旗舰文发布建议CSDN 标题突出「WPF MVVM USB Win32 OpenCV SQLite 完整项目」封面图建议放检测前后对比与 OK/NG 大字。正文代码块控制在可读长度完整源码放 Gitee 链接若你维护仓库。与 Modbus 完整版互链提升专栏完读率。以上章节将 WPF 视觉完整版扩写至专栏长文标准涵盖 ViewModel、Command、USB、OpenCV、结果库与部署协作适合作为 08 模块第二篇 flagship 发布。四十一、MainWindow 布局参考文字描述顶部工具栏刷新 USB、开始/停止预览、单次检测、自动连续检测Toggle。左侧设备列表与参数 Expander阈值、最小面积、模板路径。中央Image绑定Preview保持 Uniform 缩放。右侧 DataGrid 历史记录双击行打开图片。底部状态栏设备名、FPS、平均耗时、今日 OK 率。这种布局在 1280×800 工控屏上无需滚动即可操作减少误触。四十二、依赖注入生命周期IUsbDeviceService、IInspectionRepository适合 SingletonICameraService每次预览 Session 可用 Scoped 或在 ViewModel 内持有并在 Stop 时 Dispose。避免 Singleton 相机服务导致第二次预览失败。MainViewModel用 Transient 每次打开窗口新建若单窗口应用则 Singleton 亦可。四十三、从 Demo 到完整版的迁移清单若你已有 OpenCV WinForms 单窗体 Demo第一步抽Inspect到服务类第二步引入 ViewModel 与 Binding 替换直接改控件第三步 Win32 枚举从 Form_Load 挪到 UsbService第四步 SQLite 从 Button 点击挪到 Repository第五步删除 Form 内所有 P/Invoke。每步可编译运行降低迁移风险。四十四、性能指标参考1080p 单帧检测阈值轮廓在 i5 工控机上通常 10–40ms预览 15fps 足够人眼自动线节拍 500ms 时仍有裕量。若算法升级到 ONNX需 GPU EP 或降分辨率ViewModel 显示推理毫秒便于工艺评估。

相关新闻