2025-11-21 17:50:02 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""
|
|
|
|
|
|
激光射击系统主程序(视觉测距版)
|
|
|
|
|
|
功能:目标检测、激光校准、4G TCP 通信、OTA 升级、单目测距、INA226 电量监测
|
|
|
|
|
|
平台:MaixPy (Sipeed MAIX)
|
|
|
|
|
|
作者:ZZH
|
|
|
|
|
|
最后更新:2025-11-21
|
2026-01-12 18:06:04 +08:00
|
|
|
|
|
|
|
|
|
|
重构版本:使用模块化设计
|
2025-11-21 17:50:02 +08:00
|
|
|
|
"""
|
2026-01-12 18:06:04 +08:00
|
|
|
|
from maix import camera, display, image, app, time, uart, pinmap, i2c
|
2025-12-28 16:22:41 +08:00
|
|
|
|
from maix.peripheral import adc
|
|
|
|
|
|
import _thread
|
|
|
|
|
|
import os
|
2026-01-12 18:06:04 +08:00
|
|
|
|
import json
|
2025-12-30 16:23:17 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 导入新模块
|
|
|
|
|
|
import config
|
|
|
|
|
|
from version import VERSION
|
|
|
|
|
|
# from logger import init_logging, get_logger, stop_logging
|
|
|
|
|
|
from logger_manager import logger_manager
|
|
|
|
|
|
from time_sync import sync_system_time_from_4g
|
2026-04-18 09:33:37 +08:00
|
|
|
|
from power import init_ina226
|
2026-01-12 18:06:04 +08:00
|
|
|
|
from laser_manager import laser_manager
|
2026-04-18 09:33:37 +08:00
|
|
|
|
from vision import start_save_shot_worker
|
2026-01-12 18:06:04 +08:00
|
|
|
|
from network import network_manager
|
|
|
|
|
|
from ota_manager import ota_manager
|
|
|
|
|
|
from hardware import hardware_manager
|
2026-01-20 11:25:17 +08:00
|
|
|
|
from camera_manager import camera_manager
|
2026-04-18 09:33:37 +08:00
|
|
|
|
from shoot_manager import process_shot, preload_triangle_calib
|
2025-12-30 16:23:17 +08:00
|
|
|
|
|
2025-11-21 17:50:02 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
def laser_calibration_worker():
|
|
|
|
|
|
"""后台线程:持续检测是否需要执行激光校准"""
|
|
|
|
|
|
from laser_manager import laser_manager
|
|
|
|
|
|
from ota_manager import ota_manager
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
logger = logger_manager.logger
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info("[LASER] 激光校准线程启动")
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
while True:
|
2025-12-28 16:22:41 +08:00
|
|
|
|
try:
|
|
|
|
|
|
try:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if ota_manager.ota_in_progress:
|
|
|
|
|
|
time.sleep_ms(200)
|
2025-12-26 11:47:33 +08:00
|
|
|
|
continue
|
2026-01-12 18:06:04 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger = logger_manager.logger
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[LASER] OTA检查异常: {e}")
|
2025-12-30 16:23:17 +08:00
|
|
|
|
time.sleep_ms(200)
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if laser_manager.calibration_active:
|
2026-01-20 11:25:17 +08:00
|
|
|
|
# 调用校准方法,所有逻辑都在 LaserManager 中
|
|
|
|
|
|
result = laser_manager.calibrate_laser_position(timeout_ms=8000, check_sharpness=True)
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-20 11:25:17 +08:00
|
|
|
|
# 如果超时仍未成功,稍微休息一下
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if laser_manager.calibration_active:
|
|
|
|
|
|
time.sleep_ms(300)
|
2025-12-30 09:21:58 +08:00
|
|
|
|
else:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
time.sleep_ms(50)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
# 线程顶层异常捕获,防止线程静默退出
|
|
|
|
|
|
logger = logger_manager.logger
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[LASER] 校准线程异常: {e}")
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
logger.error(traceback.format_exc())
|
|
|
|
|
|
else:
|
|
|
|
|
|
print(f"[LASER] 校准线程异常: {e}")
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
time.sleep_ms(1000) # 等待1秒后继续
|
2025-11-21 17:50:02 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
def cmd_str():
|
|
|
|
|
|
"""主程序入口"""
|
|
|
|
|
|
# ==================== 第一阶段:硬件初始化 ====================
|
|
|
|
|
|
# 按照 main104.py 的顺序,先完成所有硬件初始化
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 1. 引脚功能映射
|
|
|
|
|
|
for pin, func in config.PIN_MAPPINGS.items():
|
2025-12-30 16:23:17 +08:00
|
|
|
|
try:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
pinmap.set_pin_function(pin, func)
|
2025-12-30 16:23:17 +08:00
|
|
|
|
except:
|
|
|
|
|
|
pass
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 2. 初始化硬件对象(UART、I2C、ADC)
|
|
|
|
|
|
hardware_manager.init_uart4g()
|
|
|
|
|
|
hardware_manager.init_bus()
|
|
|
|
|
|
hardware_manager.init_adc()
|
|
|
|
|
|
hardware_manager.init_at_client()
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-02-10 17:52:55 +08:00
|
|
|
|
# 3. 初始化激光模块(串口 + 开机关闭激光防误触发)
|
|
|
|
|
|
laser_manager.init()
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 3. 初始化 INA226 电量监测芯片
|
|
|
|
|
|
init_ina226()
|
2026-01-20 18:40:54 +08:00
|
|
|
|
|
|
|
|
|
|
# 4. 初始化显示和相机
|
2026-01-20 11:25:17 +08:00
|
|
|
|
camera_manager.init_camera(640, 480)
|
2026-04-18 09:33:37 +08:00
|
|
|
|
# camera_manager.init_camera(1280,720)
|
2026-01-20 11:25:17 +08:00
|
|
|
|
camera_manager.init_display()
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# ==================== 第二阶段:软件初始化 ====================
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 1. 初始化日志系统
|
|
|
|
|
|
import logging
|
|
|
|
|
|
logger_manager.init_logging(log_level=logging.DEBUG)
|
|
|
|
|
|
logger = logger_manager.logger
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-02-10 17:52:55 +08:00
|
|
|
|
# 补充:因为初始化的时候,激光会亮,先关了它
|
|
|
|
|
|
# laser_manager.turn_off_laser()
|
|
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 2. 从4G模块同步系统时间(需要 at_client 已初始化)
|
|
|
|
|
|
sync_system_time_from_4g()
|
2026-01-24 11:05:03 +08:00
|
|
|
|
|
2026-04-03 15:40:07 +08:00
|
|
|
|
# 2.1 WiFi 热点配网兜底:仅当 STA 与 4G 均不可用时起 AP + HTTP;提交后删 /boot/wifi.ap、建 wifi.sta 并 reboot
|
|
|
|
|
|
try:
|
|
|
|
|
|
from wifi_config_httpd import maybe_start_wifi_ap_fallback
|
|
|
|
|
|
|
|
|
|
|
|
maybe_start_wifi_ap_fallback(logger)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[WIFI-AP] 兜底配网检测/启动失败: {e}")
|
|
|
|
|
|
|
2026-01-24 11:05:03 +08:00
|
|
|
|
# 2.5. 启动存图 worker 线程(队列 + worker,避免主循环阻塞)
|
|
|
|
|
|
start_save_shot_worker()
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
|
|
|
|
|
# 2.6 预加载三角形标定/坐标文件(避免首次射箭卡顿)
|
|
|
|
|
|
try:
|
|
|
|
|
|
preload_triangle_calib()
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 3. 启动时检查:是否需要恢复备份
|
|
|
|
|
|
pending_path = f"{config.APP_DIR}/ota_pending.json"
|
|
|
|
|
|
if os.path.exists(pending_path):
|
2025-12-30 16:23:17 +08:00
|
|
|
|
try:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
with open(pending_path, 'r', encoding='utf-8') as f:
|
|
|
|
|
|
pending_obj = json.load(f)
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
restart_count = pending_obj.get('restart_count', 0)
|
|
|
|
|
|
max_restarts = pending_obj.get('max_restarts', 3)
|
|
|
|
|
|
backup_dir = pending_obj.get('backup_dir')
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info(f"检测到 ota_pending.json,重启计数: {restart_count}/{max_restarts}")
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if restart_count >= max_restarts:
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[STARTUP] 重启次数 ({restart_count}) 超过阈值 ({max_restarts}),执行恢复...")
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if backup_dir and os.path.exists(backup_dir):
|
|
|
|
|
|
if ota_manager.restore_from_backup(backup_dir):
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info(f"[STARTUP] 已从指定备份恢复: {backup_dir}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info(f"[STARTUP] 指定备份恢复失败,尝试恢复最新备份...")
|
|
|
|
|
|
ota_manager.restore_from_backup(None)
|
|
|
|
|
|
else:
|
|
|
|
|
|
if ota_manager.restore_from_backup(None):
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info(f"[STARTUP] 已从最新备份恢复")
|
|
|
|
|
|
else:
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[STARTUP] 恢复备份失败")
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
try:
|
|
|
|
|
|
os.remove(pending_path)
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info(f"[STARTUP] 已删除 ota_pending.json")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[STARTUP] 删除 pending 文件失败: {e}")
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info(f"[STARTUP] 恢复完成,准备重启系统...")
|
|
|
|
|
|
time.sleep_ms(2000)
|
|
|
|
|
|
os.system("reboot")
|
|
|
|
|
|
return
|
|
|
|
|
|
else:
|
|
|
|
|
|
pending_obj['restart_count'] = restart_count + 1
|
|
|
|
|
|
try:
|
|
|
|
|
|
with open(pending_path, 'w', encoding='utf-8') as f:
|
|
|
|
|
|
json.dump(pending_obj, f)
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info(f"[STARTUP] 已更新重启计数: {pending_obj['restart_count']}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[STARTUP] 更新重启计数失败: {e}")
|
2025-12-30 16:23:17 +08:00
|
|
|
|
except Exception as e:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[STARTUP] 检查 pending 文件时出错: {e}")
|
2025-12-30 16:23:17 +08:00
|
|
|
|
try:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info(f"[STARTUP] pending 文件可能损坏,尝试恢复备份...")
|
|
|
|
|
|
if ota_manager.restore_from_backup(None):
|
|
|
|
|
|
os.remove(pending_path)
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info(f"[STARTUP] 已恢复备份并删除损坏的 pending 文件")
|
|
|
|
|
|
time.sleep_ms(2000)
|
|
|
|
|
|
os.system("reboot")
|
|
|
|
|
|
return
|
2025-12-30 16:23:17 +08:00
|
|
|
|
except:
|
|
|
|
|
|
pass
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 4. 初始化设备ID(network_manager 内部会自动设置 device_id 和 password)
|
|
|
|
|
|
network_manager.read_device_id()
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 5. 创建照片存储目录(如果启用图像保存)
|
|
|
|
|
|
if config.SAVE_IMAGE_ENABLED:
|
|
|
|
|
|
photo_dir = config.PHOTO_DIR
|
|
|
|
|
|
if photo_dir not in os.listdir("/root"):
|
2025-12-30 16:23:17 +08:00
|
|
|
|
try:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
os.mkdir(photo_dir)
|
2025-12-30 16:23:17 +08:00
|
|
|
|
except:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 6. 启动通信与校准线程
|
2026-04-07 17:29:24 +08:00
|
|
|
|
# 若已进入 AP 配网兜底(/boot/wifi.ap),则不启动 TCP 主循环;等待用户配网后 reboot。
|
|
|
|
|
|
try:
|
|
|
|
|
|
if os.path.exists("/boot/wifi.ap"):
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.warning("[NET] 当前处于 AP 配网模式(/boot/wifi.ap 存在),跳过 TCP 主线程启动")
|
|
|
|
|
|
else:
|
|
|
|
|
|
_thread.start_new_thread(network_manager.tcp_main, ())
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[NET] 启动 TCP 主线程失败: {e}")
|
2026-01-22 17:55:11 +08:00
|
|
|
|
if not config.HARDCODE_LASER_POINT:
|
|
|
|
|
|
_thread.start_new_thread(laser_calibration_worker, ())
|
2025-12-30 16:23:17 +08:00
|
|
|
|
|
2026-01-20 18:40:54 +08:00
|
|
|
|
# 7. 加载激光点配置
|
|
|
|
|
|
laser_manager.load_laser_point()
|
|
|
|
|
|
|
2026-03-11 18:19:17 +08:00
|
|
|
|
# 8. 启动空闲计时
|
|
|
|
|
|
hardware_manager.start_idle_timer()
|
|
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info("系统准备完成...")
|
2025-12-30 16:23:17 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
last_adc_trigger = 0
|
2026-02-07 17:09:39 +08:00
|
|
|
|
# 气压采样:减少日志频率(每 N 个点输出一条),避免 logger.debug 拖慢采样
|
|
|
|
|
|
PRESSURE_BATCH_SIZE = 100
|
2026-03-11 18:19:17 +08:00
|
|
|
|
|
2026-02-07 17:09:39 +08:00
|
|
|
|
pressure_buf = []
|
|
|
|
|
|
pressure_sum = 0
|
2026-03-11 18:19:17 +08:00
|
|
|
|
pressure_abs_sum = 0
|
2026-02-07 17:09:39 +08:00
|
|
|
|
pressure_min = 4095
|
|
|
|
|
|
pressure_max = 0
|
|
|
|
|
|
pressure_t0_ms = None
|
2026-03-11 18:19:17 +08:00
|
|
|
|
last_avg_abs = 0
|
2026-02-07 17:09:39 +08:00
|
|
|
|
|
|
|
|
|
|
def _flush_pressure_buf(reason: str):
|
|
|
|
|
|
if not config.AIR_PRESSURE_lOG:
|
|
|
|
|
|
return
|
2026-03-11 18:19:17 +08:00
|
|
|
|
nonlocal pressure_buf, pressure_sum, pressure_min, pressure_max, pressure_t0_ms, logger, pressure_abs_sum, last_avg_abs
|
2026-02-07 17:09:39 +08:00
|
|
|
|
if not pressure_buf:
|
|
|
|
|
|
return
|
|
|
|
|
|
t1_ms = time.ticks_ms()
|
|
|
|
|
|
n = len(pressure_buf)
|
|
|
|
|
|
avg = (pressure_sum / n) if n else 0
|
2026-03-11 18:19:17 +08:00
|
|
|
|
avg_abs = (pressure_abs_sum / n) if n else 0
|
2026-02-07 17:09:39 +08:00
|
|
|
|
# 一行输出:方便后处理画曲线;同时带上统计信息便于快速看波峰
|
|
|
|
|
|
line = (
|
|
|
|
|
|
f"[气压批量] reason={reason} "
|
|
|
|
|
|
f"t0={pressure_t0_ms} t1={t1_ms} n={n} "
|
2026-03-11 18:19:17 +08:00
|
|
|
|
f"min={pressure_min} max={pressure_max} avg={avg:.1f} avg_abs={avg_abs:.3f} "
|
2026-02-07 17:09:39 +08:00
|
|
|
|
f"values={','.join(map(str, pressure_buf))}"
|
2026-03-11 18:19:17 +08:00
|
|
|
|
f" convert value (kpa): {(max(pressure_buf, key=lambda x: x[1])[1] - last_avg_abs) / (5 - 2.5) * config.AIR_PRESSURE_HARDWARE_MAX:.1f}"
|
2026-02-07 17:09:39 +08:00
|
|
|
|
)
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.debug(line)
|
|
|
|
|
|
else:
|
|
|
|
|
|
print(line)
|
|
|
|
|
|
pressure_buf = []
|
|
|
|
|
|
pressure_sum = 0
|
2026-03-11 18:19:17 +08:00
|
|
|
|
pressure_abs_sum = 0
|
2026-02-07 17:09:39 +08:00
|
|
|
|
pressure_min = 4095
|
|
|
|
|
|
pressure_max = 0
|
|
|
|
|
|
pressure_t0_ms = None
|
2026-03-11 18:19:17 +08:00
|
|
|
|
last_avg_abs = avg_abs
|
2025-12-30 16:23:17 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 主循环:检测扳机触发 → 拍照 → 分析 → 上报
|
|
|
|
|
|
while not app.need_exit():
|
2025-12-30 16:23:17 +08:00
|
|
|
|
try:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
current_time = time.ticks_ms()
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# OTA 期间暂停相机预览
|
|
|
|
|
|
try:
|
|
|
|
|
|
if ota_manager.ota_in_progress:
|
|
|
|
|
|
time.sleep_ms(250)
|
|
|
|
|
|
continue
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger = logger_manager.logger
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[MAIN] OTA检查异常: {e}")
|
|
|
|
|
|
time.sleep_ms(250)
|
2025-12-30 16:23:17 +08:00
|
|
|
|
continue
|
2026-03-11 18:19:17 +08:00
|
|
|
|
|
|
|
|
|
|
# 不在 OTA 状态下,检测是否空闲足够长,自动关机
|
|
|
|
|
|
# print(f"[MAIN] 空闲时间: {hardware_manager.get_idle_time_in_sec() }秒")
|
|
|
|
|
|
# print(f"配置关机时间:{config.AUTO_POWER_OFF_IN_SECONDS} 秒")
|
|
|
|
|
|
if hardware_manager.get_idle_time_in_sec() > config.AUTO_POWER_OFF_IN_SECONDS:
|
|
|
|
|
|
logger.info("[MAIN] 超过设定时间未检测活动,自动关机")
|
|
|
|
|
|
network_manager.safe_enqueue({"poweroff": "超过设定时间未检测活动,自动关机"}, 2)
|
|
|
|
|
|
time.sleep_ms(100)
|
|
|
|
|
|
hardware_manager.power_off()
|
|
|
|
|
|
time.sleep_ms(2000) # 让关机指令生效
|
|
|
|
|
|
break
|
|
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 读取ADC值(扳机检测)
|
2025-12-30 16:23:17 +08:00
|
|
|
|
try:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if network_manager.manual_trigger_flag:
|
|
|
|
|
|
network_manager.clear_manual_trigger()
|
|
|
|
|
|
adc_val = config.ADC_TRIGGER_THRESHOLD + 1
|
2026-03-11 18:19:17 +08:00
|
|
|
|
adc_abs_val = 10
|
2026-01-12 18:06:04 +08:00
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info("[TEST] TCP命令触发射箭")
|
|
|
|
|
|
else:
|
|
|
|
|
|
adc_val = hardware_manager.adc_obj.read()
|
2026-03-11 18:19:17 +08:00
|
|
|
|
adc_abs_val = hardware_manager.adc_obj.read_vol()
|
2025-12-30 16:23:17 +08:00
|
|
|
|
except Exception as e:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
logger = logger_manager.logger
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[MAIN] ADC读取异常: {e}")
|
|
|
|
|
|
time.sleep_ms(100)
|
|
|
|
|
|
continue
|
2026-02-07 17:09:39 +08:00
|
|
|
|
|
|
|
|
|
|
# ====== 气压采样缓存(每次循环都记录,批量输出日志)======
|
|
|
|
|
|
if pressure_t0_ms is None:
|
|
|
|
|
|
pressure_t0_ms = current_time
|
2026-03-11 18:19:17 +08:00
|
|
|
|
pressure_buf.append((adc_val, adc_abs_val))
|
2026-02-07 17:09:39 +08:00
|
|
|
|
pressure_sum += adc_val
|
2026-03-11 18:19:17 +08:00
|
|
|
|
pressure_abs_sum += adc_abs_val
|
2026-02-07 17:09:39 +08:00
|
|
|
|
if adc_val < pressure_min:
|
|
|
|
|
|
pressure_min = adc_val
|
|
|
|
|
|
if adc_val > pressure_max:
|
|
|
|
|
|
pressure_max = adc_val
|
|
|
|
|
|
if len(pressure_buf) >= PRESSURE_BATCH_SIZE:
|
|
|
|
|
|
_flush_pressure_buf("batch")
|
|
|
|
|
|
# if adc_val >= 2000:
|
|
|
|
|
|
# print(f"adc :{adc_val}")
|
|
|
|
|
|
if adc_val >= config.ADC_TRIGGER_THRESHOLD:
|
2026-03-11 18:19:17 +08:00
|
|
|
|
hardware_manager.start_idle_timer() # 重新计时
|
2026-01-12 18:06:04 +08:00
|
|
|
|
diff_ms = current_time - last_adc_trigger
|
|
|
|
|
|
if diff_ms < 3000:
|
2026-04-03 11:24:29 +08:00
|
|
|
|
logger.info(f"[MAIN] 扳机触发过于频繁, {diff_ms}ms")
|
2026-01-12 18:06:04 +08:00
|
|
|
|
continue
|
|
|
|
|
|
last_adc_trigger = current_time
|
2026-02-07 17:09:39 +08:00
|
|
|
|
# 触发前先把缓存刷出来,避免波形被长耗时处理截断
|
|
|
|
|
|
_flush_pressure_buf("before_trigger")
|
2026-02-10 17:52:55 +08:00
|
|
|
|
|
2026-04-18 09:33:37 +08:00
|
|
|
|
try:
|
|
|
|
|
|
process_shot(adc_val)
|
2025-12-30 16:23:17 +08:00
|
|
|
|
except Exception as e:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
logger = logger_manager.logger
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[MAIN] 图像处理异常: {e}")
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
logger.error(traceback.format_exc())
|
|
|
|
|
|
time.sleep_ms(100)
|
|
|
|
|
|
continue
|
|
|
|
|
|
else:
|
2026-02-07 17:09:39 +08:00
|
|
|
|
if config.SHOW_CAMERA_PHOTO_WHILE_SHOOTING:
|
|
|
|
|
|
try:
|
|
|
|
|
|
camera_manager.show(camera_manager.read_frame())
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger = logger_manager.logger
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[MAIN] 显示异常: {e}")
|
2026-03-11 18:19:17 +08:00
|
|
|
|
time.sleep_ms(5)
|
2025-12-30 16:23:17 +08:00
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 主循环的顶层异常捕获,防止程序静默退出
|
|
|
|
|
|
logger = logger_manager.logger
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[MAIN] 主循环异常: {e}")
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
logger.error(traceback.format_exc())
|
|
|
|
|
|
else:
|
|
|
|
|
|
print(f"[MAIN] 主循环异常: {e}")
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
traceback.print_exc()
|
2026-02-07 17:09:39 +08:00
|
|
|
|
# 异常发生时尽量把缓存刷盘,方便定位问题
|
|
|
|
|
|
try:
|
|
|
|
|
|
_flush_pressure_buf("exception")
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
2026-01-12 18:06:04 +08:00
|
|
|
|
time.sleep_ms(1000) # 等待1秒后继续
|
2025-12-30 16:23:17 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-01-20 11:25:17 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
# 主程序入口
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
try:
|
|
|
|
|
|
cmd_str()
|
2026-01-20 11:25:17 +08:00
|
|
|
|
|
|
|
|
|
|
# 用于调用测试函数
|
|
|
|
|
|
# test()
|
|
|
|
|
|
|
|
|
|
|
|
# 用于测试图片清晰度
|
|
|
|
|
|
# 方式1: 测试单张图片
|
|
|
|
|
|
# test_sharpness("/root/phot/image.bmp")
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-20 11:25:17 +08:00
|
|
|
|
# 方式2: 测试目录下所有图片
|
|
|
|
|
|
# test_sharpness("/root/phot")
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-20 11:25:17 +08:00
|
|
|
|
# 方式3: 使用默认路径(config.PHOTO_DIR)
|
|
|
|
|
|
# test_sharpness("/root/phot/")
|
2026-04-18 09:33:37 +08:00
|
|
|
|
|
2026-01-20 11:25:17 +08:00
|
|
|
|
# 用于测试激光点清晰度
|
|
|
|
|
|
# 方式1: 测试单张图片
|
|
|
|
|
|
# test_laser_point_sharpness("/root/phot/image.bmp")
|
|
|
|
|
|
# 方式2: 测试目录下所有图片
|
|
|
|
|
|
# test_laser_point_sharpness("/root/phot")
|
|
|
|
|
|
# 方式3: 使用默认路径(config.PHOTO_DIR)
|
|
|
|
|
|
# test_laser_point_sharpness("/root/phot/")
|
|
|
|
|
|
|
2026-01-12 18:06:04 +08:00
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
|
logger = logger_manager.logger
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.info("[MAIN] 收到中断信号,程序退出")
|
|
|
|
|
|
logger_manager.stop_logging()
|
2025-12-28 16:22:41 +08:00
|
|
|
|
except Exception as e:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
logger = logger_manager.logger
|
|
|
|
|
|
if logger:
|
|
|
|
|
|
logger.error(f"[MAIN] 程序异常退出: {e}")
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
logger.error(traceback.format_exc())
|
2025-11-21 17:50:02 +08:00
|
|
|
|
else:
|
2026-01-12 18:06:04 +08:00
|
|
|
|
print(f"[MAIN] 程序异常退出: {e}")
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
logger_manager.stop_logging()
|
|
|
|
|
|
raise # 重新抛出异常,让系统知道程序异常退出
|