fix(aiot): 修复离岗检测启动立即报警的三个Bug
Bug#1(严重): 无人帧不调用算法 - _batch_process_rois 中 len(boxes)>0 才调用 _handle_detections - 导致离岗检测永远收不到"人走了"的信号 - 修复: 无论检测结果是否为空都调用算法 - 同时移除 _handle_detections 中 tracks 为空的 early return Bug#2(高): WAITING 一帧就跳 ON_DUTY - 检测到人第一帧就立即从 WAITING 跳到 ON_DUTY - confirm_on_duty_sec 参数完全未被使用 - 修复: 新增 CONFIRMING 状态,需连续 10s 检测到人才确认上岗 Bug#3(中): confirm_leave_sec 默认值过短 - 默认 10 秒,用户预期 30 秒 - 修复: 所有默认值统一改为 30s Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
class LeavePostAlgorithm:
|
||||
STATE_WAITING = "WAITING"
|
||||
STATE_CONFIRMING = "CONFIRMING"
|
||||
STATE_ON_DUTY = "ON_DUTY"
|
||||
STATE_LEAVING = "LEAVING"
|
||||
STATE_OFF_DUTY = "OFF_DUTY"
|
||||
@@ -26,7 +27,7 @@ class LeavePostAlgorithm:
|
||||
def __init__(
|
||||
self,
|
||||
confirm_on_duty_sec: int = 10,
|
||||
confirm_leave_sec: int = 10,
|
||||
confirm_leave_sec: int = 30,
|
||||
cooldown_sec: int = 600,
|
||||
working_hours: Optional[List[Dict]] = None,
|
||||
target_class: Optional[str] = "person",
|
||||
@@ -140,13 +141,28 @@ class LeavePostAlgorithm:
|
||||
|
||||
if self.state == self.STATE_WAITING:
|
||||
if roi_has_person:
|
||||
self.state = self.STATE_ON_DUTY
|
||||
# 检测到人,进入上岗确认阶段
|
||||
self.state = self.STATE_CONFIRMING
|
||||
self.state_start_time = current_time
|
||||
self.detection_history.clear()
|
||||
self.detection_history.append((current_time, True))
|
||||
else:
|
||||
pass
|
||||
|
||||
elif self.state == self.STATE_CONFIRMING:
|
||||
self.detection_history.append((current_time, roi_has_person))
|
||||
if not roi_has_person:
|
||||
# 人消失,回到等待状态
|
||||
self.state = self.STATE_WAITING
|
||||
self.state_start_time = None
|
||||
self.detection_history.clear()
|
||||
else:
|
||||
elapsed = (current_time - self.state_start_time).total_seconds()
|
||||
if elapsed >= self.confirm_on_duty_sec:
|
||||
# 持续在岗达到确认时长,正式确认上岗
|
||||
self.state = self.STATE_ON_DUTY
|
||||
self.state_start_time = current_time
|
||||
|
||||
elif self.state == self.STATE_ON_DUTY:
|
||||
self.detection_history.append((current_time, roi_has_person))
|
||||
if not roi_has_person:
|
||||
@@ -395,7 +411,7 @@ class AlgorithmManager:
|
||||
self.default_params = {
|
||||
"leave_post": {
|
||||
"confirm_on_duty_sec": 10,
|
||||
"confirm_leave_sec": 10,
|
||||
"confirm_leave_sec": 30,
|
||||
"cooldown_sec": 600,
|
||||
"target_class": "person",
|
||||
},
|
||||
@@ -520,7 +536,7 @@ class AlgorithmManager:
|
||||
if algo_code == "leave_post":
|
||||
algo_params = {
|
||||
"confirm_on_duty_sec": params.get("confirm_on_duty_sec", 10),
|
||||
"confirm_leave_sec": params.get("confirm_leave_sec", 10),
|
||||
"confirm_leave_sec": params.get("confirm_leave_sec", 30),
|
||||
"cooldown_sec": params.get("cooldown_sec", 600),
|
||||
"working_hours": params.get("working_hours", []),
|
||||
"target_class": params.get("target_class", bind_config.get("target_class", "person")),
|
||||
@@ -638,7 +654,7 @@ class AlgorithmManager:
|
||||
roi_working_hours = algo_params.get("working_hours") or self.working_hours
|
||||
self.algorithms[roi_id][key]["leave_post"] = LeavePostAlgorithm(
|
||||
confirm_on_duty_sec=algo_params.get("confirm_on_duty_sec", 10),
|
||||
confirm_leave_sec=algo_params.get("confirm_leave_sec", 10),
|
||||
confirm_leave_sec=algo_params.get("confirm_leave_sec", 30),
|
||||
cooldown_sec=algo_params.get("cooldown_sec", 600),
|
||||
working_hours=roi_working_hours,
|
||||
target_class=algo_params.get("target_class", "person"),
|
||||
|
||||
@@ -265,7 +265,7 @@ class SQLiteManager:
|
||||
'target_class': 'person',
|
||||
'param_schema': json.dumps({
|
||||
"confirm_on_duty_sec": {"type": "int", "default": 10, "min": 1},
|
||||
"confirm_leave_sec": {"type": "int", "default": 10, "min": 1},
|
||||
"confirm_leave_sec": {"type": "int", "default": 30, "min": 1},
|
||||
"cooldown_sec": {"type": "int", "default": 600, "min": 0},
|
||||
"working_hours": {"type": "list", "default": []},
|
||||
}),
|
||||
|
||||
15
main.py
15
main.py
@@ -404,12 +404,12 @@ class EdgeInferenceService:
|
||||
for idx, (camera_id, roi, bind, frame, _, scale_info) in enumerate(roi_items):
|
||||
boxes, scores, class_ids = batch_results[idx]
|
||||
|
||||
if len(boxes) > 0:
|
||||
self._handle_detections(
|
||||
camera_id, roi, bind, frame,
|
||||
boxes, scores, class_ids,
|
||||
scale_info
|
||||
)
|
||||
# 无论是否检测到目标都要调用算法(离岗检测需要"无人"信号)
|
||||
self._handle_detections(
|
||||
camera_id, roi, bind, frame,
|
||||
boxes, scores, class_ids,
|
||||
scale_info
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
self._logger.error(f"批量处理 ROI 失败: {e}")
|
||||
@@ -489,8 +489,7 @@ class EdgeInferenceService:
|
||||
|
||||
tracks = self._build_tracks(roi, boxes, scores, class_ids, scale_info)
|
||||
|
||||
if not tracks:
|
||||
return
|
||||
# 离岗检测需要"无人"信号,不能因为 tracks 为空就跳过算法
|
||||
|
||||
# 诊断日志:tracks 内容(非告警诊断日志,使用 DEBUG 级别)
|
||||
self._logger.debug(f"[{camera_id}] tracks: {[t.get('class') for t in tracks]}, target_class={bind.target_class}")
|
||||
|
||||
Reference in New Issue
Block a user