首页 > 编程语言 >空战编程实现1——小雅

空战编程实现1——小雅

时间:2024-03-24 18:58:14浏览次数:14  
标签:psi 小雅 编程 state 空战 np theta array self

小雅环境和自定义gym融合

自定义Gym环境并注册

Gym的环境都保存在gym/envs目录下,envs目录下包含了各种类型的环境例如:atari、classic_control等,我们研究飞行器等类型问题所建立的自定义环境一般可以放在classic_control中。

Step1:建立环境文件

在envs/classic_control目录下,建立环境文件ace.py

QQ截图20220411193622

ace.py中应定义一个环境类一般以XxxEnv为名称,本文建立类名称为AceEnv,按照gym的工作原理,此类中应至少包含reset()、step()、init()、seed()、close()函数,其中,step函数必须返回obs, reward, done, info四个变量,reset返回初始化的状态。(注:此部分的函数定义格式可以参考classic_control内的其他函数,着重是要按照需求更改step和reset函数)。另外需要对环境的主要变量维数进行定义,方便外部强化学习算法直接调用生成模型网络,例如控制量维度、状态量维度等。

# -*- coding: utf-8 -*-
"""
Created on Thu Nov 11 16:31:53 2021

@author:
"""

import gym
from gym import spaces
from gym.utils import seeding
import numpy as np
from os import path


# from numba import jit

class Ace3DEnv(gym.Env):
    metadata = {
        'render.modes': ['human', 'rgb_array'],
        'video.frames_per_second': 30
    }

    def __init__(self):

        self.simu_step = 0.5
        self.max_step = 100
        #测试开关量
        self.k_gain = np.array([1, 1, 1, 0.2, 0.2]) #指标因子 r,aa,ata,act1,act2
        self.noi = 1
        self.if_RL_or_Trad = 1 # RL=1, Tradition_control=0

        self.v = 200

        # 行动
        self.lim_dtheta = 40 / 57.3  # theta变化量,弧度/秒
        self.lim_dpsi = 40 / 57.3  # psi变化量,弧度/秒
        self.action_dim = 2  # 行动维度

        # 行动空间
        action_high = np.array([self.lim_dtheta, self.lim_dpsi], dtype=np.float32)
        action_low = np.array([-self.lim_dtheta, -self.lim_dpsi], dtype=np.float32)
        self.action_space = spaces.Box(low=action_low, high=action_high, dtype=np.float32)

        # 观测空间上下限
        self.max_r = 10000/1000
        self.max_dr = 400/100
        self.max_q1 = 1 * np.pi
        self.max_q2 = 1 * np.pi  # q,弧度制
        self.max_theta = 1 * np.pi
        self.max_psi = 1 * np.pi

        self.min_r = 0/1000
        self.min_dr = -400/100
        self.min_q1 = -1 * np.pi
        self.min_q2 = -1 * np.pi  # 弧度制
        self.min_theta = -1 * np.pi
        self.min_psi = -1 * np.pi

        self.state_dim = 8  # 状态维度

        #敌机b的行动,随机行动,限制其随机范围
        self.u_b_theta = 0.001 * np.random.uniform(-5,5)
        self.u_b_psi = 0.001 * np.random.uniform(-5,5)

        # 观测空间
        observation_high = np.array([self.max_r, self.max_dr, self.max_q1, self.max_q2, self.max_theta, self.max_psi,self.max_theta, self.max_psi],
                                    dtype=np.float32)
        observation_low = np.array([self.min_r, self.min_dr, self.min_q1, self.min_q2, self.min_theta, self.min_psi,self.min_theta, self.min_psi],
                                   dtype=np.float32)
        self.observation_space = spaces.Box(low=observation_low, high=observation_high, dtype=np.float32)

        self.state = None
        self.obs = None

        #定义我方起始位置的锥角
        self.zhuijiao = 45 / 180 * np.pi
        self.x_max = -100
        self.x_min = -5000

        #评分记录
        self.r_r = 0
        self.r_aa = 0
        self.r_ata = 0
        self.r_act1 = 0
        self.r_act2 = 0

        self.np_random = None
        self.k = 1
        self.viewer = None
        self.seed()

    def seed(self, seed=None):  # 随机种子
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def step(self, act):
        

        # 行动,环境更新,状态方程
        # 取出state里的数
        x_r, y_r, z_r, theta_r, psi_r, x_b, y_b, z_b, theta_b, psi_b = self.state
        # 取出双方状态
        # 我方x,y,z,角度1,角度2
        state_r = np.array([x_r, y_r, z_r, theta_r, psi_r])
        # 敌方x,y,z,角度1,角度2
        state_b = np.array([x_b, y_b, z_b, theta_b, psi_b])

        '''敌方运动'''
        # 决策
        command_b = [self.u_b_theta, self.u_b_psi]  # 随机运动
        # command_b = [0, 0]  # 随机运动
        # command_b = self.OptimalChoice_r(state_b, state_r)
        next_state_b = self.euler_func(state_b, command_b, self.simu_step, 0)

        '''我方运动'''
        # 决策
        if self.if_RL_or_Trad == 1:
            command_r = np.array([act[0], act[1]])              #采用外部给定控制量
            # command_r = np.array([0, 0.0001])
        else:
            command_r = self.OptimalChoice_r(state_r, state_b)  #采用传统策略控制量
        next_state_r = self.euler_func(state_r, command_r, self.simu_step, 0)

        # 更新状态空间,观测空间, 奖励, 判断是否终止
        state_ = np.append(next_state_r, next_state_b)
        self.state = state_.copy()

        observation = self.get_obs(self.state)

        rewards = self.score_Syn(act)

        done = bool(z_r < 100 or self.r/1000 > self.max_r or self.k > self.max_step)

        self.k += 1

        return observation, rewards, done, {}

    def score_Syn(self, act):
        """
        计算当前情形下,动作的得分(奖励)

        :param act:
        :return:
        """
        # r未归一化,AA与ATA为弧度
        act1 = abs( act[0] / self.lim_dtheta) #对act进行归一化
        act2 = abs( act[1] / self.lim_dpsi) #对act进行归一化
        AA = abs(self.AA)
        ATA = abs(self.ATA)
        r = self.r

        scoring_r = (10000 - r)**2 / 25e5 - 8  # [0,10000]=rewards:[-7,32]

        scoring_aa = 3 * (np.pi - AA)**2 - 10  # aa:[0-180] -> rewards:[-10,20]

        scoring_ata = 3 * (np.pi - ATA)**2 - 10  # ata:[0-180] -> rewards:[-10,20]

        scoring_act1 = 20 * (1 - act1)**2 - 15  # act:[0,1] -> rewards[-15,5] 侧重惩罚项
        scoring_act2 = 20 * (1 - act2)**2 - 15  # act:[0,1] -> rewards[-15,5] 侧重惩罚项

        k = self.k_gain
        self.r_r    = k[0] * scoring_r
        self.r_aa   = k[1] * scoring_aa
        self.r_ata  = k[2] * scoring_ata
        self.r_act1 = k[3] * scoring_act1
        self.r_act2 = k[4] * scoring_act2

        scoring = k[0]*scoring_r + k[1]*scoring_aa + k[2]*scoring_ata + k[3]*scoring_act1 + k[4]*scoring_act2

        return scoring

    def reset(self, noi=True):

        # 重置状态
        self.k = 1

        ang_nosie = self.noi * np.random.uniform(low=-30, high=30, size=4)  # 初始指向随机值

        # 蓝方的状态初始值
        x_b = 2000
        y_b = 0
        z_b = 6500
        theta_b = ang_nosie[2] / 180 * np.pi
        psi_b = ang_nosie[3] / 180 * np.pi

        # 红方的状态初始值
        x_r = np.random.uniform(self.x_min,self.x_max)                    #锥形区域上随机选定距离,切出一个圆,x_r
        polar_radius_max = x_r * np.tan(self.zhuijiao)      #形成一个圆区域
        polar_radius = np.random.uniform(0,polar_radius_max)    #圆内以极坐标选取点(y,z),首先随机选半径:polar_r=[0,radius]
        polar_ang = np.random.uniform(0,2*np.pi)            #圆内以极坐标选取点(y,z),其次选角度:polar_angle=[0,2pi]
        y_r = np.cos(polar_ang) * polar_radius              # y = cos(ang)*radius
        z_r = np.sin(polar_ang) * polar_radius + z_b        # z = sin(ang)*radius

        theta_r = ang_nosie[0] / 180 * np.pi
        psi_r =   ang_nosie[1] / 180 * np.pi

        self.state = np.array([x_r, y_r, z_r, theta_r, psi_r, x_b, y_b, z_b, theta_b, psi_b])  # 环境状态

        return self.get_obs(self.state)

    def get_obs(self, state):
        """


        :param state:  当前状态空间,双方的位置、角度
        :return:  输出[r / 1000, dr / 100, AA, ATA, q_alpha, q_beta]
        """
        # 智能体观察值
        x_r, y_r, z_r, theta_r, psi_r, x_b, y_b, z_b, theta_b, psi_b = state
        v = self.v

        # 求r
        oyou = np.array([x_r, y_r, z_r])  # 友方(我方)位置矢量
        vx = v * np.cos(theta_r) * np.cos(psi_r)
        vy = v * np.cos(theta_r) * np.sin(psi_r)
        vz = v * np.sin(theta_r)
        vyou = np.array([vx, vy, vz])  # 友方(我方)速度矢量

        oenemy = np.array([x_b, y_b, z_b])  # 敌方(对方)位置矢量
        vx = v * np.cos(theta_b) * np.cos(psi_b)
        vy = v * np.cos(theta_b) * np.sin(psi_b)
        vz = v * np.sin(theta_b)
        venemy = np.array([vx, vy, vz])  # 敌方(对方)速度矢量

        LOS_you_enemy = oenemy - oyou  # 我方:敌我视线角
        # LOS_enemy_you = oyou - oenemy  # 敌方:敌我视线角

        self.AA = self.Cal3DAngle(venemy,LOS_you_enemy)  # 根据速度矢量(敌方)和双方位置连线,计算夹角(弧度制)
        self.ATA = self.Cal3DAngle(vyou,LOS_you_enemy)   ## 根据速度矢量(我方)和双方位置连线,计算夹角(弧度制)

        self.r = np.sqrt(LOS_you_enemy[0] ** 2 + LOS_you_enemy[1] ** 2 + LOS_you_enemy[2] ** 2)  #计算距离
        dr = np.sqrt(np.sum((vyou - venemy) ** 2))  #计算速度的

        q_alpha = np.arctan2(LOS_you_enemy[2], np.sign(LOS_you_enemy[0]) * np.sqrt(LOS_you_enemy[0] ** 2 + LOS_you_enemy[1] ** 2)) #纵向平面视线角 atarn2(z,sign(x)*sqrt(x^2+y^2))
        q_beta = np.arctan2(LOS_you_enemy[1], LOS_you_enemy[0])  # 侧向视线角 atan2(y,x) , 正psi产生正y

        return np.array([self.r / 1000, dr / 100, q_alpha, q_beta, theta_r, psi_r,theta_b, psi_b])

    def Cal2DAngle(self, v1, v2):
        """

        :param v1:
        :param v2:
        :return:
        """
        # v1旋转到v2,逆时针为正,顺时针为负
        # 2个向量模的乘积
        TheNorm = np.linalg.norm(v1) * np.linalg.norm(v2)
        # 叉乘
        rho = np.arcsin(np.cross(v1, v2) / TheNorm)
        # 点乘
        theta = np.arccos(np.dot(v1, v2) / TheNorm)
        if rho< 0:
            return - theta #弧度
        else:
            return theta #弧度

    def Cal3DAngle(self, v1, v2):
        """


        :param v1:  向量1
        :param v2:  向量1
        :return:    向量之间的反正切值
        """
        #计算弧度
        V1xV2 = np.cross(v1, v2)   #np.cross 计算两个向量的叉积
        TheNorm = np.linalg.norm(V1xV2) #求2范数
        theta = np.arctan2(TheNorm , np.dot(v1, v2)) # arctan2的输入不仅仅是正切值,而是要输入两个数x 1 x2 ,求反正切

        if V1xV2[2] < 0:
            return -theta #弧度
        else:
            return theta #弧度

    def close(self):
        if self.viewer:
            self.viewer.close()
            self.viewer = None

    def OptimalChoice_r(self, you_plane, enemy_plane):
        """
        传统策略选择函数 ,

        :param you_plane:
        :param enemy_plane:
        :return:
        """
        simu_step = self.simu_step
        # 敌方Step一次,假设不作机动
        state_enemy = enemy_plane.copy()  # 敌人x, y, z, theta, psi
        next_state_enemy = self.euler_func(state_enemy, np.array([0, 0]), simu_step , 0)

        # 我方Step一次,根据pi*策略选择最优机动
        state_you = you_plane.copy()  # 我方x, y, z, theta, psi
        # U = np.array([-5,-3,-1,1,3,5,7]) * 8 / 57.3
        U = np.random.uniform(-5,5,7) * 8 / 57.3
        S_max = -np.inf  # 初始分数非常小
        cmd1 = np.array([0, 0])  # 初始化cmd1
        # scoring = np.zeros((7,7))
        for k1 in range(np.size(U)):
            for k2 in range(np.size(U)):
                next_state_you = self.euler_func(state_you, np.array([U[k1], U[k2]]), simu_step , 0)
                state = np.append(next_state_you, next_state_enemy)
                r, dr, q1, q2, AA, ATA = self._get_obs(state)
                scoring = self.score_Syn(r *1000, AA, ATA, np.array([U[k1], U[k2]]))
                if scoring > S_max:
                    S_max = scoring
                    cmd1 = np.array([U[k1], U[k2]])  # 弧度制
        return cmd1  # 返回弧度制指令

    def CalculationCommand(self):
        '''单观察算行动'''
        # 根据策略取行动
        x_r, y_r, z_r, theta_r, psi_r, x_b, y_b, z_b, theta_b, psi_b = self.state  # 使用状态值,观察值是归一化过的

        state_r = np.array([x_r, y_r, z_r, theta_r, psi_r])
        state_b = np.array([x_b, y_b, z_b, theta_b, psi_b])
        command_r = self.OptimalChoice_r(state_r, state_b)  # 决策函数
        action = np.array([command_r[0], command_r[1]])
        return action

    def euler_func(self, state0, command, t, flag):
        """
        评估当前状态是否可行

        :param state0:
        :param command:
        :param t:
        :param flag:
        :return:
        """

        v = self.v + 0 * flag

        d_theta = command[0]
        d_psi = command[1]
        state = state0.copy()

        state[3] += d_theta * t  # 更新theta
        if state[3] > 180 / 57.3:
            state[3] = (-180 + 1e-3) / 57.3
        if state[3] < -180 / 57.3:
            state[3] = (180 - 1e-3) / 57.3

        state[4] += d_psi * t  # 更新psi
        if state[4] > 180 / 57.3:
            state[4] = (-180 + 1e-3) / 57.3
        if state[4] < -180 / 57.3:
            state[4] = (180 - 1e-3) / 57.3

        state[0] += v * np.cos(state[3]) * np.cos(state[4]) * t  # 更新x方向
        state[1] += v * np.cos(state[3]) * np.sin(state[4]) * t  # 更新y方向
        state[2] += v * np.sin(state[3]) * t  # 更新z方向

        return np.array(state)

Step2:注册

在envs目录下,包含一个初始化函数__init__.py,当访问envs类时,会通过这个初始化函数自动注册各环境,因此需要在__init__环境中注册我们定义的环境,给出其调用名“ID”以及此“ID”对应的类。

在envs/init.py内的#Classic分类下,插入注册语句:

register(

  id='Ace-v0', #此部分为环境调用的ID,可自由设定,与算法中调用一致即可。

  entry_point='gym.envs.classic_control:AceEnv',  #此部分为环境的类,注意:AceEnv是ace.py中定义的类,并不是文件名。

)

QQ截图20220411193933

Step3: 引入环境

在envs/classic_control/init.py内,加入引用ace环境的语句

注意这里的文件名和文件里面类的名字要和Step1相一致

from gym.envs.classic_control.ace import AceEnv

QQ截图20220411194233

Step4:在算法中进行调用

env = gym.make('Ace-v0')

配置小雅环境踩坑经历

主要是anaconda虚拟环境的使用

问题1 ,在anaconda中新建虚拟环境,但是在PyCharm中不显示

打开configurations

QQ截图20220314114212

QQ截图20220314114408

解决方法 新建项目

新建一个项目,这时候会显示新的python解释器,然后让他 make avaliabvle to all project ,也就是下面第二小个图中的选项要勾选上。

QQ截图20220314115157

然后关闭所有项目,再次进入,就会发现能够找到新建立的环境,使用新的解释器

问题2 PyCharm项目中的 External Libraries 没有变成新环境

QQ截图20220314115548

解决方法

File-Setting - Project Interpreter 选择新建立的环境

QQ截图20220314115813

标签:psi,小雅,编程,state,空战,np,theta,array,self
From: https://www.cnblogs.com/zuti666/p/18092797

相关文章

  • 空战博弈编程实现——2 初探JSBSIM
    [原][JSBSIM]aircraft飞机xml脚本-南水之源-博客园(cnblogs.com)1安装python版本jsbsim发布·JSBSim-Team/jsbsim·GitHubJsbsim::Anaconda.orgcondainstall-cconda-forgejsbsim2安装jsbsim.exe安装exe之后使用命令符进入所在文件夹,查看相关信息JSBSi......
  • 空战博弈实现——3 gym自定义环境
    gym1——自定义Gym环境并注册目录gym1——自定义Gym环境并注册感谢开始Step0新建文件夹Step1:新建环境文件xxEnv.pyStep2:在env下的__init__.py下注册**Step3**添加新建环境文件夹的引用Step4:在算法中进行调用感谢强化学习实战第一讲gym学习及二次开发-知乎(zhih......
  • 空战博弈编程实现——gym+jsbsim
    笔记1珠玉在前论文1RichterDJ,CalixRA.QPlane:AnOpen-SourceReinforcementLearningToolkitforAutonomousFixedWingAircraftSimulation[C]//Proceedingsofthe12thACMMultimediaSystemsConference.2021:261-266.QPlane|Proceedingsofthe12th......
  • 空战博弈编程实现
    AirSim环境搭建目录AirSim环境搭建感谢一AirSim简介二vs2019三虚幻引擎四下载编译五环境的使用1要把EpicGame语言改为英文2createProject3创建C++类。4复制Plugin5打开sln文件,进行调试6场景设置7运行8视角调节9再次进入,进行游戏六后续参考教程感谢airsi......
  • 空战编程实现——6 强化学习与控制器
    目录1付宇鹏,邓向阳,何明,等.基于强化学习的固定翼飞机姿态控制方法研究_付宇鹏[J].控制与决策,:1-6.输入输出模型结构强化学习方法——ppo奖励函数设置实现平台训练技巧效果2马一鸣.基于强化学习的前馈控制器[D].华北电力大学(北京),2021.强化学习自适应补偿控制系统结......
  • 编程界的万能钥匙:揭秘程序员常用的超实用算法!
    程序员常用的算法引言一、排序算法:为数据秩序井然二、搜索算法:高效定位数据三、图算法:理解复杂网络结构四、动态规划:优化递归求解过程五、贪心算法:简单高效的局部最优解六、数据结构相关算法:必不可少的工具七、算法的选择与实践:如何选择合适的算法结语引言大家好,这......
  • Python编程异步爬虫——aiohttp的使用
    异步爬虫—aiohttp的使用基本介绍asyncio模块其内部实现了对TCP、UDP、SSL协议的异步操作,但是对于HTTP请求来说,就需要用aiohttp实现了。aiohttp是一个基于asyncio的异步HTTP网络模块,它既提供了服务端,又提供了客户端。requests发起的是同步网络请求,aiohttp则是异步。ai......
  • Java 学习路线:基础知识、数据类型、条件语句、函数、循环、异常处理、数据结构、面向
    Java基础什么是JavaJava是一种由SunMicrosystems于1995年首次发布的编程语言和计算平台。Java是一种通用的、基于类的、面向对象的编程语言,旨在减少实现依赖性。它是一个应用程序开发的计算平台。Java快速、安全、可靠,因此在笔记本电脑、数据中心、游戏机、科学超级计......
  • 11DOM编程处理事件
    1<!DOCTYPEhtml>2<htmllang="en">3<head>4<metacharset="UTF-8">5<metaname="viewport"content="width=device-width,initial-scale=1.0">6<title>Document......
  • CUDA编程入门历险记
    说来惭愧,做CUDA相关工作已经有两年多,但是对CUDA编程的研究并不深入,最近想夯实一下基础,于是找了一个教程“cudacrashcourse“。这个教程在B站和youtube上都有:B站:https://www.bilibili.com/video/BV127411G76m?p=1&vd_source=5d09aad9eacf6d90c0a17cf81ee41eefyoutube:https://w......