1362 字
7 分钟
遗传算法优化 PID 控制器的 C 语言实现
1. 为什么使用遗传算法整定 PID?
在复杂的非线性、大滞后硬件系统中,传统的齐格勒-尼科尔斯(Z-N)法往往难以获取理想的参数。遗传算法(Genetic Algorithm)通过模拟“物竞天择”的生物进化过程,能够有效跳出局部最优,自动搜索出使系统性能指标最优的全局解。
核心进化逻辑:
- 个体编码:将 (Kp, Ki, Kd) 三个参数视为个体的染色体,通常采用实数编码。
- 适应度评估:这是算法的“灵魂”,决定了什么是“好”的 PID 参数组合。
- 算子操作:
- 选择(Selection):保留优良基因。
- 交叉(Crossover):融合父母代优势,探索新搜索区域。
- 变异(Mutation):引入随机扰动,维持种群多样性,防止早熟收敛。
2. 适应度函数设计:引入 ITAE 指标
在原始博文中,适应度仅根据绝对误差计算。在工业控制领域,我们推荐采用 ITAE(时间乘绝对误差积分) 指标。ITAE 对系统后期的残余误差惩罚更重,有助于获得更好的快速性、无超调和平稳性。
对应的适应度函数可设计为: [ fitness = \frac{1}{ITAE + \epsilon} ] 其中 ( ITAE = \int_0^{t_{sim}} t |e(t)| , dt ),(\epsilon) 是一个很小的正数(如 0.0001),用于防止除以零。
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);}
// 参数钳位,防止出现物理上不可实现的负值void clamp_params(Individual *ind) { if (ind->Kp < 0.0f) ind->Kp = 0.0f; if (ind->Ki < 0.0f) ind->Ki = 0.0f; if (ind->Kd < 0.0f) ind->Kd = 0.0f;}
// 核心:模拟系统响应并计算适应度 (基于 ITAE)// 注意:在实际硬件/嵌入式应用中,此函数应替换为真实的系统仿真或实时误差采集// 示例中用简单距离演示,实际项目中必须替换float evaluate_fitness(Individual *ind) { // TODO: 这里实现实际的系统响应仿真,计算 e(t) = r(t) - y(t) // 以下为演示逻辑,实际项目中必须替换 float total_error = fabs(ind->Kp - 4.5f) + fabs(ind->Ki - 0.2f) + fabs(ind->Kd - 1.1f); 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];
srand(time(NULL)); // 初始化随机种子
// 1. 初始化种群 for (int i = 0; i < POP_SIZE; i++) { population[i].Kp = rand_range(0.1f, 20.0f); // 给予合理初始范围 population[i].Ki = rand_range(0.0f, 10.0f); population[i].Kd = rand_range(0.0f, 5.0f); clamp_params(&population[i]); }
for (int gen = 0; gen < MAX_GEN; gen++) { float total_f = 0.0f; int best_idx = 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 > population[best_idx].fitness) { best_idx = i; } }
if (gen % 20 == 0 || gen == MAX_GEN - 1) { Individual *best = &population[best_idx]; printf("Gen %d: Best [P=%.3f I=%.3f D=%.3f] Fit=%.4f\n", gen, best->Kp, best->Ki, best->Kd, best->fitness); }
// 3. 精英保留策略 next_gen[0] = population[best_idx];
// 4. 产生下一代 for (int i = 1; i < POP_SIZE; i += 2) { int p1 = roulette_wheel_selection(population, total_f); int p2 = roulette_wheel_selection(population, total_f);
Individual child1 = population[p1]; Individual child2 = population[p2];
// 算术交叉 if (rand_range(0, 1) < CROSS_RATE) { float alpha = rand_range(0, 1); child1.Kp = alpha * population[p1].Kp + (1 - alpha) * population[p2].Kp; child1.Ki = alpha * population[p1].Ki + (1 - alpha) * population[p2].Ki; child1.Kd = alpha * population[p1].Kd + (1 - alpha) * population[p2].Kd;
child2.Kp = alpha * population[p2].Kp + (1 - alpha) * population[p1].Kp; child2.Ki = alpha * population[p2].Ki + (1 - alpha) * population[p1].Ki; child2.Kd = alpha * population[p2].Kd + (1 - alpha) * population[p1].Kd; }
// 变异 if (rand_range(0, 1) < MUTATION_RATE) { child1.Kp += rand_range(-1.0f, 1.0f); child1.Ki += rand_range(-0.5f, 0.5f); child1.Kd += rand_range(-0.3f, 0.3f); } if (rand_range(0, 1) < MUTATION_RATE) { child2.Kp += rand_range(-1.0f, 1.0f); child2.Ki += rand_range(-0.5f, 0.5f); child2.Kd += rand_range(-0.3f, 0.3f); }
clamp_params(&child1); clamp_params(&child2);
next_gen[i] = child1; if (i + 1 < POP_SIZE) next_gen[i + 1] = child2; }
// 复制下一代到当前种群 for (int i = 0; i < POP_SIZE; i++) { population[i] = next_gen[i]; } }}
int main() { run_ga(); return 0;}4. 关键设计细节优化
4.1 交叉算子的改进
原博文中交叉实现不完整。在硬件参数整定中,推荐使用算术交叉(Arithmetic Crossover)。这种方法生成的子代参数自然处于父母参数之间,能较好地保持在合理搜索空间内。
4.2 避免“早熟”与收敛性
- 参数边界约束:通过钳位函数确保 Kp, Ki, Kd 不出现负值。
- 精英保留策略(Elitism):始终将当代最优个体直接复制到下一代,防止算法退化。
- 种群多样性:合理设置变异率,并在嵌入式系统中可考虑自适应变异。
- 计算资源考虑:在资源受限的MCU上,可适当减小 POP_SIZE 和 MAX_GEN,或采用定点数运算。
5. 结论
遗传算法为 PID 参数整定提供了一种鲁棒、高效的“自适应”解决方案。通过在嵌入式系统中用 C 语言实现该算法,可以让硬件设备具备真正的自整定(Auto-Tuning) 能力,大幅降低人工调试成本,提升控制性能。
遗传算法优化 PID 控制器的 C 语言实现
https://hw.rscclub.website/posts/ycsfyhpid/