Imported from woshiwenjunjie/pc-yolo-detector (
AGENTS.md). Install upstream withnpx skills add woshiwenjunjie/pc-yolo-detector. Copyright stays with the author.
AGENTS.md — 辣椒温室 YOLO 检测系统
本文件面向 AI 编程助手。如果你正在阅读此文件,说明你对本项目一无所知;本文将告诉你如何正确理解和修改代码。
1. 项目概述
本项目是一个辣椒温室大棚 AI 视觉检测系统,基于 Ultralytics YOLO 目标检测模型,能够识别辣椒生长状态及病害/成熟度等。系统支持三种检测模式:
- 图片检测:上传单张图片,返回带标注框的结果图和检测统计
- 视频检测:上传 MP4 视频,后台异步逐帧检测,带进度条反馈
- 摄像头实时检测:通过本地摄像头或 RTSP 网络流进行实时推理,WebSocket 推流
项目采用双进程架构:
- ModelServer(FastAPI,端口
8765):负责模型加载、推理、摄像头采集、视频异步处理 - Streamlit Web App(端口
8501):负责前端 UI、用户交互、结果展示
两者通过 HTTP 通信,修改前端无需重启 ModelServer,修改服务端则需重启 ModelServer。
2. 技术栈
| 层级 | 技术 |
|---|---|
| 深度学习框架 | Ultralytics YOLOv8 / YOLOv26 |
| 前端 UI | Streamlit + 自定义 CSS(Bento Grid 风格) |
| HTTP API | FastAPI + Uvicorn |
| 图像/视频处理 | OpenCV (cv2) + Pillow + NumPy |
| 摄像头/WebSocket | FastAPI WebSocket + 后台 daemon 线程 |
| 串口通信 | PySerial(对接 ESP32 串口摄像头) |
| 设备发现 | UDP 广播 + Zeroconf mDNS |
| E2E 测试 | Playwright (Python) |
| 语言 | Python 3.12+ |
3. 目录结构
pc_yolo_detector/
├── app.py # Streamlit 前端主程序(全部 UI 代码,~534 行)
├── requirements.txt # Python 依赖(pip 安装清单)
├── packages.txt # Linux 系统依赖(Ubuntu/Debian,如 freeglut3-dev)
├── run_oneclick.bat # Windows 一键启动脚本(启动 ModelServer + Streamlit)
│
├── .scripts/ # 辅助脚本(隐藏)
│ ├── start_server.py # 启动 ModelServer(给 run_oneclick.bat 调用)
│ ├── health_check.py # 健康检查脚本(给 run_oneclick.bat 轮询使用)
│ └── download_models.py # 预下载缺失的 YOLO 权重文件到 weights/
│
├── .tests/ # 测试(隐藏)
│ └── test_system.py # Playwright E2E 测试脚本(截图验证)
│
├── detect_server/ # 核心检测服务包
│ ├── __init__.py # 公共常量:CLASS_NAMES, IMG_SIZE, DEVICE 等
│ ├── __main__.py # python -m detect_server 入口
│ ├── cli.py # CLI 入口:参数解析 + 模式派发
│ ├── detect_http.py # FastAPI HTTP 服务器 + WebSocket 摄像头流 + 视频异步任务
│ ├── detect_client.py # HTTP 客户端(Streamlit 调用 ModelServer)
│ ├── registry.py # 模型注册表 MODEL_REGISTRY + ModelManager
│ ├── camera.py # 摄像头实时采集引擎(线程安全)
│ ├── detect_local.py # 本地单图检测模式(命令行)
│ ├── detect_serial.py # 串口循环检测模式(对接 ESP32)
│ ├── protocol.py # 串口帧协议编码(CRC16)
│ └── discovery.py # UDP + mDNS 设备发现服务
│
├── weights/ # 模型权重文件(.pt)
│ ├── yolov8n.pt ... yolov8x.pt # 内置通用 COCO 模型
│ ├── yolo26n.pt ... yolo26x.pt # 内置 YOLOv26 系列
│ ├── pepper_v26n_5cls_best.pt # 自定义辣椒 5 类模型
│ ├── peppers_yolov8n_best.pt # 自定义辣椒 4 类旧模型
│ └── chili_yolo26m_best.pt # 辣椒成熟度检测模型
│
├── assets/
│ └── camera_component.html # 摄像头实时检测的独立前端页面(WebSocket 渲染)
├── images/
│ ├── default.jpg # 默认示例图
│ └── detected.jpg # 示例检测结果图
└── docs/
└── 2026-05-24-video-progress-ux-*.md # 视频进度条设计文档与实现计划
4. 关键模块说明
4.1 detect_server/registry.py — 模型注册表
MODEL_REGISTRY 是一个全局字典,定义了所有可用模型:
- 内置模型(
type: "builtin"):YOLOv8n/s/m/l/x、YOLOv26n/s/m/l/x,基于 COCO 80 类,首次使用时会自动从 Ultralytics Hub 下载.pt文件到weights/目录 - 自定义模型(
type: "custom"):辣椒温室专用模型,权重文件位于weights/目录下
ModelManager 提供线程安全的延迟加载:
manager.model属性首次访问时才调用YOLO(path)加载模型,避免阻塞 Streamlit 的初始 WebSocket 连接switch()方法支持热切换模型,失败时自动恢复旧模型- 所有共享状态操作均受
threading.Lock()保护
4.2 detect_server/detect_http.py — FastAPI 服务
核心端点:
| 方法 | 端点 | 说明 |
|---|---|---|
| GET | /health |
健康检查,返回当前模型信息 |
| POST | /detect |
图片检测,返回 JSON + base64 标注图 |
| POST | /detect_video/start |
异步视频检测启动,返回 task_id |
| GET | /detect_video/progress/{task_id} |
查询异步任务进度 |
| GET | /detect_video/result/{task_id} |
获取处理完成的视频文件(StreamingResponse) |
| POST | /camera/start |
启动摄像头采集 |
| POST | /camera/stop |
停止摄像头 |
| GET | /camera/status |
查询摄像头状态 |
| WS | /camera/ws |
WebSocket 实时推流(JPEG + 检测框) |
| GET | /models |
列出所有可用模型 |
| GET | /models/current |
当前模型信息 |
| POST | /models/switch |
切换模型 |
| GET | /static/camera.html |
摄像头组件静态页面 |
视频异步处理机制:
- 上传视频后创建
VideoTask对象,存入_video_tasks字典 - 后台
threading.Thread逐帧推理并更新进度 - 任务完成后 30 秒无访问自动清理临时文件(
_cleanup_task) _video_tasks和_video_tasks_last_access必须成对操作,且始终在外层_video_tasks_lock保护下读写
摄像头 WebSocket:
camera_ws在连接断开时会自动调用camera.stop(),避免摄像头资源泄漏- 支持通过 WebSocket 消息动态调整
confidence和skip参数
4.3 detect_server/camera.py — 摄像头引擎
CameraCapture 运行在后台 daemon 线程中:
- Windows 下优先使用
CAP_DSHOW后端避免 10 秒阻塞超时,失败则回退CAP_MSMF - 支持跳帧检测(
skip参数):每 N 帧做一次推理,中间帧复用上次结果 - 断线后自动重连(最多 5 次)
- 线程安全缓冲区:
_latest_jpeg,_latest_dets,_fps - FPS 文字叠加在画面右上角(黑色底条,减少与检测框重叠)
4.4 app.py — Streamlit 前端
全部 UI 逻辑集中在此单文件(~534 行):
- 侧栏:模型选择卡片(自定义模型置顶,内置模型按系列折叠)、置信度滑块、当前模型信息
- 图片模式:左右分栏,左原图右结果,支持默认图、检测统计、KPI 卡片
- 视频模式:三阶段状态机(空闲 → 检测中 → 完成),
st.progress()显示进度,支持取消和下载 - 摄像头模式:
st.iframe嵌入assets/camera_component.html,通过 WebSocket 与 ModelServer 通信
重要:app.py 和 ModelServer 是两个独立进程,通过 HTTP 通信(http://127.0.0.1:8765)。修改前端代码无需重启 ModelServer。
4.5 detect_server/detect_client.py — HTTP 客户端
DetectClient 封装了所有对 ModelServer 的 HTTP 调用:
- 使用
requests.Session()复用连接池 - 统一异常类型
ModelServerError - 图片上传前自动缩放(最大边 1920)并 JPEG 编码,减少传输体积
- 视频相关方法:
detect_video_start,detect_video_progress,detect_video_result,detect_video_wait
5. 构建与运行命令
5.1 安装依赖
pip install -r requirements.txt
requirements.txt 关键依赖:
streamlit>=1.26.0ultralytics>=8.0.173opencv-python-headless>=4.8.0fastapi>=0.104.0,uvicorn>=0.24.0pyserial>=3.5,zeroconf>=0.131.0pydantic>=2.5.0,python-multipart>=0.0.6,requests>=2.31.0
Linux 用户还需安装系统依赖(packages.txt):
sudo apt-get install freeglut3-dev libgtk2.0-dev libgl1-mesa-glx
5.2 一键启动(Windows)
run_oneclick.bat
流程:
- 自动查找 Python 环境(优先项目外特定虚拟环境路径,其次系统 PATH)
- 启动 ModelServer(端口 8765)
- 轮询
/health等待模型加载就绪(最多 30 次 × 2 秒) - 启动 Streamlit(端口 8501)
- 退出时自动关闭 ModelServer
5.3 手动分步启动
终端 1 — 启动模型服务器:
python .scripts/start_server.py
# 或
python -m detect_server --http --http-port 8765
终端 2 — 启动前端:
streamlit run app.py --server.port 8501
5.4 命令行模式(无需 Streamlit)
# 本地图片检测
python -m detect_server --model custom_pepper_v26n --upload ./images/default.jpg
# 启动 HTTP 服务(不带 Streamlit)
python -m detect_server --http --http-host 0.0.0.0 --http-port 5000
# 串口模式(对接 ESP32)
python -m detect_server --model custom_pepper_v26n --port COM3 --baud 921600
# 列出所有模型
python -m detect_server --list-models
5.5 预下载模型权重
python .scripts/download_models.py
脚本会下载 MODEL_REGISTRY 中缺失的内置模型到 weights/ 目录。
6. 测试说明
6.1 E2E 测试
# 确保 Streamlit 和 ModelServer 已启动
python .tests/test_system.py
test_system.py 使用 Playwright 进行无头浏览器测试:
- 访问
http://localhost:8501,截图保存到.tests/test_output/ - 验证点:标题含"辣椒"、侧栏模型卡片可见、文件上传区存在、模式切换正常
- 等待检测结果最长 120 秒(含模型预热和 WS 重连时间)
- 测试输出包括
01_loading.png,03_sidebar.png,04_back_image.png等截图
6.2 健康检查
python .scripts/health_check.py
# 输出示例:OK (custom_pepper_v26n)
7. 代码风格规范
本项目所有注释、文档字符串、变量名均使用中文。请保持以下风格:
- Docstring:使用
"""中文说明""",模块顶部写职责说明 - 日志/打印:使用
[OK],[ERR],[WARN],[INFO]前缀 - 常量:全大写,定义在
detect_server/__init__.py中,如IMG_SIZE = 640,DEVICE = "cuda:0" if torch.cuda.is_available() else "cpu" - 线程安全:共享状态必须使用
threading.Lock(),如ModelManager._lock,CameraCapture._lock,_video_tasks_lock - 错误处理:HTTP 层捕获异常后返回
JSONResponse(status_code=..., content={"error": ...});客户端统一抛出ModelServerError - 类型注解:鼓励使用,特别是公共 API 参数和返回值
7.1 修改注意事项
- 双进程架构:修改
app.py只需刷新 Streamlit 页面;修改detect_server/中的服务端代码需要重启 ModelServer - 模型切换:
ModelManager.switch()是线程安全的,但模型加载本身较耗时(数秒),FastAPI 端点使用loop.run_in_executor()避免阻塞事件循环 - 视频异步任务:
_video_tasks字典和_video_tasks_last_access必须成对操作,且始终在外层_video_tasks_lock保护下读写 - 摄像头 WebSocket:
camera_ws在连接断开时会自动调用camera.stop(),避免摄像头资源泄漏 - Streamlit session_state:
app.py大量使用st.session_state保存上传文件、任务 ID、摄像头状态等,新增状态变量需考虑页面重载(rerun)后的持久化
8. 安全注意事项
- 无身份认证:FastAPI 服务器和 WebSocket 端点均没有登录/鉴权机制,默认绑定
0.0.0.0,在公网暴露存在风险 - 文件上传:
/detect和/detect_video/start接受用户上传文件,服务端仅做cv2.imdecode解码,未设置显式大小限制,大文件可能导致内存/磁盘耗尽 - 临时文件:视频处理使用
tempfile.mkdtemp()创建临时目录,正常流程下 30 秒无访问后自动清理,但异常崩溃可能残留临时文件 - 串口协议:
detect_serial.py和protocol.py中的二进制帧协议无加密和重放保护,仅用于受信任的本地串口连接 - 路径解析:
registry.py中的resolve_model()使用Path(__file__).resolve().parent.parent拼接自定义模型路径,确保传入的相对路径始终被限制在项目目录内
9. 常见问题排查
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| ModelServer 启动后 health 检查一直失败 | 模型文件缺失,Ultralytics 下载超时 | 检查 weights/ 下是否存在对应 .pt 文件;运行 .scripts/download_models.py |
| 摄像头无法打开 | Windows 隐私设置 / 驱动 / 被其他应用占用 | 检查 Windows 设置中允许相机访问;尝试换 source 为 1, 2 |
| Streamlit 页面显示 "模型服务器未就绪" | ModelServer 未启动或端口冲突 | 检查 8765 端口是否被占用;查看 ModelServer 终端输出 |
| 视频检测进度卡在 0% | OpenCV 无法解码视频或编码器缺失 | 确保上传的是标准 H.264 MP4;检查 OpenCV 编译是否包含 mp4v 编码器 |
| WebSocket 摄像头画面不更新 | 浏览器安全策略 / 防火墙 | 摄像头页面通过 http://localhost:8765/static/camera.html 加载,确保该地址可访问 |
| 串口模式收不到数据 | 波特率不匹配 / 帧格式错误 | 确认 ESP32 端波特率为 921600;检查帧头 0xAA 0xBB 和长度字段 |
双击 run_oneclick.bat 闪退 |
中文路径 + cmd.exe 代码页冲突 | 改用 run_oneclick.ps1 直接运行,bat 只做入口调用 |
[Errno 10048] 端口占用 |
上次 ModelServer 未正常退出 | 脚本已自动清理端口 8765;也可手动运行 taskkill /f /pid $(netstat -ano | findstr :8765) |
10. 变更日志
2026-06-01 — 审查修复记录
清理
- 删除 13 个冗余文件:
protocol.py、未注册权重 6 个、无用资产图片/视频 4 个、images/detected.jpg、.scripts/health_check.py detect_serial.py保留骨架但已知有 bug(缺encode_results导入),待修复
新增
run_oneclick.ps1— PowerShell 一键启动脚本,规避中文路径 + cmd.exe 代码页兼容问题- 模型注册表新增
custom_chili_v26m(辣椒成熟度-YOLOv26m,5类:Disease/Physical Damage/Ripe/Rot/Unripe,mAP50=0.910)
修复
detect_http.py— 补import traceback,视频检测异常时不再NameError崩溃detect_http.py— 废弃旧同步/detect_video端点,引导用异步接口detect_http.py— 去重draw_boxes,统一导入camera.py版cli.py— 修正导入函数名run_server、修复调用签名camera.py— FPS 文字移至右上角 + 黑色底条,避免遮盖检测框detect_client.py— 移除老detect_video()死代码camera_component.html— emoji/cnMap 补充新模型 5 类,未知标签兜底显示英文run_oneclick.bat— 重写为 PowerShell 脚本入口,解决中文路径 flash-closerun_oneclick.bat— 启动前自动清理 8765 端口残留进程registry.py— 修正custom_chili_v26m类别名为 Chili Maturity 数据集 5 类
安全漏洞(已记录未修复)
/models/switch接受任意path参数 → pickle RCE- 全链路无身份认证
- 文件上传无大小限制
- 无 CORS 限制