文章目录

  • 遗传算法
    • 1 达尔文的自然选择学说
    • 2 遗传算法概述
    • 3 遗传算法相关概念
    • 4 遗传算法与求解优化问题
    • 5 例子
    • 6 python实现
    • 7 遗传算法特点

遗传算法

1 达尔文的自然选择学说

遗传算法是以达尔文的自然选择学说为基础发展起来的。自然选择学说包括以下3个方面:

(1)遗传:这是生物的普遍特征,亲代把生物信息交给子代,子代按照所得信息而发育、分化,因而子代总是和亲代具有相同或相似的性状。生物有了这个特征,物种才能稳定存在。

(2)变异:亲代和子代之间以及子代的不同个体之间总是有些差异,这种现象,称为变异。变异是随机发生的,变异的选择和积累是生命多样性的根源。

(3)生存斗争和适者生存:自然选择来自繁殖过剩和生存斗争。由于弱肉强食的生存斗争不断进行,其结果是适者生存,即具有适应性变异的个体被保留下来,不具有适应性变异的个体被淘汰,通过一代代的生存环境的选择作用,演变为新的适应环境的物种。

2 遗传算法概述

遗传算法(genetic algorithm,GA)是一种演化(进化)算法,用于求全局最优解。其基本原理是仿效生物界中的“物竞天(适应度函数)择、适者生存”的演化法则。遗传算法是把问题的参数编码为染色体,再利用迭代的方式进行选择(Selection)、交叉(Crossover)与变异(Mutation) 等运算来交换种群中染色体的信息产生新个体,根据适应度函数的值选择符合优化目标的染色体。

3 遗传算法相关概念

  • 编码(Coding):将一个待求解的问题的实际可行解从其解空间转换到遗传算法所能处理的搜索空间(即个体空间)的过程,就称为编码。如参数值x=3x=3x=3用二进制表示:0000011。(编码是为了交叉变异

  • 解码(Decoding) :解码为编码的逆过程。

  • 染色体/个体(choromosome/Individual) :染色体通常是由一维的串结构数据来表示(如用二进制表示:0101001),串上各个位置对应基因的取值(如染色体0101001上的0和1)。(函数的一个解

  • 种群(Population):一定数量的个体组成了种群( population)。种群中个体的数目称为种群大小。(函数的多个解

  • 适应度(Fitness):在研究自然界中生物的遗传和进化现象时,生物学家使用适应度这个术语来度量某个物种对于生存环境的适应程度。对生存环境适应程度高的物种将获得更多繁殖机会,而对生存环境适应程度较低的物种,其繁殖的机会就相对较少,甚至逐渐灭绝。种群中各个个体对环境的适应程度叫做适应度。在遗传算法中,一般用适应度函数(Fitness function) 来衡量某一个体的适应度高低(适应度用来评价给定解好不好,适应度函数由目标函数组成,用它计算适应度

  • 选择(Selection) 选择基于适者生存原理,是从一个旧种群中选择生命力强的个体产生新种群的过程。是根据适应度选择的。也就是说,具有高适应度的个体将自己的优良基因遗传给下一代。

  • 交叉操作(Crossover)
    选择操作能从旧种群中选择出优秀者,但不能创造新的染色体。而交叉模拟了生物进化过程中的繁殖现象,通过两个染色体的交换组合来产生新的优良品种。它的过程为:将群体内的各个个体随机搭配成对,对每一对个体,以某种概率(称为交叉概率)遵循某一种规则交换它们之间的部分染色体。例如单点交叉:选择一个交叉点,交换他们右边部分:

  • 变异操作(Mutation):变异运算用来模拟生物在自然的遗传环境中由于各种偶然因素引起的基因突变,它以很小的概率(变异概率)随机地改变染色体上某些基因的值。例如在染色体以二进制编码的系统中,它可以随机地将染色体的某一个基因由1变为0,或由0变为1。若只有选择和交叉,而没有变异,则无法在初始基因组合以外的空间进行搜索,使进化过程在早期就陷入局部解而进入终止过程,从而影响解的质量。为了在尽可能大的空间中获得质量较高的优化解,必须采用变异操作。

4 遗传算法与求解优化问题

优化问题建模:

(1)确定待优化的目标函数。

(2)确定目标函数的变量参数以及各种约束条件,即确定出遗传算法中的个体和问题的解空间。

遗传算法初始化:

(3)选择编码和解码策略,把参数集合转换到染色体串型结构空间;

(4)定义适应度函数,用于计算适应值;

(5)确定遗传策略,包括选择、交叉、变异操作并确定交叉概率、变异概率、种群大小和最大迭代次数等遗传参数;

遗传算法流程:

(6)随机产生初始化种群;

(7)计算种群中的个体适应度;

(8)判断种群性能是否满足某一指标,或者已达到最大迭代次数,满足则输出最优值,不满足则运用选择、交叉和变异算子作用于种群,形成下一代种群并返回第(7)步。

5 例子

优化问题建模:

(1)确定待优化的目标函数。

F(x,y)=3(−x+1)2e−x2−(y+1)2−(−10x3+2x−10y5)e−x2−y2−3−e−y2−(x+1)2F(x,y)=3 \left(- x + 1\right)^{2} e^{- x^{2} - \left(y + 1\right)^{2}} - \left(- 10 x^{3} + 2 x - 10 y^{5}\right) e^{- x^{2} - y^{2}} - 3^{- e^{- y^{2} - \left(x + 1\right)^{2}}} F(x,y)=3(−x+1)2e−x2−(y+1)2−(−10x3+2x−10y5)e−x2−y2−3−e−y2−(x+1)2
(2)确定目标函数的变量参数以及各种约束条件,即确定出遗传算法中的个体和问题的解空间,得到优化模型。

个体为(x,y)(x,y)(x,y),约束条件为x,y∈[−3,3]x,y\in[-3,3]x,y∈[−3,3]。

得到优化模型:

max⁡F(x,y)=3(−x+1)2e−x2−(y+1)2−(−10x3+2x−10y5)e−x2−y2−3−e−y2−(x+1)2\max F(x,y)=3 \left(- x + 1\right)^{2} e^{- x^{2} - \left(y + 1\right)^{2}} - \left(- 10 x^{3} + 2 x - 10 y^{5}\right) e^{- x^{2} - y^{2}} - 3^{- e^{- y^{2} - \left(x + 1\right)^{2}}} maxF(x,y)=3(−x+1)2e−x2−(y+1)2−(−10x3+2x−10y5)e−x2−y2−3−e−y2−(x+1)2
s.t.x,y∈[−3,3]\text{s.t.}\qquad x,y\in[-3,3]s.t.x,y∈[−3,3]

待优化模型可视化如下:

遗传算法初始化:

(3)选择编码和解码策略,把参数集合转换到染色体串型结构空间;
– 编码:
常用的编码方式有:二进制编码、浮点数编码等。这里采用二进制编码,将变量参数用二进制字符串表示,二进制编码的长度由所求精度确定。然后将所有变量参数量的二进制编码串连在一起,构成一个染色体。

变量xxx和yyy的定义域都为[−3,3][-3,3][−3,3],如果要求编码的精度为10−510^{-5}10−5,则需要将[−3,3][-3,3][−3,3]划分成至少(3−(−3)/10−5=600000(3-(-3)/10^{-5}=600000(3−(−3)/10−5=600000个等长的小区域,而每个小区域用一个二进制串染色体表示。设染色体的长度为LLL,则2L=6000002^L=6000002L=600000,即:
L=向上取整(log⁡2600000)=20L=向上取整(\log_2600000)=20L=向上取整(log2​600000)=20
因此xxx可以用二进制串a19a18...ai...a0a_{19}a_{18}...a_i...a_0a19​a18​...ai​...a0​表示,其中ai∈{0,1}a_i\in\{0,1\}ai​∈{0,1}。同样的,yyy可表示为a39a38...ai...a20a_{39}a_{38}...a_i...a_{20}a39​a38​...ai​...a20​表示,其中ai∈{0,1}a_i\in\{0,1\}ai​∈{0,1}。

将x,yx,yx,y编码并拼接起来。常见的编码方式为将两个参数对应的染色体拼接起来,如下:

(x,y)→a39a38...ai...a1a0,ai∈{0,1}(x,y)\rightarrow a_{39}a_{38}...a_i...a_1a_0,a_i\in\{0,1\}(x,y)→a39​a38​...ai​...a1​a0​,ai​∈{0,1}

为了方便交叉的代码编写,本文采用交错拼接:

(x,y)→a39a19a37a18...ai...a20a0(x,y)\rightarrow a_{39}a_{19}a_{37}a_{18}...a_i...a_{20}a_0(x,y)→a39​a19​a37​a18​...ai​...a20​a0​

这样仅需选择一个交叉点就能同时对x,yx,yx,y对应的染色体进行交叉。

– 解码:

x,yx,yx,y的二进制解码公式为(L=20L=20L=20;x,y∈[−3,3]:x,y\in [-3,3]:x,y∈[−3,3]:xrightx_{right}xright​为3,xleftx_{left}xleft​为-3,yrighty_{right}yright​为3,ylefty_{left}yleft​为-3):

k=∑i=0L−1ai⋅2ik=\sum_{i=0}^{L-1}a_i\cdot 2^ik=i=0∑L−1​ai​⋅2i

x^=(xright−xleft)⋅k2L−1+xleft\hat{x}=\frac{(x_{right}-x_{left})\cdot k}{2^L-1}+x_{left}x^=2L−1(xright​−xleft​)⋅k​+xleft​

k=∑i=L2L−1ai⋅2ik=\sum_{i=L}^{2L-1}a_i\cdot 2^ik=i=L∑2L−1​ai​⋅2i

y^=(yright−yleft)⋅k2L−1+yleft\hat{y}=\frac{(y_{right}-y_{left})\cdot k}{2^L-1}+y_{left}y^​=2L−1(yright​−yleft​)⋅k​+yleft​

(4)定义适应度函数,用于计算适应值;

遗传算法的目标是最大化适应度,因此,如果我们我们要最大化目标函数,直接用目标函数作为适应度函数:

F(x,y)=3(−x+1)2e−x2−(y+1)2−(−10x3+2x−10y5)e−x2−y2−3−e−y2−(x+1)2F(x,y)=3 \left(- x + 1\right)^{2} e^{- x^{2} - \left(y + 1\right)^{2}} - \left(- 10 x^{3} + 2 x - 10 y^{5}\right) e^{- x^{2} - y^{2}} - 3^{- e^{- y^{2} - \left(x + 1\right)^{2}}} F(x,y)=3(−x+1)2e−x2−(y+1)2−(−10x3+2x−10y5)e−x2−y2−3−e−y2−(x+1)2
为了避免一些运算上的问题,我们可以将F(x,y)F(x,y)F(x,y)减去当前种群中最小的适应度FminF_{min}Fmin​是使得所有适应度都为正,即得到适应度函数为:
Fitness=F(x,y)−Fmin+ϵFitness=F(x,y)-F_{min}+\epsilonFitness=F(x,y)−Fmin​+ϵ

ϵ\epsilonϵ取一个很小的数,加上它是为了防止出现0的适应度。

另外,如果我们要求目标函数的最小值,仅需要将上式取倒数:

Fitness=1F(x,y)−Fmin+ϵFitness=\frac{1}{F(x,y)-F_{min}+\epsilon}Fitness=F(x,y)−Fmin​+ϵ1​

(5)确定交叉概率、变异概率、种群大小和最大迭代次数等遗传参数并确定遗传策略,包括选择、交叉、变异操作;

– 交叉概率:pc=0.8p_c=0.8pc​=0.8

– 变异概率:pm=0.001p_m=0.001pm​=0.001

– 种群大小:N=200N=200N=200

– 最大迭代次数:G=60G=60G=60

– 选择操作:

根据个体的适应度确定个体被选择到的概率,适应度越大,被选择到的概率也越大。若某个个体pop(i)\text{pop}(i)pop(i),其适应度fif_ifi​,则其被选择的概率表示为:

pi=fi∑i=0N−1fip_i=\frac{f_i}{\sum_{i=0}^{N-1}f_i} pi​=∑i=0N−1​fi​fi​​

确定个体被选择概率后,可以选用的选择算法有:轮盘赌选择法、随机遍历抽样法、局部选择法、截断选择法和锦标赛选择法等。常用轮盘赌选择法,读者如果感兴趣可以看一下:https://blog.csdn.net/pymqq/article/details/51375522

python有一个根据概率随机选择的函数np.random.choice(),我们可以根据个体被选择概率得到要被选择出来的个体的索引。

– 交叉操作:

对种群中的每个个体(父),从种群中随机选择一个个体(母),依概率pcp_cpc​对他们采用单点交叉(选择一个交叉点,将父交叉点以右的基因替换为母交叉点以右的基因)得到一个新个体(子)。

– 变异操作:

对每个个体,随机选择个体上的一个基因,依概率pmp_mpm​对其进行变异(0变为1,1变为0)。

遗传算法流程:

(6)随机产生初始化种群;

(7)计算种群中的个体适应度;

(8)判断是否已达到最大迭代次数,达到则输出最优值,还没达到则运用选择、交叉和变异算子作用于种群,形成下一代种群并返回第(7)步。

6 python实现

# https://blog.csdn.net/ha_ha_ha233/article/details/91364937
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3DNUM_PARAMETER = 2
CHOROMOSOME_SIZE = 19
POP_SIZE = 200
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.001
N_GENERATIONS = 60
X_BOUND = [-3, 3]  # [x_left,x_right]
Y_BOUND = [-3, 3]  # [y_left,y_right]def F(x, y):return 3 * (1 - x) ** 2 * np.exp(-(x ** 2) - (y + 1) ** 2) - 10 * (x / 5 - x ** 3 - y ** 5) * np.exp(-x ** 2 - y ** 2) - 1 / 3 ** np.exp(-(x + 1) ** 2 - y ** 2)def plot_3d(ax):X = np.linspace(*X_BOUND, 100)Y = np.linspace(*Y_BOUND, 100)X, Y = np.meshgrid(X, Y)Z = F(X, Y)ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm)ax.set_zlim(-10, 10)ax.set_xlabel('x')ax.set_ylabel('y')ax.set_zlabel('z')plt.pause(3)plt.show()def get_fitness(pop):x, y = decoder(pop)pred = F(x, y)#fitness=(pred - np.min(pred)) + 1e-3     # 最大fitness = 1/(pred - np.min(pred) + 1e-3)  # 最小return fitnessdef decoder(pop):x_pop = pop[:, 1::2]  # 奇数列表示Xy_pop = pop[:, ::2]  # 偶数列表示yx = x_pop.dot(2 ** np.arange(CHOROMOSOME_SIZE)[::-1]) / float(2 ** CHOROMOSOME_SIZE - 1) * (X_BOUND[1] - X_BOUND[0]) + X_BOUND[0]y = y_pop.dot(2 ** np.arange(CHOROMOSOME_SIZE)[::-1]) / float(2 ** CHOROMOSOME_SIZE - 1) * (Y_BOUND[1] - Y_BOUND[0]) + Y_BOUND[0]return x, ydef crossover_and_mutation(pop):new_pop = []for father in pop:  # 遍历种群中的每一个个体,将该个体作为父亲child = fatherif np.random.rand() < CROSSOVER_RATE:            # 以一定的概率发生交叉mother = pop[np.random.randint(POP_SIZE)]  # 在种群中选择另一个个体,并将该个体作为母亲cross_points = np.random.randint(low=0, high=CHOROMOSOME_SIZE * NUM_PARAMETER)  # 随机产生交叉的点child[cross_points:] = mother[cross_points:]  # 孩子得到位于交叉点后的母亲的基因mutation(child)  # 逐个个体交叉变异可以提高程序运行效率new_pop.append(child)return new_popdef mutation(child):if np.random.rand() < MUTATION_RATE:  # 以MUTATION_RATE的概率进行变异mutate_point = np.random.randint(0, CHOROMOSOME_SIZE * 2)  # 随机产生一个实数,代表要变异基因的位置child[mutate_point] = child[mutate_point] ^ 1  # 将变异点的二进制反转def select(pop, fitness):idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,p=(fitness) / (fitness.sum()))return pop[idx]def print_info(pop):fitness = get_fitness(pop)max_fitness_index = np.argmax(fitness)x, y = decoder(pop)print("最优的基因型:", pop[max_fitness_index])print("(x, y):", (x[max_fitness_index], y[max_fitness_index]))print("最优的F:", F(x[max_fitness_index], y[max_fitness_index]))if __name__ == "__main__":fig1 = plt.figure()ax = Axes3D(fig1)plt.ion()  # 将画图模式改为交互模式,程序遇到plt.show不会暂停,而是继续执行plot_3d(ax)fitness_list=[]pop = np.random.randint(2, size=(POP_SIZE, CHOROMOSOME_SIZE * NUM_PARAMETER))for _ in range(N_GENERATIONS):  # 迭代N代x, y = decoder(pop)if 'sca' in locals():sca.remove()   # 去除图像中上一个种群的点sca = ax.scatter(x, y, F(x, y), c='black', marker='o')plt.show()plt.pause(0.1)pop = np.array(crossover_and_mutation(pop))fitness = get_fitness(pop)pop = select(pop, fitness)print_info(pop)plt.ioff()plot_3d(ax)

求最大:

求最小:

7 遗传算法特点

(1)遗传算法是对参数的编码进行操作,而非对参数本身,这就是使得我们在优化计算过程中可以借鉴生物学中染色体和基因等概念,模仿自然界中生物的遗传和进化等机理。

(2)遗传算法同时使用多个搜索点的搜索信息。传统的优化方法往往是从解空间的一个初始点开始最优解的迭代搜索过程,单个搜索点所提供的信息不多,搜索效率不高,有时甚至使搜索过程局限于局部最优解而停滞不前。遗传算法从由很多个体组成的一个初始群体开始最优解的搜索过程,而不是从一个单一的个体开始搜索,这是遗传算法所特有的一种隐含并行性,因此遗传算法的搜索效率较高

(3)遗传算法直接以目标函数作为搜索信息。传统的优化算法不仅需要利用目标函数值,而且需要目标函数的导数值等辅助信息才能确定搜索方向。而遗传算法仅使用由目标函数值变换来的适应度函数值,就可以确定进一步的搜索方向和搜索范围,无须目标函数的导数值等其他一些辅助信息。因此,遗传算法可应用于目标函数无法求导数或导数不存在的函数的优化问题,以及组合优化问题等。而且,直接利用目标函数值或个体适应度,也可将搜索范围集中到适应度较高的部分搜索空间中,从而提高搜索效率。

(4)遗传算法使用概率搜索技术。许多传统的优化算法使用的是确定性搜索算法,一个搜索点到另一个搜索点的转移有确定的转移方法和转移关系,这种确定性的搜索方法有可能使得搜索无法达到最优点,因而限制了算法的使用范围。遗传算法的选择、交叉和变异等运算都是以一种概率的方式来进行的,因而遗传算法的搜索过程具有很好的灵活性。随着进化过程的进行,遗传算法新的群体会更多地产生出许多新的优良的个体。理论已经证明,遗传算法在一定条件下以概率1收敛于问题的最优解。

(5)遗传算法在解空间进行高效启发式搜索,而非盲目地穷举或完全随机搜索。

(6)遗传算法对于待寻优的函数基本无限制,它既不要求函数连续,也不要求函数可微,既可以是数学解析式所表示的显函数,又可以是映射矩阵甚至是神经网络的隐函数,因而应用范围较广。

(7)遗传算法具有并行计算的特点,因而可通过大规模并行计算来提高计算速度,适合大规模复杂问题的优化。

参考:

[1] 《智能控制:理论基础、算法设计与应用作者》 作者:刘金琨

[2] https://blog.csdn.net/ha_ha_ha233/article/details/91364937

十分感谢三连支持!最靓的公式送给最靓的你:

eiπ+1=0e^{i\pi}+1=0eiπ+1=0

【演化(进化)算法】遗传算法原理及python实现相关推荐

  1. java寻优算法_模拟退火算法SA原理及python、java、php、c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径...

    模拟退火算法SA原理及python.java.php.c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径 模拟退火算法(Simulated Annealing,SA)最早的思 ...

  2. “好串”求解算法优化原理与Python实现

    佩服国防科大刘万伟老师的数学功底与编码功底,感谢刘老师无私分享! =====正文======= 题目要求:称一个 0-1 串是"好串",如果它的任何子串不在其中连续出现三次以上.编 ...

  3. 文本相似度bm25算法的原理以及Python实现(jupyter notebook)

    今天我们一起来学习一下自然语言处理中的bm25算法,bm25算法是常见的用来计算query和文章相关度的相似度的.其实这个算法的原理很简单,就是将需要计算的query分词成w1,w2,-,wn,然后求 ...

  4. 进化算法--遗传算法

    遗传算法 一.能解决的问题 如果你需要解决一个问题,这个问题的每一个可能的解均可以用位串来表示,那么遗传算法就能解决这个问题. 二.术语 个体:每一个可能的解: 种群:一群个体: 基因:个体中位的一个 ...

  5. 差分进化算法原理及优化应用

    目录 一.差分进化(differential evolution)算法的起源 二.差分进化算法的原理 (一)初始化参数

  6. 算法代码_Python进化算法之多目标优化与代码实战

    前言 自从上三篇博客详细讲解了Python遗传和进化算法工具箱及其在带约束的单目标函数值优化中的应用.利用遗传算法求解有向图的最短路径.利用进化算法优化SVM参数之后,这篇不再局限于单一的进化算法工具 ...

  7. matlab 思维进化算法,差分进化算法介绍及matlab实现

    引言 差分进化算法是基于群体智能理论的优化算法,是通过群体内个体间的合作与竞争而产生的智能优化搜索算法,它保留了基于种群的全局搜索策略,采用实数编码.基于差分的简单变异操作和"一对一&quo ...

  8. 超级计算机进化算法,差分进化算法的并行实现

    摘要: 传统的并行计算任务往往由大型的并行计算机来完成,因而并行机的研究也就成为并行计算的主要研究方向.随着经济和科技的发展,生物医学.天气预报.高能物理等领域的计算任务越来越多,其特点是计算数据多. ...

  9. 线性回归原理与python实现

    机器学习 第一章:机器学习基础 第二章:线性回归 第三章:逻辑回归 第四章:BP 神经网络 第五章:卷积神经网络 第六章:循环神经网络 第七章:决策树与随机森林 第八章:支持向量机 第九章:隐马尔科夫 ...

  10. Python遗传算法库和进化算法框架(二)Geatpy库函数和数据结构

    (转载自https://blog.csdn.net/qq_33353186/article/details/82020507) 上一篇讲了Geatpy的快速入门:https://blog.csdn.n ...

最新文章

  1. MPB:南土所褚海燕组-​非靶标代谢组测定土壤可提取有机碳组分
  2. SQL语言学习(五)流程控制函数学习
  3. Python只需要三十行代码,打造一款简单的人工语音对话
  4. java实现无序数组结构
  5. ac ap原理、_AP面板是什么?家庭AC+AP的组网方式,真的适合所有人吗?
  6. 配置docker阿里云镜像加速
  7. C++经典书籍推荐 .
  8. JVM相关知识——内存分布和垃圾回收机制
  9. U盘装系统工具哪个好用?
  10. 好程序员大数据培训之Hadoop常见问题
  11. 俄罗斯一法院对谷歌处以72亿卢布罚款
  12. Django-bootstrap3插件搭建Django+Bootstrap网站
  13. 解决 primordials is not defined 问题
  14. 自己做语料——Python爬取新闻联播文字版
  15. python验证身份证最后一位数字代表什么_身份证最后一位不是数字而是X,代表什么含义?涨知识了...
  16. Android端如何简单的防黑产
  17. Java生成验证码源代码
  18. 日语:假定形、可能形、被动形、使役形、使役被动形
  19. 连线游戏Game of Lines
  20. 基于java Springboot实现教务管理系统《视频版-建议收藏》

热门文章

  1. c#和明华RF-35LT开发通信
  2. Educoder jQuery 入门
  3. 读JavaScript高级程序设计感受
  4. 计算机视觉 CS231n Course Introduction
  5. javaweb项目的文件结构
  6. 《Linux启动过程分析》内核挂载根文件系统
  7. 硬件课程设计:步进电机控制系统
  8. Altium Designer 10.0 使用教程
  9. ISO50001认证辅导,ISO50001能源管理体系的框架审核通过系统的提高能源效率和消耗
  10. FreeRTOS 教程指南 学习笔记 第六章 中断管理(二)