0.背景
最近需要用将PID算法移植到FPGA中,对PID算法进行了一个浅浅的学习。看了网上了很多文章,对PID算法的解释都很完美,通俗易懂,也有很多用MATLAB展现出PID算法之美。但是只是显示了PID算法两个位置之间的输出变化,大部分只用了一组PID参数。而现实中的控制,对系统的输出是灵活的,也就是说系统的目标值输出是可控的,这里让我刚开始对PID算法的应用产生了一些困惑,不能很好的理解PID在控制中的具体应用场景。
1.对网上现有的PID程序进行改进
通过对晚上现有的一些代码进行分析,然后利用matlab写出了PID在多个目标值的输出过程,只有一组PID参数,代码如下:
clc
clear all;
close all;
% PID控制器参数
Kp = 1; % 比例增益
%Ki = 0; % 积分增益
Ki = 0.5; % 积分增益
Kd = 0.1; % 微分增益
% 设定目标值和时间段
setpoints = [1000, 500, 1500];
durations = [80, 50, 50];
% 初始化变量
target = setpoints(1);
output = 0;
integral = 0;
prev_error = 0;
% 控制时间步长和总时间
dt = 1;
total_time = sum(durations);
% 记录时间、输出值和误差
time = 0;
time_history = [];
output_history = [];
error_history = [];
integral_hisory=[];
control_history=[];
t_history=[];
% PID控制循环
for i = 1:numel(setpoints)
duration = durations(i);
for t = 1:(duration/dt)
% 计算误差和PID控制量
error = target - output;
integral = integral + error * dt;
derivative = (error - prev_error) / dt;
control = Kp * error + Ki * integral + Kd * derivative;
% 更新输出值
output = output + control * dt;
% 记录时间、输出值和误差
time = time + dt;
time_history = [time_history, time];
output_history = [output_history, output];
error_history = [error_history, error];
integral_hisory=[integral_hisory,integral];
control_history=[control_history,control];
t_history=[t_history,t];
% 稳定后更新目标值
%%{
% if abs(output - target) < 1e-2 && t*dt >= 1
if abs(output - target) < 1e-2 && t*dt >= 1
target = setpoints(min(i+1, numel(setpoints)));
integral = 0; % 重置积分项
end
%%}
% 更新上一个时间步的误差
prev_error = error;
end
end
% 绘制曲线图
figure;
hold on;
plot(time_history, output_history, 'LineWidth', 2);
plot(time_history, error_history, 'LineWidth', 2);
plot(time_history, integral_hisory, 'LineWidth', 2);
plot(time_history, control_history, 'LineWidth', 2);
%plot(time_history, t_history, 'LineWidth', 2);
hold off;
xlabel('时间 (秒)');
ylabel('数值');
title('PID控制器输出曲线');
legend('输出', '误差','积分','控制');
grid on;
%{
% 绘制曲线图
figure(1);
%subplot(2, 1, 1);
plot(time_history, output_history);
xlabel('时间 (秒)');
ylabel('输出');
title('PID控制器输出曲线');
grid on;
figure(2);
%subplot(2, 1, 2);
plot(time_history, error_history);
xlabel('时间 (秒)');
ylabel('误差');
title('PID控制器误差曲线');
grid on;
figure(3);
plot(time_history, integral_hisory);
xlabel('时间 (秒)');
ylabel('积分项');
title('PID控制器积分项曲线');
grid on;
figure(4);
plot(time_history, control_history);
xlabel('时间 (秒)');
ylabel('控制项');
title('PID控制器控制项曲线');
grid on;
%}
该程序主要展示系统输出曲线、误差曲线、积分项曲线以及PID输出曲线,可以改变参数,从而改变PID输出波形,对系统的调参具有一定的积极意义。令参数Ki或者Kd为0,即可观察PD控制或者PI控制的输出波形。上述代码的运行结果如下图所示:
具有多组PID参数的matlab代码如下,
% 设定从小到大和从大到小的PID系数
Kp_increase = 1; % 从小到大的比例增益
Ki_increase = 0.5; % 从小到大的积分增益
Kd_increase = 0.1; % 从小到大的微分增益
Kp_decrease = 0.5; % 从大到小的比例增益
Ki_decrease = 0.2; % 从大到小的积分增益
Kd_decrease = 0.05; % 从大到小的微分增益
% 设定目标值和时间段
setpoints = [1000, 800, 1500];
durations = [100, 200, 500];
% 初始化变量
target = setpoints(1);
output = 0;
integral = 0;
prev_error = 0;
% 控制时间步长和总时间
dt = 0.1;
total_time = sum(durations);
% 记录时间、输出值和误差
time = 0;
time_history = [];
output_history = [];
error_history = [];
% PID控制循环
for i = 1:numel(setpoints)
duration = durations(i);
if i > 1 && setpoints(i) < setpoints(i-1)
% 从大到小的过程,使用不同的PID系数
Kp = Kp_decrease;
Ki = Ki_decrease;
Kd = Kd_decrease;
else
% 从小到大的过程,使用不同的PID系数
Kp = Kp_increase;
Ki = Ki_increase;
Kd = Kd_increase;
end
for t = 1:(duration/dt)
% 计算误差和PID控制量
error = target - output;
integral = integral + error * dt;
derivative = (error - prev_error) / dt;
control = Kp * error + Ki * integral + Kd * derivative;
% 更新输出值
output = output + control * dt;
% 记录时间、输出值和误差
time = time + dt;
time_history = [time_history, time];
output_history = [output_history, output];
error_history = [error_history, error];
% 稳定后更新目标值
if abs(output - target) < 1e-2 && t*dt >= 1
target = setpoints(min(i+1, numel(setpoints)));
integral = 0; % 重置积分项
end
% 更新上一个时间步的误差
prev_error = error;
end
end
% 绘制曲线图
figure;
hold on;
plot(time_history, output_history, 'LineWidth', 2);
plot(time_history, error_history, 'LineWidth', 2);
hold off;
xlabel('时间 (秒)');
ylabel('数值');
title('PID控制器输出和误差曲线');
legend('输出', '误差');
grid on;
上述代码的运行结果如下图所示:
2.FPGA中PID参数处理
FPGA不擅长处理浮点运算,所以我的理解是:PID的参数最好不是小数,那如何让PID参数不是小数呢?我是这样处理的,将计算出来的偏差(目标值与当前值的差值)都缩小,可以采用右移的方式缩小。偏差的正负需要判断一下,如果是正值,不做处理;如果是负值,将右移后的数据乘以-1(width’b1111…111)。然后再进行PID的运算,得到比例项、积分项、微分项的结果。(PS:如果前辈们还有更加好的处理的方法,希望您不吝赐教)
3.结束
在应用PID的算法过程中,可以预留出PID参数接口,利用串口接受数据,改变PID的参数,可以提高系统调试的效率。祝各位可以快速找到系统的最适参数!