实现一个 AI 驱动的马里奥(使用双重深度 Q 网络),它可以自己玩游戏

news2024/11/19 15:21:15

训练玩马里奥的 RL 智能体

本教程将向您介绍深度强化学习的基础知识。 最后,您将实现一个 AI 驱动的马里奥(使用双重深度 Q 网络),它可以自己玩游戏。

在这里插入图片描述

# !pip install gym-super-mario-bros==7.3.0

import torch
from torch import nn
from torchvision import transforms as T
from PIL import Image
import numpy as np
from pathlib import Path
from collections import deque
import random, datetime, os, copy

# Gym is an OpenAI toolkit for RL
import gym
from gym.spaces import Box
from gym.wrappers import FrameStack

# NES Emulator for OpenAI Gym
from nes_py.wrappers import JoypadSpace

# Super Mario environment for OpenAI Gym
import gym_super_mario_bros

RL 定义

环境:智能体与之交互并学习的世界。

操作a:智能体如何响应环境。 所有可能动作的集合称为动作空间

状态s:环境的当前特征。 环境可以处于的所有可能状态的集合称为状态空间

奖励r:奖励是从环境到智能体的关键反馈。 这是驱动智能体学习并改变其未来行动的动力。 多个时间步长上的奖励汇总称为回报

最佳操作的值函数Q*(s, a):如果您以状态s开始,执行任意操作a并给出期望的回报, 然后针对每个未来时间步长采取使收益最大化的行动。 可以说Q代表状态中动作的“质量”。 我们尝试近似该函数。

环境

初始化环境

在马里奥,环境由试管,蘑菇和其他成分组成。

当马里奥采取行动时,环境会以已更改的(下一个)状态,奖励和其他信息作为响应。

# Initialize Super Mario environment
env = gym_super_mario_bros.make("SuperMarioBros-1-1-v0")

# Limit the action-space to
#   0\. walk right
#   1\. jump right
env = JoypadSpace(env, [["right"], ["right", "A"]])

env.reset()
next_state, reward, done, info = env.step(action=0)
print(f"{next_state.shape},\n {reward},\n {done},\n {info}")

出:

(240, 256, 3),
 0,
 False,
 {'coins': 0, 'flag_get': False, 'life': 2, 'score': 0, 'stage': 1, 'status': 'small', 'time': 400, 'world': 1, 'x_pos': 40, 'x_pos_screen': 40, 'y_pos': 79}

预处理环境

环境数据在next_state中返回给智能体。 正如您在上面看到的,每个状态都由[3, 240, 256]大小数组表示。 通常,这比我们的智能体需要的信息更多; 例如,马里奥的举动并不取决于管道或天空的颜色!

我们使用包装程序在将环境数据发送到智能体之前对其进行预处理。

GrayScaleObservation是将 RGB 图像转换为灰度的通用包装器; 这样做可以减少状态表示的大小,而不会丢失有用的信息。 现在每个状态的大小:[1, 240, 256]

ResizeObservation将每个观察值下采样为正方形图像。 新尺寸:[1, 84, 84]

SkipFrame是一个自定义包装器,它继承自gym.Wrapper并实现了step()函数。 由于连续的帧变化不大,因此我们可以跳过 n 个中间帧而不会丢失太多信息。 第 n 帧聚集在每个跳过的帧上累积的奖励。

FrameStack是一个包装器,它使我们可以将环境的连续帧压缩到单个观察点中,以提供给我们的学习模型。 这样,我们可以根据前几个帧中马里奥的运动方向来确定马里奥是在降落还是跳跃。

class SkipFrame(gym.Wrapper):
    def __init__(self, env, skip):
        """Return only every `skip`-th frame"""
        super().__init__(env)
        self._skip = skip

    def step(self, action):
        """Repeat action, and sum reward"""
        total_reward = 0.0
        done = False
        for i in range(self._skip):
            # Accumulate reward and repeat the same action
            obs, reward, done, info = self.env.step(action)
            total_reward += reward
            if done:
                break
        return obs, total_reward, done, info

class GrayScaleObservation(gym.ObservationWrapper):
    def __init__(self, env):
        super().__init__(env)
        obs_shape = self.observation_space.shape[:2]
        self.observation_space = Box(low=0, high=255, shape=obs_shape, dtype=np.uint8)

    def permute_orientation(self, observation):
        # permute [H, W, C] array to [C, H, W] tensor
        observation = np.transpose(observation, (2, 0, 1))
        observation = torch.tensor(observation.copy(), dtype=torch.float)
        return observation

    def observation(self, observation):
        observation = self.permute_orientation(observation)
        transform = T.Grayscale()
        observation = transform(observation)
        return observation

class ResizeObservation(gym.ObservationWrapper):
    def __init__(self, env, shape):
        super().__init__(env)
        if isinstance(shape, int):
            self.shape = (shape, shape)
        else:
            self.shape = tuple(shape)

        obs_shape = self.shape + self.observation_space.shape[2:]
        self.observation_space = Box(low=0, high=255, shape=obs_shape, dtype=np.uint8)

    def observation(self, observation):
        transforms = T.Compose(
            [T.Resize(self.shape), T.Normalize(0, 255)]
        )
        observation = transforms(observation).squeeze(0)
        return observation

# Apply Wrappers to environment
env = SkipFrame(env, skip=4)
env = GrayScaleObservation(env)
env = ResizeObservation(env, shape=84)
env = FrameStack(env, num_stack=4)

将上述包装纸应用于环境后,最终的包装状态由 4 个灰度连续的帧堆叠在一起组成,如左图所示。 每次马里奥采取行动时,环境都会以这种结构的状态做出响应。 该结构由大小为[4, 84, 84]的 3D 数组表示。

在这里插入图片描述

智能体

我们创建一个类Mario来表示我们的智能体在游戏中。 马里奥应该能够:

  • 根据(环境的)当前状态,执行最佳操作策略
  • 记住经验。 经验为(当前状态,当前动作,奖励,下一个状态)。 马里奥缓存并且后来回忆起他的经验来更新其行动策略。
  • 逐步了解更好的操作策略
class Mario:
    def __init__():
        pass

    def act(self, state):
        """Given a state, choose an epsilon-greedy action"""
        pass

    def cache(self, experience):
        """Add the experience to memory"""
        pass

    def recall(self):
        """Sample experiences from memory"""
        pass

    def learn(self):
        """Update online action value (Q) function with a batch of experiences"""
        pass

在以下各节中,我们将填充马里奥的参数并定义其函数。

行动

对于任何给定状态,智能体都可以选择执行最佳操作(利用)或执行随机操作(探索)。

马里奥随机发掘并发self.exploration_rate 当他选择利用时,他依靠MarioNet(在Learn部分中实现)提供最佳操作。

class Mario:
    def __init__(self, state_dim, action_dim, save_dir):
        self.state_dim = state_dim
        self.action_dim = action_dim
        self.save_dir = save_dir

        self.use_cuda = torch.cuda.is_available()

        # Mario's DNN to predict the most optimal action - we implement this in the Learn section
        self.net = MarioNet(self.state_dim, self.action_dim).float()
        if self.use_cuda:
            self.net = self.net.to(device="cuda")

        self.exploration_rate = 1
        self.exploration_rate_decay = 0.99999975
        self.exploration_rate_min = 0.1
        self.curr_step = 0

        self.save_every = 5e5  # no. of experiences between saving Mario Net

    def act(self, state):
        """
    Given a state, choose an epsilon-greedy action and update value of step.

    Inputs:
    state(LazyFrame): A single observation of the current state, dimension is (state_dim)
    Outputs:
    action_idx (int): An integer representing which action Mario will perform
    """
        # EXPLORE
        if np.random.rand() < self.exploration_rate:
            action_idx = np.random.randint(self.action_dim)

        # EXPLOIT
        else:
            state = state.__array__()
            if self.use_cuda:
                state = torch.tensor(state).cuda()
            else:
                state = torch.tensor(state)
            state = state.unsqueeze(0)
            action_values = self.net(state, model="online")
            action_idx = torch.argmax(action_values, axis=1).item()

        # decrease exploration_rate
        self.exploration_rate *= self.exploration_rate_decay
        self.exploration_rate = max(self.exploration_rate_min, self.exploration_rate)

        # increment step
        self.curr_step += 1
        return action_idx

缓存和回忆

这两个函数是马里奥的“记忆”过程。

cache():每次马里奥执行操作时,都会将experience存储到他的内存中。 他的经验包括当前状态动作从动作中获得的奖励下一个状态以及游戏是否为完成

recall():马里奥从他的记忆中随机抽取一批经验,并以此来学习游戏。

class Mario(Mario):  # subclassing for continuity
    def __init__(self, state_dim, action_dim, save_dir):
        super().__init__(state_dim, action_dim, save_dir)
        self.memory = deque(maxlen=100000)
        self.batch_size = 32

    def cache(self, state, next_state, action, reward, done):
        """
        Store the experience to self.memory (replay buffer)

        Inputs:
        state (LazyFrame),
        next_state (LazyFrame),
        action (int),
        reward (float),
        done(bool))
        """
        state = state.__array__()
        next_state = next_state.__array__()

        if self.use_cuda:
            state = torch.tensor(state).cuda()
            next_state = torch.tensor(next_state).cuda()
            action = torch.tensor([action]).cuda()
            reward = torch.tensor([reward]).cuda()
            done = torch.tensor([done]).cuda()
        else:
            state = torch.tensor(state)
            next_state = torch.tensor(next_state)
            action = torch.tensor([action])
            reward = torch.tensor([reward])
            done = torch.tensor([done])

        self.memory.append((state, next_state, action, reward, done,))

    def recall(self):
        """
        Retrieve a batch of experiences from memory
        """
        batch = random.sample(self.memory, self.batch_size)
        state, next_state, action, reward, done = map(torch.stack, zip(*batch))
        return state, next_state, action.squeeze(), reward.squeeze(), done.squeeze()

学习

马里奥在后台使用 DDQN 算法。 DDQN 使用两个 ConvNet-Q_onlineQ_target-独立地逼近最佳作用值函数。

在我们的实现中,我们在Q_onlineQ_target之间共享特征生成器features,但是为每个特征维护单独的 FC 分类器。 θ_targetQ_target的参数)被冻结,以防止反向传播进行更新。 而是定期与θ_online同步(稍后会对此进行详细介绍)。

神经网络

class MarioNet(nn.Module):
    """mini cnn structure
  input -> (conv2d + relu) x 3 -> flatten -> (dense + relu) x 2 -> output
  """

    def __init__(self, input_dim, output_dim):
        super().__init__()
        c, h, w = input_dim

        if h != 84:
            raise ValueError(f"Expecting input height: 84, got: {h}")
        if w != 84:
            raise ValueError(f"Expecting input width: 84, got: {w}")

        self.online = nn.Sequential(
            nn.Conv2d(in_channels=c, out_channels=32, kernel_size=8, stride=4),
            nn.ReLU(),
            nn.Conv2d(in_channels=32, out_channels=64, kernel_size=4, stride=2),
            nn.ReLU(),
            nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1),
            nn.ReLU(),
            nn.Flatten(),
            nn.Linear(3136, 512),
            nn.ReLU(),
            nn.Linear(512, output_dim),
        )

        self.target = copy.deepcopy(self.online)

        # Q_target parameters are frozen.
        for p in self.target.parameters():
            p.requires_grad = False

    def forward(self, input, model):
        if model == "online":
            return self.online(input)
        elif model == "target":
            return self.target(input)

TD 估计和 TD 目标

学习涉及两个值:

TD 估计-给定状态s的预测最佳Q*

在这里插入图片描述

TD 目标-当前奖励和下一状态s'中的估计Q*的汇总

由于我们不知道下一个动作a'是什么,因此我们在下一个状态s'中使用动作a'最大化Q_online

请注意,我们在td_target()上使用了@torch.no_grad()装饰器来禁用梯度计算(因为我们无需在θ_target上进行反向传播。)

class Mario(Mario):
    def __init__(self, state_dim, action_dim, save_dir):
        super().__init__(state_dim, action_dim, save_dir)
        self.gamma = 0.9

    def td_estimate(self, state, action):
        current_Q = self.net(state, model="online")[
            np.arange(0, self.batch_size), action
        ]  # Q_online(s,a)
        return current_Q

    @torch.no_grad()
    def td_target(self, reward, next_state, done):
        next_state_Q = self.net(next_state, model="online")
        best_action = torch.argmax(next_state_Q, axis=1)
        next_Q = self.net(next_state, model="target")[
            np.arange(0, self.batch_size), best_action
        ]
        return (reward + (1 - done.float()) * self.gamma * next_Q).float()

更新模型

当马里奥从其重播缓冲区中采样输入时,我们计算TD_tTD_e并反向传播该损失Q_online以更新其参数θ_online(\ (\ alpha \)是传递给optimizer的学习率lr

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RHayYgmL-1686743312332)(img/tex34-8.gif)]

θ_target不会通过反向传播进行更新。 相反,我们会定期将θ_online复制到θ_target

class Mario(Mario):
    def __init__(self, state_dim, action_dim, save_dir):
        super().__init__(state_dim, action_dim, save_dir)
        self.optimizer = torch.optim.Adam(self.net.parameters(), lr=0.00025)
        self.loss_fn = torch.nn.SmoothL1Loss()

    def update_Q_online(self, td_estimate, td_target):
        loss = self.loss_fn(td_estimate, td_target)
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()
        return loss.item()

    def sync_Q_target(self):
        self.net.target.load_state_dict(self.net.online.state_dict())

保存检查点

class Mario(Mario):
    def save(self):
        save_path = (
            self.save_dir / f"mario_net_{int(self.curr_step // self.save_every)}.chkpt"
        )
        torch.save(
            dict(model=self.net.state_dict(), exploration_rate=self.exploration_rate),
            save_path,
        )
        print(f"MarioNet saved to {save_path} at step {self.curr_step}")

全部放在一起

class Mario(Mario):
    def __init__(self, state_dim, action_dim, save_dir):
        super().__init__(state_dim, action_dim, save_dir)
        self.burnin = 1e4  # min. experiences before training
        self.learn_every = 3  # no. of experiences between updates to Q_online
        self.sync_every = 1e4  # no. of experiences between Q_target & Q_online sync

    def learn(self):
        if self.curr_step % self.sync_every == 0:
            self.sync_Q_target()

        if self.curr_step % self.save_every == 0:
            self.save()

        if self.curr_step < self.burnin:
            return None, None

        if self.curr_step % self.learn_every != 0:
            return None, None

        # Sample from memory
        state, next_state, action, reward, done = self.recall()

        # Get TD Estimate
        td_est = self.td_estimate(state, action)

        # Get TD Target
        td_tgt = self.td_target(reward, next_state, done)

        # Backpropagate loss through Q_online
        loss = self.update_Q_online(td_est, td_tgt)

        return (td_est.mean().item(), loss)

日志记录

import numpy as np
import time, datetime
import matplotlib.pyplot as plt

class MetricLogger:
    def __init__(self, save_dir):
        self.save_log = save_dir / "log"
        with open(self.save_log, "w") as f:
            f.write(
                f"{'Episode':>8}{'Step':>8}{'Epsilon':>10}{'MeanReward':>15}"
                f"{'MeanLength':>15}{'MeanLoss':>15}{'MeanQValue':>15}"
                f"{'TimeDelta':>15}{'Time':>20}\n"
            )
        self.ep_rewards_plot = save_dir / "reward_plot.jpg"
        self.ep_lengths_plot = save_dir / "length_plot.jpg"
        self.ep_avg_losses_plot = save_dir / "loss_plot.jpg"
        self.ep_avg_qs_plot = save_dir / "q_plot.jpg"

        # History metrics
        self.ep_rewards = []
        self.ep_lengths = []
        self.ep_avg_losses = []
        self.ep_avg_qs = []

        # Moving averages, added for every call to record()
        self.moving_avg_ep_rewards = []
        self.moving_avg_ep_lengths = []
        self.moving_avg_ep_avg_losses = []
        self.moving_avg_ep_avg_qs = []

        # Current episode metric
        self.init_episode()

        # Timing
        self.record_time = time.time()

    def log_step(self, reward, loss, q):
        self.curr_ep_reward += reward
        self.curr_ep_length += 1
        if loss:
            self.curr_ep_loss += loss
            self.curr_ep_q += q
            self.curr_ep_loss_length += 1

    def log_episode(self):
        "Mark end of episode"
        self.ep_rewards.append(self.curr_ep_reward)
        self.ep_lengths.append(self.curr_ep_length)
        if self.curr_ep_loss_length == 0:
            ep_avg_loss = 0
            ep_avg_q = 0
        else:
            ep_avg_loss = np.round(self.curr_ep_loss / self.curr_ep_loss_length, 5)
            ep_avg_q = np.round(self.curr_ep_q / self.curr_ep_loss_length, 5)
        self.ep_avg_losses.append(ep_avg_loss)
        self.ep_avg_qs.append(ep_avg_q)

        self.init_episode()

    def init_episode(self):
        self.curr_ep_reward = 0.0
        self.curr_ep_length = 0
        self.curr_ep_loss = 0.0
        self.curr_ep_q = 0.0
        self.curr_ep_loss_length = 0

    def record(self, episode, epsilon, step):
        mean_ep_reward = np.round(np.mean(self.ep_rewards[-100:]), 3)
        mean_ep_length = np.round(np.mean(self.ep_lengths[-100:]), 3)
        mean_ep_loss = np.round(np.mean(self.ep_avg_losses[-100:]), 3)
        mean_ep_q = np.round(np.mean(self.ep_avg_qs[-100:]), 3)
        self.moving_avg_ep_rewards.append(mean_ep_reward)
        self.moving_avg_ep_lengths.append(mean_ep_length)
        self.moving_avg_ep_avg_losses.append(mean_ep_loss)
        self.moving_avg_ep_avg_qs.append(mean_ep_q)

        last_record_time = self.record_time
        self.record_time = time.time()
        time_since_last_record = np.round(self.record_time - last_record_time, 3)

        print(
            f"Episode {episode} - "
            f"Step {step} - "
            f"Epsilon {epsilon} - "
            f"Mean Reward {mean_ep_reward} - "
            f"Mean Length {mean_ep_length} - "
            f"Mean Loss {mean_ep_loss} - "
            f"Mean Q Value {mean_ep_q} - "
            f"Time Delta {time_since_last_record} - "
            f"Time {datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S')}"
        )

        with open(self.save_log, "a") as f:
            f.write(
                f"{episode:8d}{step:8d}{epsilon:10.3f}"
                f"{mean_ep_reward:15.3f}{mean_ep_length:15.3f}{mean_ep_loss:15.3f}{mean_ep_q:15.3f}"
                f"{time_since_last_record:15.3f}"
                f"{datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S'):>20}\n"
            )

        for metric in ["ep_rewards", "ep_lengths", "ep_avg_losses", "ep_avg_qs"]:
            plt.plot(getattr(self, f"moving_avg_{metric}"))
            plt.savefig(getattr(self, f"{metric}_plot"))
            plt.clf()

开始吧!

在此示例中,我们运行了 10 个剧集的训练循环,但是对于马里奥要真正了解他的世界的方式,我们建议运行至少 40,000 个剧集的循环!

use_cuda = torch.cuda.is_available()
print(f"Using CUDA: {use_cuda}")
print()

save_dir = Path("checkpoints") / datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")
save_dir.mkdir(parents=True)

mario = Mario(state_dim=(4, 84, 84), action_dim=env.action_space.n, save_dir=save_dir)

logger = MetricLogger(save_dir)

episodes = 10
for e in range(episodes):

    state = env.reset()

    # Play the game!
    while True:

        # Run agent on the state
        action = mario.act(state)

        # Agent performs action
        next_state, reward, done, info = env.step(action)

        # Remember
        mario.cache(state, next_state, action, reward, done)

        # Learn
        q, loss = mario.learn()

        # Logging
        logger.log_step(reward, loss, q)

        # Update state
        state = next_state

        # Check if end of game
        if done or info["flag_get"]:
            break

    logger.log_episode()

    if e % 20 == 0:
        logger.record(episode=e, epsilon=mario.exploration_rate, step=mario.curr_step)

出:

Using CUDA: True

Episode 0 - Step 40 - Epsilon 0.9999900000487484 - Mean Reward 231.0 - Mean Length 40.0 - Mean Loss 0.0 - Mean Q Value 0.0 - Time Delta 0.444 - Time 2021-01-05T20:23:08

总结

在本教程中,我们看到了如何使用 PyTorch 来训练玩游戏的 AI。 您可以使用相同的方法训练 AI 在 OpenAI Gym上玩任何游戏。 希望您喜欢本教程,请随时通过我们的 Github 与我们联系!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/681083.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32实现延时

在STM32单片机中&#xff0c;实现延时一般都是使用定时器&#xff0c;既可以使用Systick定时器&#xff0c;也可以使用常规的定时器。 定时器在设置了定时并开启之后&#xff0c;就会进入自主运行模式&#xff0c;其中&#xff0c;初始化设置这一阶段是由CPU执行相应指令完成的…

Spring Cloud_Spring Cloud Alibaba_00000

contents 微服务介绍版本选择创建项目创建Maven工程 说明 微服务介绍 微服务架构是一种架构模式。它提倡将单一应用程序划分成一组小的服务。服务之间相互协调、相互配合&#xff0c;为用户提供最终价值。每个服务运行在其独立的进程中&#xff0c;服务与服务间采用轻量级的通…

html通过web3JS 获取当前连接的区块链信息和账号信息

前面 我们讲了 MetaMask和ganache的配置安装 并用 MetaMask管理ganache的启动的虚拟区块链 那么 我们现在也完全可以写一个网页来做这个东西的管理 您可以先查看文章web3.js获取导入做一个导入了 web3的html文件 首先我们可以来试着 获取 自己当前是在哪个区块的 getBlockNum…

【深度学习】【分布式训练】DeepSpeed:AllReduce与ZeRO-DP

AllReduce与ZeRO-DP ​ ZeRO-DP是分布式训练工具DeepSpeed的核心功能之一&#xff0c;许多其他的分布式训练工具也会集成该方法。本文从AllReduce开始&#xff0c;随后介绍大模型训练时的主要瓶颈----显存的占用情况。在介绍完成标准数据并行(DP)后&#xff0c;结合前三部分的内…

玩游戏提示d3dx9丢失-找不到d3dx9怎么修复

相信有些朋友遇到了d3dx9丢失的情况不知道怎么解决&#xff0c;而今日小编带来的这篇文章就是讲解关于d3dx9丢失进行修复的操作内容&#xff0c;d3dx9丢失怎么解决&#xff1f;&#xff08;修复方法&#xff09;d3dx9文件是DirectX中必备文件,许多游戏需要此文件运行。 d3dx9丢…

文件系统概述

目录 概述用户空间层面1.应用程序可以直接使用内核提供的系统调用访问文件&#xff1a;2.应用程序也可以使用 glibc 库封装的标准 I/O 流函数访问文件&#xff1a; 硬件层面1.块设备2.闪存3.NVDIMM 内核空间层面 概述 在 Linux 系统中&#xff0c;一切皆文件&#xff0c;除了通…

【MySQL 】:测试数据准备、SQL语句规范与基本操作

前言 欢迎来到小K的MySQL专栏&#xff0c;本节将为大家准备MySQL测试数据、以及带来SQL语句规范、数据库的基本操作的详细讲解~✨文末送书&#xff0c;小K赠书活动第二期 目录 前言一、准备测试数据二、SQL语句规范三、数据库的基本操作四、总结&#xff1a;文末赠书 一、准备测…

直线导轨在焊接领域有什么作用?

焊接技术在现代制造业中的应用越来越广泛&#xff0c;直线导轨作为重要的传动元件&#xff0c;已经成为焊接设备中不可或缺的部分。 相对于直线轴承来说&#xff0c;直线导轨具有较高的负载能力和刚度&#xff0c;能够保证高精度的直线运动&#xff0c;滑动摩擦小&#xff0c;惯…

【Python】异常处理 ③ ( 捕获所有类型的异常 | 默认捕获所有类型异常 | 捕获 Exception 异常 )

文章目录 一、Python 默认捕获所有类型异常1、默认捕获所有类型异常 - 无法获取异常类型2、代码实例 - 默认捕获所有类型异常 二、Python 捕获所有类型异常 - 捕获 Exception 异常1、捕获 Exception 类型异常 - 可获取异常类型2、代码实例 - 捕获 Exception 异常 一、Python 默…

管理类联考——逻辑——知识篇——形式逻辑——三、直言——haimian

直言 考点分析 直言 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量786223232 性质定义 直言命题也叫做性质命题&#xff0c;是判断事物是否具有某种性质的命题。直言命题由四部分组成&#xff1a;主项、谓项、联项、量项。 不同种类 对当关系 对…

PID控制算法:2、Derivative Kick(微分冲击)

什么是微分冲击Derivative Kick 引入微分&#xff0c;就是为了减少超调量的&#xff0c;但是根据PID的经典公式 就看微分部分 在PID刚开始时&#xff0c;误差值肯定是存在的&#xff0c;但是PID启动的瞬间&#xff0c;这个dt是很小的&#xff0c;这就导致是一个很大的值&#…

软件开发实习个人总结

软件开发实习个人总结篇1 一、实习目的 随着时代发展和社会进步&#xff0c;用人单位对游戏软件专业大学生的要求越来越高&#xff0c;对于即将毕业的游戏软件专业在校生而言&#xff0c;为了能更好的适应游戏软件专业严峻的就业形势&#xff0c;毕业后能够尽快的融入到社会&am…

Golang学习日志 ━━ 通过将gin-vue-admin项目上传到自己的仓库并且与原版保持更新来学习github操作

gin-vue-admin是一套国人用golang开发的后台管理系统&#xff0c;本文是从作者早期原文中截取的一部分&#xff0c;后期会以本文为框架进行扩展说明。 官网&#xff1a;https://www.gin-vue-admin.com/ 学习视频&#xff1a;https://www.bilibili.com/video/BV1kv4y1g7nT/?p6 …

Redis 2023面试5题(七)

一、Redis redlock 实现原理 Redlock是一种基于Redis的分布式锁实现&#xff0c;它可以解决在分布式系统中由于主从切换、网络延迟等导致的锁竞争问题。 Redlock的实现原理如下&#xff1a; 创建多个Redis实例&#xff0c;每个实例都有相同的锁名称。使用Redis的SETNX命令尝试…

如何选择消息中间件

一、 分布式系统消息通信技术简介 分布式系统消息通信技术主要包括以下几种&#xff1a; 1. RPC(Remote Procedure Call Protocol). 一般是C/S方式&#xff0c;同步的&#xff0c;跨语言跨平台&#xff0c;面向过程 2. CORBA(Common Object Request Broker Architecture). CO…

Vlan与ARP讲解

目录 Vlan讲解 Vlan标签 二层接口类型 ARP ARP的作用 ARP地址解析报文讲解 免费ARP报文讲解 ARP缓存表 Vlan讲解 Vlan&#xff08;Virtual Local Area Network&#xff09;虚拟局域网&#xff0c;将一个物理的LAN在逻辑上划分为多个广播域&#xff1b;可以理解为一个V…

RT-Thread-11-事件集

事件集 举例说明事件集&#xff1a; 1、A坐公交车去某地&#xff0c;只有一趟公交车去该地&#xff0c;等此公交车即可出发&#xff1b; 2、A坐公交车去某地&#xff0c;有三趟公交车去该地&#xff0c;等其中任意一辆公交车即可出发&#xff1b; 3、A约B一起去某地&#xff0…

点亮你的创意,使用Python与树莓派制作呼吸灯的详细教程

文章目录 前言PWM的介绍实现PWM控制LED亮度结果与分析 前言 在上一篇文章中&#xff0c;我们介绍了如何在树莓派上点亮一个LED灯&#xff0c;并让它以时间间隔为1秒进行闪烁。闪亮登场&#xff01;在树莓派上点亮LED灯的简单详细方法_☞黑心萝卜三条杠☜的博客-CSDN博客。现在&…

【剑指offer刷题记录 java版】数组双指针 之 其它题目

本系列文章记录labuladong的算法小抄中剑指offer题目 【剑指offer刷题记录 java版】数组双指针 之 其它题目 剑指 Offer II 018. 有效的回⽂剑指 Offer 58 - I. 翻转单词顺序剑指 Offer 21. 调整数组顺序使奇数位于偶数前⾯剑指 Offer 57. 和为s的两个数字剑指 Offer II 007. 数…

【裸机开发】IRQ 中断服务函数(三)—— 中断处理逻辑实现

实现了 IRQ 中断服务函数的汇编部分以后&#xff0c;接下来我们要使用C代码实现IRQ中断服务函数的具体逻辑&#xff0c;主要包含初始化和中断处理两部分。 全局中断初始化&#xff08;全局中断使能、IRQ中断使能&#xff09;具体中断处理逻辑实现 我们这里的中断是由按键产生…