在工业自动化领域,多路视频监控系统是实现设备状态监测、产品质量检测和安全防护的基础设施。传统方案通常采用专用硬件设备或昂贵的商业软件,而基于C#的自主开发方案则能以1/10的成本实现同等功能。
这个方案的核心设计目标是:
实测表明,在研华UNO-2372G工控机(四核J1900/4GB内存)上,本方案可稳定处理4路720P视频流(15-25fps),总内存占用控制在600MB以内。相比OpenCV默认实现,通过内存池和限流策略可降低40%的内存波动。
工业现场通常需要同时处理4-8路视频源,传统单线程轮询方式会导致严重的帧堆积问题。本方案采用"一线程一相机"的架构:
csharp复制// 相机引擎初始化
private readonly List<VideoCapture> _caps = new();
private readonly CancellationTokenSource _cts = new();
public void AddCamera(string source) {
var cap = source.StartsWith("rtsp")
? new VideoCapture(source) // IP相机
: new VideoCapture(int.Parse(source), VideoCaptureAPIs.DSHOW); // USB相机
if (cap.IsOpened()) _caps.Add(cap);
}
每个相机线程独立维护视频流状态,通过CancellationToken实现优雅退出。相比BackgroundWorker,Task+async/await的组合更适合长时间运行的IO密集型任务。
低配工控机处理多路视频时容易发生过载。我们采用SemaphoreSlim实现全局并发控制:
csharp复制private readonly SemaphoreSlim _sem = new(3, 3); // 允许3路同时处理
async Task CameraLoopAsync() {
while (!_cts.IsCancellationRequested) {
await _sem.WaitAsync(_cts.Token);
try {
// 处理帧...
} finally {
_sem.Release();
}
await Task.Delay(30); // 控制单路帧率
}
}
经验参数:
频繁的Mat对象创建会导致GC压力,我们采用两种优化策略:
csharp复制private readonly MemoryPool<float> _pool = MemoryPool<float>.Shared;
var rent = _pool.Rent(1 * 3 * 416 * 416); // 预分配张量内存
blob.GetData<float>().CopyTo(rent.Memory.Span);
csharp复制using var frame = new Mat(); // 复用Mat对象
if (!cap.Read(frame) || frame.Empty()) continue;
实测表明,在4路视频场景下,内存池技术可减少70%的GC暂停时间。
工业场景通常需要集成目标检测算法(如YOLO)。通过ONNX Runtime可实现跨平台部署:
csharp复制var opt = new SessionOptions {
IntraOpNumThreads = 2, // 限制推理线程数
GraphOptimizationLevel = GraphOptimizationLevel.ORT_ENABLE_ALL
};
_session = new InferenceSession("yolov8n_int8.onnx", opt);
关键配置:
IntraOpNumThreads:建议设为CPU核数的50%WinForms的UI线程模型要求跨线程操作必须通过Invoke:
csharp复制Invoke(() => {
_pictureBoxes[camIndex].Image?.Dispose();
_pictureBoxes[camIndex].Image = annotated.ToBitmap();
});
优化技巧:
BeginInvoke替代Invoke减少阻塞通过Modbus TCP实现检测结果输出:
csharp复制private void WritePLC(Detection d) {
using var client = new ModbusTcpClient("192.168.1.100");
client.WriteSingleRegister(0, d.Label == "defect" ? 1 : 0);
}
典型应用场景:
支持多边形禁入区域检测:
csharp复制private bool IsInRestrictedArea(Point2f pt) {
return Cv2.PointPolygonTest(_polygon, pt, false) >= 0;
}
实现要点:
工业环境要求系统具备自恢复能力:
csharp复制try {
// 视频采集逻辑...
} catch (VideoCaptureException ex) {
_logger.Error($"相机{camIndex}异常: {ex.Message}");
ReconnectCamera(camIndex); // 自动重连
} catch (OperationCanceledException) {
// 正常退出
}
建议的重试策略:
根据场景选择硬件配置:
| 路数 | CPU核心 | 内存 | 推荐型号 |
|---|---|---|---|
| 2路 | 双核 | 2GB | 研华ARK-1123 |
| 4路 | 四核 | 4GB | 研华UNO-2372G |
| 8路 | 六核 | 8GB | 研华AIMB-505 |
在VideoCapture初始化时设置关键参数:
csharp复制cap.Set(VideoCaptureProperties.CAP_PROP_BUFFERSIZE, 3); // 减少缓冲区
cap.Set(VideoCaptureProperties.CAP_PROP_FPS, 15); // 限制输入帧率
IP相机特别建议:
依赖项打包:
opencv_videoio_ffmpeg.dll与程序一起发布自启动配置:
powershell复制New-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" `
-Name "CameraMonitor" -Value "$PSScriptRoot\Monitor.exe"
看门狗机制:
csharp复制using var timer = new Timer(_ => CheckAlive(), null, 60000, 60000);
通过企业微信机器人发送报警:
csharp复制async Task SendWeChatAlertAsync(string msg) {
using var client = new HttpClient();
var json = JsonSerializer.Serialize(new {
msgtype = "text",
text = new { content = $"[报警]{msg}" }
});
await client.PostAsync("https://qyapi.weixin.qq.com/...",
new StringContent(json, Encoding.UTF8, "application/json"));
}
采用SQLite存储检测记录:
csharp复制using var conn = new SQLiteConnection("Data Source=logs.db");
conn.Execute("INSERT INTO events VALUES(@time, @label, @image)",
new { time=DateTime.Now, label=d.Label, image=frame.ToBytes() });
优化建议:
通过Mono实现跨平台运行:
bash复制# 安装依赖
sudo apt install mono-complete libopencv-dev
# 运行程序
mono CameraMonitor.exe
Docker化部署:
dockerfile复制FROM mcr.microsoft.com/dotnet/runtime:6.0
COPY --from=opencv:4.5 /usr/local/opencv /usr/local/opencv
ENV LD_LIBRARY_PATH=/usr/local/opencv/lib
在工业现场实际部署时,建议将关键组件(如相机驱动、模型文件)放在独立的配置目录,便于后期维护更新。对于需要7×24小时运行的场景,可以考虑实现双机热备机制,通过心跳检测自动切换。