新版Windows 10曝光全新的Control Center

今天早些时候,微软面向桌面端的Fast通道用户发布了Windows 10 Build 16199更新,也是秋季创作者更新的最新预览版。除了官方博客中介绍的各项新功能之外,外媒发现在Windows 10新版任务栏上添加了全新的“设置”图标,以便于让用户更方便的进行设置。

20170518115126888.gif

20170518115127174.gif

不过点击这个按钮之后并不是直接跳出设置页面,而是跳出全新的“Control Center”,用户能够进行亮度控制、激活平板模式等等,并且能够对布局进行重新排列。更大的Control Center提供了更大的空间来布局Quick Action按钮,为网络、联系人和项目提供了更大更明晰的标签。

1495066367_windows-10-control-center_story.jpg


本文转自d1net(转载)
import sensor, image, time, math from pyb import Pin, Timer # 如果不需要UART通信,可以注释掉这行 from machine import UART ############################################################# # 全局常量定义 - 集中管理所有配置参数 ############################################################# # 调试和性能选项 DEBUG = True # 设置为True可以在串行终端显示信息 DRAW_DETECTION = False # 设置为False可以减少图像处理负担,不在显示窗口显示检测框 SLEEP_MS = 20 # 循环延时(毫秒),增大可降低CPU使用率,但会降低响应速度 USE_MORPHOLOGY = True # 设置为False可以跳过形态学操作,提高性能 RESOLUTION = sensor.QQVGA # 可选: sensor.QQVGA (160x120) 或 sensor.QVGA (320x240) SHOW_STATS = False # 不在屏幕上显示FPS和其他信息 AUTO_THRESHOLD = True # 设置为True启用自适应阈值 THRESHOLD_OFFSET = 10 # 自适应阈值偏移量 MAX_LOST_FRAMES = 30 # 目标丢失多少帧后执行特定操作 # 图像处理参数 BINARY_THRESHOLD = [(0, 70)] # 对于黑色矩形,阈值可能需要调整 TEMPLATE_MATCH_THRESHOLD = 0.65 # 模板匹配阈值 TEMPLATE_MATCH_STEP = 8 # 模板匹配步长,增大可提高性能但降低精度 BLOB_AREA_THRESHOLD = 100 # Blob检测面积阈值 BLOB_PIXELS_THRESHOLD = 100 # Blob检测像素阈值 BLOB_MERGE_MARGIN = 5 # Blob合并边距 RECT_THRESHOLD = 1000 # 矩形检测阈值 # PID控制参数 KP = 0.1 # 比例系数 KI = 0.01 # 积分系数 KD = 0.05 # 微分系数 ERROR_SUM_MAX = 500 # 积分项最大值 ERROR_SUM_MIN = -500 # 积分项最小值 PW_TO_ANGLE = 0.09 # 脉冲宽度到角度的转换系数 (180/2000) # 舵机参数 SERVO_MIN_PW = 500 # 最小脉冲宽度 (0度) SERVO_MAX_PW = 2500 # 最大脉冲宽度 (180度) SERVO_FREQ = 50 # 舵机PWM频率 # 搜索模式参数 SEARCH_DWELL_FRAMES = 10 # 每个搜索位置停留的帧数 # 预定义搜索位置 - 使用常量避免重复创建 SEARCH_POSITIONS = [ (90, 90), # 中心 (70, 70), # 左上 (110, 70), # 右上 (110, 110), # 右下 (70, 110) # 左下 ] ############################################################# # 系统初始化函数 ############################################################# def init_camera(): """初始化并优化摄像头设置""" sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) # 使用灰度模式 sensor.set_framesize(RESOLUTION) sensor.skip_frames(time=2000) # 等待设置生效 # 优化自动设置 sensor.set_auto_gain(False) # 关闭自动增益 sensor.set_auto_whitebal(False) # 关闭自动白平衡 sensor.set_auto_exposure(False, exposure_us=10000) # 固定曝光时间 # 优化图像设置 sensor.set_brightness(0) sensor.set_contrast(0) # 保持镜像设置 sensor.set_hmirror(False) sensor.set_vflip(False) def init_servos(): """初始化舵机控制""" # 创建两个不同的定时器实例 tim_pan = Timer(2, freq=SERVO_FREQ) # 使用常量定义PWM频率 tim_tilt = Timer(4, freq=SERVO_FREQ) # 水平舵机(Pan) - 控制左右旋转 pan_servo = tim_pan.channel(1, Timer.PWM, pin=Pin("P6")) # 垂直舵机(Tilt) - 控制上下旋转 tilt_servo = tim_tilt.channel(1, Timer.PWM, pin=Pin("P8")) return pan_servo, tilt_servo def load_template(): """加载模板图像,使用异常处理确保程序不会崩溃""" try: template = image.Image("bazi.pgm") if DEBUG: print("成功加载模板图像 bazi.pgm") return template except Exception as e: if DEBUG: print("加载模板图像失败:", e) return None # 优化的舵机控制函数 def set_servo_angle(ch, angle): """ 设置舵机角度 (0-180度) 使用位运算和预计算常量优化计算 """ # 使用位运算优化限制角度范围 angle = min(180, max(0, angle)) # 使用预计算的常量和整数运算优化脉冲宽度计算 # 0.5ms-2.5ms脉冲范围 (500-2500) # 使用整数运算然后转换为整数,避免浮点运算 pulse = SERVO_MIN_PW + int((angle * (SERVO_MAX_PW - SERVO_MIN_PW)) / 180) # 直接设置脉冲宽度 ch.pulse_width(pulse) ############################################################# # PID控制函数 ############################################################# def apply_pid_control(current_pos, center_pos, error_sum, last_error): """ 应用PID控制算法 优化计算效率,减少内存使用 参数: current_pos: 当前位置 center_pos: 目标位置 error_sum: 累计误差 last_error: 上一次误差 返回: (输出值, 更新后的累计误差, 当前误差) """ # 计算误差(使用减法而不是乘以-1,更高效) # 负号是因为舵机方向与坐标系方向相反 error = center_pos - current_pos # 更新积分项 - 使用就地操作符 error_sum += error # 使用位运算优化限制积分项 # 使用常量避免重复计算 error_sum = min(ERROR_SUM_MAX, max(ERROR_SUM_MIN, error_sum)) # 计算微分项 - 直接使用减法 error_diff = error - last_error # 使用预计算的乘法减少浮点运算 # 可以考虑使用整数运算然后再转换为浮点数 output = (KP * error) + (KI * error_sum) + (KD * error_diff) # 使用元组返回多个值,减少临时变量 return output, error_sum, error ############################################################# # 系统初始化 ############################################################# # 初始化摄像头 init_camera() # 加载模板图像 template = load_template() # 初始化舵机 pan_servo, tilt_servo = init_servos() # 设置初始位置:90度(中间位置) pan_angle = 90 tilt_angle = 90 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) time.sleep(1) # 等待舵机到位 # PID控制参数 KP = 0.1 # 比例系数 KI = 0.01 # 积分系数 KD = 0.05 # 微分系数 # PID控制变量 pan_error_sum = 0 tilt_error_sum = 0 last_pan_error = 0 last_tilt_error = 0 # 二值化阈值 - 调整这个值以适应您的环境光线条件 BINARY_THRESHOLD = [(0, 70)] # 对于黑色矩形,阈值可能需要调整 ############################################################# # 辅助函数 ############################################################# def print_config(): """显示当前配置信息""" if DEBUG: print("=== 激光点追踪系统配置 ===") print("分辨率: {}".format("160x120" if RESOLUTION == sensor.QQVGA else "320x240")) print("二值化阈值: {}".format("自适应 (偏移量={})".format(THRESHOLD_OFFSET) if AUTO_THRESHOLD else BINARY_THRESHOLD)) print("使用形态学操作: {}".format(USE_MORPHOLOGY)) print("绘制检测结果: {}".format(DRAW_DETECTION)) print("显示统计信息: {}".format(SHOW_STATS)) print("循环延时: {}ms".format(SLEEP_MS)) print("PID参数: KP={}, KI={}, KD={}".format(KP, KI, KD)) print("目标丢失处理: {}帧后启动搜索".format(MAX_LOST_FRAMES)) print("=========================") def process_image(img, need_stats=False): """ 处理图像:二值化和形态学操作 优化内存使用和计算效率 参数: img: 要处理的图像 need_stats: 是否需要计算图像统计信息 返回: 处理后的图像 """ # 获取统计信息(仅在需要时) if need_stats: stats = img.get_statistics() if DEBUG and SHOW_STATS: print("均值: {:.1f}, 中值: {}, 标准差: {:.1f}".format( stats.mean(), stats.median(), stats.stdev())) # 使用自适应阈值或固定阈值进行二值化 if AUTO_THRESHOLD and need_stats: threshold_value = max(10, min(250, stats.mean() - THRESHOLD_OFFSET)) thresholds = [(0, threshold_value)] else: thresholds = BINARY_THRESHOLD # 二值化处理 img.binary(thresholds) # 可选的形态学操作 if USE_MORPHOLOGY: img.erode(1) img.dilate(1) return img # 主循环 ############################################################# # 主循环 ############################################################# # 创建时钟对象来跟踪FPS clock = time.clock() # 显示初始配置 print_config() # 初始化追踪状态变量 lost_frames_count = 0 # 目标丢失计数器 searching = False # 是否正在执行搜索 search_state = 0 # 搜索模式的状态 search_step_counter = 0 # 搜索步骤的计数器 while True: clock.tick() # 更新FPS计时器 # 直接处理单幅图像,不保留副本 img = sensor.snapshot() # 预先检查是否需要统计信息 need_stats = (DEBUG and SHOW_STATS) or AUTO_THRESHOLD or template # 获取统计信息(在原图上) if need_stats: stats = img.get_statistics() if DEBUG and SHOW_STATS: print("亮度均值: {:.1f}, 标准差: {:.1f}".format(stats.mean(), stats.stdev())) # 仅当需要时才进行二值化 if AUTO_THRESHOLD or BINARY_THRESHOLD: threshold = stats.mean() - THRESHOLD_OFFSET if AUTO_THRESHOLD else BINARY_THRESHOLD[0][1] img.binary([(0, max(10, min(250, threshold)))]) if USE_MORPHOLOGY: img.erode(1) img.dilate(1) # 图像处理结果直接存储在img中 # 不再需要DISPLAY_RAW切换,所有处理都在原图进行 # 优化模板匹配 - 减少计算和内存使用 template_matched = False if template and need_stats: # 直接获取统计信息 current_stats = img.get_statistics() # 使用快速条件检查跳过不必要的模板匹配 poor_image_quality = (current_stats.mean() > 200 or current_stats.mean() < 20 or (current_stats.max() - current_stats.min()) < 30) if poor_image_quality: if DEBUG: print("图像质量不佳,跳过模板匹配") else: try: # 使用更高效的模板匹配参数 # 增加step值减少计算量,使用更简单的搜索方法 r = img.find_template(template, 0.65, step=8, search=image.SEARCH_DS) if r: # 找到匹配 - 使用元组解包直接获取坐标 x, y, w, h = r # 预计算中心点和图像中心 center_x = x + (w >> 1) # 使用位移代替除法 center_y = y + (h >> 1) img_center_x = img.width() >> 1 img_center_y = img.height() >> 1 # 重置状态变量 - 使用一行代码减少指令数 lost_frames_count = search_state = search_step_counter = 0 searching = False # 标记模板匹配成功 template_matched = True # 使用我们之前定义的PID控制函数 # 计算与图像中心的偏差 dx = center_x - img_center_x dy = center_y - img_center_y # 应用PID控制 pan_output, pan_error_sum, last_pan_error = apply_pid_control( center_x, img_center_x, pan_error_sum, last_pan_error) tilt_output, tilt_error_sum, last_tilt_error = apply_pid_control( center_y, img_center_y, tilt_error_sum, last_tilt_error) # 更新舵机角度并限制范围 pan_angle = max(0, min(180, pan_angle + pan_output)) tilt_angle = max(0, min(180, tilt_angle + tilt_output)) # 设置舵机位置 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) # 只在调试模式下打印信息 if DEBUG: print("FPS: {:.1f}, 模板匹配中心: ({}, {}), 舵机角度: ({:.1f}, {:.1f})".format( clock.fps(), center_x, center_y, pan_angle, tilt_angle)) elif DEBUG: print("模板匹配失败") except Exception: # 简化异常处理,不打印详细错误信息以减少开销 if DEBUG: print("模板匹配错误") # 优化阈值计算 - 减少计算和内存使用 threshold_value = 0 if AUTO_THRESHOLD and need_stats: # 直接获取统计信息 current_stats = img.get_statistics() # 简化阈值计算 threshold_value = max(10, min(250, current_stats.mean() - THRESHOLD_OFFSET)) if DEBUG: print("自适应阈值: {}".format(threshold_value)) else: # 使用固定阈值 threshold_value = BINARY_THRESHOLD[0][1] # 优化形态学操作 - 只在必要时执行 if USE_MORPHOLOGY: # 直接使用open操作,减少内存使用 img.open(1) # 反转图像以便黑色矩形变为白色(如果需要) # binary_img.invert() # 优化检测逻辑 - 减少异常处理和条件判断 rects = [] blobs = [] # 使用标志变量而不是异常处理来控制流程 rect_detection_success = False rects = [] # 尝试矩形检测 try: rects = img.find_rects(threshold=1000) rect_detection_success = len(rects) > 0 if DEBUG and rect_detection_success: print("使用find_rects检测到", len(rects), "个矩形") except Exception: # 简化异常处理,不打印详细错误信息以减少开销 rect_detection_success = False if DEBUG: print("切换到find_blobs作为备选") # 如果矩形检测失败,尝试blob检测 if not rect_detection_success: # 确保统计信息已计算 current_stats = None if need_stats: current_stats = img.get_statistics() # 根据图像亮度调整阈值 if current_stats and current_stats.mean() > 200: threshold_value = max(threshold_value, 220) if DEBUG: print("图像较亮,调整阈值为", threshold_value) # 使用优化的参数进行blob检测 try: blobs = img.find_blobs([(threshold_value, 255)], area_threshold=100, pixels_threshold=100, merge=True, margin=5) if DEBUG and blobs: print("使用find_blobs检测到", len(blobs), "个blob") except Exception: # 简化异常处理 blobs = [] # 直接使用检测结果,不创建副本以节省内存 detected_rects = rects if &#39;rects&#39; in locals() and rects else [] detected_blobs = blobs if &#39;blobs&#39; in locals() and blobs else [] if detected_blobs: if DEBUG: print("检测到", len(detected_blobs), "个blob") # 创建一个更高效的自定义矩形类来模拟find_rects的返回对象 class CustomRect: __slots__ = [&#39;x&#39;, &#39;y&#39;, &#39;w&#39;, &#39;h&#39;, &#39;_cx&#39;, &#39;_cy&#39;] # 使用__slots__减少内存使用,添加缓存属性 def __init__(self, x, y, w, h): self.x = x self.y = y self.w = w self.h = h # 预计算中心点,避免重复计算 self._cx = x + w // 2 self._cy = y + h // 2 def rect(self): # 直接返回元组,避免创建新的元组 return self.x, self.y, self.w, self.h @property def cx(self): # 作为属性访问 return self._cx @property def cy(self): # 作为属性访问 return self._cy # 优化:将检测到的blobs转换为自定义矩形对象,并添加到detected_rects中 # 预先分配足够的空间,避免动态扩展列表 if not detected_rects: detected_rects = [None] * len(detected_blobs) else: # 扩展现有列表以容纳新的矩形 detected_rects.extend([None] * len(detected_blobs)) # 使用索引直接赋值,避免append操作 start_idx = len(detected_rects) - len(detected_blobs) for i, b in enumerate(detected_blobs): # 从blob创建一个自定义矩形对象 x, y, w, h = b.rect() detected_rects[start_idx + i] = CustomRect(x, y, w, h) # 如果模板匹配没有成功,则尝试使用矩形检测 if not template_matched and detected_rects: # 选择最大矩形(假设主要目标) max_rect = max(detected_rects, key=lambda r: r.w * r.h) if detected_rects else None if not max_rect: if DEBUG: print("未检测到有效矩形") continue # 计算矩形中心点 # 兼容不同矩形对象类型 if hasattr(max_rect, &#39;rect&#39;): x, y, w, h = max_rect.rect() else: x, y, w, h = max_rect.x, max_rect.y, max_rect.w, max_rect.h center_x = x + w // 2 center_y = y + h // 2 # 不再需要标记矩形和中心点 # 重置丢失计数器和搜索状态 lost_frames_count = 0 searching = False search_state = 0 search_step_counter = 0 # 计算与图像中心的偏差 img_center_x = img.width() // 2 img_center_y = img.height() // 2 # 应用PID控制 pan_output, pan_error_sum, last_pan_error = apply_pid_control( center_x, img_center_x, pan_error_sum, last_pan_error) tilt_output, tilt_error_sum, last_tilt_error = apply_pid_control( center_y, img_center_y, tilt_error_sum, last_tilt_error) # 更新舵机角度 pan_angle += pan_output tilt_angle += tilt_output # 限制舵机角度在有效范围内 pan_angle = max(0, min(180, pan_angle)) tilt_angle = max(0, min(180, tilt_angle)) # 设置舵机位置 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) # 打印调试信息 if DEBUG: print("FPS: {:.1f}, 矩形中心: ({}, {}), 舵机角度: ({:.1f}, {:.1f})".format( clock.fps(), center_x, center_y, pan_angle, tilt_angle)) # 不在屏幕上显示统计信息 else: # 增加丢失计数器 lost_frames_count += 1 if lost_frames_count > MAX_LOST_FRAMES: # 启动搜索模式 - 只在首次进入时初始化 if not searching: searching = True search_state = 0 search_step_counter = 0 # 优化的搜索模式实现 - 使用常量和位运算优化 # 预定义搜索位置 - 使用常量避免重复创建列表 # 这些位置在全局定义,避免每次循环重新创建 SEARCH_POSITIONS = [ (90, 90), # 中心 (70, 70), # 左上 (110, 70), # 右上 (110, 110), # 右下 (70, 110) # 左下 ] # 使用常量获取当前位置 - 避免每次重新计算 current_pos = SEARCH_POSITIONS[search_state] # 批量设置舵机角度 - 减少通信开销 set_servo_angle(pan_servo, current_pos[0]) set_servo_angle(tilt_servo, current_pos[1]) # 更新计数器和状态 - 使用位运算优化 search_step_counter += 1 if search_step_counter > 10: # 停留10帧 # 使用位运算优化模运算,如果SEARCH_POSITIONS长度是2的幂 # 否则保留原来的模运算 search_state = (search_state + 1) % len(SEARCH_POSITIONS) search_step_counter = 0 # 使用位运算优化角度计算 # 将脉冲宽度转换为角度 (500-2500 对应 0-180度) # 使用预计算的常量减少运算 # 180/2000 = 0.09 PW_TO_ANGLE = 0.09 pan_angle = (pan_servo.pulse_width() - 500) * PW_TO_ANGLE tilt_angle = (tilt_servo.pulse_width() - 500) * PW_TO_ANGLE # 只在调试模式下打印信息 if DEBUG: print("搜索模式: 状态 {}, 步骤 {}".format(search_state, search_step_counter)) if DEBUG: print("未检测到矩形, FPS: {:.1f}, 丢失帧数: {}".format(clock.fps(), lost_frames_count)) # 不在屏幕上显示统计信息 # 控制循环频率,减少CPU使用率 time.sleep_ms(SLEEP_MS) # 可配置的延时,控制更新率 这个代码报错import sensor, image, time, math from pyb import Pin, Timer # 如果不需要UART通信,可以注释掉这行 from machine import UART ############################################################# # 全局常量定义 - 集中管理所有配置参数 ############################################################# # 调试和性能选项 DEBUG = True # 设置为True可以在串行终端显示信息 DRAW_DETECTION = False # 设置为False可以减少图像处理负担,不在显示窗口显示检测框 SLEEP_MS = 20 # 循环延时(毫秒),增大可降低CPU使用率,但会降低响应速度 USE_MORPHOLOGY = True # 设置为False可以跳过形态学操作,提高性能 RESOLUTION = sensor.QQVGA # 可选: sensor.QQVGA (160x120) 或 sensor.QVGA (320x240) SHOW_STATS = False # 不在屏幕上显示FPS和其他信息 AUTO_THRESHOLD = True # 设置为True启用自适应阈值 THRESHOLD_OFFSET = 10 # 自适应阈值偏移量 MAX_LOST_FRAMES = 30 # 目标丢失多少帧后执行特定操作 # 图像处理参数 BINARY_THRESHOLD = [(0, 70)] # 对于黑色矩形,阈值可能需要调整 TEMPLATE_MATCH_THRESHOLD = 0.65 # 模板匹配阈值 TEMPLATE_MATCH_STEP = 8 # 模板匹配步长,增大可提高性能但降低精度 BLOB_AREA_THRESHOLD = 100 # Blob检测面积阈值 BLOB_PIXELS_THRESHOLD = 100 # Blob检测像素阈值 BLOB_MERGE_MARGIN = 5 # Blob合并边距 RECT_THRESHOLD = 1000 # 矩形检测阈值 # PID控制参数 KP = 0.1 # 比例系数 KI = 0.01 # 积分系数 KD = 0.05 # 微分系数 ERROR_SUM_MAX = 500 # 积分项最大值 ERROR_SUM_MIN = -500 # 积分项最小值 PW_TO_ANGLE = 0.09 # 脉冲宽度到角度的转换系数 (180/2000) # 舵机参数 SERVO_MIN_PW = 500 # 最小脉冲宽度 (0度) SERVO_MAX_PW = 2500 # 最大脉冲宽度 (180度) SERVO_FREQ = 50 # 舵机PWM频率 # 搜索模式参数 SEARCH_DWELL_FRAMES = 10 # 每个搜索位置停留的帧数 # 预定义搜索位置 - 使用常量避免重复创建 SEARCH_POSITIONS = [ (90, 90), # 中心 (70, 70), # 左上 (110, 70), # 右上 (110, 110), # 右下 (70, 110) # 左下 ] ############################################################# # 系统初始化函数 ############################################################# def init_camera(): """初始化并优化摄像头设置""" sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) # 使用灰度模式 sensor.set_framesize(RESOLUTION) sensor.skip_frames(time=2000) # 等待设置生效 # 优化自动设置 sensor.set_auto_gain(False) # 关闭自动增益 sensor.set_auto_whitebal(False) # 关闭自动白平衡 sensor.set_auto_exposure(False, exposure_us=10000) # 固定曝光时间 # 优化图像设置 sensor.set_brightness(0) sensor.set_contrast(0) # 保持镜像设置 sensor.set_hmirror(False) sensor.set_vflip(False) def init_servos(): """初始化舵机控制""" # 创建两个不同的定时器实例 tim_pan = Timer(2, freq=SERVO_FREQ) # 使用常量定义PWM频率 tim_tilt = Timer(4, freq=SERVO_FREQ) # 水平舵机(Pan) - 控制左右旋转 pan_servo = tim_pan.channel(1, Timer.PWM, pin=Pin("P6")) # 垂直舵机(Tilt) - 控制上下旋转 tilt_servo = tim_tilt.channel(1, Timer.PWM, pin=Pin("P8")) return pan_servo, tilt_servo def load_template(): """加载模板图像,使用异常处理确保程序不会崩溃""" try: template = image.Image("bazi.pgm") if DEBUG: print("成功加载模板图像 bazi.pgm") return template except Exception as e: if DEBUG: print("加载模板图像失败:", e) return None # 优化的舵机控制函数 def set_servo_angle(ch, angle): """ 设置舵机角度 (0-180度) 使用位运算和预计算常量优化计算 """ # 使用位运算优化限制角度范围 angle = min(180, max(0, angle)) # 使用预计算的常量和整数运算优化脉冲宽度计算 # 0.5ms-2.5ms脉冲范围 (500-2500) # 使用整数运算然后转换为整数,避免浮点运算 pulse = SERVO_MIN_PW + int((angle * (SERVO_MAX_PW - SERVO_MIN_PW)) / 180) # 直接设置脉冲宽度 ch.pulse_width(pulse) ############################################################# # PID控制函数 ############################################################# def apply_pid_control(current_pos, center_pos, error_sum, last_error): """ 应用PID控制算法 优化计算效率,减少内存使用 参数: current_pos: 当前位置 center_pos: 目标位置 error_sum: 累计误差 last_error: 上一次误差 返回: (输出值, 更新后的累计误差, 当前误差) """ # 计算误差(使用减法而不是乘以-1,更高效) # 负号是因为舵机方向与坐标系方向相反 error = center_pos - current_pos # 更新积分项 - 使用就地操作符 error_sum += error # 使用位运算优化限制积分项 # 使用常量避免重复计算 error_sum = min(ERROR_SUM_MAX, max(ERROR_SUM_MIN, error_sum)) # 计算微分项 - 直接使用减法 error_diff = error - last_error # 使用预计算的乘法减少浮点运算 # 可以考虑使用整数运算然后再转换为浮点数 output = (KP * error) + (KI * error_sum) + (KD * error_diff) # 使用元组返回多个值,减少临时变量 return output, error_sum, error ############################################################# # 系统初始化 ############################################################# # 初始化摄像头 init_camera() # 加载模板图像 template = load_template() # 初始化舵机 pan_servo, tilt_servo = init_servos() # 设置初始位置:90度(中间位置) pan_angle = 90 tilt_angle = 90 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) time.sleep(1) # 等待舵机到位 # PID控制参数 KP = 0.1 # 比例系数 KI = 0.01 # 积分系数 KD = 0.05 # 微分系数 # PID控制变量 pan_error_sum = 0 tilt_error_sum = 0 last_pan_error = 0 last_tilt_error = 0 # 二值化阈值 - 调整这个值以适应您的环境光线条件 BINARY_THRESHOLD = [(0, 70)] # 对于黑色矩形,阈值可能需要调整 ############################################################# # 辅助函数 ############################################################# def print_config(): """显示当前配置信息""" if DEBUG: print("=== 激光点追踪系统配置 ===") print("分辨率: {}".format("160x120" if RESOLUTION == sensor.QQVGA else "320x240")) print("二值化阈值: {}".format("自适应 (偏移量={})".format(THRESHOLD_OFFSET) if AUTO_THRESHOLD else BINARY_THRESHOLD)) print("使用形态学操作: {}".format(USE_MORPHOLOGY)) print("绘制检测结果: {}".format(DRAW_DETECTION)) print("显示统计信息: {}".format(SHOW_STATS)) print("循环延时: {}ms".format(SLEEP_MS)) print("PID参数: KP={}, KI={}, KD={}".format(KP, KI, KD)) print("目标丢失处理: {}帧后启动搜索".format(MAX_LOST_FRAMES)) print("=========================") def process_image(img, need_stats=False): """ 处理图像:二值化和形态学操作 优化内存使用和计算效率 参数: img: 要处理的图像 need_stats: 是否需要计算图像统计信息 返回: 处理后的图像 """ # 获取统计信息(仅在需要时) if need_stats: stats = img.get_statistics() if DEBUG and SHOW_STATS: print("均值: {:.1f}, 中值: {}, 标准差: {:.1f}".format( stats.mean(), stats.median(), stats.stdev())) # 使用自适应阈值或固定阈值进行二值化 if AUTO_THRESHOLD and need_stats: threshold_value = max(10, min(250, stats.mean() - THRESHOLD_OFFSET)) thresholds = [(0, threshold_value)] else: thresholds = BINARY_THRESHOLD # 二值化处理 img.binary(thresholds) # 可选的形态学操作 if USE_MORPHOLOGY: img.erode(1) img.dilate(1) return img # 主循环 ############################################################# # 主循环 ############################################################# # 创建时钟对象来跟踪FPS clock = time.clock() # 显示初始配置 print_config() # 初始化追踪状态变量 lost_frames_count = 0 # 目标丢失计数器 searching = False # 是否正在执行搜索 search_state = 0 # 搜索模式的状态 search_step_counter = 0 # 搜索步骤的计数器 while True: clock.tick() # 更新FPS计时器 # 直接处理单幅图像,不保留副本 img = sensor.snapshot() # 预先检查是否需要统计信息 need_stats = (DEBUG and SHOW_STATS) or AUTO_THRESHOLD or template # 获取统计信息(在原图上) if need_stats: stats = img.get_statistics() if DEBUG and SHOW_STATS: print("亮度均值: {:.1f}, 标准差: {:.1f}".format(stats.mean(), stats.stdev())) # 仅当需要时才进行二值化 if AUTO_THRESHOLD or BINARY_THRESHOLD: threshold = stats.mean() - THRESHOLD_OFFSET if AUTO_THRESHOLD else BINARY_THRESHOLD[0][1] img.binary([(0, max(10, min(250, threshold)))]) if USE_MORPHOLOGY: img.erode(1) img.dilate(1) # 图像处理结果直接存储在img中 # 不再需要DISPLAY_RAW切换,所有处理都在原图进行 # 优化模板匹配 - 减少计算和内存使用 template_matched = False if template and need_stats: # 直接获取统计信息 current_stats = img.get_statistics() # 使用快速条件检查跳过不必要的模板匹配 poor_image_quality = (current_stats.mean() > 200 or current_stats.mean() < 20 or (current_stats.max() - current_stats.min()) < 30) if poor_image_quality: if DEBUG: print("图像质量不佳,跳过模板匹配") else: try: # 使用更高效的模板匹配参数 # 增加step值减少计算量,使用更简单的搜索方法 r = img.find_template(template, 0.65, step=8, search=image.SEARCH_DS) if r: # 找到匹配 - 使用元组解包直接获取坐标 x, y, w, h = r # 预计算中心点和图像中心 center_x = x + (w >> 1) # 使用位移代替除法 center_y = y + (h >> 1) img_center_x = img.width() >> 1 img_center_y = img.height() >> 1 # 重置状态变量 - 使用一行代码减少指令数 lost_frames_count = search_state = search_step_counter = 0 searching = False # 标记模板匹配成功 template_matched = True # 使用我们之前定义的PID控制函数 # 计算与图像中心的偏差 dx = center_x - img_center_x dy = center_y - img_center_y # 应用PID控制 pan_output, pan_error_sum, last_pan_error = apply_pid_control( center_x, img_center_x, pan_error_sum, last_pan_error) tilt_output, tilt_error_sum, last_tilt_error = apply_pid_control( center_y, img_center_y, tilt_error_sum, last_tilt_error) # 更新舵机角度并限制范围 pan_angle = max(0, min(180, pan_angle + pan_output)) tilt_angle = max(0, min(180, tilt_angle + tilt_output)) # 设置舵机位置 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) # 只在调试模式下打印信息 if DEBUG: print("FPS: {:.1f}, 模板匹配中心: ({}, {}), 舵机角度: ({:.1f}, {:.1f})".format( clock.fps(), center_x, center_y, pan_angle, tilt_angle)) elif DEBUG: print("模板匹配失败") except Exception: # 简化异常处理,不打印详细错误信息以减少开销 if DEBUG: print("模板匹配错误") # 优化阈值计算 - 减少计算和内存使用 threshold_value = 0 if AUTO_THRESHOLD and need_stats: # 直接获取统计信息 current_stats = img.get_statistics() # 简化阈值计算 threshold_value = max(10, min(250, current_stats.mean() - THRESHOLD_OFFSET)) if DEBUG: print("自适应阈值: {}".format(threshold_value)) else: # 使用固定阈值 threshold_value = BINARY_THRESHOLD[0][1] # 优化形态学操作 - 只在必要时执行 if USE_MORPHOLOGY: # 直接使用open操作,减少内存使用 img.open(1) # 反转图像以便黑色矩形变为白色(如果需要) # binary_img.invert() # 优化检测逻辑 - 减少异常处理和条件判断 rects = [] blobs = [] # 使用标志变量而不是异常处理来控制流程 rect_detection_success = False rects = [] # 尝试矩形检测 try: rects = img.find_rects(threshold=1000) rect_detection_success = len(rects) > 0 if DEBUG and rect_detection_success: print("使用find_rects检测到", len(rects), "个矩形") except Exception: # 简化异常处理,不打印详细错误信息以减少开销 rect_detection_success = False if DEBUG: print("切换到find_blobs作为备选") # 如果矩形检测失败,尝试blob检测 if not rect_detection_success: # 确保统计信息已计算 current_stats = None if need_stats: current_stats = img.get_statistics() # 根据图像亮度调整阈值 if current_stats and current_stats.mean() > 200: threshold_value = max(threshold_value, 220) if DEBUG: print("图像较亮,调整阈值为", threshold_value) # 使用优化的参数进行blob检测 try: blobs = img.find_blobs([(threshold_value, 255)], area_threshold=100, pixels_threshold=100, merge=True, margin=5) if DEBUG and blobs: print("使用find_blobs检测到", len(blobs), "个blob") except Exception: # 简化异常处理 blobs = [] # 直接使用检测结果,不创建副本以节省内存 detected_rects = rects if &#39;rects&#39; in locals() and rects else [] detected_blobs = blobs if &#39;blobs&#39; in locals() and blobs else [] if detected_blobs: if DEBUG: print("检测到", len(detected_blobs), "个blob") # 创建一个更高效的自定义矩形类来模拟find_rects的返回对象 class CustomRect: __slots__ = [&#39;x&#39;, &#39;y&#39;, &#39;w&#39;, &#39;h&#39;, &#39;_cx&#39;, &#39;_cy&#39;] # 使用__slots__减少内存使用,添加缓存属性 def __init__(self, x, y, w, h): self.x = x self.y = y self.w = w self.h = h # 预计算中心点,避免重复计算 self._cx = x + w // 2 self._cy = y + h // 2 def rect(self): # 直接返回元组,避免创建新的元组 return self.x, self.y, self.w, self.h @property def cx(self): # 作为属性访问 return self._cx @property def cy(self): # 作为属性访问 return self._cy # 优化:将检测到的blobs转换为自定义矩形对象,并添加到detected_rects中 # 预先分配足够的空间,避免动态扩展列表 if not detected_rects: detected_rects = [None] * len(detected_blobs) else: # 扩展现有列表以容纳新的矩形 detected_rects.extend([None] * len(detected_blobs)) # 使用索引直接赋值,避免append操作 start_idx = len(detected_rects) - len(detected_blobs) for i, b in enumerate(detected_blobs): # 从blob创建一个自定义矩形对象 x, y, w, h = b.rect() detected_rects[start_idx + i] = CustomRect(x, y, w, h) # 如果模板匹配没有成功,则尝试使用矩形检测 if not template_matched and detected_rects: # 选择最大矩形(假设主要目标) max_rect = max(detected_rects, key=lambda r: r.w * r.h) if detected_rects else None if not max_rect: if DEBUG: print("未检测到有效矩形") continue # 计算矩形中心点 # 兼容不同矩形对象类型 if hasattr(max_rect, &#39;rect&#39;): x, y, w, h = max_rect.rect() else: x, y, w, h = max_rect.x, max_rect.y, max_rect.w, max_rect.h center_x = x + w // 2 center_y = y + h // 2 # 不再需要标记矩形和中心点 # 重置丢失计数器和搜索状态 lost_frames_count = 0 searching = False search_state = 0 search_step_counter = 0 # 计算与图像中心的偏差 img_center_x = img.width() // 2 img_center_y = img.height() // 2 # 应用PID控制 pan_output, pan_error_sum, last_pan_error = apply_pid_control( center_x, img_center_x, pan_error_sum, last_pan_error) tilt_output, tilt_error_sum, last_tilt_error = apply_pid_control( center_y, img_center_y, tilt_error_sum, last_tilt_error) # 更新舵机角度 pan_angle += pan_output tilt_angle += tilt_output # 限制舵机角度在有效范围内 pan_angle = max(0, min(180, pan_angle)) tilt_angle = max(0, min(180, tilt_angle)) # 设置舵机位置 set_servo_angle(pan_servo, pan_angle) set_servo_angle(tilt_servo, tilt_angle) # 打印调试信息 if DEBUG: print("FPS: {:.1f}, 矩形中心: ({}, {}), 舵机角度: ({:.1f}, {:.1f})".format( clock.fps(), center_x, center_y, pan_angle, tilt_angle)) # 不在屏幕上显示统计信息 else: # 增加丢失计数器 lost_frames_count += 1 if lost_frames_count > MAX_LOST_FRAMES: # 启动搜索模式 - 只在首次进入时初始化 if not searching: searching = True search_state = 0 search_step_counter = 0 # 优化的搜索模式实现 - 使用常量和位运算优化 # 预定义搜索位置 - 使用常量避免重复创建列表 # 这些位置在全局定义,避免每次循环重新创建 SEARCH_POSITIONS = [ (90, 90), # 中心 (70, 70), # 左上 (110, 70), # 右上 (110, 110), # 右下 (70, 110) # 左下 ] # 使用常量获取当前位置 - 避免每次重新计算 current_pos = SEARCH_POSITIONS[search_state] # 批量设置舵机角度 - 减少通信开销 set_servo_angle(pan_servo, current_pos[0]) set_servo_angle(tilt_servo, current_pos[1]) # 更新计数器和状态 - 使用位运算优化 search_step_counter += 1 if search_step_counter > 10: # 停留10帧 # 使用位运算优化模运算,如果SEARCH_POSITIONS长度是2的幂 # 否则保留原来的模运算 search_state = (search_state + 1) % len(SEARCH_POSITIONS) search_step_counter = 0 # 使用位运算优化角度计算 # 将脉冲宽度转换为角度 (500-2500 对应 0-180度) # 使用预计算的常量减少运算 # 180/2000 = 0.09 PW_TO_ANGLE = 0.09 pan_angle = (pan_servo.pulse_width() - 500) * PW_TO_ANGLE tilt_angle = (tilt_servo.pulse_width() - 500) * PW_TO_ANGLE # 只在调试模式下打印信息 if DEBUG: print("搜索模式: 状态 {}, 步骤 {}".format(search_state, search_step_counter)) if DEBUG: print("未检测到矩形, FPS: {:.1f}, 丢失帧数: {}".format(clock.fps(), lost_frames_count)) # 不在屏幕上显示统计信息 # 控制循环频率,减少CPU使用率 time.sleep_ms(SLEEP_MS) # 可配置的延时,控制更新率
最新发布
07-31
#include "dml-module.h" #include "cap.h" #include "cap_win.h" #include <nms.h> #include <opencv2/opencv.hpp> #include "AIMBOT.cpp" #include "mouse_controller.h" #include <windows.h> #include <chrono> #include <string> #include <filesystem> #include <iostream> #include <functional> #include <atomic> #include <mutex> #include <vector> #include <algorithm> #include <optional> #include "gbil_mouse_controller.h" #include "config.h" // ===== 新增:ADRC控制器结构体(自抗扰控制,解决动态目标扰动问题)===== struct ADRCController { float w0; // 观测器带宽 float b0; // 控制增益 float w_n; // 闭环带宽 float sigma; // 阻尼系数 float time_delta; // 时间步长(ms转换为秒) // 扩张状态观测器变量 float z1{ 0 }, z2{ 0 }, z3{ 0 }; // 状态观测值(位置、速度、扰动) float u_p{ 0 }; // 上一时刻控制量 std::mutex mtx; // 初始化ADRC参数(从配置文件读取) ADRCController(float w0_, float b0_, float w_n_, float sigma_, float dt) : w0(w0_), b0(b0_), w_n(w_n_), sigma(sigma_), time_delta(dt) { } // *核心方法:根据目标值和当前值计算补偿后的控制量 float update(float target, float current) { std::lock_guard<std::mutex> lock(mtx); float err = current - z1; // 跟踪误差 // 扩张状态观测器(ESO)更新 float fhan = this->fhan(err, z2, w_n, sigma); z1 += time_delta * (z2 + w0 * w0 * err); // 位置观测 z2 += time_delta * (z3 + 2 * w0 * z2 + w0 * w0 * fhan); // 速度观测 z3 += time_delta * (-3 * w0 * z3 + w0 * w0 * w0 * (current - z1)); // 扰动观测 // 控制律计算(补偿扰动) float u0 = w_n * w_n * (target - z1) - 2 * sigma * w_n * z2; float u = (u0 - z3) / b0; u_p = u; // 保存当前控制量 return u; } // 快速跟踪微分器(处理非线性特性) float fhan(float x1, float x2, float r, float h0) { float d = r * h0 * h0; float d0 = h0 * d; float y = x1 + h0 * x2; float a0 = sqrt(d * (d + 8 * abs(y))); float a; if (abs(y) > d0) { a = x2 + (a0 - d) / 2 * (y > 0 ? 1 : -1); } else { a = x2 + y / h0; } if (abs(a) > d) { return -r * (a > 0 ? 1 : -1); } else { return -r * a / d; } } void reset() { std::lock_guard<std::mutex> lock(mtx); z1 = z2 = z3 = 0; u_p = 0; } }; // ===== PID控制器结构体(保留原有功能,与ADRC级联)===== struct PIDController { float kp, ki, kd; float integral{ 0 }, last_error{ 0 }; std::mutex mtx; PIDController(float p, float i, float d) : kp(p), ki(i), kd(d) {} float compute(float error) { std::lock_guard<std::mutex> lock(mtx); integral += error; float derivative = error - last_error; last_error = error; return kp * error + ki * integral + kd * derivative; } void reset() { std::lock_guard<std::mutex> lock(mtx); integral = 0; last_error = 0; } }; // ===== 卡尔曼滤波类(参数可配置)===== class KalmanFilter { public: KalmanFilter(float process_noise, float obs_noise) { kf.init(4, 2, 0); kf.transitionMatrix = (cv::Mat_<float>(4, 4) << 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1); cv::setIdentity(kf.measurementMatrix); cv::setIdentity(kf.processNoiseCov, cv::Scalar::all(process_noise)); cv::setIdentity(kf.measurementNoiseCov, cv::Scalar::all(obs_noise)); cv::setIdentity(kf.errorCovPost, cv::Scalar::all(1)); } cv::Point2f predict(float x, float y) { cv::Mat measurement = (cv::Mat_<float>(2, 1) << x, y); kf.predict(); cv::Mat estimated = kf.correct(measurement); return cv::Point2f(estimated.at<float>(0), estimated.at<float>(1)); } private: cv::KalmanFilter kf; }; // ===== 新增:贝塞尔曲线生成工具(解决移动拖影,实现类人平滑路径)===== class BezierCurve { public: // 生成从起点到终点的贝塞尔曲线路径点 static std::vector<cv::Point2f> generatePoints( cv::Point2f start, cv::Point2f end, int num_points = 10) { // 路径点数量(越多越平滑)数值越大越慢 std::vector<cv::Point2f> points; // 生成随机内点(增加路径自然性) std::vector<cv::Point2f> control_points; control_points.push_back(start); // 添加2个随机内点(在起点和终点范围内) control_points.push_back(cv::Point2f( start.x + (end.x - start.x) * 0.3f + rand() % 5 - 2, // 微小随机偏移 start.y + (end.y - start.y) * 0.3f + rand() % 5 - 2 )); control_points.push_back(cv::Point2f( start.x + (end.x - start.x) * 0.7f + rand() % 5 - 2, start.y + (end.y - start.y) * 0.7f + rand() % 5 - 2 )); control_points.push_back(end); // 贝塞尔曲线采样 for (int i = 0; i < num_points; ++i) { float t = (float)i / (num_points - 1); points.push_back(bezier(t, control_points)); } return points; } private: // 贝塞尔曲线计算 static cv::Point2f bezier(float t, const std::vector<cv::Point2f>& points) { int n = points.size() - 1; cv::Point2f result(0, 0); for (int i = 0; i <= n; ++i) { float bernstein = binomial(n, i) * pow(t, i) * pow(1 - t, n - i); result.x += points[i].x * bernstein; result.y += points[i].y * bernstein; } return result; } // 二项式系数计算 static float binomial(int n, int k) { if (k < 0 || k > n) return 0; if (k == 0 || k == n) return 1; k = std::min(k, n - k); float res = 1; for (int i = 1; i <= k; ++i) { res = res * (n - k + i) / i; } return res; } }; // ===== 目标选择函数(安全封装)===== std::optional<Box> selectTarget( std::vector<Box>& filtered, int& tracked_id, int& tracking_counter, float anchor_x, float anchor_y, int width, int height ) { // 优先选择上次追踪目标 for (auto& box : filtered) { if (box.id == tracked_id) { tracking_counter++; return box; } } // 未找到则选择距离中心最近的目标 if (!filtered.empty()) { float min_dist = FLT_MAX; Box* best_target = nullptr; for (auto& box : filtered) { float cx = box.left + (box.right - box.left) * anchor_x; float cy = box.top + (box.bottom - box.top) * anchor_y; float dist = std::hypot(cx - width / 2.0f, cy - height / 2.0f); if (dist < min_dist) { min_dist = dist; best_target = &box; } } if (best_target) { tracked_id = best_target->id; tracking_counter = 1; return *best_target; } } return std::nullopt; } int main() { // ------ 新增:检查自身实例是否已在运行 开始 ------ // 通过创建全局命名互斥锁(Named Mutex)来判断是否已有一个实例在运行 HANDLE hInstanceMutex = CreateMutexW(nullptr, FALSE, L"Global\\GblidDemoVC_Mutex"); if (!hInstanceMutex || GetLastError() == ERROR_ALREADY_EXISTS) { MessageBoxW(NULL, L"请勿重复运行", L"提示", MB_OK | MB_ICONWARNING); if (hInstanceMutex) { CloseHandle(hInstanceMutex); } ExitProcess(0); // 已有实例,退出当前进程 } // ------ 新增:检查自身实例是否已在运行 结束 ------ // 隐藏控制台窗口 HWND consoleWindow = GetConsoleWindow(); // 获取当前控制台窗口句柄 ShowWindow(consoleWindow, SW_HIDE); // 隐藏控制台窗口(如果需要调试可以注释掉这行)//==========需要调试可以注释================== // ==== 加载配置 ==== Config config; if (!config.loadFromFile("./config.ini")) { printf("主程序-配置文件加载失败!\n"); return -1; } bool baim = (config.aim_part == 0); timeBeginPeriod(1); // 设置定时器精度为1ms // === 自动加载模型 === std::string model_dir = "onnx"; std::string model_path; for (const auto& entry : std::filesystem::directory_iterator(model_dir)) { if (entry.path().extension() == ".onnx") { model_path = entry.path().string(); break; } } if (model_path.empty()) { printf("主程序-未在 &#39;onnx&#39; 文件夹中找到任何 .onnx 模型文件!\n"); return -1; } printf("主程序-加载模型路径: %s\n", model_path.c_str()); // 初始化模型类 auto* frame = new IDML(); if (!frame->AnalyticalModel(model_path)) { printf("主程序-模型加载失败。\n"); return -2; } const auto& input_dims = frame->getInputDims(); if (input_dims.size() != 4) { printf("主程序-模型输入维度异常,当前维度数量: %llu\n", input_dims.size()); return -3; } int channels = static_cast<int>(input_dims[1]); int height = static_cast<int>(input_dims[2]); int width = static_cast<int>(input_dims[3]); printf("主程序-输入图像尺寸: %d x %d\n", width, height); // ==== 初始化控制器(*修改:新增ADRC控制器)==== PIDController pid_x(config.xkp, config.xki, config.xkd); PIDController pid_y(config.ykp, config.yki, config.ykd); // *ADRC参数(从配置文件读取,可根据动态目标特性调整) float adrc_dt = 0.016f; // 约60FPS的时间步长(1/600.016s) ADRCController adrc_x(config.adrc_w0, config.adrc_b0, config.adrc_wn, config.adrc_sigma, adrc_dt); ADRCController adrc_y(config.adrc_w0, config.adrc_b0, config.adrc_wn, config.adrc_sigma, adrc_dt); KalmanFilter kf(config.kalman_Q, config.kalman_R); printf("主程序-控制参数: PID X[%.2f, %.3f, %.2f] Y[%.2f, %.3f, %.2f]\n", config.xkp, config.xki, config.xkd, config.ykp, config.yki, config.ykd); printf("主程序-ADRC参数: w0=%.1f, b0=%.2f, wn=%.1f, sigma=%.1f\n", config.adrc_w0, config.adrc_b0, config.adrc_wn, config.adrc_sigma); int screenW = GetSystemMetrics(SM_CXSCREEN); int screenH = GetSystemMetrics(SM_CYSCREEN); std::function<BYTE* ()> capture_func; void* cap_impl = nullptr; // 选择截图方式 if (config.screenshot_method == 0) { auto* c = new capture(screenW, screenH, width, height, "CrossFire"); cap_impl = c; capture_func = [c]() -> BYTE* { return (BYTE*)c->cap(); }; printf("主程序-截图方式: DXGI\n"); } else { auto* c = new capture_win32(screenW, screenH, width, height, "CrossFire"); cap_impl = c; capture_func = [c]() -> BYTE* { return c->cap(); }; printf("主程序-截图方式: Win32 BitBlt\n"); } // 初始化鼠标控制器 IMouseController* mouse = nullptr; if (config.move_type == 0) { mouse = new WinMouseController(); printf("主程序-使用鼠标控制器: WinMove\n"); } else if (config.move_type == 1) { mouse = new GBILMouseController(); printf("主程序-使用鼠标控制器: GBIL\n"); } else { printf("主程序-未知的 move_type 类型: %d,未启用鼠标移动!\n", config.move_type); } using Clock = std::chrono::high_resolution_clock; auto last_time = Clock::now(); float fps = 0.0f; // ==== 持续追踪状态 ==== static int tracked_id = -1; static int tracking_counter = 0; static int lost_counter = 0; // ==== 跟踪功能开关状态 ==== std::atomic<bool> tracking_enabled{ true }; bool last_tracking_toggle_state = false; bool last_exit_key_state = false; // ==== 新增:记录上一时刻鼠标位置(用于贝塞尔路径生成)==== cv::Point2f last_mouse_pos(0, 0); // ==== 主循环 ==== static bool target_locked = false; static int lock_frames_counter = 0; static float last_min_dist = FLT_MAX; while (true) { auto start = Clock::now(); // ==== 检查退出键 ==== bool current_exit_state = (GetAsyncKeyState(config.exit_key) & 0x8000) != 0; if (current_exit_state && !last_exit_key_state) { // 使用MB_SYSTEMMODAL标志使对话框置顶 [1,8](@ref) int result = MessageBoxA( NULL, "确定要退出YAK吗?", "退出确认", MB_OKCANCEL | MB_ICONQUESTION | MB_SYSTEMMODAL // 添加MB_SYSTEMMODAL标志 ); if (result == IDOK) { break; } } last_exit_key_state = current_exit_state; // ==== 检查跟踪开关键 ==== bool current_tracking_toggle = (GetAsyncKeyState(config.tracking_toggle_key) & 0x8000) != 0; if (current_tracking_toggle && !last_tracking_toggle_state) { tracking_enabled = !tracking_enabled; printf("主程序-跟踪功能已%s\n", tracking_enabled ? "开启" : "关闭"); } last_tracking_toggle_state = current_tracking_toggle; // 截图 BYTE* s = capture_func(); auto after_cap = Clock::now(); // 目标检测 float* data = frame->Detect(s); auto after_detect = Clock::now(); // 计算性能指标 float capture_ms = std::chrono::duration<float, std::milli>(after_cap - start).count(); float detect_ms = std::chrono::duration<float, std::milli>(after_detect - after_cap).count(); float frame_time = std::chrono::duration<float>(after_detect - last_time).count(); last_time = after_detect; fps = 1.0f / frame_time; std::vector<Box> oldbox; std::vector<Box> newbox = generate_yolo_proposals(data, oldbox, frame->out1, frame->out2, config.conf_threshold, config.nms_threshold); // ==== 目标过滤 ==== static std::vector<Box> filtered; filtered.clear(); for (auto& box : newbox) { if (config.valid_classes.empty() || std::find(config.valid_classes.begin(), config.valid_classes.end(), box.class_label) != config.valid_classes.end()) { filtered.push_back(box); } } // ==== 目标选择与锁定机制 ==== std::optional<Box> current_target = std::nullopt; if (tracking_enabled) { if (target_locked) { for (auto& box : filtered) { if (box.id == tracked_id) { current_target = box; tracking_counter++; lost_counter = 0; break; } } if (!current_target) { if (++lost_counter > 5) { target_locked = false; tracked_id = -1; tracking_counter = 0; lost_counter = 0; last_min_dist = FLT_MAX; } } } else if (!filtered.empty()) { float min_dist = FLT_MAX; Box* best_target = nullptr; for (auto& box : filtered) { float cx = box.left + (box.right - box.left) * config.anchor_x; float cy = box.top + (box.bottom - box.top) * config.anchor_y; float dist = std::hypot(cx - width / 2.0f, cy - height / 2.0f); if (dist < min_dist && (tracked_id == -1 || dist < last_min_dist * 0.8f)) { min_dist = dist; best_target = &box; } } if (best_target) { current_target = *best_target; tracked_id = best_target->id; tracking_counter = 1; last_min_dist = min_dist; if (min_dist < config.lock_threshold) { if (++lock_frames_counter >= config.lock_frames) { target_locked = true; lock_frames_counter = 0; } } else { lock_frames_counter = 0; } } } } // ==== 鼠标控制逻辑(*核心修改:PID+ADRC级联控制+贝塞尔路径平滑)==== bool aim_key_pressed = (GetAsyncKeyState(config.aim_key) & 0x8000) != 0; bool aim_key1_pressed = (GetAsyncKeyState(config.aim_key1) & 0x8000) != 0; bool any_aim_key_pressed = aim_key_pressed || aim_key1_pressed; if (any_aim_key_pressed && mouse && tracking_enabled) { if (current_target) { // 计算锚点位置 float center_x = current_target->left + (current_target->right - current_target->left) * config.anchor_x; float center_y = current_target->top + (current_target->bottom - current_target->top) * config.anchor_y; cv::Point2f smoothed; if (config.kalman_enable) { smoothed = kf.predict(center_x, center_y); } else { smoothed = cv::Point2f(center_x, center_y); } // 计算原始偏移(目标与屏幕中心的偏差) float raw_dx = smoothed.x - width / 2.0f; float raw_dy = smoothed.y - height / 2.0f; // *步骤1:PID计算基础移动量(比例+积分+微分) float pid_dx = pid_x.compute(raw_dx); float pid_dy = pid_y.compute(raw_dy); // *步骤2:ADRC补偿动态扰动(解决动态目标跟踪延迟) float adrc_dx = adrc_x.update(pid_dx, raw_dx); // 输入PID输出和原始偏差 float adrc_dy = adrc_y.update(pid_dy, raw_dy); // *步骤3:贝塞尔曲线生成平滑路径(解决移动拖影) cv::Point2f target_pos(last_mouse_pos.x + adrc_dx, last_mouse_pos.y + adrc_dy); std::vector<cv::Point2f> path = BezierCurve::generatePoints(last_mouse_pos, target_pos); // *步骤4:按平滑路径移动鼠标 for (const auto& p : path) { mouse->updateTarget(p.x - last_mouse_pos.x, p.y - last_mouse_pos.y); last_mouse_pos = p; // 更新当前鼠标位置 Sleep(1); // 微小延迟,确保平滑效果 } } else { // 无目标时重置 mouse->updateTarget(0.0f, 0.0f); pid_x.reset(); pid_y.reset(); adrc_x.reset(); // *重置ADRC adrc_y.reset(); last_mouse_pos = cv::Point2f(0, 0); target_locked = false; tracked_id = -1; lock_frames_counter = 0; } } else { // 未按触发键时重置 if (mouse) mouse->updateTarget(0.0f, 0.0f); target_locked = false; tracked_id = -1; tracking_counter = 0; lock_frames_counter = 0; pid_x.reset(); pid_y.reset(); adrc_x.reset(); // *重置ADRC adrc_y.reset(); last_mouse_pos = cv::Point2f(0, 0); } // ==== 调试窗口显示 ==== if (config.show_window) { cv::Mat a(height, width, CV_8UC3, s); if (!newbox.empty()) { for (const Box& detection : newbox) { cv::Scalar color = (current_target && detection.id == current_target->id) ? cv::Scalar(0, 0, 255) : cv::Scalar(0, 255, 0); int thickness = (current_target && detection.id == current_target->id) ? 2 : 1; cv::rectangle(a, cv::Point((int)detection.left, (int)detection.top), cv::Point((int)detection.right, (int)detection.bottom), color, thickness ); std::string class_str = std::to_string(detection.class_label); char conf_str[16]; snprintf(conf_str, sizeof(conf_str), "%.2f", detection.confidence); int fontFace = cv::FONT_HERSHEY_SIMPLEX; double fontScale = 0.4; int textThickness = 1; cv::Point textOrg((int)detection.left, (int)detection.top - 4); if (textOrg.y < 10) textOrg.y = 10; cv::putText(a, class_str, textOrg, fontFace, fontScale, cv::Scalar(0, 0, 255), textThickness); cv::Size textSize = cv::getTextSize(class_str, fontFace, fontScale, textThickness, nullptr); cv::Point confOrg = textOrg + cv::Point(textSize.width + 4, 0); cv::putText(a, conf_str, confOrg, fontFace, fontScale, cv::Scalar(0, 255, 0), textThickness); } } // 绘制锚点和追踪线 if (current_target && any_aim_key_pressed && tracking_enabled) { int anchor_x_pos = current_target->left + (current_target->right - current_target->left) * config.anchor_x; int anchor_y_pos = current_target->top + (current_target->bottom - current_target->top) * config.anchor_y; cv::circle(a, cv::Point(anchor_x_pos, anchor_y_pos), 5, cv::Scalar(255, 0, 255), -1); cv::line(a, cv::Point(width / 2, height / 2), cv::Point(anchor_x_pos, anchor_y_pos), cv::Scalar(0, 255, 255), 1); } // 性能指标显示 int base_y = 20; int line_height = 18; double font_scale = 0.5; int thickness = 1; auto draw_metric = [&](const std::string& label, const std::string& value, int line) { int y = base_y + line * line_height; cv::putText(a, label, cv::Point(10, y), cv::FONT_HERSHEY_SIMPLEX, font_scale, cv::Scalar(0, 0, 255), thickness); cv::putText(a, value, cv::Point(80, y), cv::FONT_HERSHEY_SIMPLEX, font_scale, cv::Scalar(0, 255, 0), thickness); }; draw_metric("FPS:", std::to_string((int)fps), 0); draw_metric("CAPTURE:", std::to_string((int)capture_ms) + "ms", 1); draw_metric("DETECT:", std::to_string((int)detect_ms) + "ms", 2); draw_metric("TRACK:", tracking_enabled ? "ON" : "OFF", 3); // 状态信息显示 if (current_target) { std::string track_info = "TRACK: " + std::to_string(tracking_counter); cv::putText(a, track_info, cv::Point(width - 120, 30), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(200, 200, 0), 1); } if (target_locked) { std::string lock_info = "LOCKED: " + std::to_string(tracked_id); cv::putText(a, lock_info, cv::Point(width - 120, 60), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0, 200, 255), 1); } if (lost_counter > 0) { std::string lost_info = "LOST: " + std::to_string(lost_counter); cv::putText(a, lost_info, cv::Point(width - 120, 90), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0, 200, 200), 1); } // 窗口置顶设置 static bool topmost_set = false; if (!topmost_set) { HWND hwnd = FindWindowA(NULL, "c"); if (hwnd) { SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } topmost_set = true; } cv::imshow("c", a); cv::waitKey(1); } } // 退出前释放资源 if (mouse) { mouse->stop(); delete mouse; mouse = nullptr; } delete frame; if (cap_impl) { if (config.screenshot_method == 0) { delete static_cast<capture*>(cap_impl); } else { delete static_cast<capture_win32*>(cap_impl); } } timeEndPeriod(1); return 0; }这个是c++程序,你帮忙分析一下,为什么跟踪目标会跟不上,还会有拖影
07-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值