|
@@ -499,14 +499,16 @@ int main(int argc, char* argv[]) {
|
|
|
// NPU 核心分配: 检测绑 Core0, 姿态绑 Core1, 两模型并行推理
|
|
// NPU 核心分配: 检测绑 Core0, 姿态绑 Core1, 两模型并行推理
|
|
|
int det_core = cfg.value("det_core_mask", 1); // 默认 Core0
|
|
int det_core = cfg.value("det_core_mask", 1); // 默认 Core0
|
|
|
int pose_core = cfg.value("pose_core_mask", 2); // 默认 Core1
|
|
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, det_core)) {
|
|
|
|
|
- cerr << "RKNN模型加载失败!" << endl;
|
|
|
|
|
- return 1;
|
|
|
|
|
|
|
+ // 目标模型: 仅做一次加载验证(验证后立即释放, 实际推理用每路独立context)
|
|
|
|
|
+ if (target_enabled) {
|
|
|
|
|
+ YoloDetector probe;
|
|
|
|
|
+ if (!probe.load(model_path, input_size, conf_thresh, nms_thresh, det_core)) {
|
|
|
|
|
+ cerr << "RKNN模型加载失败!" << endl;
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ cout << "[RKNN] 检测模型验证通过, 核心掩码: " << det_core << endl;
|
|
|
}
|
|
}
|
|
|
- cout << "[RKNN] 检测核心掩码: " << det_core << endl;
|
|
|
|
|
|
|
|
|
|
- PoseDetector poseDetector;
|
|
|
|
|
FallAnalyzer fallAnalyzer;
|
|
FallAnalyzer fallAnalyzer;
|
|
|
fallAnalyzer.setThreshold(fall_thresh);
|
|
fallAnalyzer.setThreshold(fall_thresh);
|
|
|
bool pose_hand_raise = cfg.value("pose_hand_raise", true);
|
|
bool pose_hand_raise = cfg.value("pose_hand_raise", true);
|
|
@@ -553,10 +555,12 @@ int main(int argc, char* argv[]) {
|
|
|
<< " 屏蔽区=" << pose_exclude_zones.size() << "路" << endl;
|
|
<< " 屏蔽区=" << pose_exclude_zones.size() << "路" << endl;
|
|
|
bool pose_ok = false;
|
|
bool pose_ok = false;
|
|
|
if (pose_enabled) {
|
|
if (pose_enabled) {
|
|
|
- if (poseDetector.load(pose_model_path, pose_input_size,
|
|
|
|
|
- pose_conf_thresh, pose_nms_thresh, pose_core)) {
|
|
|
|
|
|
|
+ // 姿态模型: 加载验证后立即释放(实际推理用每路独立context)
|
|
|
|
|
+ PoseDetector probe;
|
|
|
|
|
+ if (probe.load(pose_model_path, pose_input_size,
|
|
|
|
|
+ pose_conf_thresh, pose_nms_thresh, pose_core)) {
|
|
|
pose_ok = true;
|
|
pose_ok = true;
|
|
|
- cout << "[Pose] 姿态模型加载成功, 跌倒检测已就绪, 核心掩码: " << pose_core << endl;
|
|
|
|
|
|
|
+ cout << "[Pose] 姿态模型验证通过, 核心掩码: " << pose_core << endl;
|
|
|
} else {
|
|
} else {
|
|
|
cerr << "[Pose] 姿态模型加载失败: " << pose_model_path
|
|
cerr << "[Pose] 姿态模型加载失败: " << pose_model_path
|
|
|
<< " (继续使用纯围栏检测)" << endl;
|
|
<< " (继续使用纯围栏检测)" << endl;
|
|
@@ -671,6 +675,15 @@ int main(int argc, char* argv[]) {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // 停止通道的推理线程(删除/替换通道前必须调用, 避免use-after-free)
|
|
|
|
|
+ auto stopChannelInfer = [](const shared_ptr<ChannelState>& ch) {
|
|
|
|
|
+ if (!ch) return;
|
|
|
|
|
+ ch->infer_running = false;
|
|
|
|
|
+ ch->infer_pending = false;
|
|
|
|
|
+ ch->infer_cv.notify_all();
|
|
|
|
|
+ if (ch->infer_thread.joinable()) ch->infer_thread.join();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
auto makeChannel = [&](int id, const string& name, const string& url) {
|
|
auto makeChannel = [&](int id, const string& name, const string& url) {
|
|
|
auto ch = make_shared<ChannelState>();
|
|
auto ch = make_shared<ChannelState>();
|
|
|
ch->id = id;
|
|
ch->id = id;
|
|
@@ -684,10 +697,14 @@ int main(int argc, char* argv[]) {
|
|
|
if (target_enabled) {
|
|
if (target_enabled) {
|
|
|
ch->det_loaded = ch->det_model.load(model_path, input_size,
|
|
ch->det_loaded = ch->det_model.load(model_path, input_size,
|
|
|
conf_thresh, nms_thresh, core);
|
|
conf_thresh, nms_thresh, core);
|
|
|
|
|
+ if (!ch->det_loaded)
|
|
|
|
|
+ cerr << "[CH" << id << "] 检测模型加载失败(该路不做目标识别)" << endl;
|
|
|
}
|
|
}
|
|
|
if (pose_enabled) {
|
|
if (pose_enabled) {
|
|
|
ch->pose_loaded = ch->pose_model.load(pose_model_path, pose_input_size,
|
|
ch->pose_loaded = ch->pose_model.load(pose_model_path, pose_input_size,
|
|
|
pose_conf_thresh, pose_nms_thresh, core);
|
|
pose_conf_thresh, pose_nms_thresh, core);
|
|
|
|
|
+ if (!ch->pose_loaded)
|
|
|
|
|
+ cerr << "[CH" << id << "] 姿态模型加载失败(该路不做姿态识别)" << endl;
|
|
|
}
|
|
}
|
|
|
// 启动每路独立推理线程
|
|
// 启动每路独立推理线程
|
|
|
if (ch->det_loaded || ch->pose_loaded) {
|
|
if (ch->det_loaded || ch->pose_loaded) {
|
|
@@ -1330,7 +1347,10 @@ int main(int argc, char* argv[]) {
|
|
|
if (!old) channels.push_back(nu);
|
|
if (!old) channels.push_back(nu);
|
|
|
}
|
|
}
|
|
|
registerChannelProviders(nu);
|
|
registerChannelProviders(nu);
|
|
|
- if (old) old->capture->stop(); // 锁外停旧流(join可能耗时)
|
|
|
|
|
|
|
+ if (old) {
|
|
|
|
|
+ stopChannelInfer(old); // 先停旧通道推理线程
|
|
|
|
|
+ old->capture->stop(); // 锁外停旧流(join可能耗时)
|
|
|
|
|
+ }
|
|
|
persistRtspList();
|
|
persistRtspList();
|
|
|
cout << "[Channel] " << (old ? "修改" : "添加")
|
|
cout << "[Channel] " << (old ? "修改" : "添加")
|
|
|
<< " CH" << id << ": " << url << endl;
|
|
<< " CH" << id << ": " << url << endl;
|
|
@@ -1359,7 +1379,8 @@ int main(int argc, char* argv[]) {
|
|
|
if (!victim)
|
|
if (!victim)
|
|
|
return R"({"ok":false,"error":"通道不存在"})";
|
|
return R"({"ok":false,"error":"通道不存在"})";
|
|
|
web.removeFrameProvider(id);
|
|
web.removeFrameProvider(id);
|
|
|
- victim->capture->stop(); // 锁外停止并回收解码线程
|
|
|
|
|
|
|
+ stopChannelInfer(victim); // 先停推理线程(避免访问已释放的ChannelState)
|
|
|
|
|
+ victim->capture->stop(); // 锁外停止并回收解码线程
|
|
|
persistRtspList();
|
|
persistRtspList();
|
|
|
cout << "[Channel] 删除 CH" << id << endl;
|
|
cout << "[Channel] 删除 CH" << id << endl;
|
|
|
return R"({"ok":true})";
|
|
return R"({"ok":true})";
|
|
@@ -2482,22 +2503,14 @@ int main(int argc, char* argv[]) {
|
|
|
lock_guard<mutex> lk(channels_mtx);
|
|
lock_guard<mutex> lk(channels_mtx);
|
|
|
for (auto& ch : channels) {
|
|
for (auto& ch : channels) {
|
|
|
ch->capture->stop();
|
|
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();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ stopChannelInfer(ch); // 内含 notify_all, 避免worker永久阻塞在wait
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
{
|
|
{
|
|
|
lock_guard<mutex> lk(bucket_channels_mtx);
|
|
lock_guard<mutex> lk(bucket_channels_mtx);
|
|
|
for (auto& ch : bucket_channels) {
|
|
for (auto& ch : bucket_channels) {
|
|
|
ch->capture->stop();
|
|
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();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ stopChannelInfer(ch);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
relay.stop(); // 先停继电器线程, 避免HTTP处理线程阻塞在transact上拖住web.stop()
|
|
relay.stop(); // 先停继电器线程, 避免HTTP处理线程阻塞在transact上拖住web.stop()
|