Browse Source

多路推理并行化: 每路独立RKNN context + 独立线程 + NPU三核

- ChannelState: 每路独立 YoloDetector/PoseDetector + 推理worker线程
- 按通道id轮转分配NPU核(Core0/1/2), 6路推理三核并行
- worker用condition_variable阻塞等待, 提交最新帧推理, 结果异步回填
- 实测: NPU从2核(50%)变3核满载(Core0 70%+Core1 95%+Core2 73%)
  6路MJPEG从8fps提升到9.7fps且各路均匀, 通道延迟11-38ms
- 退出/重建时正确停止并join推理线程
dev 1 tuần trước cách đây
mục cha
commit
a974162d52
3 tập tin đã thay đổi với 148 bổ sung và 30 xóa
  1. 4 4
      config/config.json
  2. 142 24
      src/main.cpp
  3. 2 2
      src/web_server.cpp

+ 4 - 4
config/config.json

@@ -45,9 +45,9 @@
         }
     ],
     "conf_thresh": 0.3499999940395355,
-    "det_core_mask": 1,
+    "det_core_mask": 2,
     "enable_bucket": false,
-    "fall_frames": 3,
+    "fall_frames": 1,
     "fall_relays": [
         "y01"
     ],
@@ -71,7 +71,7 @@
         "person"
     ],
     "pose_conf_thresh": 0.35,
-    "pose_core_mask": 2,
+    "pose_core_mask": 1,
     "pose_crouch": true,
     "pose_enabled": true,
     "pose_hand_raise": true,
@@ -177,7 +177,7 @@
         }
     ],
     "rtsp_timeout": 10,
-    "skip_frames": 4,
+    "skip_frames": 9,
     "steel_output": false,
     "target_enabled": false,
     "web_auth": {

+ 142 - 24
src/main.cpp

@@ -78,6 +78,25 @@ struct ChannelState {
     bool alarm_latched = false;      // 报警沿触发: 每次新报警只截一张
     chrono::steady_clock::time_point last_alarm_ts{};
     chrono::steady_clock::time_point episode_start{};  // 本回合报警开始时刻
+    // 每路独立推理器(多核并行时避免共享context冲突)
+    YoloDetector det_model;
+    PoseDetector pose_model;
+    bool det_loaded = false;
+    bool pose_loaded = false;
+    // 每路独立推理线程: 主循环提交RGB, worker在独立核上推理
+    std::thread infer_thread;
+    std::atomic<bool> infer_running{false};
+    std::atomic<bool> infer_pending{false};   // 有新RGB待推理
+    std::atomic<uint64_t> infer_seq{0};       // 推理请求序号
+    std::atomic<uint64_t> infer_done_seq{0};  // 已完成的推理序号
+    vector<float> infer_rgb;                  // worker待推理的RGB
+    float infer_scale = 1.0f;
+    int infer_pad_x = 0, infer_pad_y = 0;
+    int infer_w = 0, infer_h = 0;
+    mutex infer_mtx;
+    condition_variable infer_cv;
+    vector<Detection> infer_dets;             // worker推理结果
+    vector<PersonPose> infer_poses;
     mutex mtx;
 };
 
@@ -90,6 +109,59 @@ static void saveJson(const string& path, const json& j) {
     ofstream ofs(path); ofs << j.dump(4) << endl;
 }
 
+// 每路推理 worker: 在独立线程/独立NPU核上循环推理
+static void channelInferLoop(ChannelState* ch, bool target_enabled,
+                             bool pose_ok) {
+    while (ch->infer_running.load()) {
+        // 阻塞等待新的推理请求
+        {
+            std::unique_lock<std::mutex> lk(ch->infer_mtx);
+            ch->infer_cv.wait(lk, [&] {
+                return ch->infer_pending.load() || !ch->infer_running.load();
+            });
+            if (!ch->infer_running.load()) break;
+            ch->infer_pending.store(false);
+        }
+
+        vector<Detection> dets;
+        vector<PersonPose> poses;
+        bool do_det = target_enabled && ch->det_loaded;
+        bool do_pose = (pose_ok && ch->pose_running && ch->pose_loaded);
+
+        if (do_det && do_pose) {
+            std::thread tPose([&]() {
+                poses = ch->pose_model.detect(ch->infer_rgb.data(),
+                                              ch->infer_w, ch->infer_h,
+                                              ch->infer_scale,
+                                              ch->infer_pad_x, ch->infer_pad_y);
+            });
+            dets = ch->det_model.detect(ch->infer_rgb.data(),
+                                        ch->infer_w, ch->infer_h,
+                                        ch->infer_scale,
+                                        ch->infer_pad_x, ch->infer_pad_y);
+            tPose.join();
+        } else if (do_det) {
+            dets = ch->det_model.detect(ch->infer_rgb.data(),
+                                        ch->infer_w, ch->infer_h,
+                                        ch->infer_scale,
+                                        ch->infer_pad_x, ch->infer_pad_y);
+        } else if (do_pose) {
+            poses = ch->pose_model.detect(ch->infer_rgb.data(),
+                                          ch->infer_w, ch->infer_h,
+                                          ch->infer_scale,
+                                          ch->infer_pad_x, ch->infer_pad_y);
+        }
+
+        {
+            lock_guard<mutex> lk(ch->infer_mtx);
+            ch->infer_dets = std::move(dets);
+            ch->infer_poses = std::move(poses);
+            ch->infer_done_seq.store(ch->infer_seq.load(std::memory_order_relaxed),
+                                     std::memory_order_relaxed);
+        }
+    }
+}
+
 static void buildFenceFill(cv::Mat& fill, const vector<FenceRegion>& fences,
                            const cv::Size& size, float s) {
     fill = cv::Mat::zeros(size, CV_8UC3);
@@ -564,6 +636,23 @@ int main(int argc, char* argv[]) {
         ch->pose_running = pose_ok;   // 姿态识别默认开启(模型加载成功时)
         ch->rga.init(input_size, input_size);
         ch->capture = make_shared<RtspCapture>(id, url, name);
+        // 每路独立加载模型: 按通道id轮转分配NPU核(1=Core0 2=Core1 4=Core2)
+        static const int CORE_TABLE[3] = {1, 2, 4};
+        int core = CORE_TABLE[id % 3];
+        if (target_enabled) {
+            ch->det_loaded = ch->det_model.load(model_path, input_size,
+                                                conf_thresh, nms_thresh, core);
+        }
+        if (pose_enabled) {
+            ch->pose_loaded = ch->pose_model.load(pose_model_path, pose_input_size,
+                                                  pose_conf_thresh, pose_nms_thresh, core);
+        }
+        // 启动每路独立推理线程
+        if (ch->det_loaded || ch->pose_loaded) {
+            ch->infer_running = true;
+            ch->infer_thread = std::thread(channelInferLoop, ch.get(),
+                                           target_enabled, pose_ok);
+        }
         return ch;
     };
     auto registerChannelProviders = [&](shared_ptr<ChannelState> ch) {
@@ -1665,6 +1754,17 @@ int main(int argc, char* argv[]) {
     auto last_cleanup = chrono::steady_clock::now();
     auto last_rb_check = chrono::steady_clock::now() - chrono::seconds(20);
 
+    // 路间并行推理: 每路临时结果槽, 3个NPU核=3线程并行
+    struct InferSlot {
+        shared_ptr<ChannelState> ch;
+        vector<Detection> dets;
+        vector<PersonPose> poses;
+        bool do_det = false;
+        bool do_pose = false;
+        bool ready = false;
+    };
+    vector<InferSlot> infer_slots;
+
     while (g_running.load()) {
         this_thread::sleep_for(chrono::milliseconds(1));
 
@@ -1810,37 +1910,37 @@ int main(int argc, char* argv[]) {
 
             float scale = 1.0f;
             int pad_x = 0, pad_y = 0;
-            if (do_infer)
+            if (do_infer) {
                 ch->rga.process(nv12, ch->rgb_buf, scale, pad_x, pad_y);
+            }
 
-            if (do_infer && !ch->rgb_buf.empty()) {
+            if (do_infer && !ch->rgb_buf.empty() && ch->infer_running.load()) {
                 if (ch->frame_count <= 4)
                     cout << "[CH" << ch->id << "] infer frame=" << ch->frame_count
                          << " rgb=" << ch->rgb_buf.size() << endl;
 
-                // 检测与姿态并行推理(不同NPU核), 缩短单帧处理时间
+                // 提交给独立推理线程(在各自NPU核上并行)
+                {
+                    lock_guard<mutex> lk(ch->infer_mtx);
+                    ch->infer_rgb = ch->rgb_buf;
+                    ch->infer_scale = scale;
+                    ch->infer_pad_x = pad_x;
+                    ch->infer_pad_y = pad_y;
+                    ch->infer_w = nv12.width;
+                    ch->infer_h = nv12.height;
+                    ch->infer_seq.fetch_add(1, std::memory_order_relaxed);
+                    ch->infer_pending.store(true);
+                }
+                ch->infer_cv.notify_one();
+
+                // 若上一次推理已完成, 取回结果用于报警/显示
                 vector<Detection> dets;
                 vector<PersonPose> poses;
-                bool do_det = target_enabled;
-                bool do_pose = (pose_ok && ch->pose_running);
-                if (do_det && do_pose) {
-                    std::thread tPose([&]() {
-                        poses = poseDetector.detect(ch->rgb_buf.data(),
-                                                    nv12.width, nv12.height,
-                                                    scale, pad_x, pad_y);
-                    });
-                    dets = detector.detect(ch->rgb_buf.data(),
-                                           nv12.width, nv12.height,
-                                           scale, pad_x, pad_y);
-                    tPose.join();
-                } else if (do_det) {
-                    dets = detector.detect(ch->rgb_buf.data(),
-                                           nv12.width, nv12.height,
-                                           scale, pad_x, pad_y);
-                } else if (do_pose) {
-                    poses = poseDetector.detect(ch->rgb_buf.data(),
-                                                nv12.width, nv12.height,
-                                                scale, pad_x, pad_y);
+                if (ch->infer_done_seq.load(std::memory_order_relaxed) >=
+                    ch->infer_seq.load(std::memory_order_relaxed) - 1) {
+                    lock_guard<mutex> lk(ch->infer_mtx);
+                    dets = ch->infer_dets;
+                    poses = ch->infer_poses;
                 }
 
                 vector<cv::Point2f> centers;
@@ -2122,7 +2222,25 @@ int main(int argc, char* argv[]) {
     cout << "[Main] 正在退出..." << endl;
     {
         lock_guard<mutex> lk(channels_mtx);
-        for (auto& ch : channels) ch->capture->stop();
+        for (auto& ch : channels) {
+            ch->capture->stop();
+            if (ch->infer_running.load()) {
+                ch->infer_running = false;
+                ch->infer_pending = false;
+                if (ch->infer_thread.joinable()) ch->infer_thread.join();
+            }
+        }
+    }
+    {
+        lock_guard<mutex> lk(bucket_channels_mtx);
+        for (auto& ch : bucket_channels) {
+            ch->capture->stop();
+            if (ch->infer_running.load()) {
+                ch->infer_running = false;
+                ch->infer_pending = false;
+                if (ch->infer_thread.joinable()) ch->infer_thread.join();
+            }
+        }
     }
     relay.stop();   // 先停继电器线程, 避免HTTP处理线程阻塞在transact上拖住web.stop()
     web.stop();

+ 2 - 2
src/web_server.cpp

@@ -246,8 +246,8 @@ public:
                 "multipart/x-mixed-replace; boundary=mjpegboundary",
                 [this, fp, seqfn](size_t, httplib::DataSink& sink) -> bool {
                     uint64_t last_seq = 0;
-                    // MJPEG推流节流: 约12fps, 避免并发编码吃满CPU
-                    const int interval_ms = 80;
+                    // MJPEG推流节流: 约10fps, 避免并发编码吃满CPU
+                    const int interval_ms = 100;
                     while (!stopping.load() && mjpeg_enabled.load() && sink.is_writable()) {
                         uint64_t seq = seqfn ? seqfn() : 0;
                         if (seq != 0 && seq == last_seq) {