ui界面版本
This commit is contained in:
99
CHANGELOG.md
Normal file
99
CHANGELOG.md
Normal file
@@ -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` 中读取并使用新配置
|
||||
|
||||
141
README.md
Normal file
141
README.md
Normal file
@@ -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值)
|
||||
- 检查采集卡画面是否清晰
|
||||
- 确认游戏窗口位置正确
|
||||
|
||||
22
config.json
Normal file
22
config.json
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
111
config.py
Normal file
111
config.py
Normal file
@@ -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()
|
||||
|
||||
223
gui_config.py
Normal file
223
gui_config.py
Normal file
@@ -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('<<ListboxSelect>>', 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()
|
||||
|
||||
68
launcher.py
Normal file
68
launcher.py
Normal file
@@ -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()
|
||||
|
||||
69
main.py
69
main.py
@@ -1,37 +1,57 @@
|
||||
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
|
||||
}
|
||||
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:
|
||||
@@ -56,9 +76,11 @@ def yolo_shibie(im_PIL,detections,model):
|
||||
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,6 +111,7 @@ 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)
|
||||
@@ -109,8 +132,11 @@ def move_randomly(rw, k):
|
||||
time.sleep(suiji_t)
|
||||
keyboard.release() # Release
|
||||
return k + 1
|
||||
|
||||
def move_to(rw, mb):
|
||||
v=470
|
||||
"""使用配置的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))
|
||||
@@ -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
|
||||
print("🚀 开始自动化...")
|
||||
while True:
|
||||
detections = {
|
||||
'center': None,
|
||||
@@ -141,10 +169,10 @@ while True:
|
||||
'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)
|
||||
@@ -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]
|
||||
@@ -239,7 +266,6 @@ 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:
|
||||
@@ -273,8 +299,6 @@ while True:
|
||||
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出现了 但是没有中心
|
||||
@@ -298,3 +322,4 @@ while True:
|
||||
mouse_gui.send_data_absolute(rw[0], rw[1] - 110, may=1)
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
|
||||
325
main_new.py
Normal file
325
main_new.py
Normal file
@@ -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
|
||||
|
||||
300
main_old.py
Normal file
300
main_old.py
Normal file
@@ -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]:#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
|
||||
229
preview.py
Normal file
229
preview.py
Normal file
@@ -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('<Button-1>', 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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
# get_image 将在main.py中初始化
|
||||
get_image = None
|
||||
@@ -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):
|
||||
"""
|
||||
用贝塞尔曲线模拟鼠标移动(安全版)
|
||||
@@ -53,5 +70,6 @@ class Mouse_guiji():
|
||||
mouse.click1() # 点击右
|
||||
self.point=(x,y)
|
||||
|
||||
mouse_gui = Mouse_guiji()
|
||||
# mouse_gui 将在main.py中初始化
|
||||
mouse_gui = None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user