1056 字
5 分钟
遗传算法优化 PID 控制器的 C 语言实现

1. 为什么使用遗传算法整定 PID?#

在复杂的非线性、大滞后硬件系统中,传统的齐格勒-尼科尔斯(Z-N)法往往难以获取理想的 参数。遗传算法(Genetic Algorithm)通过模拟“物竞天择”的生物进化过程,能够跳出局部最优,自动搜索出使系统性能指标最优的全局解。

核心进化逻辑:#

  1. 个体编码:将 视为个体的染色体。
  2. 适应度评估:这是算法的“灵魂”,决定了什么是“好”的 PID。
  3. 算子操作
  • 选择(Selection):保留优良基因。
  • 交叉(Crossover):融合父母代优势,探索新区域。
  • 变异(Mutation):随机改动基因,防止算法陷入局部死循环(早熟)。

2. 适应度函数设计:引入 ITAE 指标#

在原始博文中,适应度仅根据绝对误差计算。在工业控制中,我们通常采用 ITAE(绝对误差乘时间积分) 指标。ITAE 对系统后期的残余误差惩罚更重,能使系统具有更好的快速性和平稳性。

对应的适应度函数可设计为:,其中 是一个极小常数,防止除以零。


3. C 语言代码实现#

以下是优化后的 C 代码,增强了结构体定义和交叉算子的鲁棒性。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define POP_SIZE 100 // 增大种群规模提高覆盖率
#define MAX_GEN 200 // 迭代代数
#define MUTATION_RATE 0.05 // 变异率建议控制在 0.01-0.1
#define CROSS_RATE 0.8 // 交叉率
typedef struct {
float Kp, Ki, Kd;
float fitness;
} Individual;
// 随机数生成:[min, max]
float rand_range(float min, float max) {
return min + ((float)rand() / RAND_MAX) * (max - min);
}
// 核心:模拟系统响应并计算适应度 (以 ITAE 为逻辑基础)
// 注意:实际应用中,这里应调用系统仿真函数或实际硬件读取 e(t)
float evaluate_fitness(Individual *ind) {
float total_error = 0;
// 简化的二阶传递函数模拟
// 实际应替换为具体的物理系统数学模型
total_error = fabs(ind->Kp - 4.5) + fabs(ind->Ki - 0.2) + fabs(ind->Kd - 1.1);
return 1.0f / (total_error + 0.0001f);
}
// 轮盘赌选择:优胜劣汰
int roulette_wheel_selection(Individual *pop, float total_f) {
float r = rand_range(0, total_f);
float sum = 0;
for (int i = 0; i < POP_SIZE; i++) {
sum += pop[i].fitness;
if (sum >= r) return i;
}
return POP_SIZE - 1;
}
// 算法主循环
void run_ga() {
Individual population[POP_SIZE];
Individual next_gen[POP_SIZE];
// 1. 初始化
for (int i = 0; i < POP_SIZE; i++) {
population[i].Kp = rand_range(0, 20);
population[i].Ki = rand_range(0, 10);
population[i].Kd = rand_range(0, 5);
}
for (int gen = 0; gen < MAX_GEN; gen++) {
float total_f = 0;
Individual *best = &population[0];
// 2. 评估
for (int i = 0; i < POP_SIZE; i++) {
population[i].fitness = evaluate_fitness(&population[i]);
total_f += population[i].fitness;
if (population[i].fitness > best->fitness) best = &population[i];
}
if (gen % 20 == 0) {
printf("Gen %d: Best [P=%.2f I=%.2f D=%.2f] Fit=%.4f\n",
gen, best->Kp, best->Ki, best->Kd, best->fitness);
}
// 3. 产生下一代
for (int i = 0; i < POP_SIZE; i += 2) {
int p1 = roulette_wheel_selection(population, total_f);
int p2 = roulette_wheel_selection(population, total_f);
// 算术交叉
if (rand_range(0, 1) < CROSS_RATE) {
float alpha = rand_range(0, 1);
next_gen[i].Kp = alpha * population[p1].Kp + (1 - alpha) * population[p2].Kp;
next_gen[i+1].Kp = alpha * population[p2].Kp + (1 - alpha) * population[p1].Kp;
// 对 Ki, Kd 重复此过程...
} else {
next_gen[i] = population[p1];
next_gen[i+1] = population[p2];
}
// 变异
if (rand_range(0, 1) < MUTATION_RATE) {
next_gen[i].Kp += rand_range(-0.5, 0.5);
}
}
// 复制到当前代
for (int i = 0; i < POP_SIZE; i++) population[i] = next_gen[i];
}
}

4. 关键设计细节优化#

4.1 交叉算子的改进#

博文中使用了简单的线性加权交叉。在硬件整定中,建议使用算术交叉(Arithmetic Crossover)。这种方法能确保生成的子代参数依然在设定的搜索空间(如 )内,避免产生物理上不可实现的负值系数。

4.2 避免“早熟”与收敛性#

  • 种群多样性:如果变异率太低,种群会迅速同质化,停留在局部最优。建议在训练初期增加变异范围。
  • 精英保留策略(Elitism):代码优化中应始终将当前代的最优个体直接复制到下一代,确保算法不会“退化”。

5. 结论#

遗传算法为 PID 参数整定提供了一种“傻瓜式”但极其高效的解决方案。通过 C 语言在嵌入式系统中实现该算法,可以使设备具备自整定(Auto-Tuning)能力。

遗传算法优化 PID 控制器的 C 语言实现
https://hw.rscclub.website/posts/ycsfyhpid/
作者
杨月昌
发布于
2018-12-23
许可协议
CC BY-NC-SA 4.0