YOLOv11+PyQt红绿灯检测系统:从模型到界面全链路实战

发布时间:2026/10/7 11:06:08

YOLOv11+PyQt红绿灯检测系统:从模型到界面全链路实战 1. 从红绿灯检测说起这套系统到底解决了什么问题红绿灯目标检测这个方向看起来只是目标检测里一个很窄的细分场景但真正落地过的人都知道它比通用目标检测要麻烦得多。通用检测你只要框出物体、给个类别就行红绿灯检测不行——它要求你在复杂的城市道路场景里准确区分红、黄、绿三种状态还要应对强光、逆光、雨雾、遮挡、远距离小目标等一系列现实问题。更关键的是检测结果往往要直接对接决策逻辑错一帧可能就意味着一次误判。我这次做的这套系统核心目标很明确用YOLOv11作为检测引擎配一套PyQt的可视化界面把图片推理、视频推理、摄像头实时推理三条链路全部打通形成一个可以直接交付、可以演示、可以继续迭代的完整工程。它不是那种跑个demo就结束的脚本而是一个带界面、带状态管理、带结果保存的完整系统。为什么选YOLOv11而不是YOLOv8或者更早的版本原因很实际。YOLOv11在Ultralytics的维护下API统一、文档清晰、导出格式丰富而且在小目标检测上的表现比前代有可见提升。红绿灯在远距离画面里往往只占几十个像素这种小目标场景下YOLOv11的特征融合结构确实更占优势。至于PyQt选它的理由更简单Python生态里做桌面端界面PyQt的成熟度、控件丰富度和跨平台能力都是第一梯队配合OpenCV做视频流渲染几乎没有学习成本。这套系统适合谁如果你是想入门目标检测但不知道怎么做成完整项目的学生这套代码可以让你直接看到从模型加载到界面交互的全流程如果你是需要快速搭建演示原型的工程师这套架构可以省掉你至少一周的界面和线程管理调试时间如果你是做智慧交通方向的产品验证这套系统可以直接作为算法验证平台来用。2. 整体架构设计与技术选型拆解2.1 为什么采用分层架构而不是单文件脚本很多人做目标检测项目习惯把所有逻辑塞进一个main.py里加载模型、读图片、画框、显示几百行搞定。这种写法跑demo没问题但一旦要加视频、加摄像头、加界面代码会迅速失控。我踩过这个坑所以这套系统从一开始就采用分层设计。整个系统分成四层模型推理层负责YOLOv11的加载和前后处理业务逻辑层负责图片、视频、摄像头三种输入的统一调度界面表现层负责PyQt的控件布局和信号响应线程管理层负责把耗时推理放到子线程里避免界面卡死。这四层之间通过信号槽和回调函数通信耦合度低任何一层要替换都不会牵动全局。这样设计的好处在实际开发中非常明显。比如后来我想把YOLOv11换成YOLOv11-seg做分割只需要改推理层的模型加载和输出解析界面层完全不用动。再比如我想加一个检测结果导出CSV的功能只需要在业务逻辑层加一个回调界面加一个按钮就行。2.2 YOLOv11模型选型n/s/m/l/x怎么挑Ultralytics提供了YOLOv11的五个规格n、s、m、l、x。参数量从2.6M到56.9M不等mAP从39.5到54.7。选哪个不是拍脑袋决定的要看你的部署环境和精度要求。模型规格参数量mAP(val)推理速度(CPU)适用场景YOLOv11n2.6M39.5约80ms边缘设备、实时性优先YOLOv11s9.4M47.0约120ms平衡型、推荐起点YOLOv11m20.1M51.5约200ms精度优先、有GPUYOLOv11l25.3M53.4约280ms高精度离线分析YOLOv11x56.9M54.7约450ms极致精度、不计速度我的建议是先用s跑通全流程再根据实际badcase决定是否升级。红绿灯检测如果只是近距离、清晰画面n就够用如果是远距离小目标s或m更稳妥。不要一上来就上x推理速度会让你在摄像头实时场景里痛不欲生。2.3 PyQt界面框架的线程模型设计这是整套系统里最容易出问题的地方。PyQt的主线程负责界面刷新如果你在按钮点击的回调里直接跑推理界面会直接卡死用户点什么都点不动。我见过太多人在这里翻车。正确的做法是所有推理任务放到QThread子线程里执行通过pyqtSignal把结果传回主线程更新界面。具体来说我定义了一个InferenceWorker类继承QThread在run方法里循环读取帧、推理、发信号。主线程收到信号后只做一件事把带框的图像转成QImage显示到QLabel上。这里有个细节要注意OpenCV读到的帧是BGR格式PyQt显示需要RGB转换时要用cv2.cvtColor而且QImage构造时要注意数据内存的生命周期否则会出现花屏或者崩溃。我的做法是在子线程里完成BGR到RGB的转换再把numpy数组拷贝一份传给主线程避免内存被回收。3. 核心模块实现细节与实操要点3.1 模型加载与推理封装模型加载看起来简单就一行YOLO(yolo11s.pt)但实际项目里要考虑的事情不少。首先是权重文件的管理我习惯在项目根目录建一个weights文件夹把pt文件统一放进去代码里用相对路径引用。这样换模型只需要改一个配置项不用满代码找路径。推理封装我写了一个Detector类对外暴露detect(frame)方法内部处理resize、归一化、推理、NMS、坐标还原。这里有个关键点输入尺寸的选择。YOLOv11默认640但红绿灯检测如果画面里目标很小可以适当提高到960或1280。代价是推理时间增加实测下来640到960大约增加1.8倍耗时960到1280再增加1.6倍。你需要根据实际场景权衡。from ultralytics import YOLO import cv2 import numpy as np class Detector: def __init__(self, weight_path, conf0.25, iou0.45, imgsz640): self.model YOLO(weight_path) self.conf conf self.iou iou self.imgsz imgsz def detect(self, frame): results self.model.predict( sourceframe, confself.conf, iouself.iou, imgszself.imgsz, verboseFalse ) return results[0]置信度阈值conf的设置也有讲究。红绿灯检测里漏检比误检更危险所以conf可以适当放低到0.2左右宁可多框几个再后处理过滤。但也不能太低否则背景里的红色物体、绿色广告牌都会被误检。我的经验值是0.25到0.35之间具体看你的数据集质量。3.2 图片推理链路的实现图片推理是最简单的链路但要做好用户体验细节不少。用户点击选择图片按钮后弹出文件对话框选中后要立即显示原图然后自动触发推理推理完成后在同一区域显示带框结果。这里我用了一个小技巧原图和结果图用同一个QLabel显示通过状态变量切换这样界面简洁用户也能直观看到对比。结果保存功能我做了两种一种是保存带框图像用cv2.imwrite直接写另一种是保存检测信息到txt每行格式是类别 置信度 x1 y1 x2 y2。后者对于后续做数据分析很有用比如统计某个路口红灯出现的频率。注意保存路径不要用中文OpenCV在某些平台上对中文路径支持不好会静默失败。我一般用时间戳命名比如result_20250101_143022.jpg避免重名覆盖。3.3 视频推理与进度控制视频推理比图片复杂的地方在于你要逐帧读取、逐帧推理、逐帧显示还要处理视频结束、暂停、继续这些状态。我用OpenCV的VideoCapture读视频用一个while循环驱动每读一帧就调用detector推理然后emit信号给界面显示。这里最大的坑是视频帧率和推理速度不匹配。比如视频是30fps但你的推理只能跑到10fps如果直接按视频帧率播放画面会严重滞后。我的处理方式是不做帧率同步读一帧推一帧推完就显示这样视频会变慢但不会丢帧。如果你要求实时播放那就需要跳帧策略比如每3帧取1帧推理中间帧复用上一帧结果。cap cv2.VideoCapture(video_path) while cap.isOpened(): ret, frame cap.read() if not ret: break result detector.detect(frame) annotated result.plot() self.frame_signal.emit(annotated) if self.is_paused: while self.is_paused: QThread.msleep(100) cap.release()暂停功能的实现用了一个布尔标志位加循环等待简单但有效。不要用time.sleep会阻塞整个线程导致无法响应恢复操作。3.4 摄像头实时推理的特殊处理摄像头推理和视频推理代码结构几乎一样但有几个关键差异。第一摄像头没有总帧数进度条没法用我改成了显示实时FPS。第二摄像头要处理设备索引问题一般笔记本自带摄像头是0外接USB摄像头是1或2我加了一个下拉框让用户选择。第三摄像头推理对延迟极其敏感我做了两件事来降低延迟一是把imgsz降到480二是开启半精度推理如果有GPU。FPS的计算我用了一个滑动窗口取最近30帧的平均耗时倒数这样显示的数字不会剧烈跳动。实测在CPU上YOLOv11s跑480尺寸FPS大约在8到12之间如果有中端GPU可以到30以上基本满足实时要求。实操心得摄像头刚打开的前几帧往往是黑的或者过曝的这是自动曝光在调整。我一般会丢弃前5帧再开始推理避免第一帧结果异常吓到用户。4. 完整实操流程从零跑通这套系统4.1 环境配置的坑与正确姿势环境配置是新手最容易卡住的地方。我的建议是用conda建独立环境Python版本选3.9或3.10这两个版本和PyQt5、OpenCV、PyTorch的兼容性最稳。3.11以上有些包还没跟上3.8以下又太老。conda create -n yolo11_gui python3.10 conda activate yolo11_gui pip install ultralytics pip install PyQt5 pip install opencv-pythonPyTorch的安装要注意如果你有NVIDIA显卡去官网查对应CUDA版本的安装命令不要直接pip install torch那样装的是CPU版。CPU版跑推理慢不说还占内存。装完之后用torch.cuda.is_available()验证一下返回True才算成功。Ultralytics安装完会自动装依赖但有时候会和已有的numpy版本冲突。如果报numpy.core.multiarray failed to import就是版本问题pip install numpy1.24.3基本能解决。4.2 权重文件获取与自定义训练官方权重可以直接从Ultralytics的release页面下载yolo11s.pt大约18MB。下载后放到weights文件夹代码里指定路径即可。但红绿灯检测如果你要真正好用必须用自己的数据微调。官方权重是在COCO上训练的COCO里根本没有红绿灯这个类别只有traffic light一个粗类不分颜色。你需要准备自己的数据集标注格式用YOLO的txt格式每行类别id x_center y_center width height坐标都是归一化到0到1的。训练命令很简单yolo detect train datatraffic_light.yaml modelyolo11s.pt epochs100 imgsz640 batch16data.yaml里指定训练集、验证集路径和类别名称。类别就三个red、yellow、green。训练100轮在单张RTX3060上大约需要2到3小时。训练完成后best.pt就是你的自定义权重。注意事项红绿灯数据集最容易出现类别不平衡红灯样本远多于黄灯。我一般会对黄灯样本做过采样或者在loss里给黄灯更高权重否则模型会倾向于把黄灯预测成红灯。4.3 界面布局与信号槽连接PyQt界面我用Qt Designer画了初版然后手写代码微调。主窗口分三个区域左侧是控制面板按钮、下拉框、滑块中间是显示区域QLabel底部是状态栏显示FPS、检测数量、耗时。信号槽的连接是界面逻辑的核心。比如开始检测按钮的clicked信号连接到start_detection槽函数槽函数里创建worker线程并启动。worker的frame_signal连接到主线程的update_frame槽槽函数里把图像显示到QLabel。停止按钮则设置worker的stop标志位等线程自然结束。self.start_btn.clicked.connect(self.start_detection) self.stop_btn.clicked.connect(self.stop_detection) self.worker.frame_signal.connect(self.update_frame) self.worker.stats_signal.connect(self.update_stats)这里有个容易忽略的点线程结束后要调用wait()等待真正退出否则下次启动会报QThread: Destroyed while thread is still running。我在stop_detection里加了self.worker.wait(3000)等3秒超时再强制terminate。4.4 打包成exe分发给别人用如果你要把这套系统给别人用打包成exe是最省事的。用PyInstaller命令大致是pyinstaller --windowed --onefile --add-data weights;weights main.py但PyInstaller打包PyQt和OpenCV的坑非常多。常见问题包括缺少Qt插件导致界面起不来、OpenCV的dll没打进去、权重文件路径在打包后变成临时目录。我的经验是不要用--onefile用--onedir把weights文件夹手动拷到dist目录里代码里用sys._MEIPASS判断运行环境来定位资源路径。这样虽然文件夹大一点但稳定性高很多。5. 常见问题与排查技巧实录5.1 推理结果异常问题速查现象可能原因排查方法解决方案画面全黑或全白图像格式转换错误检查cvtColor的转换码BGR转RGB用COLOR_BGR2RGB框位置偏移坐标还原时缩放比例算错打印原图尺寸和输入尺寸按比例还原注意padding检测不到目标conf阈值过高逐步降低conf观察降到0.1测试再回调类别全是同一类权重文件不匹配检查names字典用对应数据集的权重界面卡死推理在主线程执行看是否用了QThread所有推理放子线程5.2 摄像头打不开的几种情况摄像头打不开是最常见的报错。首先确认设备索引对不对Windows下可以用cv2.VideoCapture(0, cv2.CAP_DSHOW)试试DSHOW后端有时候比默认后端更稳。如果还是不行检查是不是被其他程序占用了比如微信、钉钉的视频通话会占用摄像头。另外某些USB摄像头需要先插上再启动程序热插拔识别不到。还有一种情况是能打开但读不到帧cap.read()一直返回False。这通常是驱动问题换个USB口或者换台电脑试试。我在一台老笔记本上遇到过最后发现是USB供电不足换到USB3.0口就好了。5.3 长时间运行的稳定性处理这套系统如果要做长期现场测试稳定性是重中之重。我做过连续72小时的摄像头推理测试总结了几条经验。第一内存泄漏要防每处理1000帧手动调用一次gc.collect()虽然Python有自动回收但OpenCV的Mat对象有时候回收不及时。第二摄像头断连要能自动重连我在循环里加了异常捕获读帧失败超过10次就释放重新打开。第三日志要落盘用logging模块把关键事件写到文件出问题了能回溯。实操心得长时间运行建议把显示帧率降下来比如每3帧才更新一次界面减少GUI刷新开销。推理照常每帧都做只是显示抽帧这样既保证检测不丢帧又降低界面负担。5.4 小目标检测效果差的优化思路红绿灯在远距离下就是典型小目标几十个像素YOLOv11默认的640输入很难检到。我试过几种优化手段效果从低到高排列提高输入尺寸到960或1280、在数据集中增加小目标样本、用SAHI切片推理、修改模型结构加P2层。前两种成本最低建议先试。SAHI切片推理是把大图切成小块分别检测再合并对小目标提升明显但推理时间成倍增加。改P2层需要重新训练适合有充足数据的情况。实测下来输入尺寸从640提到960小目标召回率能提升15到20个百分点代价是推理时间增加约80%。如果你的场景对实时性要求不高这个交换是值得的。6. 后续可扩展的方向与个人体会这套系统目前是单模型单任务的但架构上留了扩展空间。比如你可以把检测结果接一个简单的状态机连续多帧检测到红灯才判定为红灯避免单帧误检。也可以把检测框坐标传给一个跟踪器做红绿灯状态跟踪这样即使某帧漏检也能靠跟踪补上。再往大了说这套PyQt加YOLO的框架不只能做红绿灯换成其他权重就是其他检测系统。我后来用同样的架构做了工地安全帽检测、停车场车位检测界面和线程管理代码几乎没改只换了模型和类别名称。这就是分层架构的价值。最后分享一个我在调试时常用的小技巧在推理结果上叠加显示当前conf阈值和imgsz截图发给别人的时候一目了然省得反复问你用的什么参数。这个信息我放在状态栏右侧不占地方但很实用。
延伸阅读

更多相关文章

2026/10/7 11:06:08

eFuse+MCU智能电源保护:TPS259483与TM4C129实战详解

在嵌入式和工业产品里,电源路径的可靠性往往决定整机的生死。上电瞬间的浪涌电流、负载端短路、输入过压、热插拔产生的打火……任何一个环节出问题,轻则系统复位,重则烧掉整块板子。我这两年做工业网关和边缘控制器,花在电源保护…

2026/10/7 11:06:08

浊度传感器温度补偿实战指南:从硬件到LSTM的四层方案

1. 为什么“测得准”比“测出来”难十倍:浊度传感器的真实困境我第一次在污水处理站调试在线浊度仪时,被现场工程师一句话钉在原地:“你这数值,早上8点和下午3点差23 NTU,水样根本没动,泵也没换——温度变了…

2026/10/7 11:01:07

金融数据库审计与监测方案:从留痕到预警的实战指南

先说个背景。2025年国内金融行业对数据库的要求,已经从单纯的“能跑就行”变成了“跑得稳、看得清、查得明”。无论是银行核心系统、支付清算通道,还是券商资管、保险承保链路,数据就是业务本身。数据库一旦出问题,轻则交易卡顿&a…

2026/10/7 12:06:20

编程语言怎么选?从榜单热词看未来十年的学习方向

“编程语言排行榜”能挂在热搜上,我确实有点意外。以往这种榜单只是开发者圈子里互相调侃的话题,今年却成了大众围观的对象,背后肯定不只是榜单本身的问题。经常有读者问我“未来十年最值得学什么语言”,问多了以后我意识到&#…

2026/10/7 12:06:20

OTN技术体系深度解析:从G.709帧结构到ODU交叉调度与保护倒换实战

简介:这份PDF面向光通信与传输网方向的工程师、运维人员及通信专业学生,系统梳理OTN技术体系的标准框架与核心机制,帮助读者建立从网络架构到物理层的完整认知。资源为单文件PDF,压缩包约955KB,内容以图文结合方式呈现…

2026/10/7 12:06:20

Java基础面试228题:核心考点拆解与避坑实战指南

2026年了,Java基础面试依然是很多人绕不过去的一道坎。我整理了一套“Java面试题基础系列228道”,不是想让你埋头背题,而是想聊聊怎么把这228道题真正变成自己的知识体系。这套题覆盖了数据类型、集合框架、JVM、并发、异常、反射泛型等基础模…

2026/10/7 12:06:20

2026Java面试基础228题解析:面向对象、集合与异常核心考点

2026年的Java招聘市场,跟几年前最大的变化就是:八股文依旧在考,但面试官已经不满足于听你背结论了。我整理了这份《2026年Java面试题基础系列228道》,不是让你把答案死记硬背下来,而是把Java基础里最关键的考点按知识块…

2026/10/7 12:01:20

读懂IBIS模型中的Pullup和Pulldown曲线:信号完整性仿真的关键

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/7 8:18:33

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/6 17:46:51

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

ESP32免重刷固件:浏览器直接修改NVS键值实现WiFi配置更新

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

SAP HANA查询结果导出CSV:避开乱码、性能与权限的实用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

数字后端Placement阶段Density与Congestion控制实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑