v1.1.5
This commit is contained in:
190
main.py
190
main.py
@@ -23,10 +23,11 @@ from logger_manager import logger_manager
|
||||
from time_sync import sync_system_time_from_4g
|
||||
from power import init_ina226, get_bus_voltage, voltage_to_percent
|
||||
from laser_manager import laser_manager
|
||||
from vision import detect_circle_v3, estimate_distance, compute_laser_position, save_shot_image, save_calibration_image
|
||||
from vision import detect_circle_v3, estimate_distance, save_shot_image, save_calibration_image
|
||||
from network import network_manager
|
||||
from ota_manager import ota_manager
|
||||
from hardware import hardware_manager
|
||||
from camera_manager import camera_manager
|
||||
|
||||
|
||||
# def laser_calibration_worker():
|
||||
@@ -104,10 +105,8 @@ from hardware import hardware_manager
|
||||
# time.sleep_ms(1000) # 等待1秒后继续
|
||||
def laser_calibration_worker():
|
||||
"""后台线程:持续检测是否需要执行激光校准"""
|
||||
from maix import camera
|
||||
from laser_manager import laser_manager
|
||||
from ota_manager import ota_manager
|
||||
from vision import save_calibration_image # 添加导入
|
||||
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
@@ -127,47 +126,10 @@ def laser_calibration_worker():
|
||||
continue
|
||||
|
||||
if laser_manager.calibration_active:
|
||||
cam = None
|
||||
try:
|
||||
cam = camera.Camera(640, 480)
|
||||
start = time.ticks_ms()
|
||||
timeout_ms = 8000
|
||||
while laser_manager.calibration_active and time.ticks_diff(time.ticks_ms(), start) < timeout_ms:
|
||||
frame = cam.read()
|
||||
pos = laser_manager.find_red_laser(frame)
|
||||
if pos:
|
||||
# 保存校准图像(带标注)
|
||||
try:
|
||||
save_calibration_image(frame, pos)
|
||||
except Exception as e:
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.error(f"[LASER] 保存校准图像失败: {e}")
|
||||
|
||||
laser_manager.set_calibration_result(pos)
|
||||
laser_manager.stop_calibration()
|
||||
laser_manager.save_laser_point(pos)
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.info(f"✅ 后台校准成功: {pos}")
|
||||
break
|
||||
time.sleep_ms(60)
|
||||
except Exception as e:
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.error(f"[LASER] calibration error: {e}")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
time.sleep_ms(200)
|
||||
finally:
|
||||
try:
|
||||
if cam is not None:
|
||||
del cam
|
||||
except Exception as e:
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.error(f"[LASER] 释放相机资源异常: {e}")
|
||||
|
||||
# 调用校准方法,所有逻辑都在 LaserManager 中
|
||||
result = laser_manager.calibrate_laser_position(timeout_ms=8000, check_sharpness=True)
|
||||
|
||||
# 如果超时仍未成功,稍微休息一下
|
||||
if laser_manager.calibration_active:
|
||||
time.sleep_ms(300)
|
||||
else:
|
||||
@@ -211,8 +173,8 @@ def cmd_str():
|
||||
laser_manager.load_laser_point()
|
||||
|
||||
# 5. 初始化显示和相机
|
||||
disp = display.Display()
|
||||
cam = camera.Camera(640, 480)
|
||||
camera_manager.init_camera(640, 480)
|
||||
camera_manager.init_display()
|
||||
|
||||
# ==================== 第二阶段:软件初始化 ====================
|
||||
|
||||
@@ -358,8 +320,37 @@ def cmd_str():
|
||||
last_adc_trigger = current_time
|
||||
|
||||
try:
|
||||
frame = cam.read()
|
||||
laser_point = laser_manager.laser_point
|
||||
frame = camera_manager.read_frame()
|
||||
|
||||
# 先检测靶心以获取距离(用于计算激光点)
|
||||
# 第一次检测不使用激光点,仅用于获取距离
|
||||
result_img_temp, center_temp, radius_temp, method_temp, best_radius1_temp, ellipse_params_temp = detect_circle_v3(frame, None)
|
||||
|
||||
# 计算距离
|
||||
distance_m = estimate_distance(best_radius1_temp) if best_radius1_temp else None
|
||||
|
||||
# 根据距离动态计算激光点坐标(如果未检测到靶心,使用硬编码值或默认值)
|
||||
laser_point_method = None # 记录激光点选择方法
|
||||
if config.HARDCODE_LASER_POINT:
|
||||
# 硬编码模式:使用硬编码值
|
||||
laser_point = laser_manager.laser_point
|
||||
laser_point_method = "hardcode"
|
||||
elif laser_manager.has_calibrated_point():
|
||||
# 假如校准过,并且有保存值,使用校准值
|
||||
laser_point = laser_manager.laser_point
|
||||
laser_point_method = "calibrated"
|
||||
logger_manager.logger.info(f"[算法] 使用校准值: {laser_manager.laser_point}")
|
||||
elif distance_m and distance_m > 0:
|
||||
# 动态计算模式:根据距离计算激光点
|
||||
laser_point = laser_manager.calculate_laser_point_from_distance(distance_m)
|
||||
laser_point_method = "dynamic"
|
||||
logger_manager.logger.info(f"[算法] 使用比例尺: {laser_point}")
|
||||
else:
|
||||
# 未检测到靶心且未启用硬编码:使用默认激光点或从配置文件加载
|
||||
laser_point = laser_manager.laser_point
|
||||
laser_point_method = "default"
|
||||
logger_manager.logger.info(f"[算法] 使用默认值: {laser_point}")
|
||||
|
||||
if laser_point is None:
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
@@ -383,13 +374,13 @@ def cmd_str():
|
||||
)
|
||||
frame.draw_circle(int(x), int(y), 1, color, config.LASER_THICKNESS)
|
||||
|
||||
# 检测靶心
|
||||
# 重新检测靶心(使用计算出的激光点)
|
||||
result_img, center, radius, method, best_radius1, ellipse_params = detect_circle_v3(frame, laser_point)
|
||||
disp.show(result_img)
|
||||
camera_manager.show(result_img)
|
||||
|
||||
# 计算偏移与距离(如果检测到靶心)
|
||||
if center and radius:
|
||||
dx, dy = compute_laser_position(center, (x, y), radius, method)
|
||||
dx, dy = laser_manager.compute_laser_position(center, (x, y), radius, method)
|
||||
distance_m = estimate_distance(best_radius1)
|
||||
else:
|
||||
# 未检测到靶心
|
||||
@@ -401,27 +392,34 @@ def cmd_str():
|
||||
# 快速激光测距(激光一闪而过,约500-600ms)
|
||||
laser_distance_m = None
|
||||
laser_signal_quality = 0
|
||||
try:
|
||||
result = laser_manager.quick_measure_distance()
|
||||
if isinstance(result, tuple) and len(result) == 2:
|
||||
laser_distance_m, laser_signal_quality = result
|
||||
else:
|
||||
# 向后兼容:如果返回的是单个值
|
||||
laser_distance_m = result if isinstance(result, (int, float)) else 0.0
|
||||
laser_signal_quality = 0
|
||||
if logger:
|
||||
if laser_distance_m > 0:
|
||||
logger.info(f"[MAIN] 激光测距成功: {laser_distance_m:.3f} m, 信号质量: {laser_signal_quality}")
|
||||
else:
|
||||
logger.warning("[MAIN] 激光测距失败或返回0")
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.error(f"[MAIN] 激光测距异常: {e}")
|
||||
# try:
|
||||
# result = laser_manager.quick_measure_distance()
|
||||
# if isinstance(result, tuple) and len(result) == 2:
|
||||
# laser_distance_m, laser_signal_quality = result
|
||||
# else:
|
||||
# # 向后兼容:如果返回的是单个值
|
||||
# laser_distance_m = result if isinstance(result, (int, float)) else 0.0
|
||||
# laser_signal_quality = 0
|
||||
# if logger:
|
||||
# if laser_distance_m > 0:
|
||||
# logger.info(f"[MAIN] 激光测距成功: {laser_distance_m:.3f} m, 信号质量: {laser_signal_quality}")
|
||||
# else:
|
||||
# logger.warning("[MAIN] 激光测距失败或返回0")
|
||||
# except Exception as e:
|
||||
# if logger:
|
||||
# logger.error(f"[MAIN] 激光测距异常: {e}")
|
||||
|
||||
# 读取电量
|
||||
voltage = get_bus_voltage()
|
||||
battery_percent = voltage_to_percent(voltage)
|
||||
|
||||
# 生成射箭ID
|
||||
from shot_id_generator import shot_id_generator
|
||||
shot_id = shot_id_generator.generate_id() # 不需要使用device_id
|
||||
|
||||
if logger:
|
||||
logger.info(f"[MAIN] 射箭ID: {shot_id}")
|
||||
|
||||
# 保存图像(无论是否检测到靶心都保存)
|
||||
# save_shot_image 函数会确保绘制激光十字线和检测标注(如果有)
|
||||
# 如果未检测到靶心,文件名会包含 "no_target" 标识
|
||||
@@ -433,11 +431,13 @@ def cmd_str():
|
||||
ellipse_params,
|
||||
(x, y),
|
||||
distance_m,
|
||||
shot_id=shot_id,
|
||||
photo_dir=config.PHOTO_DIR if config.SAVE_IMAGE_ENABLED else None
|
||||
)
|
||||
|
||||
# 构造上报数据
|
||||
inner_data = {
|
||||
"shot_id": shot_id, # 射箭ID,用于关联图片和服务端日志
|
||||
"x": float(dx) if dx is not None else 200.0,
|
||||
"y": float(dy) if dy is not None else 200.0,
|
||||
"r": 90.0,
|
||||
@@ -445,18 +445,39 @@ def cmd_str():
|
||||
"d_laser": round((laser_distance_m or 0.0) * 100), # 激光测距值(厘米)
|
||||
"d_laser_quality": laser_signal_quality, # 激光测距信号质量
|
||||
"m": method if method else "no_target",
|
||||
"adc": adc_val
|
||||
"adc": adc_val,
|
||||
# 新增字段:激光点选择方法
|
||||
"laser_method": laser_point_method, # 激光点选择方法:hardcode/calibrated/dynamic/default
|
||||
# 激光点坐标(像素)
|
||||
"target_x": float(x), # 激光点 X 坐标(像素)
|
||||
"target_y": float(y), # 激光点 Y 坐标(像素)
|
||||
}
|
||||
|
||||
# 添加椭圆参数(如果存在)
|
||||
if ellipse_params:
|
||||
(ell_center, (width, height), angle) = ellipse_params
|
||||
inner_data["ellipse_major_axis"] = float(max(width, height)) # 长轴(像素)
|
||||
inner_data["ellipse_minor_axis"] = float(min(width, height)) # 短轴(像素)
|
||||
inner_data["ellipse_angle"] = float(angle) # 椭圆角度(度)
|
||||
inner_data["ellipse_center_x"] = float(ell_center[0]) # 椭圆中心 X 坐标(像素)
|
||||
inner_data["ellipse_center_y"] = float(ell_center[1]) # 椭圆中心 Y 坐标(像素)
|
||||
else:
|
||||
inner_data["ellipse_major_axis"] = None
|
||||
inner_data["ellipse_minor_axis"] = None
|
||||
inner_data["ellipse_angle"] = None
|
||||
inner_data["ellipse_center_x"] = None
|
||||
inner_data["ellipse_center_y"] = None
|
||||
|
||||
report_data = {"cmd": 1, "data": inner_data}
|
||||
network_manager.safe_enqueue(report_data, msg_type=2, high=True)
|
||||
if logger:
|
||||
if center and radius:
|
||||
logger.info("射箭事件已加入发送队列(已检测到靶心)")
|
||||
logger.info(f"射箭事件已加入发送队列(已检测到靶心),ID: {shot_id}")
|
||||
else:
|
||||
logger.info("射箭事件已加入发送队列(未检测到靶心,已保存图像)")
|
||||
logger.info(f"射箭事件已加入发送队列(未检测到靶心,已保存图像),ID: {shot_id}")
|
||||
|
||||
# 闪一下激光(射箭反馈)
|
||||
# laser_manager.flash_laser(1000)
|
||||
laser_manager.flash_laser(1000)
|
||||
|
||||
time.sleep_ms(100)
|
||||
except Exception as e:
|
||||
@@ -469,7 +490,7 @@ def cmd_str():
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
disp.show(cam.read())
|
||||
camera_manager.show(camera_manager.read_frame())
|
||||
except Exception as e:
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
@@ -490,10 +511,35 @@ def cmd_str():
|
||||
time.sleep_ms(1000) # 等待1秒后继续
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 主程序入口
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
cmd_str()
|
||||
|
||||
# 用于调用测试函数
|
||||
# test()
|
||||
|
||||
# 用于测试图片清晰度
|
||||
# 方式1: 测试单张图片
|
||||
# test_sharpness("/root/phot/image.bmp")
|
||||
|
||||
# 方式2: 测试目录下所有图片
|
||||
# test_sharpness("/root/phot")
|
||||
|
||||
# 方式3: 使用默认路径(config.PHOTO_DIR)
|
||||
# test_sharpness("/root/phot/")
|
||||
|
||||
# 用于测试激光点清晰度
|
||||
# 方式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/")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
|
||||
Reference in New Issue
Block a user