Explorar o código

重大性能优化: 消除主循环重复处理同一帧 + 显示帧降频

1. 帧去重(核心): 之前主循环每秒重复grabNv12/转换同一帧上千次
   - 新增last_cap_seq, 只在解码器产出新帧时处理该通道
   - 主线程CPU 65% -> 16%, 进程总CPU 186% -> 48%
2. 显示帧生成降频(display_fps默认12): 输出端HDMI/MJPEG/手机最高10-12fps
   - 不再每输入帧(25fps)都做RGA转换+clone+cvtColor+绘制
   - config新增display_fps
3. 实测: 6路NPU推理正常, MJPEG 9-10fps, 手机stream正常, 画面无跳帧
dev hai 23 horas
pai
achega
6d2c9530f5
Modificáronse 2 ficheiros con 25 adicións e 10 borrados
  1. 8 7
      config/config.json
  2. 17 3
      src/main.cpp

+ 8 - 7
config/config.json

@@ -8,7 +8,7 @@
         "train"
     ],
     "alarm_retention_days": 30,
-    "app_name": "钢包工位视频监测系统",
+    "app_name": "\u94a2\u5305\u5de5\u4f4d\u89c6\u9891\u76d1\u6d4b\u7cfb\u7edf",
     "auto_reboot": {
         "cycle": 1,
         "enabled": false,
@@ -18,7 +18,7 @@
         {
             "code_x": "00000001",
             "code_y": "",
-            "description": "1号工位",
+            "description": "1\u53f7\u5de5\u4f4d",
             "id": 1,
             "left_camera": {
                 "topic": "",
@@ -32,7 +32,7 @@
         {
             "code_x": "00000002",
             "code_y": "",
-            "description": "2号工位",
+            "description": "2\u53f7\u5de5\u4f4d",
             "id": 2,
             "left_camera": {
                 "topic": "",
@@ -98,9 +98,9 @@
     "relay": {
         "alarm_max_sec": 30,
         "aliases": {
-            "x1": "大门雷达",
-            "y01": "警示灯",
-            "y02": "声光报警器"
+            "x1": "\u5927\u95e8\u96f7\u8fbe",
+            "y01": "\u8b66\u793a\u706f",
+            "y02": "\u58f0\u5149\u62a5\u8b66\u5668"
         },
         "device_addr": 1,
         "di_bindings": {
@@ -202,5 +202,6 @@
         "username": "admin"
     },
     "web_port": 8080,
-    "watchdog_sec": 120
+    "watchdog_sec": 120,
+    "display_fps": 12
 }

+ 17 - 3
src/main.cpp

@@ -86,6 +86,8 @@ struct ChannelState {
     std::atomic<uint64_t> disp_seq{0};
     int frame_count = 0;
     int skip_frames = 3;
+    int64_t last_display_ms = 0;           // 上次生成显示帧时刻(显示降频用)
+    uint64_t last_cap_seq = 0;             // 上次处理的解码帧序号(去重复处理)
     bool size_recorded = false;
     bool alarm_latched = false;      // 报警沿触发: 每次新报警只截一张
     chrono::steady_clock::time_point last_alarm_ts{};
@@ -395,11 +397,15 @@ int main(int argc, char* argv[]) {
     bool steel_output = cfg.value("steel_output", true);
     int hdmi_grid_cols = cfg.value("hdmi_grid_cols", 0);
     bool hdmi_fullscreen = cfg.value("hdmi_fullscreen", true);
+    // 显示帧生成帧率(所有输出端HDMI/MJPEG/手机最高约10-12fps, 无需每输入帧都生成)
+    int display_fps = clamp(cfg.value("display_fps", 12), 1, 30);
+    int display_interval_ms = 1000 / display_fps;
     cout << "[Display] 网格输出: " << (grid_output ? "启用" : "禁用")
          << " 钢包位输出: " << (steel_output ? "启用" : "禁用")
          << " 工位功能: " << (enable_bucket ? "启用" : "禁用")
          << " 网格列=" << hdmi_grid_cols
-         << " 全屏=" << (hdmi_fullscreen ? "是" : "否") << endl;
+         << " 全屏=" << (hdmi_fullscreen ? "是" : "否")
+         << " 显示帧率=" << display_fps << "fps" << endl;
 
     // ========== 姿态识别(跌倒检测) ==========
     string pose_model_path = cfg.value("pose_model_path",
@@ -2059,6 +2065,12 @@ int main(int argc, char* argv[]) {
                 ch_snap.push_back(bc);
         }
         for (auto& ch : ch_snap) {
+            // 只在解码器产出新帧时才处理, 避免主循环高速重复处理同一帧
+            // (之前每秒重复memcpy/转换同一帧上千次, 是CPU主要浪费)
+            uint64_t capSeq = ch->capture->framesDecoded();
+            if (capSeq == ch->last_cap_seq) continue;
+            ch->last_cap_seq = capSeq;
+
             Nv12Frame nv12;
             if (!ch->capture->grabNv12(nv12)) {
                 if (ch->frame_count == 0 && ch->capture->isRunning())
@@ -2471,10 +2483,12 @@ int main(int argc, char* argv[]) {
                 }
             }
 
-            // 显示帧: 每帧都生成(视频不跳帧), 推理仍按 skip_frames 跳帧
+            // 显示帧: 按 display_fps 降频生成(输出端最高10-12fps, 避免每输入帧白做转换)
             // 检测框/围栏只在推理帧更新, 非推理帧复用最近一次推理结果
             cv::Mat display;
-            {
+            bool do_display = (nowMs() - ch->last_display_ms >= display_interval_ms);
+            if (do_display) {
+                ch->last_display_ms = nowMs();
                 std::vector<uint8_t> rgb8;
                 int out_w, out_h;
                 ch->rga.processToRgb(nv12, rgb8, out_w, out_h);