简介:LntonAIServer视频智能分析服务v1.0.01是一款轻量级、CPU友好的边缘端视频AI分析平台,面向安防监控、安全生产、智慧餐饮等场景的开发者与集成商,提供无需强依赖GPU的实时算法能力,重点解决烟火识别、抽烟检测、行人入侵、厨帽佩戴、玩手机打电话等典型行为与事件识别问题。资源包共152个文件,含78个核心DLL动态库(承载推理与算法逻辑)、37个JS前端交互脚本(支持Web可视化控制台)、11个CSS样式文件(构建响应式界面)、6个weights模型权重及配套proto/param定义文件,另含bat启动脚本、exe服务主程序与wasm/web组件,整体306.43MB,结构完整覆盖服务部署、前端展示与模型加载全链路。目前已有247人学习下载,用户可直接部署运行,获取开箱即用的多算法融合分析能力、标准化接口调用示例、清晰的服务启停控制逻辑(如stop.bat)及适配主流浏览器的Web管理界面。
1. 为什么一个纯 CPU 的视频智能分析服务,反而在边缘场景里更稳?
你可能已经见过太多“AI视频分析”方案:动辄要求 NVIDIA T4、A10 显卡,部署要 Docker + Kubernetes,模型一更新就得重训重导出,现场设备一断网,算法直接哑火。但现实是——大量工业巡检、社区安防、仓储监控节点,用的是老旧 IPC 摄像头 + 低功耗工控机(i5-6300U / N5105),连 PCIe 插槽都没有,更别说 GPU 驱动兼容性问题。LntonAIServer v1.0.01 就是为这类「没显卡、没运维、没网络冗余」的真实边缘现场而生的:它不依赖 CUDA,全链路跑在 CPU 上;烟火识别与抽烟检测两个核心算法模块,单路 1080p 视频流在 4 核 8 线程 CPU 上平均推理延迟 ≤ 320ms(实测 i5-8250U @ 1.6GHz);所有模型封装为 ONNX 格式,无 Python 运行时依赖,C++ 主服务启动后仅占用 120MB 内存;配置即改即生效,无需重启进程。这不是“降级妥协”,而是对边缘交付本质的理解:稳定压倒参数,可维护性大于 F1 分数。适合正在做安防类毕设、课设,或需要快速落地到百台以下边缘点位的中小型集成项目。
2. 总体架构设计:三层解耦结构与四个核心模块的职责边界
LntonAIServer 不是“把 YOLO 改个名打包就叫智能分析”,它的分层逻辑从第一天就锚定在「可替换、可审计、可离线」上。整个系统划分为采集层 → 分析层 → 业务层 → 管理层四个明确模块,彼此通过内存队列 + 协议缓冲区通信,零共享内存、零全局变量。这种设计让 A 同学改抽烟检测模型、B 同学调烟火识别阈值、C 同学对接第三方告警平台,能完全并行开发,互不影响。更重要的是——当客户突然说“你们的抽烟检测误报太多,明天就要上线新规则”,我们不需要重编译整个服务,只需替换models/smoking_v2.onnx和更新config/rules_smoking.json,30 秒内热加载生效。
2.1 采集层:轻量帧提取器与自适应丢帧策略
采集层不走 OpenCV VideoCapture 那套“开摄像头→读帧→转 BGR”的通用路径,而是针对 RTSP 流做了深度裁剪。它基于 FFmpeg 的libavcodec直接解析 H.264 Annex-B 码流,跳过解码环节,仅提取关键帧(I 帧)的 YUV420P 原始数据块。为什么只取 I 帧?因为 P/B 帧依赖前后帧,解码失败率高;而 I 帧独立完整,即使网络抖动丢包,只要收到一个 I 帧就能立刻送入分析。实测在 2Mbps 码率下,I 帧间隔约 500ms(GOP=25@50fps),足够满足烟火/抽烟这类慢变化事件的检测粒度。
# 采集层核心启动参数(config/capture.yaml) rtsp: url: "rtsp://192.168.1.100:554/stream1" timeout_ms: 5000 i_frame_only: true # 强制只取 I 帧 max_fps: 2 # 业务层最大处理帧率,非采集帧率 buffer_size: 8 # 内存环形缓冲区长度(帧数)提示:
max_fps: 2是关键控制阀。它不是限制采集速度,而是告诉分析层“每秒最多处理 2 帧”。当分析层因 CPU 占用高暂时卡住,采集层会自动丢弃后续 I 帧,避免缓冲区溢出导致 OOM。这比“硬限流+阻塞等待”更符合边缘设备资源紧张的现实。
2.2 分析层:双模型流水线与 CPU 友好型预处理
分析层是整个服务的“大脑”,但它被拆成两个物理隔离的子模块:fire_analyzer和smoke_analyzer,各自拥有独立线程、独立 ONNX Runtime 实例、独立输入队列。它们不共享任何模型句柄或上下文,彻底规避多模型间内存竞争和推理干扰。每个模块内部采用三级流水线:
- 预处理线程:将 YUV420P 转为 RGB,再 resize 到模型输入尺寸(烟火:640×360;抽烟:320×240),全程使用 Intel IPP 库的
ippsConvertYUV420ToRGB_8u_P3C3R函数,比 OpenCVcv::cvtColor快 3.2 倍(实测 i5-8250U); - 推理线程:ONNX Runtime 配置为
ExecutionMode.ORT_SEQUENTIAL+GraphOptimizationLevel.ORT_ENABLE_EXTENDED,禁用ORT_ENABLE_ALL(会触发 CUDA 相关优化,CPU 下反而拖慢); - 后处理线程:执行 NMS(IoU=0.45)、置信度过滤(烟火≥0.65,抽烟≥0.72)、坐标反算(将归一化坐标映射回原始画面像素位置)。
# analysis/fire_analyzer.py 片段(ONNX 加载关键配置) import onnxruntime as ort def load_fire_model(model_path): sess_options = ort.SessionOptions() sess_options.intra_op_num_threads = 2 # 限定本模型最多用 2 核 sess_options.inter_op_num_threads = 1 # 禁止跨算子并行,防 CPU 抢占 sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_EXTENDED # 关键:不设置 providers,默认使用 CPUExecutionProvider,显式指定会报错 session = ort.InferenceSession(model_path, sess_options) return session注意:ONNX Runtime 在 CPU 模式下,
providers=['CPUExecutionProvider']是冗余且危险的写法。v1.16+ 版本中,显式指定反而会触发 provider 初始化失败(日志报Failed to create provider)。正确做法是留空,让 ORT 自动选择——这是很多同学在调试阶段卡住超过 2 小时的玄学坑。
2.3 业务层:事件聚合引擎与状态机驱动的告警生成
业务层不直接输出“检测到烟火”,而是构建了一个轻量状态机:每个检测目标(如画面左上角某区域)被赋予唯一track_id,连续 3 帧出现 >0.7 置信度的烟火框,才触发FIRE_CONFIRMED事件;若中间断帧 ≥2 帧,则重置计数器。抽烟检测同理,但要求连续 5 帧(因手部动作更易抖动)。这种设计大幅降低瞬时误报——比如强光反射、飞鸟掠过、镜头污渍,在单帧产生高分框,但无法维持 3 帧以上,就被状态机自动过滤。
// config/rules_fire.json(可热更新) { "min_duration_frames": 3, "confidence_threshold": 0.70, "iou_suppression": 0.45, "region_mask": [ {"x": 0.0, "y": 0.0, "w": 1.0, "h": 0.6} // 仅检测画面下半部(排除天空干扰) ] }提示:
region_mask是血泪经验。某次在仓库顶棚安装的摄像头,顶部常年有金属反光带,YOLO 模型总把反光识别为“火焰”。加了这个掩膜后,误报率从 12 次/天降到 0.3 次/天。它不是靠改模型,而是靠业务逻辑兜底——这才是工程思维。
2.4 管理层:HTTP API 与零依赖配置中心
管理层提供/api/v1/status(健康检查)、/api/v1/config(获取当前配置)、/api/v1/reload(热重载全部配置)三个端点,全部基于 C++ 的 CivetWeb 嵌入式 HTTP 服务器实现,无外部 Web 框架依赖。/api/v1/reload接口接收POST application/json,内容为完整 JSON 配置对象,服务端校验格式后,原子性替换内存中的配置结构体,并向各分析模块发送SIGUSR1信号触发重载。整个过程耗时 < 80ms,期间不影响正在处理的视频流。
注意:不要用
curl -X POST http://localhost:8080/api/v1/reload -d '{"rtsp":{"url":"..."}'直接推送新 RTSP 地址——这会覆盖整个config/capture.yaml,导致采集参数丢失。正确做法是先GET /api/v1/config拉取全量配置,修改其中rtsp.url字段,再POST回去。这是很多同学在答辩演示时翻车的点:改完地址,服务直接卡死,其实是配置结构体校验失败触发了 panic。
3. 算法模块实现:烟火识别与抽烟检测的 CPU 适配实践
这两个算法模块是 LntonAIServer 的核心价值点,它们不是简单套用公开模型,而是经历了三轮真实场景打磨:第一轮用 COCO 预训练权重微调,第二轮在 2000+ 张工地/仓库/楼梯间实拍图上做 hard negative mining(挖难例),第三轮针对 CPU 推理做结构精简。最终烟火模型参数量 1.8MB(YOLOv5s 原版 14MB),抽烟模型 0.9MB(YOLOv5n 原版 4.3MB),精度损失 < 2.3% mAP,但推理速度提升 4.7 倍。
3.1 烟火识别:多尺度特征融合与火焰色域先验
烟火检测难点在于:小目标(远处打火机)、低对比度(阴天仓库)、形态多变(篝火/电焊/烟雾)。我们的模型 backbone 采用轻量级 GhostNetV2 替代 CSPDarknet,颈部引入 BiFPN 结构(仅保留 P3/P4/P5 三层),头部用 Decoupled Head(分类与回归分支分离)。最关键的是——在预处理阶段嵌入火焰色域先验:对输入 RGB 图像,计算每个像素的(R-G)/(R+G+B)和(R-B)/(R+G+B)比值,生成两个通道的“火焰响应图”,拼接到原始图像后作为模型输入(共 5 通道)。这相当于给模型装了一个硬件级火焰滤镜,让 CNN 更快聚焦于可疑区域。
# preprocess/fire_preprocess.py def add_flame_prior(rgb_img): r, g, b = rgb_img[:, :, 0], rgb_img[:, :, 1], rgb_img[:, :, 2] denom = np.clip(r + g + b, 1e-6, None) # 防除零 rg_ratio = (r - g) / denom rb_ratio = (r - b) / denom # 归一化到 [0,1],与 RGB 合并 rg_norm = (rg_ratio + 1.0) * 0.5 rb_norm = (rb_ratio + 1.0) * 0.5 return np.dstack([rgb_img, rg_norm, rb_norm]) # shape: (H,W,5)提示:这个 5 通道输入必须与模型输入层严格对齐。如果 ONNX 模型导出时定义的是
input: float32[1,3,360,640],那上面代码就会崩溃。正确做法是在训练脚本中,模型输入层就定义为input: float32[1,5,360,640],导出 ONNX 时保持一致。很多同学在模型转换阶段没注意这点,导致服务启动时报Input shape mismatch。
3.2 抽烟检测:手-烟-嘴三角关系建模与动态 ROI 裁剪
抽烟动作本质是“手部移动→靠近嘴部→出现香烟轮廓”的时序过程。纯目标检测容易把打哈欠、揉眼睛、拿笔动作误判为抽烟。我们的方案是两阶段:第一阶段用轻量级 SSD 检测“手”、“嘴”、“香烟”三个部件;第二阶段用规则引擎判断三者空间关系——香烟 bbox 中心点到嘴 bbox 中心距离 < 手 bbox 宽度 × 0.8,且香烟方向角与手-嘴连线夹角 < 30°,才判定为有效抽烟。为降低计算量,第二阶段只在第一阶段检测到“手”和“嘴”同时存在时才激活,否则跳过。
// analysis/smoke_analyzer.cpp 片段(三角关系判断) bool is_valid_smoking(const BBox& hand, const BBox& mouth, const BBox& cigarette) { float dx = (mouth.cx + hand.cx) * 0.5f - cigarette.cx; // 手-嘴中点到烟中心偏移 float dy = (mouth.cy + hand.cy) * 0.5f - cigarette.cy; float dist_hand_mouth = sqrtf(powf(mouth.cx - hand.cx, 2) + powf(mouth.cy - hand.cy, 2)); float dist_center_cig = sqrtf(dx*dx + dy*dy); // 关键约束:烟必须在手与嘴之间,且距离足够近 return (dist_center_cig < dist_hand_mouth * 0.8f) && (cigarette.angle_in_degree > -30 && cigarette.angle_in_degree < 30); }注意:
cigarette.angle_in_degree是模型额外输出的一个回归值(通过在 head 层加一个 1D 全连接层预测角度),不是后处理计算的。这意味着你的训练数据必须标注香烟朝向角度(用 0~360° 或 -180~180° 表示)。没有这个标注,这段代码永远返回 false——这是某高校毕设组集体翻车的根源。
3.3 模型量化与 ONNX Runtime CPU 优化组合技
两个模型均采用 INT8 量化(非 FP16),但不是简单用onnxruntime.quantization.quantize_static一把梭。我们采用三步法:
- Calibration 阶段:用 500 张典型场景图(含强光、逆光、雨雾)跑一遍 FP32 推理,收集各层 activation 的 min/max;
- Quantize 阶段:指定
QuantFormat.QDQ(QuantizeDequantize),保留 Dequantize 节点,方便后期调试; - Runtime 阶段:ONNX Runtime 启动时,强制设置
session_options.add_session_config_entry("session.quantized_operators", "1"),否则量化模型会被当作 FP32 加载。
# 模型验证脚本(validate_model.sh) onnxruntime_tester \ --model_path models/fire_int8.onnx \ --test_data_path data/calib_set/ \ --provider cpu \ --enable_profiling true \ --profile_file profile_fire.json提示:profile 文件里重点关注
io_binding_time和kernel_time。如果io_binding_time占比 > 40%,说明数据拷贝成了瓶颈——此时应检查输入 tensor 是否用了 pinned memory(CPU 锁页内存)。LntonAIServer 在初始化时已调用ort::Env::EnableMemPattern()并预分配 IO binding buffer,所以实测io_binding_time稳定在 8~12ms。
3.4 避坑:CPU 视频分析四大高频翻车点
这些不是理论风险,而是我在三个不同客户现场亲手填过的坑,每一条都附带现象 → 原因 → 解决:
3.4.1 现象:服务运行 2 小时后 CPU 占用率从 40% 暴涨到 99%,top显示LntonAIServer进程持续满载
原因:采集层未正确释放 FFmpeg AVPacket 内存。FFmpeg 解析 RTSP 时,av_packet_unref(&pkt)调用遗漏,导致内存泄漏,最终触发 Linux OOM Killer 杀掉其他进程,系统假死。
解决:在capture_rtsp.cpp的每一处av_read_frame()后,强制添加av_packet_unref(&pkt),并在异常分支(如ret < 0)中同样调用。用valgrind --leak-check=full ./LntonAIServer验证无definitely lost。
3.4.2 现象:同一台机器上,./LntonAIServer -c config1.yaml正常,但./LntonAIServer -c config2.yaml启动失败,报Segmentation fault (core dumped)
原因:config2.yaml中rtsp.timeout_ms被误写为字符串"5000"而非整数5000,YAML 解析器将其转为std::string,后续赋值给int类型变量时发生未定义行为。
解决:所有数值型配置项,在 YAML 解析后立即做类型断言(if (!node["timeout_ms"].IsScalar() || !node["timeout_ms"].IsInt()) { throw std::runtime_error("timeout_ms must be integer"); })。
3.4.3 现象:抽烟检测在白天准确率 92%,但晚上降到 63%,红外补光灯开启后更差(仅 41%)
原因:红外光下,香烟燃烧端的热辐射在灰度图中呈现为高亮白点,但模型训练数据全是可见光图像,未覆盖红外谱段。
解决:在采集层增加红外模式判断逻辑——若 RTSP 流中SDP包含a=fmtp:96且profile-level-id=420029(H.264 Baseline Profile),则自动启用红外增强预处理:对 YUV 数据,仅增强 Y 通道(亮度)的高频分量,U/V 通道保持原样,避免色偏。
3.4.4 现象:客户要求增加“安全帽佩戴检测”,我们替换了模型和配置,但服务启动后dmesg报Out of memory: Kill process 1234 (LntonAIServer) score 897 or sacrifice child
原因:新增模型加载后,ONNX Runtime 默认为每个模型分配 2GB 内存池(arena_extend_strategy),双模型已占 4GB,加上采集缓冲区(8 帧 × 1080p × 3bytes ≈ 25MB),超出 4GB 工控机物理内存。
解决:在session_options中显式设置session_options.add_session_config_entry("session.memory.limit", "1073741824")(1GB),并关闭 arena 扩展:session_options.add_session_config_entry("session.memory.enable_memory_arena", "0")。
4. 部署与配置实战:从裸机到可交付系统的七步闭环
LntonAIServer 的交付物不是“一个能跑的二进制”,而是一套可审计、可复现、可批量下发的部署体系。我们用 Ansible + Shell 脚本封装了标准流程,但这里只讲人工部署的核心七步——因为毕设/课设答辩时,老师最可能让你现场演示的就是这七步。
4.1 环境确认:CPU 指令集与系统库版本双校验
不是所有 x86_64 CPU 都能跑。必须确认两点:
- 指令集支持:
grep -E "avx|sse" /proc/cpuinfo | sort -u必须包含avx2(ONNX Runtime CPU 版本最低要求); - 系统库版本:
ldd --version≥ 2.17(CentOS 7.2+ / Ubuntu 16.04+),libstdc++.so.6版本 ≥ GLIBCXX_3.4.21(用strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBCXX查看)。
提示:某次在客户提供的“国产 ARM 工控机”上部署失败,表面报
Illegal instruction,实际是 CPU 不支持 AVX2。我们临时编译了 AVX 指令集版本(牺牲 18% 速度),用gcc -march=x86-64 -mtune=generic重新编译核心库,成功交付。记住:指令集不匹配,永远是第一条排查项。
4.2 二进制分发:静态链接与符号剥离
交付包中LntonAIServer是完全静态链接的 ELF 文件(ldd LntonAIServer输出not a dynamic executable),大小 12.4MB。它内置了:
- OpenSSL 1.1.1w(TLS 1.2 支持)
- CivetWeb 1.15(HTTP 服务)
- ONNX Runtime 1.16.3(CPU only)
- Intel IPP 2021.5(图像处理加速)
所有调试符号已剥离:strip --strip-all LntonAIServer。这样做的好处是——客户拿到文件后,chmod +x就能直接运行,无需安装任何依赖,也杜绝了“缺 libxxx.so.1”类报错。
4.3 配置生成:模板化 yaml 与自动化校验
config/目录下提供template.yaml,包含所有可配参数及注释。我们用 Python 脚本gen_config.py自动生成客户专属配置:
python gen_config.py \ --rtsp-url "rtsp://192.168.10.5:554/live" \ --fire-thresh 0.68 \ --smoke-thresh 0.75 \ --output config/customer_a.yaml该脚本会:
- 自动计算
region_mask(根据摄像头安装高度/倾角估算有效检测区域); - 校验 RTSP URL 格式(必须含
rtsp://且端口为数字); - 生成唯一
server_id(MAC 地址哈希 + 时间戳)用于日志追踪。
4.4 日志与监控:结构化日志与 Prometheus 指标暴露
日志不是简单printf,而是 JSON 格式,每行一个事件:
{"level":"INFO","ts":"2024-06-12T09:23:45.123Z","module":"fire_analyzer","event":"frame_processed","frame_id":12456,"inference_ms":287,"conf":0.82,"bbox":[120,85,165,112]}同时,/metrics端点暴露 Prometheus 格式指标:
# HELP lnton_ai_fire_detect_total 烟火检测事件总数 # TYPE lnton_ai_fire_detect_total counter lnton_ai_fire_detect_total{camera="warehouse_1"} 12 # HELP lnton_ai_cpu_usage_percent CPU 使用率(百分比) # TYPE lnton_ai_cpu_usage_percent gauge lnton_ai_cpu_usage_percent 42.3注意:
/metrics不是额外 HTTP 服务,而是 CivetWeb 的一个 handler,与主服务共享线程池。因此不会增加额外资源开销,但需在config.yaml中显式开启monitoring.enabled: true。
4.5 故障自愈:Watchdog 机制与核心进程守护
服务内置 Watchdog 线程,每 5 秒检查:
- 采集层是否收到新 I 帧(
last_i_frame_ts与当前时间差 < 2000ms); - 分析层是否完成至少 1 帧推理(
last_inference_ts更新); - 内存占用是否超阈值(
/proc/self/status中VmRSS> 300MB)。
任一条件失败,Watchdog 发送SIGUSR2给主进程,触发:
- 记录
WATCHDOG_TRIGGERED事件到日志; - 清空所有内存队列;
- 重新初始化采集器(
avformat_close_input+avformat_open_input); - 10 秒后自动恢复。
这比 systemd 的Restart=always更精准——它不重启整个进程,只重置故障模块。
4.6 多路扩展:共享内存队列与负载均衡策略
单台服务器支持最多 8 路 RTSP 流(实测 i5-8250U)。扩展方式不是“开 8 个进程”,而是:
- 采集层启动 8 个独立线程,每线程绑定 1 路 RTSP;
- 所有线程将 I 帧数据写入同一个 POSIX 共享内存区(
/dev/shm/lnton_frame_pool),按 ring buffer 管理; - 分析层线程池(默认 4 线程)从该共享内存区轮询读取,自动负载均衡。
# 共享内存区创建(deploy.sh 中) sudo ipcs -m | grep lnton && sudo ipcrm -M `sudo ipcs -m | grep lnton | awk '{print $2}'` sudo ipcmk -M 16777216 -n /dev/shm/lnton_frame_pool # 16MB提示:
ipcmk创建的共享内存,权限默认为600(仅 root 可读写)。必须在服务启动前,用sudo chmod 666 /dev/shm/lnton_frame_pool开放权限,否则分析层线程读取失败。
4.7 验收交付物清单:不只是二进制
一份合格的交付,必须包含以下 7 个文件(缺一不可):
| 文件名 | 类型 | 说明 | 是否可选 |
|---|---|---|---|
LntonAIServer | 二进制 | 主程序,静态链接 | 否 |
config/customer.yaml | YAML | 客户定制配置 | 否 |
models/fire_int8.onnx | ONNX | 烟火识别模型 | 否 |
models/smoke_int8.onnx | ONNX | 抽烟检测模型 | 否 |
doc/deployment_manual.pdf | 含环境检查、启动命令、日志定位、常见问题 | 否 | |
log/health_check.log | 文本 | 首次启动后 5 分钟内的完整日志(含HEALTH_CHECK_PASSED) | 否 |
script/verify.sh | Shell | 自动化校验脚本:检查端口、模型加载、API 响应、指标暴露 | 是 |
注意:
verify.sh不是摆设。它会在客户现场运行:bash script/verify.sh && echo "✅ 部署通过" || echo "❌ 需人工介入"。这是让客户技术负责人一眼建立信任的关键动作。
5. 验收与答辩:用三张表、一个日志片段、两次现场演示征服评委
答辩不是讲原理,是证明“这东西真能用”。我带过的 12 个毕设项目,评委最常问的三个问题,全部可以用下面这套验收方法闭环回答。别背稿,把这三张表打印出来,答辩时放在手边,评委问到哪,你就翻到哪。
5.1 性能验收表:用真实硬件跑出可复现数据
这张表必须是你自己在答辩设备上实测的(不是复制粘贴论文数据)。表格字段必须包含:测试环境、输入源、指标、实测值、达标线。
| 测试项 | 测试环境 | 输入源 | 指标 | 实测值 | 达标线 | 是否达标 |
|---|---|---|---|---|---|---|
| 单路烟火检测延迟 | i5-8250U @ 1.6GHz, 8GB RAM | 1080p RTSP (2Mbps) | 平均推理 ms | 298ms | ≤ 350ms | ✅ |
| 单路抽烟检测吞吐 | 同上 | 同上 | FPS(处理帧率) | 2.1 fps | ≥ 2.0 fps | ✅ |
| 内存占用 | 同上 | 同上 | VmRSS (MB) | 124MB | ≤ 300MB | ✅ |
| 多路稳定性 | 同上 | 4 路 720p RTSP | 连续运行 24h 后 CPU 占用 | 43% | ≤ 70% | ✅ |
| 网络中断恢复 | 同上 + 断网 30s | 同上 | 重连后首帧检测时间 | 1.2s | ≤ 3s | ✅ |
提示:实测时用
ffmpeg -i rtsp://... -vframes 1000 -f null -先压测网络,确保不是带宽瓶颈;用stress-ng --cpu 4 --timeout 60s模拟 CPU 高负载,再测服务表现。评委如果质疑“你这数据太理想”,你就当场打开终端,运行time ffmpeg -i rtsp://... -vframes 100 -f null -,让他看 real time。
5.2 功能验收表:覆盖正例、边界、负例的 12 个场景
功能不能只说“能检测”,要证明“在什么条件下能,什么条件下不能,为什么不能”。这张表是我每次答辩必带的“后悔药”。
| 场景编号 | 场景描述 | 预期结果 | 实际结果 | 原因分析 | 是否通过 |
|---|---|---|---|---|---|
| F01 | 白天仓库内明火(打火机) | 烟火框 + conf≥0.75 | ✅ | — | ✅ |
| F02 | 夜晚红外补光下香烟燃烧端 | 抽烟框 + conf≥0.70 | ✅ | 启用红外增强预处理 | ✅ |
| F03 | 强光反射(金属顶棚) | 无烟火框 | ✅ | region_mask 掩膜生效 | ✅ |
| F04 | 快速挥手(无烟) | 无抽烟框 | ✅ | 三角关系不满足 | ✅ |
| F05 | 雨雾天气(能见度<5m) | 烟火框置信度下降至 0.52 | ⚠️ | 模型未训练雨雾数据 | ⚠️(记录为待优化) |
| F06 | 摄像头离线(RTSP 断连) | Watchdog 自动重连,1.2s 恢复 | ✅ | SIGUSR2 信号处理正常 | ✅ |
| F07 | 修改 config/fire_thresh 为 0.85 | 误报减少,漏报增加(F01 检出 conf=0.78) | ✅ | 阈值生效验证 | ✅ |
| F08 | 同时存在烟火与抽烟 | 两个框独立输出,无互相干扰 | ✅ | 双模型隔离设计 | ✅ |
| F09 | CPU 占用 95%(stress-ng 模拟) | 检测延迟升至 410ms,但不崩溃 | ✅ | Watchdog 未触发(<700ms) | ✅ |
| F10 | 内存不足(ulimit -v 100000) | 进程被 OOM Killer 杀死 | ❌ | 内存保护未覆盖极端情况 | ❌(记录为风险项) |
| F11 | RTSP URL 错误(不存在) | 日志报RTSP connect timeout,不退出 | ✅ | 采集层错误处理完备 | ✅ |
| F12 | 同时启动 2 个实例(端口冲突) | 第二个实例报bind: Address already in use | ✅ | HTTP 端口占用检查 | ✅ |
注意:F05 和 F10 的“未通过”不是缺陷,而是诚实记录。评委更欣赏“知道边界在哪”的学生,而不是吹嘘“100% 准确”的人。答辩时主动指出这两条,并说“我们已在 v1.1.0 Roadmap 中规划雨雾数据增强和内存熔断机制”,可信度拉满。
5.3 日志诊断片段:用一行日志讲清整个系统状态
评委如果问“你怎么知道服务正常?”,不要说“我看它没报错”,直接打开log/latest.log,找到这一行:
[2024-06-12 14:22:33.876] [INFO] [main] HEALTH_CHECK_PASSED: capture=OK(2.1fps), fire_analyzer=OK(298ms), smoke_analyzer=OK(312ms), memory=124MB, watchdog=active这一行日志是LntonAIServer启动后第 60 秒自动生成的
本文还有配套的精品资源,点击获取