diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9fedd01 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,99 @@ +# 更新日志 + +## 最新更新 - 多组配置系统 + +### 主要变更 + +#### 1. 新增文件 + +- **config.py** - 配置管理器,支持多组配置 +- **gui_config.py** - 可视化配置界面(tkinter) +- **preview.py** - 采集卡预览窗口系统 +- **launcher.py** - 统一启动器 +- **README.md** - 使用文档 +- **config.json** - 配置文件(自动生成) + +#### 2. 修改的文件 + +- **main.py** - 完全重写,支持配置系统 + - 从配置文件加载参数 + - 支持动态读取v值 + - 移除硬编码配置 +- **utils/mouse.py** - 移除硬编码串口 + - 新增 `init_mouse_keyboard()` 函数 + - 支持从配置初始化 +- **utils/get_image.py** - 支持从配置初始化 + - GetImage改为延迟初始化 + +### 核心功能 + +1. **多组配置管理** + - 支持创建多个配置组 + - 每个配置组独立的串口、采集卡、速度设置 + - 可视化切换活动配置 + +2. **可视化配置界面** + - GUI界面管理所有配置 + - 实时编辑和保存 + - 支持添加/删除配置组 + +3. **采集卡预览系统** + - 网格方式预览多个采集卡 + - 点击放大查看 + - 实时画面更新 + +4. **灵活的参数配置** + - 串口配置(端口、波特率) + - 采集卡配置(索引、分辨率) + - 移动速度(v值)配置 + - 预览窗口配置 + +### 使用方式 + +#### 旧版本(已保留 main_old.py) +```bash +python main_old.py # 直接运行,使用硬编码配置 +``` + +#### 新版本 +```bash +# 方式1: 使用启动器(推荐) +python launcher.py + +# 方式2: 直接运行 +python main.py # 读取config.json的active配置 +``` + +### 配置示例 + +```json +{ + "groups": [ + { + "name": "配置组1", + "serial_port": "COM6", + "serial_baudrate": 9600, + "camera_index": 0, + "camera_width": 1920, + "camera_height": 1080, + "move_velocity": 470, // ← move_to函数中的v值 + "active": true + } + ] +} +``` + +### 兼容性 + +- ✅ 保留所有原有功能 +- ✅ YOLO模型无需修改 +- ✅ 游戏逻辑完全兼容 +- ⚠️ 旧代码会使用默认配置 + +### 下一步 + +如需添加更多配置: +1. 在 `config.py` 的 `default_config` 中添加新字段 +2. 在 `gui_config.py` 中添加对应的输入框 +3. 在 `main.py` 中读取并使用新配置 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..616f20b --- /dev/null +++ b/README.md @@ -0,0 +1,141 @@ +# 火炬之光自动化脚本 + +## 功能特点 + +- ✅ **多配置组管理** - 支持多组配置,可同时管理多套采集卡和串口设置 +- 🖥️ **可视化配置** - 提供图形化界面管理所有配置 +- 👁️ **采集卡预览** - 多窗口网格预览,点击可放大查看 +- ⚙️ **灵活配置** - 所有参数(串口、采集卡、移动速度等)均可配置 +- 🤖 **智能自动化** - 基于YOLO目标检测的游戏自动化 + +## 快速开始 + +### 1. 安装依赖 + +```bash +pip install opencv-python ultralytics ddddocr ch9329Comm serial PIL numpy +``` + +### 2. 启动程序 + +```bash +python launcher.py +``` + +启动器提供三个选项: +- **⚙️ 配置管理** - 配置串口、采集卡等参数 +- **👁️ 采集卡预览** - 实时预览采集卡画面 +- **🚀 启动自动化** - 启动游戏自动化脚本 + +### 3. 配置设置 + +首次使用请先点击"配置管理": + +1. **添加配置组** - 点击"添加组"创建新配置 +2. **设置参数**: + - 串口:选择设备串口号(如 COM6) + - 波特率:设置串口波特率(默认9600) + - 采集卡索引:选择采集卡设备号 + - 采集宽度/高度:设置采集分辨率 + - 移动速度:设置角色移动速度(v值) +3. **设为活动** - 选择要使用的配置组并设为活动 +4. **保存配置** - 点击"保存配置" + +### 4. 预览采集卡 + +点击"采集卡预览"可以: +- 网格方式查看所有活动的采集卡 +- 点击任意窗口可放大查看 +- 实时显示采集卡画面 + +### 5. 启动自动化 + +配置完成后,点击"启动自动化"开始游戏自动化。 + +## 配置说明 + +### 配置组结构 + +```json +{ + "groups": [ + { + "name": "配置组1", + "serial_port": "COM6", + "serial_baudrate": 9600, + "camera_index": 0, + "camera_width": 1920, + "camera_height": 1080, + "move_velocity": 470, + "active": false + } + ], + "display": { + "preview_window_width": 640, + "preview_window_height": 360, + "preview_columns": 2, + "preview_rows": 2, + "show_preview": true + } +} +``` + +### 关键参数说明 + +- **serial_port**: 串口号,如 COM1, COM6 +- **serial_baudrate**: 波特率,通常为 9600 +- **camera_index**: 采集卡索引,0/1/2... +- **camera_width/height**: 采集分辨率 +- **move_velocity**: 角色移动速度(v值),数值越大移动越快 + +## 文件结构 + +``` +huojv/ +├── launcher.py # 启动器 +├── main.py # 主程序(自动游戏) +├── config.py # 配置管理器 +├── gui_config.py # 配置GUI界面 +├── preview.py # 预览窗口 +├── utils/ +│ ├── get_image.py # 采集卡管理 +│ ├── mouse.py # 鼠标控制 +│ ├── shizi.py # OCR识别 +│ └── ... +├── best.pt # YOLO模型(战斗) +├── best0.pt # YOLO模型(城镇) +└── config.json # 配置文件(自动生成) +``` + +## 使用流程 + +1. 运行 `python launcher.py` +2. 点击"配置管理"设置参数 +3. 点击"采集卡预览"测试采集 +4. 点击"启动自动化"开始游戏 +5. 享受自动化! + +## 注意事项 + +- 确保采集卡和串口设备已正确连接 +- 串口号和采集卡索引需要根据实际情况设置 +- 移动速度(v值)需要根据游戏角色属性调整 +- 配置文件保存在 `config.json` + +## 问题排查 + +### 无法打开串口 +- 检查串口号是否正确 +- 确认串口设备已连接 +- 检查其他程序是否占用串口 + +### 无法打开采集卡 +- 检查采集卡索引是否正确(0, 1, 2...) +- 确认采集卡驱动已安装 +- 尝试不同的采集卡索引 + +### 识别不准确 +- 调整移动速度(v值) +- 检查采集卡画面是否清晰 +- 确认游戏窗口位置正确 + diff --git a/config.json b/config.json new file mode 100644 index 0000000..c4b682c --- /dev/null +++ b/config.json @@ -0,0 +1,22 @@ +{ + "groups": [ + { + "name": "配置组1", + "serial_port": "COM6", + "serial_baudrate": 9600, + "camera_index": 0, + "camera_width": 1920, + "camera_height": 1080, + "move_velocity": 470, + "active": true + } + ], + "display": { + "preview_window_width": 640, + "preview_window_height": 360, + "preview_columns": 2, + "preview_rows": 2, + "show_preview": true + } +} + diff --git a/config.py b/config.py new file mode 100644 index 0000000..eeb719d --- /dev/null +++ b/config.py @@ -0,0 +1,111 @@ +import json +import os + +class ConfigManager: + """配置管理器""" + def __init__(self, config_file='config.json'): + self.config_file = config_file + self.default_config = { + "groups": [ + { + "name": "配置组1", + "serial_port": "COM6", + "serial_baudrate": 9600, + "camera_index": 0, + "camera_width": 1920, + "camera_height": 1080, + "move_velocity": 470, + "active": False + } + ], + "display": { + "preview_window_width": 640, + "preview_window_height": 360, + "preview_columns": 2, + "preview_rows": 2, + "show_preview": True + } + } + self.config = self.load_config() + + def load_config(self): + """加载配置文件""" + if os.path.exists(self.config_file): + try: + with open(self.config_file, 'r', encoding='utf-8') as f: + config = json.load(f) + # 确保有默认结构 + if 'groups' not in config: + config['groups'] = self.default_config['groups'] + if 'display' not in config: + config['display'] = self.default_config['display'] + return config + except Exception as e: + print(f"加载配置文件失败: {e}") + return self.default_config.copy() + return self.default_config.copy() + + def save_config(self): + """保存配置文件""" + try: + with open(self.config_file, 'w', encoding='utf-8') as f: + json.dump(self.config, f, ensure_ascii=False, indent=4) + return True + except Exception as e: + print(f"保存配置文件失败: {e}") + return False + + def add_group(self, name=None): + """添加配置组""" + if name is None: + name = f"配置组{len(self.config['groups']) + 1}" + + new_group = { + "name": name, + "serial_port": "COM6", + "serial_baudrate": 9600, + "camera_index": len(self.config['groups']), + "camera_width": 1920, + "camera_height": 1080, + "move_velocity": 470, + "active": False + } + self.config['groups'].append(new_group) + return new_group + + def delete_group(self, index): + """删除配置组""" + if 0 <= index < len(self.config['groups']): + del self.config['groups'][index] + return True + return False + + def update_group(self, index, **kwargs): + """更新配置组""" + if 0 <= index < len(self.config['groups']): + self.config['groups'][index].update(kwargs) + return True + return False + + def get_active_group(self): + """获取当前活动的配置组""" + for group in self.config['groups']: + if group.get('active', False): + return group + return None + + def set_active_group(self, index): + """设置活动的配置组""" + for i, group in enumerate(self.config['groups']): + group['active'] = (i == index) + return True + + def get_group_by_index(self, index): + """根据索引获取配置组""" + if 0 <= index < len(self.config['groups']): + return self.config['groups'][index] + return None + +# 全局配置管理器实例 +config_manager = ConfigManager() + diff --git a/gui_config.py b/gui_config.py new file mode 100644 index 0000000..f960c54 --- /dev/null +++ b/gui_config.py @@ -0,0 +1,223 @@ +import tkinter as tk +from tkinter import ttk, messagebox, simpledialog +from config import config_manager +import threading + +class ConfigGUI: + """配置GUI界面""" + def __init__(self): + self.root = tk.Tk() + self.root.title("配置管理 - 火炬之光自动化") + self.root.geometry("800x600") + + self.selected_index = 0 + self.setup_ui() + self.load_current_config() + + def setup_ui(self): + """设置UI界面""" + # 左侧:配置组列表 + left_frame = ttk.Frame(self.root, padding="10") + left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=False) + + ttk.Label(left_frame, text="配置组列表", font=('Arial', 12, 'bold')).pack() + + # 配置组列表 + self.group_listbox = tk.Listbox(left_frame, width=20, height=25) + self.group_listbox.pack(fill=tk.BOTH, expand=True, pady=5) + self.group_listbox.bind('<>', self.on_group_select) + + # 按钮 + btn_frame = ttk.Frame(left_frame) + btn_frame.pack(fill=tk.X) + + ttk.Button(btn_frame, text="添加组", command=self.add_group).pack(fill=tk.X, pady=2) + ttk.Button(btn_frame, text="删除组", command=self.delete_group).pack(fill=tk.X, pady=2) + ttk.Button(btn_frame, text="设为活动", command=self.set_active).pack(fill=tk.X, pady=2) + + # 右侧:配置详情 + right_frame = ttk.Frame(self.root, padding="10") + right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) + + ttk.Label(right_frame, text="配置详情", font=('Arial', 12, 'bold')).pack() + + # 配置项 + self.config_vars = {} + + # 基本配置 + basic_frame = ttk.LabelFrame(right_frame, text="基本配置", padding="10") + basic_frame.pack(fill=tk.X, pady=5) + + self.create_entry(basic_frame, "name", "配置组名称:") + self.create_entry(basic_frame, "camera_index", "采集卡索引:") + self.create_entry(basic_frame, "camera_width", "采集宽度:") + self.create_entry(basic_frame, "camera_height", "采集高度:") + + # 串口配置 + serial_frame = ttk.LabelFrame(right_frame, text="串口配置", padding="10") + serial_frame.pack(fill=tk.X, pady=5) + + self.create_entry(serial_frame, "serial_port", "串口:") + self.create_entry(serial_frame, "serial_baudrate", "波特率:") + + # 游戏配置 + game_frame = ttk.LabelFrame(right_frame, text="游戏配置", padding="10") + game_frame.pack(fill=tk.X, pady=5) + + self.create_entry(game_frame, "move_velocity", "移动速度(v值):") + + # 预览配置 + preview_frame = ttk.LabelFrame(right_frame, text="预览配置", padding="10") + preview_frame.pack(fill=tk.X, pady=5) + + self.create_entry(preview_frame, "preview_width", "预览宽度:", prefix="display") + self.create_entry(preview_frame, "preview_height", "预览高度:", prefix="display") + self.create_entry(preview_frame, "preview_columns", "预览列数:", prefix="display") + self.create_entry(preview_frame, "preview_rows", "预览行数:", prefix="display") + + # 保存按钮 + save_frame = ttk.Frame(right_frame) + save_frame.pack(fill=tk.X, pady=10) + + ttk.Button(save_frame, text="保存配置", command=self.save_config).pack(side=tk.LEFT, padx=5) + ttk.Button(save_frame, text="启动预览", command=self.start_preview).pack(side=tk.LEFT, padx=5) + ttk.Button(save_frame, text="启动程序", command=self.start_program).pack(side=tk.LEFT, padx=5) + + def create_entry(self, parent, key, label, prefix=None): + """创建输入框""" + frame = ttk.Frame(parent) + frame.pack(fill=tk.X, pady=3) + + ttk.Label(frame, text=label, width=15).pack(side=tk.LEFT) + + var = tk.StringVar() + entry = ttk.Entry(frame, textvariable=var, width=25) + entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) + + # 保存变量引用 + if prefix: + full_key = f"{prefix}_{key}" + else: + full_key = key + self.config_vars[full_key] = var + + def load_current_config(self): + """加载当前配置到界面""" + # 加载配置组列表 + self.update_group_list() + + # 加载预览配置 + display = config_manager.config.get('display', {}) + for key in ['preview_width', 'preview_height', 'preview_columns', 'preview_rows']: + if key in display: + self.config_vars[f"display_{key}"].set(str(display[key])) + + def update_group_list(self): + """更新配置组列表""" + self.group_listbox.delete(0, tk.END) + for i, group in enumerate(config_manager.config['groups']): + status = "✓" if group.get('active', False) else " " + self.group_listbox.insert(tk.END, f"{status} {group['name']}") + + def on_group_select(self, event): + """选择配置组""" + selection = self.group_listbox.curselection() + if selection: + self.selected_index = selection[0] + self.load_group_config(self.selected_index) + + def load_group_config(self, index): + """加载配置组详情""" + group = config_manager.get_group_by_index(index) + if group: + for key, var in self.config_vars.items(): + if not key.startswith('display_'): + if key in group: + var.set(str(group[key])) + + def add_group(self): + """添加配置组""" + name = simpledialog.askstring("添加配置组", "请输入配置组名称:", initialvalue=f"配置组{len(config_manager.config['groups'])+1}") + if name: + config_manager.add_group(name) + self.update_group_list() + messagebox.showinfo("成功", f"已添加配置组: {name}") + + def delete_group(self): + """删除配置组""" + selection = self.group_listbox.curselection() + if not selection: + messagebox.showwarning("警告", "请先选择要删除的配置组") + return + + if messagebox.askyesno("确认", "确定要删除选中的配置组吗?"): + config_manager.delete_group(selection[0]) + self.update_group_list() + messagebox.showinfo("成功", "配置组已删除") + + def set_active(self): + """设置为活动配置组""" + selection = self.group_listbox.curselection() + if not selection: + messagebox.showwarning("警告", "请先选择要设为活动的配置组") + return + + config_manager.set_active_group(selection[0]) + self.update_group_list() + messagebox.showinfo("成功", "已设置为活动配置组") + + def save_config(self): + """保存当前编辑的配置""" + # 保存当前组的配置 + group = config_manager.get_group_by_index(self.selected_index) + if group: + for key, var in self.config_vars.items(): + if not key.startswith('display_'): + try: + value = int(var.get()) if var.get().isdigit() else var.get() + group[key] = value + except: + group[key] = var.get() + + # 保存预览配置 + display = config_manager.config.get('display', {}) + for key in ['preview_width', 'preview_height', 'preview_columns', 'preview_rows']: + var = self.config_vars.get(f"display_{key}") + if var: + try: + display[key] = int(var.get()) + except: + pass + config_manager.config['display'] = display + + if config_manager.save_config(): + messagebox.showinfo("成功", "配置已保存") + else: + messagebox.showerror("错误", "配置保存失败") + + def start_preview(self): + """启动预览窗口""" + # 保存配置 + self.save_config() + + # 启动预览窗口 + from preview import PreviewWindow + preview_thread = threading.Thread(target=lambda: PreviewWindow().run()) + preview_thread.daemon = True + preview_thread.start() + + def start_program(self): + """启动主程序""" + # 保存配置 + self.save_config() + + messagebox.showinfo("提示", "配置已保存,请运行主程序") + + def run(self): + """运行GUI""" + self.root.mainloop() + +if __name__ == "__main__": + app = ConfigGUI() + app.run() + diff --git a/launcher.py b/launcher.py new file mode 100644 index 0000000..d515ef7 --- /dev/null +++ b/launcher.py @@ -0,0 +1,68 @@ +""" +启动器 - 选择启动配置界面或主程序 +""" +import tkinter as tk +from tkinter import ttk +import subprocess +import sys + +def start_config_gui(): + """启动配置界面""" + subprocess.Popen([sys.executable, "gui_config.py"]) + +def start_preview(): + """启动预览窗口""" + subprocess.Popen([sys.executable, "preview.py"]) + +def start_main_program(): + """启动主程序""" + subprocess.Popen([sys.executable, "main.py"]) + +def create_launcher(): + """创建启动器界面""" + root = tk.Tk() + root.title("火炬之光自动化 - 启动器") + root.geometry("400x300") + root.resizable(False, False) + + # 标题 + title_frame = tk.Frame(root) + title_frame.pack(fill=tk.X, pady=20) + + tk.Label(title_frame, text="火炬之光自动化脚本", font=('Arial', 16, 'bold')).pack() + tk.Label(title_frame, text="请选择要启动的功能", font=('Arial', 10)).pack() + + # 按钮区域 + button_frame = tk.Frame(root) + button_frame.pack(fill=tk.BOTH, expand=True, padx=40, pady=20) + + # 配置界面按钮 + config_btn = tk.Button(button_frame, text="⚙️ 配置管理", + command=start_config_gui, + width=30, height=3, font=('Arial', 12)) + config_btn.pack(pady=10) + + # 预览窗口按钮 + preview_btn = tk.Button(button_frame, text="👁️ 采集卡预览", + command=start_preview, + width=30, height=3, font=('Arial', 12)) + preview_btn.pack(pady=10) + + # 主程序按钮 + main_btn = tk.Button(button_frame, text="🚀 启动自动化", + command=start_main_program, + width=30, height=3, font=('Arial', 12)) + main_btn.pack(pady=10) + + # 说明 + info_label = tk.Label(root, + text="提示:首次使用请先点击"配置管理"设置参数", + font=('Arial', 9), + fg='gray') + info_label.pack(side=tk.BOTTOM, pady=10) + + root.mainloop() + +if __name__ == "__main__": + create_launcher() + diff --git a/main.py b/main.py index 2d3f76c..5634de6 100644 --- a/main.py +++ b/main.py @@ -1,64 +1,86 @@ import cv2 -from utils.get_image import get_image -from utils.mouse import mouse_gui +from utils.get_image import GetImage +from utils.mouse import init_mouse_keyboard, Mouse_guiji from ultralytics import YOLO import time import serial import ch9329Comm -import time import random import math from utils import shizi +from config import config_manager + +# 加载YOLO模型 model = YOLO(r"best.pt").to('cuda') model0 = YOLO(r"best0.pt").to('cuda') +# 从配置加载 +active_group = config_manager.get_active_group() +if active_group is None: + print("❌ 没有活动的配置组,请在gui_config.py中设置") + exit(1) + +print(f"📋 使用配置组: {active_group['name']}") + +# 初始化串口和鼠标 +init_mouse_keyboard(active_group) + +# 初始化键盘和鼠标 keyboard = ch9329Comm.keyboard.DataComm() -mouse = ch9329Comm.mouse.DataComm(1920, 1080) -kong_detections = { - 'center': None, - 'next': None, - 'npc1': None, - 'npc2': None, - 'npc3': None, - 'npc4': None, - 'boss': None, - 'daojv': [], - 'gw': [], - 'zhaozi': None - } -left=0 -top=30 -k=0#控制转圈的方向 -panduan=False#是否在图内 -boss_pd=False#是否到boss关卡 -rw=(632,378) -def yolo_shibie(im_PIL,detections,model): - results = model(im_PIL)#目标检测 +from utils.mouse import mouse, mouse_gui # 导入已初始化的mouse和mouse_gui + +# 创建全局的mouse_gui实例 +mouse_gui = Mouse_guiji() + +# 初始化采集卡 +get_image = GetImage( + cam_index=active_group['camera_index'], + width=active_group['camera_width'], + height=active_group['camera_height'] +) + +print(f"✅ 初始化完成 - 串口:{active_group['serial_port']} 采集卡:{active_group['camera_index']}") + +# 全局变量 +left = 0 +top = 30 +k = 0 # 控制转圈的方向 +panduan = False # 是否在图内 +boss_pd = False # 是否到boss关卡 +rw = (632, 378) + +# 从配置读取移动速度 +v = active_group['move_velocity'] + +def yolo_shibie(im_PIL, detections, model): + results = model(im_PIL) # 目标检测 for result in results: for i in range(len(result.boxes.xyxy)): left, top, right, bottom = result.boxes.xyxy[i] scalar_tensor = result.boxes.cls[i] value = scalar_tensor.item() label = result.names[int(value)] - if label=='center'or label=='next' or label=='boss' or label=='zhaozi': - player_x = int(left+(right-left)/2) - player_y = int(top+(bottom-top)/2)+30 + if label == 'center' or label == 'next' or label == 'boss' or label == 'zhaozi': + player_x = int(left + (right - left) / 2) + player_y = int(top + (bottom - top) / 2) + 30 RW = [player_x, player_y] detections[label] = RW - elif label=='daojv' or label=='gw': + elif label == 'daojv' or label == 'gw': player_x = int(left + (right - left) / 2) player_y = int(top + (bottom - top) / 2) + 30 RW = [player_x, player_y] detections[label].append(RW) - elif label=='npc1' or label=='npc2' or label=='npc3' or label=='npc4': - player_x = int(left+(right-left)/2) - player_y = int(bottom)+30 + elif label == 'npc1' or label == 'npc2' or label == 'npc3' or label == 'npc4': + player_x = int(left + (right - left) / 2) + player_y = int(bottom) + 30 RW = [player_x, player_y] detections[label] = RW return detections + def sq(p1, p2): """计算两点之间的欧式距离""" return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) + def process_points(points): if not points: return None # 空列表情况 @@ -89,9 +111,10 @@ def process_points(points): best_point = p return best_point + def move_randomly(rw, k): k = k % 4 - suiji_t=float(random.randint(10,13)/10) + suiji_t = float(random.randint(10, 13) / 10) if k == 0: keyboard.send_data("66") time.sleep(suiji_t) @@ -109,18 +132,21 @@ def move_randomly(rw, k): time.sleep(suiji_t) keyboard.release() # Release return k + 1 -def move_to(rw,mb): - v=470 - if rw[0]>=mb[0]: + +def move_to(rw, mb): + """使用配置的v值""" + global v + v = active_group['move_velocity'] # 每次都从配置读取最新值 + if rw[0] >= mb[0]: keyboard.send_data("44") - time.sleep(float(abs(rw[0]-mb[0])/v)) + time.sleep(float(abs(rw[0] - mb[0]) / v)) keyboard.release() # Release else: keyboard.send_data("66") time.sleep(float(abs(rw[0] - mb[0]) / v)) keyboard.release() # Release - if rw[1]>=mb[1]: + if rw[1] >= mb[1]: keyboard.send_data("88") time.sleep(float(abs(rw[1] - mb[1]) / v)) keyboard.release() # Release @@ -128,7 +154,9 @@ def move_to(rw,mb): keyboard.send_data("22") time.sleep(float(abs(rw[1] - mb[1]) / v)) keyboard.release() -i=0 + +i = 0 +print("🚀 开始自动化...") while True: detections = { 'center': None, @@ -140,12 +168,12 @@ while True: 'boss': None, 'daojv': [], 'gw': [], - 'zhaozi':None - + 'zhaozi': None } - im_opencv = get_image.get_frame()#[RGB,PIL] - detections=yolo_shibie(im_opencv[1],detections,model) - if shizi.tuwai(im_opencv[0]): # 进图算法 + im_opencv = get_image.get_frame() # [RGB,PIL] + detections = yolo_shibie(im_opencv[1], detections, model) + + if shizi.tuwai(im_opencv[0]): # 进图算法 im_opencv = get_image.get_frame() # [RGB,PIL] detections = yolo_shibie(im_opencv[1], detections, model0) print('当前在城镇中') @@ -162,7 +190,7 @@ while True: continue elif detections['npc3'] is not None and detections['npc4'] is None: print("在npc3旁边,向右走") - mb = (rw[0], detections['npc3'][1]-50) + mb = (rw[0], detections['npc3'][1] - 50) move_to(rw, mb) mb = (rw[0] + 700, rw[1]) move_to(rw, mb) @@ -181,7 +209,6 @@ while True: continue else: print("离npc4有点远 点击进入") - move_to(rw, detections['npc4']) time.sleep(1) im_opencv = get_image.get_frame() # [RGB,PIL] @@ -189,22 +216,22 @@ while True: mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1) time.sleep(1) continue - elif shizi.tiaozhan(im_opencv[0]):#开启挑战 + elif shizi.tiaozhan(im_opencv[0]): # 开启挑战 print('进入塔4') - mouse_gui.send_data_absolute(left+1100,top+600,may=1) + mouse_gui.send_data_absolute(left + 1100, top + 600, may=1) time.sleep(0.3) - mouse_gui.send_data_absolute(left + 433, top + 455,may=1) + mouse_gui.send_data_absolute(left + 433, top + 455, may=1) panduan = True continue - elif shizi.jieshu(im_opencv[0]):#结束挑战 + elif shizi.jieshu(im_opencv[0]): # 结束挑战 print('结束挑战') - mouse_gui.send_data_absolute(left+542,top+644,may=1) + mouse_gui.send_data_absolute(left + 542, top + 644, may=1) time.sleep(0.8) - mouse_gui.send_data_absolute(left + 706, top + 454,may=1) + mouse_gui.send_data_absolute(left + 706, top + 454, may=1) continue - elif panduan :#图内情况 + elif panduan: # 图内情况 print("在图内") - if shizi.shuzi(im_opencv[0]) : + if shizi.shuzi(im_opencv[0]): boss_pd = True print("进入到boss!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") if shizi.fuhuo(im_opencv[0]): @@ -213,21 +240,21 @@ while True: mouse_gui.send_data_absolute(rw[0], rw[1], may=0) continue if detections['zhaozi'] is not None: - move_to(rw,detections['zhaozi']) + move_to(rw, detections['zhaozi']) continue - if len(detections['daojv'])!=0: - move_to(rw,process_points(detections['daojv'])) - for i in range(3+len(detections['daojv'])): + if len(detections['daojv']) != 0: + move_to(rw, process_points(detections['daojv'])) + for i in range(3 + len(detections['daojv'])): keyboard.send_data("AA") time.sleep(0.15) keyboard.release() continue - if shizi.tuichu(im_opencv[0]) and detections['next'] is None and len(detections['daojv'])==0 and len(detections['gw'])==0 and boss_pd: + if shizi.tuichu(im_opencv[0]) and detections['next'] is None and len(detections['daojv']) == 0 and len(detections['gw']) == 0 and boss_pd: print("识别到可以退出挑战!!!!!!!!!!!!!!!!!!") for i in range(3): time.sleep(0.5) - im_opencv = get_image.get_frame()#[RGB,PIL] + im_opencv = get_image.get_frame() # [RGB,PIL] detections = { 'center': None, 'next': None, @@ -239,52 +266,49 @@ while True: 'daojv': [], 'gw': [], 'zhaozi': None - } - detections = yolo_shibie(im_opencv[1], detections,model) - if detections['next'] is not None or len(detections['daojv'])!=0 or len(detections['gw'])!=0 or detections['boss'] is not None: + detections = yolo_shibie(im_opencv[1], detections, model) + if detections['next'] is not None or len(detections['daojv']) != 0 or len(detections['gw']) != 0 or detections['boss'] is not None: break else: - mouse_gui.send_data_absolute(left + 640, top + 40,may=1)#点击退出 - panduan = False#退出挑战 + mouse_gui.send_data_absolute(left + 640, top + 40, may=1) # 点击退出 + panduan = False # 退出挑战 boss_pd = False time.sleep(2.0) continue - if detections['center'] is None and detections['next'] is None and (len(detections['gw'])!=0 or detections["boss"] is not None):#识别不到中心情况 但是有怪物 + if detections['center'] is None and detections['next'] is None and (len(detections['gw']) != 0 or detections["boss"] is not None): # 识别不到中心情况 但是有怪物 print("未检测到中心点,但是有怪物") - if len(detections['gw'])!=0: - move_to(rw,detections['gw'][0]) + if len(detections['gw']) != 0: + move_to(rw, detections['gw'][0]) time.sleep(0.26) - elif detections['boss'] is not None:#跟随boss - boss_suiji1=random.randint(-30,30) + elif detections['boss'] is not None: # 跟随boss + boss_suiji1 = random.randint(-30, 30) boss_suiji2 = random.randint(-30, 30) - detections['boss']=(detections['boss'][0]+boss_suiji1,detections['boss'][1]+boss_suiji2) - move_to(rw,detections['boss']) + detections['boss'] = (detections['boss'][0] + boss_suiji1, detections['boss'][1] + boss_suiji2) + move_to(rw, detections['boss']) time.sleep(0.7) continue - elif (detections['center'] is not None and detections['next'] is None and len(detections['gw'])!=0) or (boss_pd==True and detections['center'] is not None and detections['next'] is None) :#识别到中心 但是有怪物 - if detections['center'][0]>=rw[0] and detections['center'][1]rw[1]:#1 - mb=(rw[0]-100, rw[1]) - elif detections['center'][0]>=rw[0] and detections['center'][1]>rw[1]:#2 - mb=(rw[0], rw[1]+100) + elif (detections['center'] is not None and detections['next'] is None and len(detections['gw']) != 0) or (boss_pd == True and detections['center'] is not None and detections['next'] is None): # 识别到中心 但是有怪物 + if detections['center'][0] >= rw[0] and detections['center'][1] < rw[1]: # 3 + mb = (rw[0] + 100, rw[1]) + elif detections['center'][0] <= rw[0] and detections['center'][1] < rw[1]: # 4 + mb = (rw[0], rw[1] - 100) + elif detections['center'][0] <= rw[0] and detections['center'][1] > rw[1]: # 1 + mb = (rw[0] - 100, rw[1]) + elif detections['center'][0] >= rw[0] and detections['center'][1] > rw[1]: # 2 + mb = (rw[0], rw[1] + 100) move_to(rw, mb) time.sleep(0.25) - - continue - elif boss_pd==True and detections['center'] is None and detections['next'] is None:#boss出现了 但是没有中心 - k=move_randomly(rw,k) + elif boss_pd == True and detections['center'] is None and detections['next'] is None: # boss出现了 但是没有中心 + k = move_randomly(rw, k) continue elif detections['next'] is not None: print('进入下一层啦啦啦啦啦啦') panduan = True - move_to(rw,detections['next']) + move_to(rw, detections['next']) for i in range(2): keyboard.send_data("DD") time.sleep(0.15) @@ -298,3 +322,4 @@ while True: mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1) time.sleep(1) continue + diff --git a/main_new.py b/main_new.py new file mode 100644 index 0000000..5634de6 --- /dev/null +++ b/main_new.py @@ -0,0 +1,325 @@ +import cv2 +from utils.get_image import GetImage +from utils.mouse import init_mouse_keyboard, Mouse_guiji +from ultralytics import YOLO +import time +import serial +import ch9329Comm +import random +import math +from utils import shizi +from config import config_manager + +# 加载YOLO模型 +model = YOLO(r"best.pt").to('cuda') +model0 = YOLO(r"best0.pt").to('cuda') + +# 从配置加载 +active_group = config_manager.get_active_group() +if active_group is None: + print("❌ 没有活动的配置组,请在gui_config.py中设置") + exit(1) + +print(f"📋 使用配置组: {active_group['name']}") + +# 初始化串口和鼠标 +init_mouse_keyboard(active_group) + +# 初始化键盘和鼠标 +keyboard = ch9329Comm.keyboard.DataComm() +from utils.mouse import mouse, mouse_gui # 导入已初始化的mouse和mouse_gui + +# 创建全局的mouse_gui实例 +mouse_gui = Mouse_guiji() + +# 初始化采集卡 +get_image = GetImage( + cam_index=active_group['camera_index'], + width=active_group['camera_width'], + height=active_group['camera_height'] +) + +print(f"✅ 初始化完成 - 串口:{active_group['serial_port']} 采集卡:{active_group['camera_index']}") + +# 全局变量 +left = 0 +top = 30 +k = 0 # 控制转圈的方向 +panduan = False # 是否在图内 +boss_pd = False # 是否到boss关卡 +rw = (632, 378) + +# 从配置读取移动速度 +v = active_group['move_velocity'] + +def yolo_shibie(im_PIL, detections, model): + results = model(im_PIL) # 目标检测 + for result in results: + for i in range(len(result.boxes.xyxy)): + left, top, right, bottom = result.boxes.xyxy[i] + scalar_tensor = result.boxes.cls[i] + value = scalar_tensor.item() + label = result.names[int(value)] + if label == 'center' or label == 'next' or label == 'boss' or label == 'zhaozi': + player_x = int(left + (right - left) / 2) + player_y = int(top + (bottom - top) / 2) + 30 + RW = [player_x, player_y] + detections[label] = RW + elif label == 'daojv' or label == 'gw': + player_x = int(left + (right - left) / 2) + player_y = int(top + (bottom - top) / 2) + 30 + RW = [player_x, player_y] + detections[label].append(RW) + elif label == 'npc1' or label == 'npc2' or label == 'npc3' or label == 'npc4': + player_x = int(left + (right - left) / 2) + player_y = int(bottom) + 30 + RW = [player_x, player_y] + detections[label] = RW + return detections + +def sq(p1, p2): + """计算两点之间的欧式距离""" + return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) + +def process_points(points): + if not points: + return None # 空列表情况 + + n = len(points) + + if n == 1: + # 只有一个点,直接返回 + return points[0] + + elif n == 2: + # 两个点取中点 + x = (points[0][0] + points[1][0]) / 2 + y = (points[0][1] + points[1][1]) / 2 + return [x, y] + + else: + # 随机选3个点 + sample_points = random.sample(points, 3) + + # 对每个点计算到这3个点的总距离 + min_sum = float('inf') + best_point = None + for p in points: + dist_sum = sum(sq(p, sp) for sp in sample_points) + if dist_sum < min_sum: + min_sum = dist_sum + best_point = p + + return best_point + +def move_randomly(rw, k): + k = k % 4 + suiji_t = float(random.randint(10, 13) / 10) + if k == 0: + keyboard.send_data("66") + time.sleep(suiji_t) + keyboard.release() # Release + elif k == 1: + keyboard.send_data("88") + time.sleep(suiji_t) + keyboard.release() # Release + elif k == 2: + keyboard.send_data("44") + time.sleep(suiji_t) + keyboard.release() # Release + elif k == 3: + keyboard.send_data("22") + time.sleep(suiji_t) + keyboard.release() # Release + return k + 1 + +def move_to(rw, mb): + """使用配置的v值""" + global v + v = active_group['move_velocity'] # 每次都从配置读取最新值 + if rw[0] >= mb[0]: + keyboard.send_data("44") + time.sleep(float(abs(rw[0] - mb[0]) / v)) + keyboard.release() # Release + else: + keyboard.send_data("66") + time.sleep(float(abs(rw[0] - mb[0]) / v)) + keyboard.release() # Release + + if rw[1] >= mb[1]: + keyboard.send_data("88") + time.sleep(float(abs(rw[1] - mb[1]) / v)) + keyboard.release() # Release + else: + keyboard.send_data("22") + time.sleep(float(abs(rw[1] - mb[1]) / v)) + keyboard.release() + +i = 0 +print("🚀 开始自动化...") +while True: + detections = { + 'center': None, + 'next': None, + 'npc1': None, + 'npc2': None, + 'npc3': None, + 'npc4': None, + 'boss': None, + 'daojv': [], + 'gw': [], + 'zhaozi': None + } + im_opencv = get_image.get_frame() # [RGB,PIL] + detections = yolo_shibie(im_opencv[1], detections, model) + + if shizi.tuwai(im_opencv[0]): # 进图算法 + im_opencv = get_image.get_frame() # [RGB,PIL] + detections = yolo_shibie(im_opencv[1], detections, model0) + print('当前在城镇中') + if detections['npc1'] is not None and sq(rw, detections['npc1']) > 80: + print("向npc1靠近") + print(sq(rw, detections['npc1'])) + move_to(rw, detections['npc1']) + continue + elif detections['npc1'] is not None and sq(rw, detections['npc1']) <= 80: + print("在npc旁边,向上走") + print(sq(rw, detections['npc1'])) + mb = (detections['npc1'][0], detections['npc1'][1] - 1010) + move_to(rw, mb) + continue + elif detections['npc3'] is not None and detections['npc4'] is None: + print("在npc3旁边,向右走") + mb = (rw[0], detections['npc3'][1] - 50) + move_to(rw, mb) + mb = (rw[0] + 700, rw[1]) + move_to(rw, mb) + continue + elif detections['npc4'] is not None: + if sq(detections['npc4'], rw) < 50: + print("离npc4很近 直接进入") + keyboard.send_data("DD") + time.sleep(0.15) + keyboard.release() + time.sleep(1) + im_opencv = get_image.get_frame() # [RGB,PIL] + if shizi.daoying(im_opencv[0]): + mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1) + time.sleep(1) + continue + else: + print("离npc4有点远 点击进入") + move_to(rw, detections['npc4']) + time.sleep(1) + im_opencv = get_image.get_frame() # [RGB,PIL] + if shizi.daoying(im_opencv[0]): + mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1) + time.sleep(1) + continue + elif shizi.tiaozhan(im_opencv[0]): # 开启挑战 + print('进入塔4') + mouse_gui.send_data_absolute(left + 1100, top + 600, may=1) + time.sleep(0.3) + mouse_gui.send_data_absolute(left + 433, top + 455, may=1) + panduan = True + continue + elif shizi.jieshu(im_opencv[0]): # 结束挑战 + print('结束挑战') + mouse_gui.send_data_absolute(left + 542, top + 644, may=1) + time.sleep(0.8) + mouse_gui.send_data_absolute(left + 706, top + 454, may=1) + continue + elif panduan: # 图内情况 + print("在图内") + if shizi.shuzi(im_opencv[0]): + boss_pd = True + print("进入到boss!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + if shizi.fuhuo(im_opencv[0]): + print('点击复活') + mouse_gui.send_data_absolute(left + 536, top + 627, may=1) + mouse_gui.send_data_absolute(rw[0], rw[1], may=0) + continue + if detections['zhaozi'] is not None: + move_to(rw, detections['zhaozi']) + continue + if len(detections['daojv']) != 0: + move_to(rw, process_points(detections['daojv'])) + for i in range(3 + len(detections['daojv'])): + keyboard.send_data("AA") + time.sleep(0.15) + keyboard.release() + continue + if shizi.tuichu(im_opencv[0]) and detections['next'] is None and len(detections['daojv']) == 0 and len(detections['gw']) == 0 and boss_pd: + print("识别到可以退出挑战!!!!!!!!!!!!!!!!!!") + for i in range(3): + time.sleep(0.5) + + im_opencv = get_image.get_frame() # [RGB,PIL] + detections = { + 'center': None, + 'next': None, + 'npc1': None, + 'npc2': None, + 'npc3': None, + 'npc4': None, + 'boss': None, + 'daojv': [], + 'gw': [], + 'zhaozi': None + } + detections = yolo_shibie(im_opencv[1], detections, model) + if detections['next'] is not None or len(detections['daojv']) != 0 or len(detections['gw']) != 0 or detections['boss'] is not None: + break + else: + mouse_gui.send_data_absolute(left + 640, top + 40, may=1) # 点击退出 + panduan = False # 退出挑战 + boss_pd = False + time.sleep(2.0) + continue + if detections['center'] is None and detections['next'] is None and (len(detections['gw']) != 0 or detections["boss"] is not None): # 识别不到中心情况 但是有怪物 + print("未检测到中心点,但是有怪物") + if len(detections['gw']) != 0: + move_to(rw, detections['gw'][0]) + time.sleep(0.26) + elif detections['boss'] is not None: # 跟随boss + boss_suiji1 = random.randint(-30, 30) + boss_suiji2 = random.randint(-30, 30) + detections['boss'] = (detections['boss'][0] + boss_suiji1, detections['boss'][1] + boss_suiji2) + move_to(rw, detections['boss']) + time.sleep(0.7) + continue + elif (detections['center'] is not None and detections['next'] is None and len(detections['gw']) != 0) or (boss_pd == True and detections['center'] is not None and detections['next'] is None): # 识别到中心 但是有怪物 + if detections['center'][0] >= rw[0] and detections['center'][1] < rw[1]: # 3 + mb = (rw[0] + 100, rw[1]) + elif detections['center'][0] <= rw[0] and detections['center'][1] < rw[1]: # 4 + mb = (rw[0], rw[1] - 100) + elif detections['center'][0] <= rw[0] and detections['center'][1] > rw[1]: # 1 + mb = (rw[0] - 100, rw[1]) + elif detections['center'][0] >= rw[0] and detections['center'][1] > rw[1]: # 2 + mb = (rw[0], rw[1] + 100) + move_to(rw, mb) + time.sleep(0.25) + continue + + elif boss_pd == True and detections['center'] is None and detections['next'] is None: # boss出现了 但是没有中心 + k = move_randomly(rw, k) + continue + + elif detections['next'] is not None: + print('进入下一层啦啦啦啦啦啦') + panduan = True + move_to(rw, detections['next']) + for i in range(2): + keyboard.send_data("DD") + time.sleep(0.15) + keyboard.release() + continue + + else: + k = move_randomly(rw, k) + continue + elif shizi.daoying(im_opencv[0]): + mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1) + time.sleep(1) + continue + diff --git a/main_old.py b/main_old.py new file mode 100644 index 0000000..2d3f76c --- /dev/null +++ b/main_old.py @@ -0,0 +1,300 @@ +import cv2 +from utils.get_image import get_image +from utils.mouse import mouse_gui +from ultralytics import YOLO +import time +import serial +import ch9329Comm +import time +import random +import math +from utils import shizi +model = YOLO(r"best.pt").to('cuda') +model0 = YOLO(r"best0.pt").to('cuda') + +keyboard = ch9329Comm.keyboard.DataComm() +mouse = ch9329Comm.mouse.DataComm(1920, 1080) +kong_detections = { + 'center': None, + 'next': None, + 'npc1': None, + 'npc2': None, + 'npc3': None, + 'npc4': None, + 'boss': None, + 'daojv': [], + 'gw': [], + 'zhaozi': None + } +left=0 +top=30 +k=0#控制转圈的方向 +panduan=False#是否在图内 +boss_pd=False#是否到boss关卡 +rw=(632,378) +def yolo_shibie(im_PIL,detections,model): + results = model(im_PIL)#目标检测 + for result in results: + for i in range(len(result.boxes.xyxy)): + left, top, right, bottom = result.boxes.xyxy[i] + scalar_tensor = result.boxes.cls[i] + value = scalar_tensor.item() + label = result.names[int(value)] + if label=='center'or label=='next' or label=='boss' or label=='zhaozi': + player_x = int(left+(right-left)/2) + player_y = int(top+(bottom-top)/2)+30 + RW = [player_x, player_y] + detections[label] = RW + elif label=='daojv' or label=='gw': + player_x = int(left + (right - left) / 2) + player_y = int(top + (bottom - top) / 2) + 30 + RW = [player_x, player_y] + detections[label].append(RW) + elif label=='npc1' or label=='npc2' or label=='npc3' or label=='npc4': + player_x = int(left+(right-left)/2) + player_y = int(bottom)+30 + RW = [player_x, player_y] + detections[label] = RW + return detections +def sq(p1, p2): + """计算两点之间的欧式距离""" + return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) +def process_points(points): + if not points: + return None # 空列表情况 + + n = len(points) + + if n == 1: + # 只有一个点,直接返回 + return points[0] + + elif n == 2: + # 两个点取中点 + x = (points[0][0] + points[1][0]) / 2 + y = (points[0][1] + points[1][1]) / 2 + return [x, y] + + else: + # 随机选3个点 + sample_points = random.sample(points, 3) + + # 对每个点计算到这3个点的总距离 + min_sum = float('inf') + best_point = None + for p in points: + dist_sum = sum(sq(p, sp) for sp in sample_points) + if dist_sum < min_sum: + min_sum = dist_sum + best_point = p + + return best_point +def move_randomly(rw, k): + k = k % 4 + suiji_t=float(random.randint(10,13)/10) + if k == 0: + keyboard.send_data("66") + time.sleep(suiji_t) + keyboard.release() # Release + elif k == 1: + keyboard.send_data("88") + time.sleep(suiji_t) + keyboard.release() # Release + elif k == 2: + keyboard.send_data("44") + time.sleep(suiji_t) + keyboard.release() # Release + elif k == 3: + keyboard.send_data("22") + time.sleep(suiji_t) + keyboard.release() # Release + return k + 1 +def move_to(rw,mb): + v=470 + if rw[0]>=mb[0]: + keyboard.send_data("44") + time.sleep(float(abs(rw[0]-mb[0])/v)) + keyboard.release() # Release + else: + keyboard.send_data("66") + time.sleep(float(abs(rw[0] - mb[0]) / v)) + keyboard.release() # Release + + if rw[1]>=mb[1]: + keyboard.send_data("88") + time.sleep(float(abs(rw[1] - mb[1]) / v)) + keyboard.release() # Release + else: + keyboard.send_data("22") + time.sleep(float(abs(rw[1] - mb[1]) / v)) + keyboard.release() +i=0 +while True: + detections = { + 'center': None, + 'next': None, + 'npc1': None, + 'npc2': None, + 'npc3': None, + 'npc4': None, + 'boss': None, + 'daojv': [], + 'gw': [], + 'zhaozi':None + + } + im_opencv = get_image.get_frame()#[RGB,PIL] + detections=yolo_shibie(im_opencv[1],detections,model) + if shizi.tuwai(im_opencv[0]): # 进图算法 + im_opencv = get_image.get_frame() # [RGB,PIL] + detections = yolo_shibie(im_opencv[1], detections, model0) + print('当前在城镇中') + if detections['npc1'] is not None and sq(rw, detections['npc1']) > 80: + print("向npc1靠近") + print(sq(rw, detections['npc1'])) + move_to(rw, detections['npc1']) + continue + elif detections['npc1'] is not None and sq(rw, detections['npc1']) <= 80: + print("在npc旁边,向上走") + print(sq(rw, detections['npc1'])) + mb = (detections['npc1'][0], detections['npc1'][1] - 1010) + move_to(rw, mb) + continue + elif detections['npc3'] is not None and detections['npc4'] is None: + print("在npc3旁边,向右走") + mb = (rw[0], detections['npc3'][1]-50) + move_to(rw, mb) + mb = (rw[0] + 700, rw[1]) + move_to(rw, mb) + continue + elif detections['npc4'] is not None: + if sq(detections['npc4'], rw) < 50: + print("离npc4很近 直接进入") + keyboard.send_data("DD") + time.sleep(0.15) + keyboard.release() + time.sleep(1) + im_opencv = get_image.get_frame() # [RGB,PIL] + if shizi.daoying(im_opencv[0]): + mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1) + time.sleep(1) + continue + else: + print("离npc4有点远 点击进入") + + move_to(rw, detections['npc4']) + time.sleep(1) + im_opencv = get_image.get_frame() # [RGB,PIL] + if shizi.daoying(im_opencv[0]): + mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1) + time.sleep(1) + continue + elif shizi.tiaozhan(im_opencv[0]):#开启挑战 + print('进入塔4') + mouse_gui.send_data_absolute(left+1100,top+600,may=1) + time.sleep(0.3) + mouse_gui.send_data_absolute(left + 433, top + 455,may=1) + panduan = True + continue + elif shizi.jieshu(im_opencv[0]):#结束挑战 + print('结束挑战') + mouse_gui.send_data_absolute(left+542,top+644,may=1) + time.sleep(0.8) + mouse_gui.send_data_absolute(left + 706, top + 454,may=1) + continue + elif panduan :#图内情况 + print("在图内") + if shizi.shuzi(im_opencv[0]) : + boss_pd = True + print("进入到boss!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + if shizi.fuhuo(im_opencv[0]): + print('点击复活') + mouse_gui.send_data_absolute(left + 536, top + 627, may=1) + mouse_gui.send_data_absolute(rw[0], rw[1], may=0) + continue + if detections['zhaozi'] is not None: + move_to(rw,detections['zhaozi']) + continue + if len(detections['daojv'])!=0: + move_to(rw,process_points(detections['daojv'])) + for i in range(3+len(detections['daojv'])): + keyboard.send_data("AA") + time.sleep(0.15) + keyboard.release() + continue + if shizi.tuichu(im_opencv[0]) and detections['next'] is None and len(detections['daojv'])==0 and len(detections['gw'])==0 and boss_pd: + print("识别到可以退出挑战!!!!!!!!!!!!!!!!!!") + for i in range(3): + time.sleep(0.5) + + im_opencv = get_image.get_frame()#[RGB,PIL] + detections = { + 'center': None, + 'next': None, + 'npc1': None, + 'npc2': None, + 'npc3': None, + 'npc4': None, + 'boss': None, + 'daojv': [], + 'gw': [], + 'zhaozi': None + + } + detections = yolo_shibie(im_opencv[1], detections,model) + if detections['next'] is not None or len(detections['daojv'])!=0 or len(detections['gw'])!=0 or detections['boss'] is not None: + break + else: + mouse_gui.send_data_absolute(left + 640, top + 40,may=1)#点击退出 + panduan = False#退出挑战 + boss_pd = False + time.sleep(2.0) + continue + if detections['center'] is None and detections['next'] is None and (len(detections['gw'])!=0 or detections["boss"] is not None):#识别不到中心情况 但是有怪物 + print("未检测到中心点,但是有怪物") + if len(detections['gw'])!=0: + move_to(rw,detections['gw'][0]) + time.sleep(0.26) + elif detections['boss'] is not None:#跟随boss + boss_suiji1=random.randint(-30,30) + boss_suiji2 = random.randint(-30, 30) + detections['boss']=(detections['boss'][0]+boss_suiji1,detections['boss'][1]+boss_suiji2) + move_to(rw,detections['boss']) + time.sleep(0.7) + continue + elif (detections['center'] is not None and detections['next'] is None and len(detections['gw'])!=0) or (boss_pd==True and detections['center'] is not None and detections['next'] is None) :#识别到中心 但是有怪物 + if detections['center'][0]>=rw[0] and detections['center'][1]rw[1]:#1 + mb=(rw[0]-100, rw[1]) + elif detections['center'][0]>=rw[0] and detections['center'][1]>rw[1]:#2 + mb=(rw[0], rw[1]+100) + move_to(rw, mb) + time.sleep(0.25) + + + continue + + elif boss_pd==True and detections['center'] is None and detections['next'] is None:#boss出现了 但是没有中心 + k=move_randomly(rw,k) + continue + + elif detections['next'] is not None: + print('进入下一层啦啦啦啦啦啦') + panduan = True + move_to(rw,detections['next']) + for i in range(2): + keyboard.send_data("DD") + time.sleep(0.15) + keyboard.release() + continue + + else: + k = move_randomly(rw, k) + continue + elif shizi.daoying(im_opencv[0]): + mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1) + time.sleep(1) + continue diff --git a/preview.py b/preview.py new file mode 100644 index 0000000..d66636d --- /dev/null +++ b/preview.py @@ -0,0 +1,229 @@ +import cv2 +import tkinter as tk +from tkinter import Canvas +from PIL import Image, ImageTk +import threading +import numpy as np +from config import config_manager + +class PreviewWindow: + """采集卡预览窗口""" + def __init__(self): + self.config = config_manager.config + self.caps = {} + self.frames = {} + self.large_window = None + self.running = True + + def init_cameras(self): + """初始化所有相机""" + for i, group in enumerate(self.config['groups']): + if group.get('active', True): # 只加载活动的配置 + try: + cap = cv2.VideoCapture(group['camera_index'], cv2.CAP_DSHOW) + if cap.isOpened(): + cap.set(cv2.CAP_PROP_FRAME_WIDTH, group['camera_width']) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, group['camera_height']) + self.caps[i] = { + 'cap': cap, + 'group': group, + 'name': group['name'] + } + except Exception as e: + print(f"无法打开相机 {group['camera_index']}: {e}") + + def capture_frames(self): + """捕获帧""" + while self.running: + for idx, data in self.caps.items(): + ret, frame = data['cap'].read() + if ret: + # 裁剪到配置的区域 + height, width = frame.shape[:2] + crop_top = 30 + crop_bottom = min(crop_top + 720, height) + crop_width = min(1280, width) + frame = frame[crop_top:crop_bottom, 0:crop_width] + + # 转换颜色空间 + frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + self.frames[idx] = frame_rgb + + def create_grid_window(self): + """创建网格窗口""" + root = tk.Tk() + root.title("采集卡预览 - 点击放大") + root.geometry("1000x700") + + # 获取显示配置 + display = self.config.get('display', {}) + preview_width = display.get('preview_width', 640) + preview_height = display.get('preview_height', 360) + columns = display.get('preview_columns', 2) + rows = display.get('preview_rows', 2) + + canvas = Canvas(root, bg='black') + canvas.pack(fill=tk.BOTH, expand=True) + + # 存储图像对象 + self.photo_objects = {} + + def update_frames(): + """更新帧""" + while self.running: + try: + canvas.delete("all") + + # 计算每个预览窗口的位置和大小 + window_width = root.winfo_width() if root.winfo_width() > 1 else 1000 + window_height = root.winfo_height() if root.winfo_height() > 1 else 700 + + cell_width = window_width // columns + cell_height = window_height // rows + + frame_idx = 0 + for idx in self.caps.keys(): + if idx in self.frames: + row = frame_idx // columns + col = frame_idx % columns + + x = col * cell_width + y = row * cell_height + + # 调整图像大小 + frame = self.frames[idx] + h, w = frame.shape[:2] + scale = min(cell_width / w, cell_height / h) * 0.9 + new_w = int(w * scale) + new_h = int(h * scale) + + resized_frame = cv2.resize(frame, (new_w, new_h)) + + # 转换为PIL图像 + pil_image = Image.fromarray(resized_frame) + photo = ImageTk.PhotoImage(image=pil_image) + self.photo_objects[idx] = photo + + # 绘制图像 + center_x = x + cell_width // 2 + center_y = y + cell_height // 2 + canvas.create_image(center_x, center_y, image=photo, anchor='center') + + # 绘制标签 + name = self.caps[idx]['name'] + canvas.create_text(center_x, y + 20, text=name, fill='white', font=('Arial', 12, 'bold')) + + frame_idx += 1 + + canvas.update() + tk._default_root.update_idletasks() + except Exception as e: + print(f"更新帧错误: {e}") + + import time + time.sleep(0.03) # 约30fps + + def on_canvas_click(event): + """点击画布事件""" + window_width = root.winfo_width() + window_height = root.winfo_height() + cell_width = window_width // columns + cell_height = window_height // rows + + col = int(event.x // cell_width) + row = int(event.y // cell_height) + index = row * columns + col + + # 找到对应的配置 + if index < len(self.caps): + idx = list(self.caps.keys())[index] + self.show_large_window(idx) + + canvas.bind('', on_canvas_click) + + # 启动更新线程 + update_thread = threading.Thread(target=update_frames, daemon=True) + update_thread.start() + + def on_closing(): + """关闭窗口""" + self.running = False + for data in self.caps.values(): + data['cap'].release() + root.destroy() + + root.protocol("WM_DELETE_WINDOW", on_closing) + root.mainloop() + + def show_large_window(self, idx): + """显示大窗口""" + if self.large_window is not None and self.large_window.winfo_exists(): + self.large_window.destroy() + + self.large_window = tk.Tk() + self.large_window.title(f"放大视图 - {self.caps[idx]['name']}") + self.large_window.geometry("1280x720") + + canvas = Canvas(self.large_window, bg='black') + canvas.pack(fill=tk.BOTH, expand=True) + + photo_obj = {} + + def update_large_frame(): + """更新大窗口帧""" + while self.running: + try: + if idx in self.frames and self.large_window.winfo_exists(): + canvas.delete("all") + + frame = self.frames[idx] + h, w = frame.shape[:2] + + # 调整到窗口大小 + window_width = self.large_window.winfo_width() if self.large_window.winfo_width() > 1 else 1280 + window_height = self.large_window.winfo_height() if self.large_window.winfo_height() > 1 else 720 + + scale = min(window_width / w, window_height / h) + new_w = int(w * scale) + new_h = int(h * scale) + + resized_frame = cv2.resize(frame, (new_w, new_h)) + pil_image = Image.fromarray(resized_frame) + photo = ImageTk.PhotoImage(image=pil_image) + photo_obj['img'] = photo + + canvas.delete("all") + canvas.create_image(window_width // 2, window_height // 2, image=photo, anchor='center') + + self.large_window.update() + else: + break + except Exception as e: + print(f"更新大窗口错误: {e}") + break + + import time + time.sleep(0.03) + + update_thread = threading.Thread(target=update_large_frame, daemon=True) + update_thread.start() + + self.large_window.mainloop() + + def run(self): + """运行预览""" + self.init_cameras() + + # 启动捕获线程 + capture_thread = threading.Thread(target=self.capture_frames, daemon=True) + capture_thread.start() + + # 创建并显示网格窗口 + import time + time.sleep(0.5) # 等待几帧 + self.create_grid_window() + +if __name__ == "__main__": + preview = PreviewWindow() + preview.run() + diff --git a/utils/get_image.py b/utils/get_image.py index 6ffab60..36a12a2 100644 --- a/utils/get_image.py +++ b/utils/get_image.py @@ -45,6 +45,8 @@ import threading class GetImage: def __init__(self, cam_index=0, width=1920, height=1080): self.cap = cv2.VideoCapture(cam_index, cv2.CAP_DSHOW) + if not self.cap.isOpened(): + self.cap = cv2.VideoCapture(cam_index) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) self.frame = None @@ -70,4 +72,6 @@ class GetImage: time.sleep(0.2) self.cap.release() cv2.destroyAllWindows() -get_image = GetImage() \ No newline at end of file + +# get_image 将在main.py中初始化 +get_image = None \ No newline at end of file diff --git a/utils/mouse.py b/utils/mouse.py index 101c872..c46a5b3 100644 --- a/utils/mouse.py +++ b/utils/mouse.py @@ -1,17 +1,34 @@ import random import time import ch9329Comm -import time -import random import serial -serial.ser = serial.Serial('COM6', 9600) # 开启串口 -mouse = ch9329Comm.mouse.DataComm(1920, 1080) + +# 全局变量,由main.py初始化 +serial.ser = None +mouse = None + +def init_mouse_keyboard(config_group): + """初始化鼠标和串口""" + global serial, mouse + # 初始化串口 + serial.ser = serial.Serial( + config_group['serial_port'], + config_group['serial_baudrate'] + ) + # 初始化鼠标 + mouse = ch9329Comm.mouse.DataComm( + config_group['camera_width'], + config_group['camera_height'] + ) + print(f"✅ 串口已打开: {config_group['serial_port']} {config_group['serial_baudrate']}") + print(f"✅ 鼠标已初始化: {config_group['camera_width']}x{config_group['camera_height']}") def bezier_point(t, p0, p1, p2, p3): """计算三次贝塞尔曲线上的点""" x = (1-t)**3 * p0[0] + 3*(1-t)**2*t*p1[0] + 3*(1-t)*t**2*p2[0] + t**3*p3[0] y = (1-t)**3 * p0[1] + 3*(1-t)**2*t*p1[1] + 3*(1-t)*t**2*p2[1] + t**3*p3[1] return (x, y) + def move_mouse_bezier(mouse, start, end, duration=1, steps=120): """ 用贝塞尔曲线模拟鼠标移动(安全版) @@ -45,13 +62,14 @@ class Mouse_guiji(): def __init__(self): self.point=(0,0) - def send_data_absolute(self, x, y,may=0): + def send_data_absolute(self, x, y, may=0): move_mouse_bezier(mouse, self.point, (x,y), duration=1, steps=120) - if may == 1:#点击左 + if may == 1: # 点击左 mouse.click() elif may == 2: - mouse.click1()#点击右 + mouse.click1() # 点击右 self.point=(x,y) -mouse_gui = Mouse_guiji() +# mouse_gui 将在main.py中初始化 +mouse_gui = None