This site relates to TCLab kit, developed by John Hedengreen​
http://apmonitor.com/pdc/index.php/Main/ArduinoTemperatureControl
Other code from author's web page
https://apmonitor.com/heat.htm
Main results come from a MSc dissertation at FEUP, by Bruno Aguiar
https://hdl.handle.net/10216/132773 (Full text PDF available!)
By Paulo Moura Oliveira:
PT: https://www.youtube.com/playlist?list=PLHlMBjz_1_oR5FsAfavUbH3Cdodcq6Mwj
EN: https://www.youtube.com/playlist?list=PLHlMBjz_1_oRBGOninzuhftv4aCrMP1zp
Code from MSc Bruno Aguiar, supervised by Paulo Moura Oliveira and Armando Sousa:
%--------------------------------------
% Program by Paulo Moura Oliveira and Bruno Aguiar
% PID control with TCLab
% Jul-2020
%-------------------------------------
disp('Test Heater 1')
disp('LED Indicates Temperature')
figure(1)
T1s = []; % Stores temperature 1 values
T2s = []; % Stores temperature 2 values
Q1s = []; % Stores heater 1 values
Q2s = []; % Stores heater 2 values
SP_T1s = [] % Stores set-point 1 values
% initial heater values
Q1 = 0;
Q2 = 0;
h1(Q1);
h2(Q2);
K = 0.877; %PSO
L = 18.49;
T = 155.9;
Kp = 7.21;
Ti = 90;
Td = 6.06;
fflush (stdout);
Decision = input('Choose your control method: ','s');
% PMO- For storing the elapsed time in each sample
t_plot=[];
sim_time_s=[];
real_time=0;
real_time_s= [];
Decision1 = input('Do you want to configure steps and running time?: ','s');
switch (Decision1)
case {"Y" "y" "yes" "YES" "Yes"}
ns=input('Choose test duration: ');
STP1 =input('Choose when the first step will occur: ');
STPM1=input('Choose the amplitude of the first step: ');
STP2 =input('Choose when the second step will occur: ');
STPM2=input('Choose the amplitude of the second step: ');
STP3 =input('Choose when the third step will occur: ');
STPM3=input('Choose the amplitude of the third step: ');
SP_T1(STP1:STP2-1) = STPM1; % Set-point step 1
SP_T1(STP2:ns) = STPM2; % Set-point step 2
SP_T1(STP3:ns) = STPM3; % Set-point step 3
case {"N" "n" "no" "NO" "No"}
ns=300; % 620 samples for simulation time
SP_T1(1:99) = 60.0; % Set-point step 1
SP_T1(100:ns) = 60.0; % Set-point step 2
%SP_T1(300:ns) = 23.0; % Set-point step 3
otherwise
error("invalid input, try again");
endswitch
Pk=[]; % Proportional Action
Dk=[]; % Derivative Action
Ik=[]; % Integral Action
zk_1 = 0;
Tf=5;
Dk_1=0; % Previous Derivative Action
T1_1=0; % Previous Temperature T1
SP_T1_1 = 0;
ek_1 = 0;
ek_2 = 0;
uk_1 = 0;
d_uk = 0;
T = 1;
T1_1 =1;
for i = 1:ns
tic;
% read temperatures
T1 = T1C(); % Temperature 1
T2 = T2C(); % Temperature 2
function y = f (ek)
y = ek;
endfunction
switch(Decision)
case {"ONOFF" "ON-OFF" "ON OFF" "OnOff" "On-Off" "On Off" "onoff" "on-off" "on off"}
disp('ONOFF')
if T1>SP_T1(i)
Q1 = 0;
else
Q1 = 100;
end
h1(Q1);
case {"ONOFF H" "ON-OFF H" "ON OFF H" "OnOff H" "On-Off H" "On Off H" "onoff H" "on-off H" "on off H""ONOFF h" "ON-OFF h" "ON OFF h" "OnOff h" "On-Off h" "On Off h" "onoff h" "on-off h" "on off h"}
disp('ONOFF H')
if T1-SP_T1(i)>0.5
Q1=0;
else
Q1=Q1;
end
if SP_T1(i)-T1>1
Q1=100;
else
Q1=Q1;
end
h1(Q1);
case {"P" "p" "Proportional" "proportional" "PROPORCIONAL"}
ek = SP_T1(i)-T1; #Error calculation
Pk = Kp*ek; #Proportional contribution
Q1 = Pk; #Output calculation
if Q1>100 #Heater saturation
Q1=100;
end
if Q1<0
Q1=0;
end
h1(Q1); #Heater activation
case {"PI AW" "pi aw" "pI AW" "Pi aw" "Integrative AW" "integrative AW" "INTEGRATIVE AW"}
ek = SP_T1(i)-T1; #Error calculation
Pk = Kp*ek; #Proportional contribution
zk = zk_1+T*ek; #Error integer
Ik = (Kp/Ti)*zk; #Integrative contribution
Q1 = Pk+Ik; #Output calculation
if Q1>100 #Heater saturation
Q1=100;
zk = zk_1; #Error integer reset
end
if Q1<0
Q1=0;
zk = zk_1; #Error integer reset
end
h1(Q1); #Heater activation
zk_1=zk; #Previous error integer
case {"PI" "pi" "pI" "Pi" "Integrative" "integrative" "INTEGRATIVE"}
ek = SP_T1(i)-T1; #Error calculation
Pk = Kp*ek; #Proportional contribution
zk =zk_1+T*ek; #Error integer
Ik = (Kp/Ti)*zk; #Integrative contribution
Q1 = Pk+Ik; #Output calculation
if Q1>100 #Heater saturation
Q1=100;
end
if Q1<0
Q1=0;
end
h1(Q1); #Heater activation
zk_1=zk; #Previous error integer
case {"PD ADK" "pd adk" "pD DK" "Pd adk" "Derivative ADK" "derivative adk" "DERIVATIVE ADK"}
ek = SP_T1(i)-T1; #Error calculation
Pk = Kp*ek; #Proportional contribution
dyk = (T1-T1_1)/T; #Output error derivative
Dk = Kp*Td*dyk; #Derivative contribution
%Dk=(Tf/(Tf+T))*Dk_1 + (T/(Tf+T))*Dk; #Filter
Q1 = Pk-Dk; #Output calculation
if Q1>100 #Heater saturation
Q1=100;
end
if Q1<0
Q1=0;
end
h1(Q1); #Heater activation
T1_1=T1; #Previous sample temperature reading
%Dk_1=Dk; #Previous sample derivative contribution
case {"PD" "pd" "pD" "Pd" "Derivative" "derivative" "DERIVATIVE"}
ek = SP_T1(i)-T1; #Error calculation
Pk = Kp*ek; #Proportional contribution
dek =(ek-ek_1)/T; #Error derivative
Dk = Kp*Td*dek; #Derivative contribution
%Dk=(Tf/(Tf+T))*Dk_1 + (T/(Tf+T))*Dk; #Filter
Q1 = Pk+Dk; #Output calculation
if Q1>100 #Heater saturation
Q1=100;
end
if Q1<0
Q1=0;
end
h1(Q1); #Heater activation
ek_1 = ek; #Previous sample error
%Dk_1=Dk; #Previous sample derivative contribution
case {"PID" "pid" "pID" "PiD" "PId" "pId" "piD" "Pid" "Proportional Integrative and Derivative" "proportional integrative and derivative" "PROPORCIONAL INTEGRATIVE AND DERIVATIVE"}
ek = SP_T1(i)-T1; #Error calculation
Pk = (Kp)*ek; #Proportional contribution
zk = zk_1+T*ek; #Error integer
Ik = (Kp/Ti)*zk; #Integrative contribution
dyk = (T1-T1_1)/T; #Error derivative
Dk = (Kp)*Td*dyk; #Derivative contribution
Dk=(Tf/(Tf+T))*Dk_1 + (T/(Tf+T))*Dk; #Filter
Q1 = Pk+Ik-Dk; #Output calculation
if Q1>100 #Heater saturation
Q1=100;
zk = zk_1; #Error integer reset
end
if Q1<0
Q1=0;
zk = zk_1; #Error integer reset
end
if i>=400 #Load disturbance
Q1=Q1-40;
end
h1(Q1); #Heater activation
ek_1=ek; #Previous sample error
zk_1=zk; #Previous error integer
T1_1=T1; #Previous sample temperature reading
Dk_1=Dk; #Previous sample derivative contribution
otherwise
error("invalid input, try again");
endswitch
% LED brightness
% brightness1 = (t1 - 30)/50.0; % <30degC off, >100degC full brightness
% brightness2 = (t2 - 30)/50.0; % <30degC off, >100degC full brightness
% brightness = max(brightness1,brightness2);
% brightness = max(0,min(1,brightness)); % limit 0-1
% led(brightness);
% plot heater and temperature data
Q1s = [Q1s,Q1];
T1s = [T1s,T1];
% Set-point 1
SP_T1s = [SP_T1s,SP_T1(i)];
n = length(T1s);
time = linspace(0,n+1,n);
time_up=time(n);
clf
subplot(2,1,1)
plot(time,SP_T1s,'b--','LineWidth',1);
hold on
plot(time,T1s,'r-','LineWidth',1);
ylabel('Temperature (degC)')
legend('Set-point T1','Temperature 1','Location','SouthEast')
axis([0 time_up 0 70])
subplot(2,1,2)
plot(time,Q1s,'r.','MarkerSize',8);
plot(time,Q1s,'r-','LineWidth',1);
hold on
ylabel('Heater (0-100%)')
xlabel('Time (sec)')
legend('Heater 1','Location','SouthEast')
axis([0 time_up -5 105])
drawnow;
t = toc;
t_plot(i)=t;
real_time=real_time+t;
real_time_s(i)=real_time;
sim_time_s(i)=i;
pause(max(0.01,1.0-t))
end
disp('Turn off heaters')
h1(0);
h2(0);
% PMO
figure
plot(time,t_plot,'bo')
ylabel('Elapsed time in each sample')
xlabel('Time (sec)')
disp('Heater Test Complete')
% save txt file with data
%data = [sim_time_s',Q1s',Q2s',T1s',T2s',SP_T1s'];
%csvwrite('P_test_1.txt',data);
Armando Sousa - https://fe.up.pt/asousa - https://www.cienciavitae.pt//en/1C17-7D93-4CF3
Paulo Moura Oliveira - https://sites.google.com/site/pbmouraoliveira/