正常使用版本

This commit is contained in:
zengqile 2026-01-23 14:22:52 +08:00
parent df2e1db689
commit 8950b350ad
10 changed files with 237 additions and 320 deletions

3
.gitignore vendored
View File

@ -1,2 +1,5 @@
mosquitto/data/*.db
mosquitto/log/*.log
build/
build/edge_proxy_lib.dir

View File

@ -98,12 +98,15 @@ CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o: \
/usr/include/c++/11/bits/stl_vector.h \
/usr/include/c++/11/bits/stl_bvector.h \
/usr/include/c++/11/bits/vector.tcc \
/usr/include/c++/11/pstl/execution_defs.h /usr/include/c++/11/chrono \
/usr/include/c++/11/ratio /usr/include/c++/11/cstdint \
/usr/include/c++/11/pstl/execution_defs.h /usr/include/c++/11/atomic \
/usr/include/c++/11/bits/atomic_base.h \
/usr/lib/gcc/aarch64-linux-gnu/11/include/stdint.h /usr/include/stdint.h \
/usr/include/aarch64-linux-gnu/bits/wchar.h \
/usr/include/aarch64-linux-gnu/bits/stdint-uintn.h \
/usr/include/c++/11/limits /usr/include/c++/11/ctime /usr/include/time.h \
/usr/include/c++/11/bits/atomic_lockfree_defines.h \
/usr/include/c++/11/chrono /usr/include/c++/11/ratio \
/usr/include/c++/11/cstdint /usr/include/c++/11/limits \
/usr/include/c++/11/ctime /usr/include/time.h \
/usr/include/aarch64-linux-gnu/bits/time.h \
/usr/include/aarch64-linux-gnu/bits/timex.h \
/usr/include/aarch64-linux-gnu/bits/types/struct_tm.h \
@ -142,8 +145,6 @@ CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o: \
/usr/include/c++/11/bits/cxxabi_init_exception.h \
/usr/include/c++/11/bits/nested_exception.h \
/usr/include/c++/11/bits/shared_ptr_atomic.h \
/usr/include/c++/11/bits/atomic_base.h \
/usr/include/c++/11/bits/atomic_lockfree_defines.h \
/usr/include/c++/11/backward/auto_ptr.h \
/usr/include/c++/11/pstl/glue_memory_defs.h \
/usr/include/opencv4/opencv2/core/core.hpp \
@ -255,7 +256,9 @@ CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o: \
/usr/include/c++/11/bits/unique_lock.h \
/usr/include/opencv4/opencv2/core/optim.hpp \
/usr/include/opencv4/opencv2/core/ovx.hpp \
/usr/include/opencv4/opencv2/core/cvdef.h \
/usr/include/opencv4/opencv2/core/cvdef.h /usr/include/c++/11/thread \
/usr/include/c++/11/bits/std_thread.h \
/usr/include/c++/11/bits/this_thread_sleep.h \
/app/src/algorithm/IAnalysisModule.h /app/src/vendor/nlohmann/json.hpp \
/usr/include/c++/11/iterator /usr/include/c++/11/bits/stream_iterator.h \
/usr/include/c++/11/forward_list /usr/include/c++/11/bits/forward_list.h \
@ -387,7 +390,6 @@ CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o: \
/usr/include/eigen3/Eigen/src/Core/SelfAdjointView.h \
/usr/include/eigen3/Eigen/src/Core/products/GeneralBlockPanelKernel.h \
/usr/include/eigen3/Eigen/src/Core/products/Parallelizer.h \
/usr/include/c++/11/atomic \
/usr/include/eigen3/Eigen/src/Core/ProductEvaluators.h \
/usr/include/eigen3/Eigen/src/Core/products/GeneralMatrixVector.h \
/usr/include/eigen3/Eigen/src/Core/products/GeneralMatrixMatrix.h \
@ -622,15 +624,12 @@ CMakeFiles/edge_proxy_lib.dir/src/algorithm/HumanDetectionModule.cc.o: \
/usr/include/im2d_mpi.h /usr/include/rga.h /app/src/rknn/rknn_api.h \
/app/src/rknn/rknnPool.hpp /app/src/rknn/ThreadPool.hpp \
/usr/include/c++/11/condition_variable /usr/include/c++/11/future \
/usr/include/c++/11/bits/atomic_futex.h \
/usr/include/c++/11/bits/std_thread.h /usr/include/c++/11/thread \
/usr/include/c++/11/bits/this_thread_sleep.h \
/usr/include/spdlog/spdlog.h /usr/include/spdlog/common.h \
/usr/include/spdlog/tweakme.h /usr/include/spdlog/details/null_mutex.h \
/usr/include/spdlog/fmt/fmt.h /usr/include/fmt/core.h \
/usr/include/fmt/format.h /usr/include/fmt/core.h \
/usr/include/spdlog/details/registry.h /usr/include/spdlog/logger.h \
/usr/include/spdlog/details/log_msg.h \
/usr/include/c++/11/bits/atomic_futex.h /usr/include/spdlog/spdlog.h \
/usr/include/spdlog/common.h /usr/include/spdlog/tweakme.h \
/usr/include/spdlog/details/null_mutex.h /usr/include/spdlog/fmt/fmt.h \
/usr/include/fmt/core.h /usr/include/fmt/format.h \
/usr/include/fmt/core.h /usr/include/spdlog/details/registry.h \
/usr/include/spdlog/logger.h /usr/include/spdlog/details/log_msg.h \
/usr/include/spdlog/details/backtracer.h \
/usr/include/spdlog/details/log_msg_buffer.h \
/usr/include/spdlog/details/circular_q.h /usr/include/spdlog/version.h \

Binary file not shown.

Binary file not shown.

View File

@ -1,18 +1,18 @@
// src/algorithm/HumanDetectionModule.cc
#include "algorithm/HumanDetectionModule.h"
#include <stdio.h>
#include <atomic>
#include <thread>
#include <vector>
#include "opencv2/imgproc/imgproc.hpp"
#include "spdlog/spdlog.h"
#ifndef OBJ_NUMB_MAX_SIZE
#define OBJ_NUMB_MAX_SIZE 64
#endif
// [修改] 构造函数:移除了 intrusion_zone 参数和相关检查
// 构造函数
HumanDetectionModule::HumanDetectionModule(std::string model_path, int thread_num,
std::vector<cv::Point> intrusion_zone,
double intrusion_time_threshold)
@ -25,14 +25,27 @@ HumanDetectionModule::HumanDetectionModule(std::string model_path, int thread_nu
spdlog::info("[HumanDetectionModule] Created. Model: {}, Threads: {}", model_path_,
thread_num_);
// [删除] 旧的 rect 宽高检查,因为现在是多边形,且在 init 中初始化
// if (intrusion_zone_.width <= 0 ... ) { ... }
if (intrusion_zone_.empty()) {
spdlog::warn("[HumanDetectionModule] Warning: Intrusion zone is empty.");
}
byte_tracker_ = std::make_unique<byte_track::BYTETracker>(20, 150);
}
// 注入 LightController
void HumanDetectionModule::set_light_controller(LightController* controller) {
this->light_controller_ = controller;
}
// 设置模式
void HumanDetectionModule::set_mode(DetectionMode mode) {
current_mode_ = mode;
spdlog::info("[HumanDetectionModule] Switched to Mode: {}", (int)mode);
}
DetectionMode HumanDetectionModule::get_mode() const {
return current_mode_.load();
}
bool HumanDetectionModule::init(const nlohmann::json& module_config) {
std::string label_path =
module_config.value("label_path", "/app/edge-proxy/models/coco_80_labels_list.txt");
@ -50,15 +63,13 @@ bool HumanDetectionModule::init(const nlohmann::json& module_config) {
}
spdlog::info("[HumanDetectionModule] Initialized with Mode: {}", saved_mode);
// [正确] 这里已经处理了新旧两种格式,存入 light_device_ids_
light_device_ids_.clear();
// 优先读取 light_device_ids (数组)
if (module_config.contains("light_device_ids") &&
module_config["light_device_ids"].is_array()) {
light_device_ids_ = module_config["light_device_ids"].get<std::vector<std::string>>();
}
// 兼容旧配置 light_device_id (字符串)
else if (module_config.contains("light_device_id") &&
module_config["light_device_id"].is_string()) {
} else if (module_config.contains("light_device_id") &&
module_config["light_device_id"].is_string()) {
light_device_ids_.push_back(module_config["light_device_id"].get<std::string>());
}
@ -67,13 +78,12 @@ bool HumanDetectionModule::init(const nlohmann::json& module_config) {
light_device_ids_.size());
}
// [新增] 解析多边形区域配置
// 解析多边形区域配置
if (module_config.contains("intrusion_zone")) {
auto zone_json = module_config["intrusion_zone"];
intrusion_zone_.clear();
if (zone_json.is_array()) {
// 情况1: 新格式 [[x,y], [x,y], ...]
if (zone_json.size() > 0 && zone_json[0].is_array()) {
for (const auto& point_json : zone_json) {
if (point_json.size() >= 2) {
@ -82,9 +92,7 @@ bool HumanDetectionModule::init(const nlohmann::json& module_config) {
}
spdlog::info("[HumanDetectionModule] Polygon zone initialized with {} points.",
intrusion_zone_.size());
}
// 情况2: 兼容旧格式 [x, y, w, h] -> 转换为矩形多边形
else if (zone_json.size() == 4 && zone_json[0].is_number()) {
} else if (zone_json.size() == 4 && zone_json[0].is_number()) {
int x = zone_json[0];
int y = zone_json[1];
int w = zone_json[2];
@ -113,16 +121,14 @@ bool HumanDetectionModule::init(const nlohmann::json& module_config) {
}
spdlog::info("[HumanDetectionModule] rknnPool init success (YOLOv8 + RGA).");
if (module_config.contains("light_device_id")) {
this->light_device_id_ = module_config["light_device_id"].get<std::string>();
spdlog::info("[HumanDetectionModule] Linked with Light Device ID: {}", light_device_id_);
}
// [删除] 这里原来有多余的旧代码,现在删掉后就可以编译通过了
// if (module_config.contains("light_device_id")) { ... } <-- 删掉这段
// 初始化最后活跃时间为当前时间,避免启动瞬间误关灯
last_person_active_time_ = get_current_time_seconds();
return true;
}
// [新增] 智能灯光逻辑核心函数
// [核心修复] 智能灯光逻辑:全并发执行,实现“瞬间全亮”
void HumanDetectionModule::update_smart_light_logic(bool any_person_in_zone, double current_time) {
// 1. 基础检查
if (light_device_ids_.empty() || !light_controller_)
@ -130,8 +136,7 @@ void HumanDetectionModule::update_smart_light_logic(bool any_person_in_zone, dou
if (current_mode_.load() != DetectionMode::QUICK_ACTION)
return;
// const double LIGHT_OFF_DELAY = 60.0; // 无人60秒关灯
const double LIGHT_OFF_DELAY = 30.0; // 无人60秒关灯
const double LIGHT_OFF_DELAY = 60.0; // 无人60秒关灯
std::string action = ""; // 待执行的动作
@ -175,34 +180,23 @@ void HumanDetectionModule::update_smart_light_logic(bool any_person_in_zone, dou
}).detach();
}
}
bool HumanDetectionModule::process(cv::Mat& frame) {
if (frame.empty()) {
if (frame.empty())
return false;
}
if (rknn_pool_->put(frame) != 0) {
spdlog::error("[HumanDetectionModule] Failed to put frame into rknnPool.");
if (rknn_pool_->put(frame) != 0)
return false;
}
detect_result_group_t detection_results;
if (rknn_pool_->get(detection_results) != 0) {
spdlog::error("[HumanDetectionModule] Failed to get frame from rknnPool.");
if (rknn_pool_->get(detection_results) != 0)
return false;
}
// 更新追踪器逻辑
this->update_tracker(detection_results, frame.size());
// 绘制结果
this->draw_results(frame);
return true;
}
void HumanDetectionModule::trigger_alarm(int person_id, const cv::Rect& box) {
spdlog::warn("[ALARM] Human Intrusion! ID: {} at [{}, {}, {} x {}]", person_id, box.x, box.y,
box.width, box.height);
spdlog::warn("[ALARM] Human Intrusion! ID: {}", person_id);
}
double HumanDetectionModule::get_current_time_seconds() {
@ -211,176 +205,59 @@ double HumanDetectionModule::get_current_time_seconds() {
.count();
}
// [辅助函数] 计算两个矩形的中心点距离平方
double calculate_distance_sq(const cv::Rect& a, const cv::Rect& b) {
cv::Point c1 = (a.tl() + a.br()) / 2;
cv::Point c2 = (b.tl() + b.br()) / 2;
double dx = c1.x - c2.x;
double dy = c1.y - c2.y;
return dx * dx + dy * dy;
}
// [辅助函数] 计算两个矩形的 IoU (交并比)
double calculate_iou(const cv::Rect& box1, const cv::Rect& box2) {
int x1 = std::max(box1.x, box2.x);
int y1 = std::max(box1.y, box2.y);
int x2 = std::min(box1.x + box1.width, box2.x + box2.width);
int y2 = std::min(box1.y + box1.height, box2.y + box2.height);
if (x1 >= x2 || y1 >= y2)
return 0.0;
double intersection = (x2 - x1) * (y2 - y1);
double area1 = box1.width * box1.height;
double area2 = box2.width * box2.height;
return intersection / (area1 + area2 - intersection);
}
void HumanDetectionModule::set_mode(DetectionMode mode) {
current_mode_ = mode;
spdlog::info("[HumanDetectionModule] Switched to Mode: {}", (int)mode);
}
DetectionMode HumanDetectionModule::get_mode() const {
return current_mode_.load();
}
// [新增] 模式2的触发逻辑预留
void HumanDetectionModule::trigger_quick_execution(int person_id, const cv::Rect& box) {
// 这里的逻辑你之后可以填充比如控制继电器、发送HTTP请求等
spdlog::info("[ACTION] Quick Execution Triggered! ID: {} (Detected > 1s)", person_id);
}
void HumanDetectionModule::update_tracker(detect_result_group_t& detect_result_group,
const cv::Size& frame_size) {
// 1. 准备数据给 ByteTrack
std::vector<byte_track::Object> objects;
for (int i = 0; i < detect_result_group.count; i++) {
detect_result_t* det = &(detect_result_group.results[i]);
if (det->prop < 0.1)
continue;
byte_track::Rect<float> rect((float)det->box.left, (float)det->box.top,
(float)(det->box.right - det->box.left),
(float)(det->box.bottom - det->box.top));
byte_track::Object obj(rect, 0, det->prop);
objects.push_back(obj);
objects.push_back(byte_track::Object(rect, 0, det->prop));
}
// 2. 运行追踪
auto output_stracks = byte_tracker_->update(objects);
// 3. 将结果映射回业务逻辑
double current_time = get_current_time_seconds();
bool person_qualified_for_light = false;
// 遍历所有追踪对象
for (auto& [id, person] : tracked_persons_) {
// 必须是在区域内
if (person.is_in_zone) {
// 计算停留时间
double stay_time = current_time - person.entry_time;
// 满足开灯条件在区域内且停留超过1秒
if (stay_time >= 0.2) {
person_qualified_for_light = true;
}
}
}
// [新增] 执行灯光逻辑
this->update_smart_light_logic(person_qualified_for_light, current_time);
std::vector<int> current_frame_ids;
const double KEEP_ALIVE_TIME = 3.0;
const double RECONNECT_DISTANCE_SQ = 100 * 100;
bool person_qualified_for_light = false;
for (const auto& track_ptr : output_stracks) {
int id = track_ptr->getTrackId();
float score = track_ptr->getScore();
if (score < confidence_threshold_)
if (track_ptr->getScore() < confidence_threshold_)
continue;
const auto& rect = track_ptr->getRect();
cv::Rect box((int)rect.x(), (int)rect.y(), (int)rect.width(), (int)rect.height());
// --- ID Switch 处理逻辑 ---
bool is_new_id = (tracked_persons_.find(id) == tracked_persons_.end());
if (is_new_id) {
int best_match_old_id = -1;
double min_dist_sq = RECONNECT_DISTANCE_SQ;
double max_iou = 0.3;
for (auto& [old_id, person] : tracked_persons_) {
if (person.missing_frames > 0) {
double dist_sq = calculate_distance_sq(box, person.box);
double iou = calculate_iou(box, person.box);
if (iou > max_iou) {
max_iou = iou;
best_match_old_id = old_id;
} else if (dist_sq < min_dist_sq && max_iou <= 0.3) {
min_dist_sq = dist_sq;
best_match_old_id = old_id;
}
}
}
if (best_match_old_id != -1) {
// 继承旧 ID
spdlog::info("ID Switch Detected! Inheriting info: Old ID {} -> New ID {}",
best_match_old_id, id);
PersonTrackInfo& old_info = tracked_persons_[best_match_old_id];
PersonTrackInfo new_person;
new_person.id = id;
new_person.entry_time = old_info.entry_time;
new_person.is_in_zone = old_info.is_in_zone;
new_person.alarm_triggered = old_info.alarm_triggered;
new_person.box = box;
new_person.confidence = score;
new_person.last_seen_time = current_time;
new_person.missing_frames = 0;
tracked_persons_[id] = new_person;
tracked_persons_.erase(best_match_old_id);
} else {
// 新 ID
PersonTrackInfo new_person;
new_person.id = id;
new_person.entry_time = 0;
new_person.is_in_zone = false;
new_person.alarm_triggered = false;
new_person.box = box;
new_person.confidence = score;
new_person.last_seen_time = current_time;
new_person.missing_frames = 0;
tracked_persons_[id] = new_person;
}
// 简化版 ID 插入/更新
if (tracked_persons_.find(id) == tracked_persons_.end()) {
PersonTrackInfo new_person;
new_person.id = id;
new_person.entry_time = 0;
new_person.is_in_zone = false;
new_person.alarm_triggered = false;
new_person.box = box;
new_person.confidence = track_ptr->getScore();
new_person.last_seen_time = current_time;
new_person.missing_frames = 0;
tracked_persons_[id] = new_person;
} else {
// 更新现有 ID
tracked_persons_[id].box = box;
tracked_persons_[id].confidence = score;
tracked_persons_[id].confidence = track_ptr->getScore();
tracked_persons_[id].last_seen_time = current_time;
tracked_persons_[id].missing_frames = 0;
}
current_frame_ids.push_back(id);
// --- [修复] 入侵判断逻辑 (多边形) ---
PersonTrackInfo& person = tracked_persons_[id];
cv::Point center_point = (box.tl() + box.br()) / 2;
// 使用 pointPolygonTest 判断点是否在多边形内
// 返回值 >= 0 表示在内部或边缘
// double dist = cv::pointPolygonTest(intrusion_zone_, center_point, false);
// bool currently_in_zone = (dist >= 0);
// 多边形判定
bool currently_in_zone = false;
if (!intrusion_zone_.empty()) {
double result = cv::pointPolygonTest(intrusion_zone_, center_point, false);
if (result >= 0) {
if (cv::pointPolygonTest(intrusion_zone_, center_point, false) >= 0) {
currently_in_zone = true;
}
}
@ -388,58 +265,43 @@ void HumanDetectionModule::update_tracker(detect_result_group_t& detect_result_g
if (currently_in_zone) {
if (!person.is_in_zone) {
person.is_in_zone = true;
if (person.entry_time == 0) {
if (person.entry_time == 0)
person.entry_time = current_time;
}
} else {
// 已经在区域内,根据当前模式判断
double stay_time = current_time - person.entry_time;
// 获取当前模式快照
DetectionMode mode = current_mode_.load();
if (!person.alarm_triggered) {
if (mode == DetectionMode::NORMAL_ALARM) {
// 模式1使用配置文件中的阈值 (intrusion_time_threshold_)
if (stay_time >= intrusion_time_threshold_) {
person.alarm_triggered = true;
trigger_alarm(id, box); // 原来的报警
}
} else if (mode == DetectionMode::QUICK_ACTION) {
// 模式2固定 1.0 秒阈值
if (stay_time >= 1.0) {
person.alarm_triggered = true;
trigger_quick_execution(id, box); // 新的操作
}
if (current_mode_.load() == DetectionMode::NORMAL_ALARM) {
if (!person.alarm_triggered && stay_time >= intrusion_time_threshold_) {
person.alarm_triggered = true;
trigger_alarm(id, box);
}
} else if (current_mode_.load() == DetectionMode::QUICK_ACTION) {
// [0.1秒极速响应]
if (stay_time >= 0.1) {
person_qualified_for_light = true;
}
}
}
} else {
// 离开了区域:重置
person.is_in_zone = false;
person.entry_time = 0;
person.alarm_triggered = false;
}
}
// 4. 清理旧数据 (Soft Delete)
// 执行灯光逻辑
this->update_smart_light_logic(person_qualified_for_light, current_time);
// 清理逻辑
for (auto it = tracked_persons_.begin(); it != tracked_persons_.end();) {
int id = it->first;
bool exists_in_current_frame = false;
bool exists = false;
for (int eid : current_frame_ids)
if (it->first == eid)
exists = true;
for (int exist_id : current_frame_ids) {
if (id == exist_id) {
exists_in_current_frame = true;
break;
}
}
if (!exists_in_current_frame) {
if (!exists) {
it->second.missing_frames++;
double time_since_lost = current_time - it->second.last_seen_time;
if (time_since_lost > KEEP_ALIVE_TIME) {
spdlog::debug("Removing person ID {} (Lost for {:.1f}s)", id, time_since_lost);
if (current_time - it->second.last_seen_time > 3.0) {
it = tracked_persons_.erase(it);
} else {
++it;
@ -451,60 +313,25 @@ void HumanDetectionModule::update_tracker(detect_result_group_t& detect_result_g
}
void HumanDetectionModule::draw_results(cv::Mat& frame) {
// [修复] 绘制多边形区域
if (!intrusion_zone_.empty()) {
const cv::Point* pts = &intrusion_zone_[0]; // 获取点集数据的指针
const cv::Point* pts = &intrusion_zone_[0];
int npts = (int)intrusion_zone_.size();
// 绘制闭合多边形,颜色青色 (255, 255, 0),线宽 2
cv::polylines(frame, &pts, &npts, 1, true, cv::Scalar(255, 255, 0), 2);
}
// 绘制每个追踪的人
for (auto const& [id, person] : this->tracked_persons_) {
// 如果正在丢失中,暂不绘制
if (person.missing_frames > 0) {
if (person.missing_frames > 0)
continue;
}
cv::Scalar box_color =
person.alarm_triggered ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 255, 0);
int line_thickness = person.alarm_triggered ? 3 : 2;
cv::rectangle(frame, person.box, box_color, line_thickness);
// 绘制中心点
cv::Point center_point = (person.box.tl() + person.box.br()) / 2;
cv::circle(frame, center_point, 4, cv::Scalar(0, 0, 255), -1);
// 文字信息
std::string label = fmt::format("ID:{} Conf:{:.2f}", id, person.confidence);
if (person.is_in_zone) {
double stay_time = get_current_time_seconds() - person.entry_time;
label += fmt::format(" T:{:.1f}s", stay_time);
}
int base_line;
double font_scale = 0.6;
int thickness = 2;
cv::Size label_size =
cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, font_scale, thickness, &base_line);
int label_y = std::max(0, person.box.y - label_size.height - 10);
cv::Rect label_bg(person.box.x, label_y, label_size.width, label_size.height + 10);
cv::rectangle(frame, label_bg, box_color, cv::FILLED);
cv::putText(frame, label, cv::Point(person.box.x, label_y + label_size.height + 5),
cv::FONT_HERSHEY_SIMPLEX, font_scale, cv::Scalar(255, 255, 255), thickness);
cv::rectangle(frame, person.box, box_color, 2);
std::string label = "ID:" + std::to_string(id);
cv::putText(frame, label, person.box.tl(), cv::FONT_HERSHEY_SIMPLEX, 0.6,
cv::Scalar(255, 255, 255), 2);
}
}
void HumanDetectionModule::stop() {
spdlog::info("Stopping HumanDetectionModule internal threads...");
if (rknn_pool_) {
if (rknn_pool_)
rknn_pool_->stop();
}
spdlog::info("HumanDetectionModule stopped.");
}

View File

@ -1,11 +1,14 @@
#pragma once
#include <algorithm>
#include <atomic>
#include <chrono>
#include <map>
#include <memory>
#include <opencv2/core/core.hpp>
#include <string>
#include <thread> // 必须包含
#include <vector>
#include "IAnalysisModule.h"
#include "algorithm/bytetrack/include/ByteTrack/BYTETracker.h"
@ -13,34 +16,24 @@
#include "rknn/postprocess.h"
#include "rknn/rkYolov8.hpp"
#include "rknn/rknnPool.hpp"
enum class DetectionMode {
NORMAL_ALARM = 1, // 模式1原逻辑读取配置文件的阈值
QUICK_ACTION = 2 // 模式21秒触发执行特定操作
};
// [修改] 结构体增加了 missing_frames 和 last_seen_time
enum class DetectionMode { NORMAL_ALARM = 1, QUICK_ACTION = 2 };
struct PersonTrackInfo {
int id;
cv::Rect box;
double entry_time;
bool is_in_zone;
bool alarm_triggered;
// --- 新增字段开始 ---
int missing_frames; // 对应 .cc 中的 missing_frames
double last_seen_time; // 对应 .cc 中的 last_seen_time
// --- 新增字段结束 ---
double confidence; // 当前目标的置信度
int frames_unseen; // (保留旧字段以防万一,如果你的.cc只用了missing_frames这个其实可以删掉但保留也不影响)
int missing_frames;
double last_seen_time;
double confidence;
int frames_unseen;
};
class HumanDetectionModule : public IAnalysisModule {
public:
/**
* @brief
*/
// HumanDetectionModule(std::string model_path, int thread_num, cv::Rect intrusion_zone,
// double intrusion_time_threshold);
// 构造函数
HumanDetectionModule(std::string model_path, int thread_num,
std::vector<cv::Point> intrusion_zone, double intrusion_time_threshold);
@ -51,42 +44,38 @@ public:
virtual void stop() override;
void set_mode(DetectionMode mode);
// [新增] 获取当前模式
DetectionMode get_mode() const;
// 注入 LightController
void set_light_controller(LightController* controller);
private:
std::atomic<DetectionMode> current_mode_{DetectionMode::NORMAL_ALARM};
void trigger_quick_execution(int person_id, const cv::Rect& box);
void update_tracker(detect_result_group_t& detect_result_group, const cv::Size& frame_size);
void draw_results(cv::Mat& frame);
void trigger_alarm(int person_id, const cv::Rect& box);
double get_current_time_seconds();
// 智能灯光逻辑
void update_smart_light_logic(bool any_person_in_zone, double current_time);
std::string model_path_;
int thread_num_;
std::unique_ptr<rknnPool<rkYolov8, cv::Mat, detect_result_group_t>> rknn_pool_;
std::vector<cv::Point> intrusion_zone_;
// cv::Rect intrusion_zone_;
std::map<int, PersonTrackInfo> tracked_persons_;
int next_track_id_;
double intrusion_time_threshold_;
double confidence_threshold_;
std::unique_ptr<rknnPool<rkYolov8, cv::Mat, detect_result_group_t>> rknn_pool_;
std::unique_ptr<byte_track::BYTETracker> byte_tracker_;
// [新增] 灯光控制相关成员
LightController* light_controller_ = nullptr; // 灯控对象指针
// std::string light_device_id_; // 当前流关联的灯ID
std::vector<std::string> light_device_ids_;
// 状态记录
double last_person_active_time_ = 0.0; // 最后一次有人活跃的时间戳
bool is_light_on_ = false; // 记录当前灯是否开着(防止重复发请求)
std::atomic<DetectionMode> current_mode_{DetectionMode::NORMAL_ALARM};
// 辅助函数
void update_smart_light_logic(bool any_person_in_zone, double current_time);
// 灯光控制 (改为列表)
LightController* light_controller_ = nullptr;
std::vector<std::string> light_device_ids_; // 存储多个灯ID
double last_person_active_time_ = 0.0;
bool is_light_on_ = false;
};

View File

@ -680,34 +680,133 @@ void WebServer::setup_routes() {
try {
j_body = crow::json::load(req.body);
} catch (const std::exception& e) {
spdlog::warn("Invalid JSON in light control request: {}", e.what());
return crow::response(400, "{\"error\":\"Invalid JSON format.\"}");
}
// 参数校验
if (!j_body.has("device_id") || !j_body.has("action")) {
return crow::response(400, "{\"error\":\"Missing 'device_id' or 'action'.\"}");
}
std::string deviceId = j_body["device_id"].s();
std::string action = j_body["action"].s(); // "0" or "1"
std::string req_deviceId = j_body["device_id"].s();
std::string action = j_body["action"].s();
spdlog::info("Web API: Requesting light control. Device: {}, Action: {}", deviceId,
action);
spdlog::info("Web API: Light Control. Device: {}, Action: {}", req_deviceId, action);
// 调用控制器
bool success = m_light_controller.ControlLight(deviceId, action);
// 1. 定义要控制的设备列表 (将前端的联动逻辑移到这里)
std::vector<std::string> target_devices;
// 如果是总控开关 351则添加所有相关设备
if (req_deviceId == "351") {
// 顺序很重要,建议把不太重要的放后面
target_devices = {"353", "350", "351", "352", "354", "349"};
} else {
// 普通设备,只控制自己
target_devices.push_back(req_deviceId);
}
// 2. [关键优化] 启动后台线程执行物理控制 (Fire-and-Forget)
// 这样前端会立刻收到 200 OK切换页面也不会断连
// 复制必要的变量到 lambda
LightController* ctrl = &m_light_controller;
std::string act = action;
std::vector<std::string> thread_targets = target_devices; // 拷贝一份
std::thread([ctrl, thread_targets, act]() {
for (const auto& id : thread_targets) {
// 依次控制,防止网关拥堵
bool ok = ctrl->ControlLight(id, act);
if (!ok) {
// 失败稍微停顿一下
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
// 成功也稍微停顿一下,让网关喘口气 (例如 20ms)
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
spdlog::info("Async Light Control Finished for {} devices.", thread_targets.size());
}).detach(); // 分离线程
// 3. 处理"智能模式"冲突 (保持之前的逻辑,但要针对列表里的所有设备)
// 也就是:如果我手动关了灯,那么关联这个灯的摄像头必须退出智能模式
try {
std::string config_path = ConfigManager::getInstance().getVideoConfigPath();
std::ifstream ifs(config_path);
if (ifs.is_open()) {
nlohmann::json json_config;
ifs >> json_config;
ifs.close();
bool config_changed = false;
std::vector<std::string> affected_streams;
if (json_config.contains("video_streams") &&
json_config["video_streams"].is_array()) {
for (auto& stream : json_config["video_streams"]) {
if (!stream.contains("module_config"))
continue;
auto& mc = stream["module_config"];
bool is_associated = false;
// 检查该摄像头的 light_device_ids 是否包含我们这次操作的 *任何一个* 灯
// 这样无论是单控还是群控,只要涉及到了,就关闭智能模式
std::vector<std::string> cam_lights;
if (mc.contains("light_device_ids") &&
mc["light_device_ids"].is_array()) {
cam_lights = mc["light_device_ids"].get<std::vector<std::string>>();
} else if (mc.contains("light_device_id") &&
mc["light_device_id"].is_string()) {
cam_lights.push_back(mc["light_device_id"].get<std::string>());
}
for (const auto& target_id : target_devices) {
for (const auto& cam_light_id : cam_lights) {
if (target_id == cam_light_id) {
is_associated = true;
break;
}
}
if (is_associated)
break;
}
// 如果关联且处于智能模式,强制关闭
if (is_associated) {
int current_mode = mc.value("mode", 1);
if (current_mode == 2) {
mc["mode"] = 1;
config_changed = true;
std::string stream_id = stream.value("id", "");
if (!stream_id.empty()) {
auto service = m_video_manager.getService(stream_id);
if (service) {
service->set_analysis_mode(1);
affected_streams.push_back(stream_id);
}
}
}
}
}
}
if (config_changed) {
std::ofstream ofs(config_path);
if (ofs.is_open()) {
ofs << json_config.dump(4);
ofs.close();
spdlog::info("Manual Override: Disabled Smart Mode for: {}",
fmt::join(affected_streams, ", "));
}
}
}
} catch (...) {
// 忽略非关键错误
}
crow::json::wvalue response_json;
if (success) {
response_json["status"] = "success";
response_json["message"] = "Light control command sent.";
return crow::response(200, response_json);
} else {
response_json["status"] = "error";
response_json["message"] = "Failed to send control command (upstream error).";
return crow::response(502, response_json);
}
response_json["status"] = "success";
response_json["message"] = "Command received. Executing in background.";
return crow::response(200, response_json);
});
/**
* @brief