基于遗传算法的PID参数整定研究
1.3.1遗传算法的介绍
遗传算法最早是由J.hollland教授提出的一种将编码技术等同于染色体基因的方法,与以往常规的优化算法比较,首先遗传算法是对所需要优化参数的编码,并在有限解集空间中进行启发式搜索。其次遗传算法是利用目标函数进行计算适应度,而且遗传算法的寻优规则是由概率决定的。最后遗传算法的计算简单,功能强,更加适合大规模复杂问题的优化。以上这些优点使遗传算法在许多领域如参数整定、目标寻优、路径规划等实际问题中得到广泛应用。
遗传算法的基本操作:根据待寻优问题的目标函数,构造一个适应度函数。然后生成初始种群,对种群进行评价、交叉、变异、选择等操作。并通过数次进化,得到适应度最高的个体作为问题的最优解。如图8所示。
图8 遗传算法的基本操作
1.3.2遗传算法的案例
如果直接应用以下代码可得:最大坐标值为(7.8567, 24.8554)。
%%直接代入求解
x = 0: 0.0001: 9;
y = x + 10*sin(5*x) + 7*cos(4*x);
[maxY, index] = max(y)
maxX = x(index)
应用遗传算法进行求解,其关键的是编码和解码。本次采取的是二进制编码,求解步骤如下:
遗传算法的步骤
1. 种群初始化
2. 计算每个种群的适应度值
3. 选择(Selection)
4. 交叉(Crossover)
5. 变异(Mutation)
6. 重复2-5步直至达到进化次数。
主函数 main.m
%% I. 清空变量
clc;
clear all
%% II. 自定义参数初始化
popSize = 100; % 种群大小
cLength = 17; % 染色体的长度
generationTime = 200; % 进化次数
crossRate = 0.6; % 交叉概率
mutateRate = 0.01; % 变异概率
fitnessAverageValueMatrix = zeros(generationTime, 1); % 每代平均适应度值
fitnessBestValueMatrix = zeros(generationTime, 1); % 每代最优适应度值
bestIndividual = zeros(generationTime, 1); % 每代最佳自变量(十进制)
%% III. 初始化种群
popMat = popInit(popSize, cLength);
%% IV. 迭代繁衍获取更好的个体(选择、交叉、变异)
for currentTime = 1: generationTime
% 求适应度值,返回适应度值矩阵
fitnessValueMatrix = getfitnessValue(popMat);
% 记录当前最好的数据
if currentTime == 1
[bestValue, bestIndex] = max(fitnessValueMatrix);
fitnessBestValueMatrix(currentTime) = bestValue;
fitnessAverageValueMatrix(currentTime) = mean(fitnessValueMatrix);
else
[bestValue, bestIndex] = max(fitnessValueMatrix);
fitnessBestValueMatrix(currentTime) = max(fitnessBestValueMatrix(currentTime - 1), bestValue);
fitnessAverageValueMatrix(currentTime) = mean(fitnessValueMatrix);
end
bestChromosome = popMat(bestIndex, :); % 最佳适应度值所对应的个体(二进制)
bestIndividual(currentTime) = getdecimalValue(bestChromosome); % 解码,将二进制转为十进制,得到最佳适应度值所对应的x值
if currentTime ~= generationTime
% 选择
popMat = selection(fitnessValueMatrix, popMat, 1); % 保留亲代最佳个体
% 交叉
popMat = crossover(popMat, 1, crossRate); % 单点交叉
% 变异
popMat = mutation(popMat, mutateRate);
end
end
%% V. 画图并展示结果
% 画图
plotGraph(fitnessAverageValueMatrix, generationTime);
% 展示数据
[fitnessBestValue, index] = max(fitnessBestValueMatrix);
disp 最优适应度
fitnessBestValue
disp 最优个体对应的自变量值
bestIndividual(index)
第一步开始编写我们的代码,为了让我们的遗传算法的代码具有更好的包容性,即针对不同的题目,我们不想每一次都要大幅度的重写我们的代码,因此,我们希望能够把一些步骤的功能编写成函数,这样针对不同的题目,我们只需要调用我们事先编写好的函数,输入不同的参数和数据即可。
种群初始化函数popInit(),能根据提供的种群大小和染色体长度产生初始的种群。代码如下:
%% [name] -- popInit(种群初始化函数)
% [function]-- 构建种群矩阵,其中行数为种群个数,列数为染色体长度(即基因的个数),并随机分配染色体的样式
% [input] -- 1. popSize(种群大小/个数)
% -- 2. cLength(染色体长度)
% [output] -- popMat(种群矩阵)
function popMat = popInit(popSize, cLength) % 种群大小100,cLength(染色体长度)
% popSize = 100;
% cLength = 17;
popMat = zeros(popSize, cLength); % 预分配内存17
for i = 1: popSize
for j = 1: cLength
popMat(i, j) = round(rand); % rand产生(0,1)之间的随机数,round()是四舍五入函数,四舍五入取整
% 随机数100x17d的0和1矩阵
end
end
clear i;
clear j;
end
计算每个种群的适应度值函数getfitnessValue(),这个函数,针对不同的题目有不同的适应度值,因此,对于不同的题目,这个函数需要更改。在基于本章的题目中,该函数可以实现对种群的适应度值的计算。代码如下:
%% [name] -- getfitnessValue(计算种群个体适应度值)
% [function]-- 根据不同的题目要求,设计适应度方程计算的算法,本例中,适应度函数为f(x) = x + 10*sin(5*x) + 7*cos(4*x),x ∈[0, 9],其解码规则为:
% x = lower_bound + decimal(chromosome) * (upper_bound - lower_bound) / (2 ^ chromosome_length - 1)
% [input] -- popMat (种群矩阵)
% [output] -- fitValMat(每个个体的适应度值)
function fitnessValueMatrix = getfitnessValue(popMat)
[popSize, cLength] = size(popMat); % 获取种群的数目(行)和染色体长度(列)
fitnessValueMatrix = zeros(popSize, 1); % 初始化适应度矩阵
X = zeros(popSize, 1); % 预分配内存
lower_bound = 0; % 自变量区间下限,在定义域内的函数f(x)的最大值
upper_bound = 9; % 自变量区间上限
% 1. 首先先将种群中个体的染色体所对应的十进制求出来
for i = 1: popSize
for j = 1: cLength
if popMat(i, j) == 1
X(i) = X(i) + 2 ^ (j - 1);
end
end
% 2. 根据十进制值进行解码
X(i) = lower_bound + X(i) * (upper_bound - lower_bound) / (2 ^ cLength - 1);
% 3. 获取适应度值,目标函数作为适应度函数
fitnessValueMatrix(i) = X(i) + 10 * sin(5 * X(i)) + 7 * cos(4 * X(i));
end
clear i;
clear j;
end
选择函数selection(),可以对种群进行选择,具体算法和代码如下:
%% [name] -- selection(选择操作)
% [function]-- 采用轮盘赌的一个形式来进行选择,增加不确定性,这样种群就不容易趋向局部最优
% [input] -- 1. fitnessValueMatrix (适应度值矩阵)
% -- 2. popMat(未选择的种群矩阵)
% -- 3. type -- 1: 保留亲代最优个体
% 0: 不保留亲代最优个体
% [output] -- updatedPopMat(经选择后的种群矩阵)
function updatedPopMat = selection(fitnessValueMatrix, popMat, type)
[popSize, cLength] = size(popMat);
updatedPopMat = zeros(popSize, cLength);
% 剔除适应值为负的种群,使其适应值为0
for i = 1: popSize
if fitnessValueMatrix(i, 1) < 0
fitnessValueMatrix(i, 1) = 0;
end
end
% 轮盘赌算法
P = fitnessValueMatrix / sum(fitnessValueMatrix);
Pc = cumsum(P);
for i = 1: popSize
index = find(Pc >= rand);
updatedPopMat(i, :) = popMat(index(1), :);
end
% 是否要保留亲本适应度值最高的,若是,则type = 1,否则为0
if type
[~, bestIndex] = max(fitnessValueMatrix);
updatedPopMat(popSize, :) = popMat(bestIndex, :); % 将亲代最优染色体放到子代的最后一个个体中
end
clear i;
clear j;
end
交叉函数crossover(),可以对种群进行交叉,交叉的方式又分为单点交叉和多点交叉,根据输入不同的参数来选择不同的实现方式,具体算法和代码如下:
%% [name] -- crossover(交叉操作)
% [function]-- 选定交叉点并进行互换
% [input] -- 1. popMat (未交叉的种群矩阵)
% -- 2. type -- 1: 单点交叉
% -- 2: 多点交叉
% -- 3. crossrate (交叉率) -- 建议值为0.6
% [output] -- updatedPopMat(经交叉后的种群矩阵)
function updatedPopMat = crossover(popMat, type, crossrate)
[popSize, cLength] = size(popMat);
if type == 1
% 单点交叉
for i = 1: 2: popSize
if crossrate >= rand
crossPosition = round(rand() * (cLength - 2) + 2); % 随机获取交叉点,去除0和1
% 对 crossPosition及之后的二进制串进行交换
for j = crossPosition: cLength
temp = popMat(i, j);
popMat(i, j) = popMat(i + 1, j);
popMat(i + 1, j) = temp;
end
end
end
updatedPopMat = popMat;
elseif type == 2
% 多点交叉
for i = 1: 2: popSize
if crossrate >= rand
crossPosition1 = round(rand() * (cLength - 2) + 2); % 第一个交叉点
crossPosition2 = round(rand() * (cLength - 2) + 2); % 第二个交叉点
first = min(crossPosition1, crossPosition2);
last = max(crossPosition1, crossPosition2);
for j = first: last
temp = popMat(i, j);
popMat(i, j) = popMat(i + 1, j);
popMat(i + 1, j) = temp;
end
end
end
updatedPopMat = popMat;
else
h = errordlg('type的类型只能为1(单点交叉)或者2(多点交叉)', '进行交叉时发生错误');
end
clear i;
clear j;
end
变异函数mutation(),可以对种群进行变异,具体算法和代码如下:
%% [name] -- mutation(变异操作)
% [function]-- 单点变异:随机选择变异点,将其变为0或1
% [input] -- 1. popMat (未交叉的种群矩阵)
% -- 2. mutateRate (交叉率) -- 建议值为0.1
% [output] -- updatedPopMat(经交叉后的种群矩阵)
function updatedPopMat = mutation(popMat, mutateRate)
[popSize, cLength] = size(popMat);
for i = 1: popSize
if mutateRate >= rand
mutatePosition = round(rand() * (cLength - 1) + 1); % 随机获取交叉点,去除0
% 对mutatePosition点进行变异
popMat(i, mutatePosition) = 1 - popMat(i, mutatePosition);
end
end
updatedPopMat = popMat;
clear i;
clear j;
end
另外解码,针对不同的优化对象,我们需要将二进制的染色体与题目十进制的自变量x值关联起来,因此,我们需要编写一个**函数getDecimalValue()**来实现这样的工作。代码如下:
%% [name] -- getDecimalValue(根据染色体个体(二进制)算出所对应的x值(十进制))
% [function]-- 根据不同的题目要求,设计适应度方程计算的算法,本例中,适应度函数为f(x) = x + 10*sin(5*x) + 7*cos(4*x),x ∈[0, 9],其解码规则为:
% x = lower_bound + decimal(chromosome) * (upper_bound - lower_bound) / (2 ^ chromosome_length - 1)
% [input] -- chromosome (染色体)
% [output] -- decimalValue(每个个体的物理意义值)
function decimalValue = getdecimalValue(chromosome)
decimalValue = 0.0; % 初始化
cLength = size(chromosome, 2); % 获取种群的数目(行)和染色体长度(列)
lower_bound = 0; % 自变量区间下限
upper_bound = 9; % 自变量区间上限
% 1. 首先先将种群中个体的染色体所对应的十进制求出来
for i = 1: cLength
if chromosome(1, i) == 1
decimalValue = decimalValue + 2 ^ (i - 1);
end
end
% 2. 解码
decimalValue = lower_bound + decimalValue * (upper_bound - lower_bound) / (2 ^ cLength - 1);
clear i;
clear j;
end
编写了一个画图的函数,用于直观的显示在进化的过程中,种群平均适应度值的变化。代码如下:
%% [name] -- plotGraph(画图)
% [function]-- 直观的展示在进化过程中,平均适应度值的趋势变化
% [input] -- 1. generationTime(进化次数)
% 2. fitnessAverageValueMatrix(平均适应度值矩阵)
% [output] -- none
function plotGraph(fitnessAverageValueMatrix, generationTime)
x = 1: 1: generationTime;
y = fitnessAverageValueMatrix;
plot(x, y);
xlabel('迭代次数');
ylabel('平均函数最大值');
title('种群平均函数最大值的进化曲线');
end
结果输出图如下:
最终运行结果为:
参考文献
[1]https://www.cnblogs.com/Qling/p/9505380.html