# rm_simple_simulator **Repository Path**: CeasarSmj/rm_simple_simulator ## Basic Information - **Project Name**: rm_simple_simulator - **Description**: No description available - **Primary Language**: Python - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-23 - **Last Updated**: 2025-10-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 简易 RM 3v3 模拟器 ## 需求分析 - 对局: - 场地 - 机器人 - 规则 - 可视化 - opencv - pygame [√] - pyqt5 - 键盘接口、代码接口 - TCP接口 进程安排: - 主进程 - 对局管理类:加载资源、管理对局、提供画面、执行输入 - 通信 server - 键盘接口进程 - 对局输入类: 显示对局画面 、 接收键盘输入 - 通信 client ## 文件结构 ``` rm_simple_simulator/ |- resources/ # 存储地图、机器人结构和参数文件 |- resourceloader.py # 加载资源,缩放图像等 |- environment.py # 对局管理类,使用 gym 框架 |- server_client.py # 通过 TCP 通信的方式传递视野的动作 |- server_main.py # 启动服务器进程,包括初始化环境(environment),初始化通信的服务端(server_client),完成所有连接的建立之后根据环境接口进行更新。连接时需要进行角色分配,需要分配的角色由一个矩阵表示 {infantry, hero, sentry} x {red, blue} ,矩阵中的值为 1 表示需要连接和参与分配,0表示使用随即动作。 |- client_main.py # 启动客户端进程,利用一个pygame窗口来显示接收到的机器人视野、血量等信息,并接收键盘动作作为动作输入。键盘动作为 wasd 控制方向(支持两个键同时按下表示向45度方向移动),空格表示攻击。 ``` ## 资源类 用一个类 ResourcesLoader 加载资源。 包括下面几种的yaml和对应的图片文件。 加载完图片文件之后要根据说明进行解析,提取出各个组件的位置以及各个层图像,然后进行缩放,使得不同元件的大小符合同一个比例尺。 比例尺取固定值,参见参数文件。 注:统一整个项目的编码为 utf-8 ### 场地地图 图片文件,使用的颜色标记和含义如下: | 名称 | 颜色 | | :----------- | :------ | | 可行区域 | #FFFFFF | | 障碍物 | #000000 | | 红方步兵 | #FF0000 | | 红方英雄 | #FFa0a0 | | 红方哨兵 | #FFb0b0 | | 蓝方步兵 | #0000FF | | 蓝方英雄 | #a0a0FF | | 蓝方哨兵 | #b0b0FF | | 增益区域 | #00FF00 | | 红方装弹区域 | #FFFF00 | | 蓝方装弹区域 | #00FFFF | 其中步兵、英雄、哨兵单位的起始位置表示为大小为 1pixel 地图文件的路径、比例等信息由一个 yaml 文件所描述。 ["resources/map.yaml"] ```yaml path: "map.bmp" real_width: 12 # meters real_height: 8 # meters ``` ### 机器人结构 图片文件,使用颜色标记和含义如下: | 颜色 | 含义 | | :------ | :------------------------------------------------- | | #000000 | 机器人本体 | | #00FF00 | 装甲板,可视化时改为对应方的颜色 | | #FF00FF | 机器人种类标记,用于在可视化的时候区分某一方的兵种 | 机器人结构文件路径和比例由一个 yaml 文件所描述。 ["resources/robot.yaml"] example: 同上 ### 参数文件 一个yaml文件。 ["resources/params.yaml"] ```yaml # 缩放参数 resolution: 100 # pixels/meters # 全局参数 time: 300 # seconds 一局的时间长度 victory_reward: 200 # 胜利分数 robot_vision: 3 # meters # 机器人属性 infantry_hp: 300 # 步兵生命值 hero_hp: 300 # 英雄生命值 sentry_hp: 700 # 哨兵生命值 infantry_reborn_time: 10 # s 步兵重生需要的时间 hero_reborn_time: 10 # s 英雄重生需要时间 sentry_reborn_time: 300 # s 哨兵重生需要时间 infantry_attack: 10 # 步兵攻击力 hero_attack: 50 # 英雄攻击力 sentry_attack: 10 # 哨兵攻击力 infantry_attack_frequency: 2 # 次/秒 步兵攻击频率 hero_attack_frequency: 0.5 # 次/秒 英雄攻击频率 sentry_attack_frequency: 5 # 次/秒 哨兵攻击频率 infantry_bullet_max: 100 # 步兵最大弹数 hero_bullet_max: 10 # 英雄最大弹数 sentry_bullet_max: 800 # 哨兵最大弹数 bullet_load_time: 2.0 attack_accuracy_1m: 0.95 # 1m 距离内的单发命中率 attack_accuracy_2m: 0.9 # 2m 距离内的单发命中率 attack_accuracy_3m: 0.7 # 3m 距离内的单发命中率 attack_accuracy_5m: 0.6 # 5m 距离内的单发命中率 attack_accuracy_8m: 0.1 # 8m 距离内的单发命中率 attack_accuracy_20m: 0.0 # 20m 距离内的单发命中率 # 得分规则 reward_center_occupy: 1 # 占领中央增益区域的每秒的得分 reward_kill: 20 # 击杀对手得分 ``` ## 主程序代码 environment.py 中的类 rm_simple_simulator ,负责管理、渲染、执行动作等。 使用 gym 框架,所以需要实现动作、观测等接口 ### 观测/动作/奖励值 观测:是一个表示机器人视野的RGB图像,大小根据参数文件算出来,位置根据机器人的位置进行计算。视野框根据机器人移动,但是不跟随它旋转。 动作: 单个机器人的动作为:[velocity, rotation, if_attack] 类型为 [float,float,bool],表示机器人的移动和攻击行为。 对于整个环境的动作为: num_robots * [velocity, rotation, if_attack] 奖励值参考规则部分。 ### 移动和碰撞检测 碰撞检测时检查机器人四个角是否在障碍物上,也要检查机器人的四个角是否在其它机器人内部。 ### 攻击索敌 action 中 attack 为 True 时,进行攻击。 攻击目标的选取规则为: 1. 遍历对方机器人,把其中和本机器人直线连通的(本机器人和目标中间的1pixel粗细的直线上没有障碍物)放到攻击选取列表中 2. 计算本机器人到攻击选取列表中的机器人的单位方向矢量,放到攻击选取矢量列表中 3. 把本机器人自身的方向矢量和攻击选取矢量列表中的矢量进行内积计算,得到本机器人对攻击选取列表中机器人的得分 4. 选取得分最高的机器人进行攻击 5. 攻击是否命中根据参数文件中的写法处理 6. 不论是否命中都算发射,下一次发射还是要等设计冷却时间 ### 载弹量机制 每个机器人最大载弹量参考参数文件。子弹打完后去己方装弹区域可以在经历一段时间后把子弹回满。 同时装弹区域还可以把血量回满,CD和装弹相同。 ### 规则 比赛规则本身是类似于战地,占领中央增益区域或者击败敌人可以得分。具体分数参考参数文件。 #### 结束和胜负规则 如果双方都没有达到胜利分数,则时间到后分数高的获胜。 如果时间到之前一方达到胜利分数,则次方获胜。 #### 占领中央增益区域 当中央增益区域中只有一方机器人时算此方占据。 #### 命中率 距离越远命中率越低,采用线性插值。 #### 重生 机器人被击杀后需要一定时间后才能够重生,具体时间参考参数文件。 ### 渲染 使用 pygame 绘制。 渲染的内容来自主类中的比赛进程等。 渲染顺序为先地图,然后逐个绘制机器人。 #### 地图 只保留黑白和中央增益区域进行显示 #### 机器人 根据机器人的分配进行颜色修改。 - 装甲板颜色,根据队伍分配修改为红色或者蓝色。 - 机器人种类标记,根据种类修改为下面的颜色: | 种类 | 颜色 | | :--- | :------ | | 步兵 | #000000 | | 英雄 | #00FF00 | | 哨兵 | #FF00FF | ## 通信 通过 TCP 通信,将动作传递给服务器,服务器将动作传递给客户端。 代码存放为:server_client.py ,服务端和客户端的通信各自写成一个类,供服务器进程和客户端进程调用。 ### 通信协议 | 数据名称 | 数据含义 | 方向 | | :--------------------- | :-------------------------------------------------------------------------------------------- | :--------------- | | connection_established | 连接建立成功,内容为机器人队伍和类型 | 服务器 -> 客户端 | | observation | 观测数据, 内容为[观测RGB图像,reward,done,extrainfo={比赛时间,hp,attack_cooldown,reborn_time}] | 服务器 -> 客户端 | | action | 动作数据, 内容为[动作] | 客户端 -> 服务器 | ## 主进程 包括 server_main 和 client_main 两个文件,用于调用其它代码。 # 强化学习 - 各个智能体各自都用这一套相同的网络,自身信息可以从观测中获取,无需特异化 - 并行训练,估计当前设备能够接受的并行数为 4 - 采用 ppo 算法 - 各个智能体之间互相不要通信 - 除了 score 外,还额外以血量等为额外奖励项 - 除了视野外,额外信息中的自身血量和弹量等也作为网络输入的一部分 - 网络结构上使用 actor-critic 架构: - 双层卷积头[conv2d+relu+maxpool] + MLP 作为视觉头 - MLP 来融合视觉特征和其它输入 - 后面接 MLP 的 actor/critic 头 - 使用 tensorboard 可视化训练曲线 ## 文件结构 ``` rm_simple_simulator |- environment.py |- parallel_env.py // 并行训练,基于 environment.py 封装 |- model.py // 网络结构 |- ppo.py // ppo 算法的实现 |- train.py // 训练流程入口 ```