# 巡检任务工作流程说明 ## 📋 功能概述 本文档说明巡检任务的完整执行流程,包括视频录制、保存、AI识别和告警创建。 ## 🔄 完整工作流程 ### 1. 任务启动 当巡检任务启动时: ``` InspectionTaskServiceImpl.executeInspectionTask(taskId) ├── 创建 InspectionTaskRecord(记录ID) ├── 更新任务状态为"执行中" └── 调用 performVideoAnalysisWithRecord() ``` ### 2. 视频录制和保存 ``` performVideoAnalysisWithRecord() ├── 从RTSP流抓取视频 ├── 录制指定时长的视频 ├── 保存为临时文件 ├── 上传视频到MinIO ├── 更新InspectionTaskRecord.accessory(视频URL) └── 调用Python服务进行分析 ``` ### 3. AI识别处理 ``` VideoAnalysisService.analyzeVideoWithRecord() ├── 创建HttpYoloDetector(连接Python服务) ├── 逐帧分析视频 ├── 每10帧调用一次Python API检测 ├── 绘制检测框 ├── 去重检测结果(避免重复告警) ├── 创建告警记录 ├── 上传处理后的视频 ├── 生成检测结果摘要 └── 更新InspectionTaskRecord.result(识别结果) ``` ### 4. 告警创建(去重) ``` createAlarmRecordForRecord() ├── 提取检测区域图像 ├── 上传告警图片到MinIO ├── 创建AlarmRecord │ ├── 设备ID │ ├── 告警类型 │ ├── 告警内容(检测类别+置信度) │ ├── 关联的任务ID │ ├── 图片URL │ └── 帧位置 └── 保存到数据库(仅新检测的对象) ``` ## 📊 数据表关系 ``` InspectionTask (巡检任务) ↓ 1:N InspectionTaskRecord (巡检记录) ├── accessory: 原始视频URL + 处理后视频URL ├── result: AI识别结果摘要 ├── duration: 执行时长 └── status: 0=成功, 1=失败, 2=部分成功 InspectionTaskRecord → AlarmRecord (1:N) ├── 同一个record可以有多个告警 └── 告警自动去重(相同位置的相同对象只记录一次) ``` ## 🎯 关键字段说明 ### InspectionTaskRecord | 字段 | 说明 | 示例 | |------|------|------| | recordId | 记录ID | 自增主键 | | taskId | 关联的任务ID | 1001 | | executeTime | 执行时间 | 2025-09-30 14:30:00 | | duration | 执行时长(秒) | 30 | | accessory | 附件URL | video1.mp4;video2.mp4 | | result | 识别结果 | 共检测到3个问题,详情:垃圾(2) 烟雾(1) | | status | 执行状态 | 0=成功, 1=失败, 2=部分成功 | ### AlarmRecord | 字段 | 说明 | 示例 | |------|------|------| | alarmId | 告警ID | 自增主键 | | deviceId | 设备ID | 1001 | | taskId | 任务ID | 1001 | | alarmType | 告警类型 | detection | | alarmContent | 告警内容 | 垃圾 - 置信度: 0.95 | | imageOssId | 告警图片ID | MinIO对象ID | | framePosition | 视频帧位置 | 150 | | confidence | 置信度 | 0.95 | | status | 处理状态 | 0=未处理, 1=已处理 | ## 🔧 关键实现细节 ### 1. 去重机制 使用`generateDetectionKey`生成唯一键: ```java private String generateDetectionKey(Detection detection) { Rect rect = detection.getRect(); // 取10的倍数,允许小范围波动 int x = rect.x() / 10 * 10; int y = rect.y() / 10 * 10; int w = rect.width() / 10 * 10; int h = rect.height() / 10 * 10; return String.format("%s_%d_%d_%d_%d", detection.getLabel(), x, y, w, h); } ``` **原理**: - 相同类别 + 相似位置 → 认为是同一个对象 - 允许10像素的波动 - 超过60秒未检测到自动清除 ### 2. Python服务调用 使用容器名调用: ```java private static final String PYTHON_API_URL = "http://rtsp-python-service:8000/api/detect/file"; private static final String MODEL_NAME = "yolov8_detector"; ``` ### 3. 视频处理流程 ``` RTSP流 → FFmpegFrameGrabber → 录制 → 临时文件 → 上传MinIO → 保存URL到record.accessory → 逐帧分析 → 调用Python API → 绘制检测框 → 保存处理后视频 → 追加URL到record.accessory → 更新record.result ``` ### 4. 附件字段格式 `accessory`字段使用分号分隔多个URL: ``` 原始视频URL;处理后视频URL ``` 示例: ``` http://minio.com/inspection-videos/inspection_1001_1234567890.mp4;http://minio.com/inspection-videos/processed_1234567891.mp4 ``` ## 🚀 使用方法 ### 1. 创建巡检任务 ```java InspectionTask task = new InspectionTask(); task.setDeviceId(deviceId); task.setDuration(30); // 录制30秒 task.setStatus(0); // 待执行 inspectionTaskService.insertInspectionTask(task); ``` ### 2. 启动任务 ```java // 异步执行 inspectionTaskService.executeInspectionTask(taskId); ``` ### 3. 查看执行记录 ```sql -- 查询某任务的所有执行记录 SELECT * FROM v_inspection_task_record WHERE task_id = 1001 ORDER BY execute_time DESC; -- 查询成功的记录 SELECT * FROM v_inspection_task_record WHERE status = 0; -- 查询某记录的所有告警 SELECT * FROM v_alarm_record WHERE task_id = 1001; ``` ### 4. 查看告警 ```sql -- 查询某任务的所有告警 SELECT * FROM v_alarm_record WHERE task_id = 1001 ORDER BY create_time DESC; -- 查询未处理的告警 SELECT * FROM v_alarm_record WHERE status = 0; ``` ## 📝 执行示例 ### 执行流程 1. **任务创建** ``` Task ID: 1001 Device ID: 5001 Duration: 30秒 ``` 2. **记录创建** ``` Record ID: 2001 Task ID: 1001 Execute Time: 2025-09-30 14:30:00 Status: 1 (执行中) ``` 3. **视频录制** ``` 录制30秒视频 保存到MinIO: inspection_1001_1234567890.mp4 更新Record.accessory: http://minio.com/.../inspection_1001_1234567890.mp4 ``` 4. **AI识别** ``` 调用Python服务 检测到: 垃圾(2个), 烟雾(1个) ``` 5. **告警创建**(去重) ``` Alarm 1: 垃圾 - 位置(100,200) - 置信度0.95 Alarm 2: 垃圾 - 位置(300,400) - 置信度0.87 Alarm 3: 烟雾 - 位置(500,100) - 置信度0.92 ``` 6. **处理后视频** ``` 带检测框的视频上传 保存到MinIO: processed_1234567891.mp4 更新Record.accessory: 原始URL;处理后URL ``` 7. **更新记录** ``` Record.result: "共检测到 3 个问题,详情:垃圾(2) 烟雾(1)" Record.status: 0 (成功) Record.duration: 32秒 ``` ## ⚙️ 配置说明 ### Python服务配置 在Docker环境中,Python服务地址为: ``` http://rtsp-python-service:8000 ``` ### 模型配置 确保Python服务使用正确的模型名称: ```json { "name": "yolov8_detector", "path": "models/yolov8_model.py", "size": [640, 640] } ``` ### 检测参数 在`VideoAnalysisService`中可调整: ```java // 检测频率(每N帧检测一次) if (frameCount % 10 == 0) { ... } // 去重时间窗口(秒) detectedGarbageCache.entrySet().removeIf(entry -> (currentId - entry.getValue()) > grabber.getFrameRate() * 60); ``` ## 🐛 故障排查 ### 问题1: 视频未保存 **检查**: ```sql -- 查看record的accessory字段 SELECT record_id, accessory FROM v_inspection_task_record WHERE task_id = ?; -- 查看MinIO对象 SELECT * FROM v_minio_object WHERE bucket_name = 'inspection-videos' ORDER BY create_time DESC; ``` **解决**: - 检查MinIO服务是否可用 - 检查网络连接 - 查看后端日志 ### 问题2: Python识别未执行 **检查**: ```bash # 查看Python服务日志 docker-compose logs python-service # 测试Python服务 curl http://rtsp-python-service:8000/health curl http://rtsp-python-service:8000/api/models ``` **解决**: - 确认Python服务运行正常 - 确认模型文件存在 - 检查网络连通性 ### 问题3: 告警未创建 **检查**: ```sql -- 查看告警记录 SELECT * FROM v_alarm_record WHERE task_id = ? ORDER BY create_time DESC; -- 查看检测结果 SELECT result FROM v_inspection_task_record WHERE record_id = ?; ``` **解决**: - 检查检测置信度阈值 - 查看视频内容是否有检测对象 - 检查Python服务返回结果 ### 问题4: 重复告警 **检查**: - 去重机制是否正常工作 - `generateDetectionKey`逻辑是否合理 **调整**: ```java // 调整去重的位置容差 int x = rect.x() / 20 * 20; // 从10改为20,更宽松的去重 ``` ## 📊 性能优化 ### 1. 检测频率 ```java // 降低检测频率以提升性能(CPU模式) if (frameCount % 30 == 0) { // 从10改为30 // 每30帧检测一次 } ``` ### 2. 视频质量 ```java // 降低视频比特率节省存储 recorder.setVideoBitrate(500000); // 降低比特率 ``` ### 3. 去重时间窗口 ```java // 缩短去重时间窗口 (currentId - entry.getValue()) > grabber.getFrameRate() * 30 // 从60秒改为30秒 ``` ## 🔍 调试方法 ### 查看执行日志 ```bash # 查看后端日志 docker-compose logs -f backend | grep "inspection" # 查看Python服务日志 docker-compose logs -f python-service # 查看特定记录的处理过程 docker-compose logs backend | grep "recordId=2001" ``` ### 数据库查询 ```sql -- 查看最新的执行记录 SELECT r.record_id, r.task_id, r.execute_time, r.duration, r.status, r.result, LENGTH(r.accessory) as accessory_length FROM v_inspection_task_record r ORDER BY r.create_time DESC LIMIT 10; -- 查看记录对应的告警 SELECT a.alarm_id, a.alarm_content, a.confidence, a.frame_position, a.create_time FROM v_alarm_record a WHERE a.task_id = ? ORDER BY a.create_time DESC; -- 统计告警数量 SELECT r.record_id, r.execute_time, COUNT(a.alarm_id) as alarm_count FROM v_inspection_task_record r LEFT JOIN v_alarm_record a ON r.task_id = a.task_id AND a.create_time >= r.execute_time AND a.create_time <= DATE_ADD(r.execute_time, INTERVAL r.duration SECOND) GROUP BY r.record_id ORDER BY r.create_time DESC; ``` ## 💡 扩展建议 ### 1. 添加检测类型过滤 在`createAlarmRecordForRecord`中: ```java // 只对特定类型创建告警 List alarmTypes = Arrays.asList("垃圾", "烟雾", "火焰"); if (!alarmTypes.contains(detection.getLabel())) { return; // 忽略其他类型 } ``` ### 2. 添加置信度阈值 ```java // 只对高置信度的检测创建告警 if (detection.getConfidence() < 0.7) { return; // 忽略低置信度检测 } ``` ### 3. 添加区域过滤 ```java // 只对特定区域的检测创建告警 Rect rect = detection.getRect(); if (!isInMonitorArea(rect, task)) { return; // 忽略监控区域外的检测 } ``` ### 4. 添加告警级别 ```java // 根据检测类型设置告警级别 String alarmLevel = "medium"; if (detection.getLabel().contains("火焰")) { alarmLevel = "high"; } else if (detection.getLabel().contains("垃圾")) { alarmLevel = "low"; } alarmRecord.setAlarmLevel(alarmLevel); ``` ## 🔒 安全考虑 ### 1. 异常处理 所有方法都包含完整的异常处理: - 视频录制失败 → 更新record状态为失败 - Python服务调用失败 → 记录错误但不影响整体流程 - MinIO上传失败 → 记录错误并回滚 ### 2. 资源清理 使用try-finally确保资源释放: - FFmpegFrameGrabber自动关闭 - FFmpegFrameRecorder自动关闭 - 临时文件自动删除 ### 3. 并发控制 使用`@Async`异步执行,避免阻塞: - 任务执行不阻塞API响应 - 多个任务可并发执行 - 通过runningTasks避免重复执行 ## 📈 监控指标 ### 建议监控的指标 1. **执行成功率** ```sql SELECT COUNT(CASE WHEN status = 0 THEN 1 END) * 100.0 / COUNT(*) as success_rate FROM v_inspection_task_record WHERE execute_time >= DATE_SUB(NOW(), INTERVAL 1 DAY); ``` 2. **平均执行时长** ```sql SELECT AVG(duration) as avg_duration FROM v_inspection_task_record WHERE status = 0 AND execute_time >= DATE_SUB(NOW(), INTERVAL 1 DAY); ``` 3. **告警统计** ```sql SELECT alarm_type, COUNT(*) as count FROM v_alarm_record WHERE create_time >= DATE_SUB(NOW(), INTERVAL 1 DAY) GROUP BY alarm_type; ``` ## 📞 技术支持 如有问题,请查看: 1. 后端日志:`docker-compose logs backend` 2. Python服务日志:`docker-compose logs python-service` 3. 数据库记录:查询`v_inspection_task_record`和`v_alarm_record`表 4. MinIO对象:查询`v_minio_object`表 --- **文档版本**: 1.0 **最后更新**: 2025-09-30 **适用版本**: YOLOv8, Docker Compose部署