safety_main.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. """
  2. 施工现场安全行为智能识别系统 - 主程序
  3. 系统功能:
  4. 1. 实时视频监控
  5. 2. 人员、安全帽、反光衣检测
  6. 3. 安全违规识别(未戴安全帽、未穿反光衣)
  7. 4. 事件推送至业务平台
  8. 5. 接收平台指令,TTS 语音播报
  9. """
  10. import os
  11. import sys
  12. import time
  13. import argparse
  14. import logging
  15. import threading
  16. import signal
  17. from typing import Optional
  18. import cv2
  19. import numpy as np
  20. # 添加项目路径
  21. sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
  22. from config import (
  23. LOG_CONFIG, PANORAMA_CAMERA, SDK_PATH,
  24. SAFETY_DETECTION_CONFIG, EVENT_PUSHER_CONFIG,
  25. VOICE_ANNOUNCER_CONFIG, SYSTEM_CONFIG,
  26. LLM_CONFIG, LLM_SAFETY_CONFIG
  27. )
  28. from safety_detector import (
  29. SafetyDetector, SafetyDetection, PersonSafetyStatus,
  30. draw_safety_result, SafetyViolationType, LLMSafetyDetector
  31. )
  32. from llm_service import SafetyAnalyzer, NumberRecognizer
  33. from event_pusher import EventPusher, SafetyEvent, EventType
  34. from voice_announcer import VoiceAnnouncer, VoicePriority
  35. from safety_coordinator import SafetyCoordinator, SimpleCamera
  36. # 配置日志
  37. def setup_logging():
  38. """设置日志配置"""
  39. log_level = getattr(logging, LOG_CONFIG.get('level', 'INFO'), logging.INFO)
  40. log_format = LOG_CONFIG.get('format', '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  41. log_file = LOG_CONFIG.get('file')
  42. handlers = [logging.StreamHandler()]
  43. if log_file:
  44. from logging.handlers import RotatingFileHandler
  45. file_handler = RotatingFileHandler(
  46. log_file,
  47. maxBytes=LOG_CONFIG.get('max_bytes', 10*1024*1024),
  48. backupCount=LOG_CONFIG.get('backup_count', 5)
  49. )
  50. file_handler.setFormatter(logging.Formatter(log_format))
  51. handlers.append(file_handler)
  52. logging.basicConfig(
  53. level=log_level,
  54. format=log_format,
  55. handlers=handlers
  56. )
  57. setup_logging()
  58. logger = logging.getLogger(__name__)
  59. class SafetyMonitorSystem:
  60. """
  61. 施工现场安全监控系统
  62. """
  63. def __init__(self, config: dict = None):
  64. """
  65. 初始化系统
  66. Args:
  67. config: 配置覆盖
  68. """
  69. self.config = config or {}
  70. # 摄像头
  71. self.camera = None
  72. self.camera_source = None
  73. # 组件
  74. self.detector = None # 安全检测器 (支持 LLM)
  75. self.llm_analyzer = None # 大模型安全分析器
  76. self.number_recognizer = None # 编号识别器
  77. self.event_pusher = None # 事件推送器
  78. self.voice_announcer = None # 语音播报器
  79. # 功能开关 - 从 SYSTEM_CONFIG 读取
  80. self.enable_panorama_camera = SYSTEM_CONFIG.get('enable_panorama_camera', True)
  81. self.enable_ptz_camera = SYSTEM_CONFIG.get('enable_ptz_camera', True)
  82. self.enable_detection = SYSTEM_CONFIG.get('enable_detection', True)
  83. self.enable_safety_detection = SYSTEM_CONFIG.get('enable_safety_detection', True)
  84. self.enable_calibration = SYSTEM_CONFIG.get('enable_calibration', True)
  85. self.enable_ptz_tracking = SYSTEM_CONFIG.get('enable_ptz_tracking', True)
  86. self.enable_ocr = SYSTEM_CONFIG.get('enable_ocr', True)
  87. self.enable_llm = SYSTEM_CONFIG.get('enable_llm', True)
  88. self.enable_event_push = SYSTEM_CONFIG.get('enable_event_push', True)
  89. self.enable_voice_announce = SYSTEM_CONFIG.get('enable_voice_announce', True)
  90. # 状态
  91. self.running = False
  92. self.display = True # 是否显示画面
  93. # 帧处理
  94. self.current_frame = None
  95. self.frame_lock = threading.Lock()
  96. # 统计
  97. self.stats = {
  98. 'frames_processed': 0,
  99. 'persons_detected': 0,
  100. 'violations_detected': 0,
  101. 'events_pushed': 0,
  102. 'voice_announced': 0,
  103. 'start_time': None
  104. }
  105. self.stats_lock = threading.Lock()
  106. # 工作线程
  107. self.detection_thread = None
  108. def initialize(self, camera_source=0) -> bool:
  109. """
  110. 初始化系统组件
  111. Args:
  112. camera_source: 摄像头源 (索引/RTSP/视频文件)
  113. Returns:
  114. 是否成功
  115. """
  116. logger.info("=" * 60)
  117. logger.info(f"初始化 {SYSTEM_CONFIG['name']} v{SYSTEM_CONFIG['version']}")
  118. logger.info("=" * 60)
  119. # 初始化摄像头
  120. self.camera_source = camera_source
  121. if self.enable_panorama_camera:
  122. self.camera = SimpleCamera(camera_source)
  123. if not self.camera.connect():
  124. logger.error("连接摄像头失败")
  125. return False
  126. logger.info(f"摄像头连接成功: {camera_source}")
  127. else:
  128. self.camera = None
  129. logger.info("摄像头功能已禁用")
  130. # 初始化 LLM 大模型服务
  131. if self.enable_llm:
  132. try:
  133. llm_config = {**LLM_CONFIG}
  134. if 'llm_host' in self.config:
  135. llm_config['api_host'] = self.config['llm_host']
  136. if 'llm_port' in self.config:
  137. llm_config['api_port'] = self.config['llm_port']
  138. self.llm_analyzer = SafetyAnalyzer(llm_config)
  139. logger.info(f"大模型分析器初始化成功: {llm_config['api_host']}:{llm_config['api_port']}")
  140. except Exception as e:
  141. logger.warning(f"大模型分析器初始化失败: {e},将使用规则判断")
  142. self.use_llm = False
  143. # 初始化安全检测器 (支持 LLM)
  144. if self.enable_detection and self.enable_safety_detection:
  145. try:
  146. llm_config = {**LLM_CONFIG} if self.enable_llm else None
  147. self.detector = LLMSafetyDetector(
  148. yolo_model_path=self.config.get('model_path', SAFETY_DETECTION_CONFIG.get('model_path')),
  149. llm_config=llm_config,
  150. use_gpu=self.config.get('use_gpu', SAFETY_DETECTION_CONFIG.get('use_gpu', True)),
  151. use_llm=self.enable_llm
  152. )
  153. logger.info("安全检测器初始化成功")
  154. except Exception as e:
  155. logger.error(f"安全检测器初始化失败: {e}")
  156. return False
  157. else:
  158. self.detector = None
  159. logger.info("安全检测功能已禁用")
  160. # 初始化编号识别器
  161. if self.enable_ocr and self.enable_llm:
  162. try:
  163. self.number_recognizer = NumberRecognizer(LLM_CONFIG)
  164. logger.info("编号识别器初始化成功")
  165. except Exception as e:
  166. logger.warning(f"编号识别器初始化失败: {e}")
  167. self.number_recognizer = None
  168. # 初始化事件推送器
  169. if self.enable_event_push:
  170. try:
  171. push_config = {**EVENT_PUSHER_CONFIG}
  172. if 'api_host' in self.config:
  173. push_config['api_host'] = self.config['api_host']
  174. if 'api_port' in self.config:
  175. push_config['api_port'] = self.config['api_port']
  176. self.event_pusher = EventPusher(push_config)
  177. logger.info("事件推送器初始化成功")
  178. except Exception as e:
  179. logger.warning(f"事件推送器初始化失败: {e}")
  180. # 初始化语音播报器
  181. if self.enable_voice_announce:
  182. try:
  183. self.voice_announcer = VoiceAnnouncer(
  184. tts_config=VOICE_ANNOUNCER_CONFIG.get('tts', {}),
  185. player_config=VOICE_ANNOUNCER_CONFIG.get('player', {})
  186. )
  187. logger.info("语音播报器初始化成功")
  188. except Exception as e:
  189. logger.warning(f"语音播报器初始化失败: {e}")
  190. logger.info("系统初始化完成")
  191. return True
  192. def start(self) -> bool:
  193. """启动系统"""
  194. if self.running:
  195. logger.warning("系统已在运行")
  196. return True
  197. logger.info("启动安全监控系统...")
  198. # 启动事件推送器
  199. if self.event_pusher:
  200. self.event_pusher.start()
  201. # 启动语音播报器
  202. if self.voice_announcer:
  203. self.voice_announcer.start()
  204. # 启动检测线程
  205. self.running = True
  206. self.detection_thread = threading.Thread(target=self._detection_worker, daemon=True)
  207. self.detection_thread.start()
  208. with self.stats_lock:
  209. self.stats['start_time'] = time.time()
  210. logger.info("安全监控系统启动成功")
  211. return True
  212. def stop(self):
  213. """停止系统"""
  214. if not self.running:
  215. return
  216. logger.info("停止安全监控系统...")
  217. self.running = False
  218. if self.detection_thread:
  219. self.detection_thread.join(timeout=3)
  220. if self.event_pusher:
  221. self.event_pusher.stop()
  222. if self.voice_announcer:
  223. self.voice_announcer.stop()
  224. if self.camera:
  225. self.camera.disconnect()
  226. self._print_stats()
  227. logger.info("安全监控系统已停止")
  228. def _detection_worker(self):
  229. """检测工作线程"""
  230. # 检查摄像头和检测是否启用
  231. if not self.enable_panorama_camera or not self.enable_detection:
  232. logger.info("摄像头或检测功能已禁用,检测线程休眠")
  233. while self.running:
  234. time.sleep(1)
  235. return
  236. detection_interval = SAFETY_DETECTION_CONFIG.get('detection_interval', 0.1)
  237. last_detection_time = 0
  238. # 告警冷却
  239. alert_cooldown = {}
  240. cooldown_time = SAFETY_DETECTION_CONFIG.get('alert_cooldown', 3.0)
  241. # 跟踪状态
  242. tracks = {}
  243. next_track_id = 1
  244. while self.running:
  245. try:
  246. current_time = time.time()
  247. # 获取帧
  248. frame = self.camera.get_frame() if self.camera else None
  249. if frame is None:
  250. time.sleep(0.01)
  251. continue
  252. with self.frame_lock:
  253. self.current_frame = frame.copy()
  254. self._update_stats('frames_processed')
  255. # 周期性检测
  256. if current_time - last_detection_time >= detection_interval:
  257. last_detection_time = current_time
  258. # 执行检测
  259. detections = self.detector.detect(frame)
  260. status_list = self.detector.check_safety(frame, detections)
  261. self._update_stats('persons_detected', len(status_list))
  262. # 更新跟踪
  263. persons = [d for d in detections if d.class_id == 3]
  264. used_ids = set()
  265. for person in persons:
  266. # 匹配现有跟踪
  267. best_id = None
  268. min_dist = float('inf')
  269. for tid, track in tracks.items():
  270. if tid in used_ids:
  271. continue
  272. dist = np.sqrt(
  273. (person.center[0] - track['center'][0])**2 +
  274. (person.center[1] - track['center'][1])**2
  275. )
  276. if dist < min_dist and dist < 100:
  277. min_dist = dist
  278. best_id = tid
  279. if best_id is not None:
  280. tracks[best_id]['center'] = person.center
  281. tracks[best_id]['last_update'] = current_time
  282. person.track_id = best_id
  283. used_ids.add(best_id)
  284. else:
  285. person.track_id = next_track_id
  286. tracks[next_track_id] = {
  287. 'center': person.center,
  288. 'last_update': current_time
  289. }
  290. next_track_id += 1
  291. # 清理过期跟踪
  292. expired = [
  293. tid for tid, t in tracks.items()
  294. if current_time - t['last_update'] > 5.0
  295. ]
  296. for tid in expired:
  297. del tracks[tid]
  298. alert_cooldown.pop(tid, None)
  299. # 处理违规
  300. for status in status_list:
  301. if status.is_violation:
  302. tid = status.track_id
  303. # 检查冷却
  304. if tid in alert_cooldown:
  305. if current_time - alert_cooldown[tid] < cooldown_time:
  306. continue
  307. alert_cooldown[tid] = current_time
  308. self._handle_violation(status, frame)
  309. # 显示画面
  310. if self.display:
  311. self._display_frame(frame, detections, status_list)
  312. time.sleep(0.01)
  313. except Exception as e:
  314. logger.error(f"检测错误: {e}")
  315. time.sleep(0.1)
  316. def _handle_violation(self, status: PersonSafetyStatus, frame: np.ndarray):
  317. """处理违规"""
  318. description = status.get_violation_desc()
  319. self._update_stats('violations_detected')
  320. # 裁剪人体区域
  321. x1, y1, x2, y2 = status.person_bbox
  322. margin = 20
  323. x1 = max(0, x1 - margin)
  324. y1 = max(0, y1 - margin)
  325. x2 = min(frame.shape[1], x2 + margin)
  326. y2 = min(frame.shape[0], y2 + margin)
  327. person_image = frame[y1:y2, x1:x2].copy()
  328. # 编号识别
  329. number_text = None
  330. if self.number_recognizer:
  331. try:
  332. number_result = self.number_recognizer.recognize_person_number(person_image)
  333. number_text = number_result.get('number')
  334. if number_text:
  335. logger.info(f"识别到编号: {number_text}")
  336. except Exception as e:
  337. logger.warning(f"编号识别失败: {e}")
  338. # 如果识别到编号,添加到描述中
  339. if number_text:
  340. description = f"{description} (编号: {number_text})"
  341. # 推送事件
  342. if self.event_pusher:
  343. self.event_pusher.push_safety_violation(
  344. description=description,
  345. image=person_image,
  346. track_id=status.track_id,
  347. confidence=status.person_conf
  348. )
  349. self._update_stats('events_pushed')
  350. # 语音播报
  351. if self.voice_announcer:
  352. self.voice_announcer.announce_violation(description, urgent=True)
  353. self._update_stats('voice_announced')
  354. logger.warning(f"[违规] {description}, 跟踪ID: {status.track_id}")
  355. def _display_frame(self, frame: np.ndarray,
  356. detections: List[SafetyDetection],
  357. status_list: List[PersonSafetyStatus]):
  358. """显示帧"""
  359. # 绘制检测结果
  360. result_frame = draw_safety_result(frame, detections, status_list)
  361. # 添加统计信息
  362. stats_text = f"FPS: {self._get_fps():.1f}"
  363. cv2.putText(result_frame, stats_text, (10, 30),
  364. cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
  365. cv2.imshow('Safety Monitor', result_frame)
  366. # 按 'q' 退出
  367. if cv2.waitKey(1) & 0xFF == ord('q'):
  368. self.running = False
  369. def _get_fps(self) -> float:
  370. """获取帧率"""
  371. with self.stats_lock:
  372. if self.stats['start_time']:
  373. elapsed = time.time() - self.stats['start_time']
  374. if elapsed > 0:
  375. return self.stats['frames_processed'] / elapsed
  376. return 0.0
  377. def _update_stats(self, key: str, value: int = 1):
  378. """更新统计"""
  379. with self.stats_lock:
  380. if key in self.stats:
  381. self.stats[key] += value
  382. def _print_stats(self):
  383. """打印统计"""
  384. with self.stats_lock:
  385. if self.stats['start_time']:
  386. elapsed = time.time() - self.stats['start_time']
  387. print("\n" + "=" * 50)
  388. print("安全检测统计")
  389. print("=" * 50)
  390. print(f"运行时长: {elapsed:.1f} 秒")
  391. print(f"处理帧数: {self.stats['frames_processed']}")
  392. print(f"检测人员: {self.stats['persons_detected']} 次")
  393. print(f"违规检测: {self.stats['violations_detected']} 次")
  394. print(f"事件推送: {self.stats['events_pushed']} 次")
  395. print(f"语音播报: {self.stats['voice_announced']} 次")
  396. if self.event_pusher:
  397. push_stats = self.event_pusher.get_stats()
  398. print(f"推送成功: {push_stats['pushed_events']}")
  399. print(f"推送失败: {push_stats['failed_events']}")
  400. if self.voice_announcer:
  401. voice_stats = self.voice_announcer.get_stats()
  402. print(f"播报成功: {voice_stats['played_commands']}")
  403. print(f"播报失败: {voice_stats['failed_commands']}")
  404. print("=" * 50 + "\n")
  405. def get_stats(self) -> dict:
  406. """获取统计"""
  407. with self.stats_lock:
  408. return self.stats.copy()
  409. def announce(self, text: str):
  410. """
  411. 手动播报语音
  412. Args:
  413. text: 播报文本
  414. """
  415. if self.voice_announcer:
  416. self.voice_announcer.announce(text, priority=VoicePriority.NORMAL)
  417. def run_interactive(system: SafetyMonitorSystem):
  418. """
  419. 交互模式运行
  420. Args:
  421. system: 系统实例
  422. """
  423. print("\n施工现场安全监控系统 - 交互模式")
  424. print("=" * 50)
  425. print("命令:")
  426. print(" s - 开始/停止监控")
  427. print(" a - 手动播报 (输入文本)")
  428. print(" r - 查看统计信息")
  429. print(" q - 退出")
  430. print("=" * 50)
  431. running = False
  432. while True:
  433. try:
  434. cmd = input("\n> ").strip().lower()
  435. if cmd == 'q':
  436. break
  437. elif cmd == 's':
  438. if running:
  439. system.stop()
  440. running = False
  441. print("监控已停止")
  442. else:
  443. if system.start():
  444. running = True
  445. print("监控已启动")
  446. elif cmd == 'a':
  447. text = input("输入播报文本: ").strip()
  448. if text:
  449. system.announce(text)
  450. print(f"已播报: {text}")
  451. elif cmd == 'r':
  452. stats = system.get_stats()
  453. print("\n统计信息:")
  454. for k, v in stats.items():
  455. if v is not None:
  456. print(f" {k}: {v}")
  457. else:
  458. print("未知命令")
  459. except KeyboardInterrupt:
  460. break
  461. except Exception as e:
  462. print(f"错误: {e}")
  463. print("退出交互模式")
  464. def main():
  465. """主函数"""
  466. parser = argparse.ArgumentParser(
  467. description='施工现场安全行为智能识别系统'
  468. )
  469. # 摄像头参数
  470. parser.add_argument('--camera', type=str, default='0',
  471. help='摄像头源 (索引/RTSP地址/视频文件)')
  472. parser.add_argument('--no-display', action='store_true',
  473. help='不显示画面')
  474. # 检测参数
  475. parser.add_argument('--model', type=str,
  476. help='安全检测模型路径')
  477. parser.add_argument('--conf', type=float, default=0.5,
  478. help='置信度阈值')
  479. parser.add_argument('--person-conf', type=float, default=0.8,
  480. help='人员检测置信度阈值')
  481. parser.add_argument('--no-gpu', action='store_true',
  482. help='不使用GPU')
  483. # LLM 大模型参数
  484. parser.add_argument('--llm-host', type=str,
  485. help='大模型 API 主机')
  486. parser.add_argument('--llm-port', type=int,
  487. help='大模型 API 端口')
  488. parser.add_argument('--no-llm', action='store_true',
  489. help='禁用大模型判断,使用规则判断')
  490. parser.add_argument('--no-ocr', action='store_true',
  491. help='禁用编号识别')
  492. # 业务平台参数
  493. parser.add_argument('--api-host', type=str,
  494. help='业务平台 API 主机')
  495. parser.add_argument('--api-port', type=int,
  496. help='业务平台 API 端口')
  497. parser.add_argument('--no-push', action='store_true',
  498. help='禁用事件推送')
  499. parser.add_argument('--no-voice', action='store_true',
  500. help='禁用语音播报')
  501. # 运行模式
  502. parser.add_argument('--interactive', action='store_true',
  503. help='交互模式')
  504. parser.add_argument('--demo', action='store_true',
  505. help='演示模式')
  506. args = parser.parse_args()
  507. # 构建配置
  508. config = {}
  509. if args.model:
  510. config['model_path'] = args.model
  511. config['conf_threshold'] = args.conf
  512. config['person_threshold'] = args.person_conf
  513. config['use_gpu'] = not args.no_gpu
  514. # LLM 配置
  515. if args.llm_host:
  516. config['llm_host'] = args.llm_host
  517. if args.llm_port:
  518. config['llm_port'] = args.llm_port
  519. if args.api_host:
  520. config['api_host'] = args.api_host
  521. if args.api_port:
  522. config['api_port'] = args.api_port
  523. # 演示模式
  524. if args.demo:
  525. print("\n施工现场安全行为智能识别系统")
  526. print("=" * 60)
  527. print("""
  528. 系统功能:
  529. 1. 实时视频监控
  530. 2. YOLO11 检测: 人员、安全帽、反光衣
  531. 3. 大模型判断: 安全状态分析
  532. 4. 编号识别: OCR 识别衣服上的工号
  533. 5. 事件推送到业务平台
  534. 6. 接收平台指令,TTS 语音播报
  535. 系统架构:
  536. ┌─────────────────────────────────────────────────────┐
  537. │ 摄像头视频流 │
  538. └─────────────────────────────────────────────────────┘
  539. ┌─────────────────────────────────────────────────────┐
  540. │ YOLO11 安全检测模型 │
  541. │ 检测类别: 人员(3)、安全帽(0)、反光衣(4) │
  542. └─────────────────────────────────────────────────────┘
  543. ┌─────────────────────────────────────────────────────┐
  544. │ 大模型安全状态判断 │
  545. │ 分析: 是否佩戴安全帽、是否穿反光衣 │
  546. │ 识别: 衣服上的工号/编号 │
  547. └─────────────────────────────────────────────────────┘
  548. ┌─────────────┼─────────────┐
  549. ▼ ▼ ▼
  550. ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
  551. │ 事件推送 │ │ 语音播报 │ │ 编号记录 │
  552. │ 业务平台API │ │ TTS服务 │ │ 身份关联 │
  553. └─────────────┘ └─────────────┘ └─────────────┘
  554. 运行命令:
  555. python safety_main.py --camera 0 # 使用默认摄像头
  556. python safety_main.py --camera rtsp://... # RTSP 流
  557. python safety_main.py --camera video.mp4 # 视频文件
  558. python safety_main.py --interactive # 交互模式
  559. python safety_main.py --no-display # 无界面模式
  560. python safety_main.py --no-llm # 禁用大模型,使用规则判断
  561. python safety_main.py --no-ocr # 禁用编号识别
  562. python safety_main.py --llm-host localhost --llm-port 8111 # 指定大模型服务
  563. """)
  564. return 0
  565. # 创建系统
  566. system = SafetyMonitorSystem(config)
  567. system.display = not args.no_display
  568. # 设置信号处理
  569. def signal_handler(sig, frame):
  570. print("\n接收到停止信号")
  571. system.stop()
  572. sys.exit(0)
  573. signal.signal(signal.SIGINT, signal_handler)
  574. signal.signal(signal.SIGTERM, signal_handler)
  575. try:
  576. # 解析摄像头源
  577. camera_source = args.camera
  578. if camera_source.isdigit():
  579. camera_source = int(camera_source)
  580. # 初始化
  581. if not system.initialize(camera_source):
  582. print("\n系统初始化失败!")
  583. return 1
  584. # 禁用功能 (命令行参数覆盖配置)
  585. if args.no_llm:
  586. system.enable_llm = False
  587. print("大模型判断已禁用,使用规则判断")
  588. if args.no_ocr:
  589. system.enable_ocr = False
  590. system.number_recognizer = None
  591. print("编号识别已禁用")
  592. if args.no_push:
  593. system.enable_event_push = False
  594. system.event_pusher = None
  595. print("事件推送已禁用")
  596. if args.no_voice:
  597. system.enable_voice_announce = False
  598. system.voice_announcer = None
  599. print("语音播报已禁用")
  600. # 运行
  601. if args.interactive:
  602. run_interactive(system)
  603. else:
  604. # 自动模式
  605. if not system.start():
  606. print("启动失败")
  607. return 1
  608. print("\n系统运行中,按 Ctrl+C 停止")
  609. print("(按 'q' 键退出显示窗口)\n")
  610. # 主循环
  611. while system.running:
  612. time.sleep(0.1)
  613. except KeyboardInterrupt:
  614. print("\n接收到停止信号")
  615. finally:
  616. system.stop()
  617. return 0
  618. if __name__ == '__main__':
  619. sys.exit(main() or 0)