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

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

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

核心进化逻辑:#

  1. 个体编码:将 (Kp, Ki, Kd) 三个参数视为个体的染色体,通常采用实数编码。
  2. 适应度评估:这是算法的“灵魂”,决定了什么是“好”的 PID 参数组合。
  3. 算子操作
    • 选择(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/
作者
杨月昌
发布于
2018-12-23
许可协议
CC BY-NC-SA 4.0