Преглед на файлове

修复双模型卡顿: 检测/姿态并行推理 + NPU双核分配

- YoloDetector/PoseDetector 支持 core_mask(检测绑Core0, 姿态绑Core1)
- 检测与姿态改为并行推理(两个线程), 单帧耗时从87.9ms降到max(检测,姿态)
- 实测: NPU从单核72%变为双核(Core0 34%+Core1 66%), 6路MJPEG 9-11fps
- config新增 det_core_mask/pose_core_mask
dev преди 1 седмица
родител
ревизия
b6ea822602
променени са 7 файла, в които са добавени 42 реда и са изтрити 17 реда
  1. 4 2
      config/config.json
  2. 28 7
      src/main.cpp
  3. 3 1
      src/pose_detector.cpp
  4. 2 1
      src/pose_detector.h
  5. 3 1
      src/yolo_detector.cpp
  6. 2 1
      src/yolo_detector.h
  7. 0 4
      web/index.html

+ 4 - 2
config/config.json

@@ -45,6 +45,7 @@
         }
     ],
     "conf_thresh": 0.3499999940395355,
+    "enable_bucket": false,
     "fall_frames": 3,
     "fall_relays": [
         "y01"
@@ -174,11 +175,12 @@
     "rtsp_timeout": 10,
     "skip_frames": 3,
     "steel_output": false,
+    "target_enabled": true,
     "web_auth": {
         "password": "admin",
         "username": "admin"
     },
     "web_port": 8080,
-    "enable_bucket": false,
-    "target_enabled": true
+    "det_core_mask": 1,
+    "pose_core_mask": 2
 }

+ 28 - 7
src/main.cpp

@@ -414,11 +414,15 @@ int main(int argc, char* argv[]) {
          << alarm_max_sec << "秒/重新武装" << rearm_sec << "秒" << endl;
     cout << "========================================" << endl;
 
+    // NPU 核心分配: 检测绑 Core0, 姿态绑 Core1, 两模型并行推理
+    int det_core = cfg.value("det_core_mask", 1);   // 默认 Core0
+    int pose_core = cfg.value("pose_core_mask", 2); // 默认 Core1
     YoloDetector detector;
-    if (target_enabled && !detector.load(model_path, input_size, conf_thresh, nms_thresh)) {
+    if (target_enabled && !detector.load(model_path, input_size, conf_thresh, nms_thresh, det_core)) {
         cerr << "RKNN模型加载失败!" << endl;
         return 1;
     }
+    cout << "[RKNN] 检测核心掩码: " << det_core << endl;
 
     PoseDetector poseDetector;
     FallAnalyzer fallAnalyzer;
@@ -426,9 +430,9 @@ int main(int argc, char* argv[]) {
     bool pose_ok = false;
     if (pose_enabled) {
         if (poseDetector.load(pose_model_path, pose_input_size,
-                              pose_conf_thresh, pose_nms_thresh)) {
+                              pose_conf_thresh, pose_nms_thresh, pose_core)) {
             pose_ok = true;
-            cout << "[Pose] 姿态模型加载成功, 跌倒检测已就绪" << endl;
+            cout << "[Pose] 姿态模型加载成功, 跌倒检测已就绪, 核心掩码: " << pose_core << endl;
         } else {
             cerr << "[Pose] 姿态模型加载失败: " << pose_model_path
                  << " (继续使用纯围栏检测)" << endl;
@@ -1729,11 +1733,31 @@ int main(int argc, char* argv[]) {
                 if (ch->frame_count <= 4)
                     cout << "[CH" << ch->id << "] infer frame=" << ch->frame_count
                          << " rgb=" << ch->rgb_buf.size() << endl;
+
+                // 检测与姿态并行推理(不同NPU核), 缩短单帧处理时间
                 vector<Detection> dets;
-                if (target_enabled)
+                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);
+                }
 
                 vector<cv::Point2f> centers;
                 vector<int> cids;
@@ -1762,9 +1786,6 @@ int main(int argc, char* argv[]) {
 
                 // ========== 姿态识别(跌倒检测) ==========
                 if (pose_ok && ch->pose_running) {
-                    auto poses = poseDetector.detect(ch->rgb_buf.data(),
-                                                     nv12.width, nv12.height,
-                                                     scale, pad_x, pad_y);
                     vector<PersonPose> falls;
                     for (auto& p : poses) {
                         if (pose_alarm_classes.empty() ||

+ 3 - 1
src/pose_detector.cpp

@@ -23,7 +23,7 @@ PoseDetector::~PoseDetector() {
 }
 
 bool PoseDetector::load(const string& model_path, int input_size,
-                        float conf_thresh, float nms_thresh) {
+                        float conf_thresh, float nms_thresh, int core_mask) {
     input_size_ = input_size;
     conf_thresh_ = conf_thresh;
     nms_thresh_ = nms_thresh;
@@ -44,6 +44,8 @@ bool PoseDetector::load(const string& model_path, int input_size,
         cerr << "[Pose] rknn_init failed: " << ret << endl;
         return false;
     }
+    if (core_mask != 0 && rknn_set_core_mask(ctx_, (rknn_core_mask)core_mask) != 0)
+        cerr << "[Pose] set_core_mask " << core_mask << " 失败, 使用默认调度" << endl;
 
     rknn_input_output_num io_num;
     ret = rknn_query(ctx_, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));

+ 2 - 1
src/pose_detector.h

@@ -16,8 +16,9 @@ public:
     PoseDetector() = default;
     ~PoseDetector();
 
+    // core_mask: 0=自动; 1=Core0; 2=Core1; 4=Core2; 7=0/1/2
     bool load(const std::string& model_path, int input_size,
-              float conf_thresh, float nms_thresh);
+              float conf_thresh, float nms_thresh, int core_mask = 0);
 
     std::vector<PersonPose> detect(const float* rgb_data, int img_w, int img_h,
                                    float scale, int pad_x, int pad_y);

+ 3 - 1
src/yolo_detector.cpp

@@ -30,7 +30,7 @@ YoloDetector::~YoloDetector() {
 }
 
 bool YoloDetector::load(const string& model_path, int input_size,
-                         float conf_thresh, float nms_thresh) {
+                         float conf_thresh, float nms_thresh, int core_mask) {
     input_size_ = input_size;
     conf_thresh_ = conf_thresh;
     nms_thresh_ = nms_thresh;
@@ -51,6 +51,8 @@ bool YoloDetector::load(const string& model_path, int input_size,
         cerr << "[RKNN] rknn_init failed: " << ret << endl;
         return false;
     }
+    if (core_mask != 0 && rknn_set_core_mask(ctx_, (rknn_core_mask)core_mask) != 0)
+        cerr << "[RKNN] set_core_mask " << core_mask << " 失败, 使用默认调度" << endl;
 
     rknn_input_output_num io_num;
     ret = rknn_query(ctx_, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));

+ 2 - 1
src/yolo_detector.h

@@ -16,8 +16,9 @@ public:
     YoloDetector() = default;
     ~YoloDetector();
     
+    // core_mask: 0=自动; 1=Core0; 2=Core1; 4=Core2; 7=0/1/2
     bool load(const std::string& model_path, int input_size, 
-              float conf_thresh, float nms_thresh);
+              float conf_thresh, float nms_thresh, int core_mask = 0);
     
     std::vector<Detection> detect(const float* rgb_data, int img_w, int img_h,
                                    float scale, int pad_x, int pad_y);

+ 0 - 4
web/index.html

@@ -123,10 +123,6 @@ border-left:3px solid #e94560;padding:8px 10px;margin-bottom:8px;cursor:pointer}
 .sys-wrap{max-width:640px;margin:0 auto;padding:24px}
 
 /* ---------- PiP ---------- */
-#pip{display:none;position:fixed;right:16px;bottom:16px;width:260px;background:#0e1426;border:1px solid #0f3460;z-index:50}
-#pip img{width:100%;height:146px;object-fit:contain;display:block;background:#000}
-#pip .pip-bar{display:flex;justify-content:space-between;align-items:center;padding:4px 8px;font-size:11px;color:#aaa;border-top:1px solid #0f3460}
-#pip .pip-bar button{background:none;border:none;color:#e94560;cursor:pointer;font-size:13px;padding:0 4px}
 
 /* ---------- 弹窗 ---------- */
 .modal-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.85);z-index:100;justify-content:center;align-items:center}